From 3bb7285df626fe816ee805fc3fd1f0de0147094b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Wed, 27 May 2026 19:57:17 +0200 Subject: [PATCH 01/17] Structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 19 +++++++++++++++++++ README.md | 0 common_server/.gitkeep | 0 fast_dds/.gitkeep | 0 safe_dds/.gitkeep | 0 scripts/.gitkeep | 0 6 files changed, 19 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 common_server/.gitkeep create mode 100644 fast_dds/.gitkeep create mode 100644 safe_dds/.gitkeep create mode 100644 scripts/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..185b23e --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Local IDE / agent state +.vscode/ +.claude/settings.local.json +.codex +**/.vscode/ + +# Generic build/install outputs +build/ +install/ +cmake-build-*/ +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +compile_commands.json +*.o +*.a +*.so +*.d +*.dep \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/common_server/.gitkeep b/common_server/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/fast_dds/.gitkeep b/fast_dds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/safe_dds/.gitkeep b/safe_dds/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/.gitkeep b/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 From 702d0009d6b5932e5e0075ad1f0319c1588ae86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 28 May 2026 18:46:15 +0200 Subject: [PATCH 02/17] Major code upload, safe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 10 +- common_server/.gitkeep | 0 common_server/CMakeLists.txt | 75 + common_server/src/PilotServerClient.cpp | 161 + .../src/PilotServerPayloadParser.cpp | 240 ++ .../test/launch_server_common_test.sh | 55 + common_server/test/test_server_common.cpp | 316 ++ idl/common.idl | 44 + idl/edge.idl | 33 + idl/pilot_server.idl | 77 + safe_dds/.gitkeep | 0 safe_dds/edge/CMakeLists.txt | 138 + safe_dds/edge/src/apps/edge_gateway_main.cpp | 11 + safe_dds/edge/src/common/HeaderFactory.cpp | 48 + safe_dds/edge/src/common/RuntimeConfig.cpp | 21 + safe_dds/edge/src/common/TopicNames.cpp | 36 + safe_dds/edge/src/logic/EdgeAdvisor.cpp | 41 + safe_dds/edge/src/nodes/EdgeGatewayNode.cpp | 549 ++++ safe_dds/edge/test/test_edge_integration.cpp | 527 ++++ safe_dds/idl/SafeDDSGenAux.hpp | 2467 +++++++++++++++ safe_dds/idl/SafeDDSGenTypeTraits.hpp | 270 ++ safe_dds/idl/SafeDDSGenUtils.hpp | 225 ++ safe_dds/idl/common.hpp | 1228 ++++++++ safe_dds/idl/edge.hpp | 1250 ++++++++ safe_dds/idl/pilot_server.hpp | 2781 +++++++++++++++++ safe_dds/server/CMakeLists.txt | 149 + safe_dds/server/src/apps/server_main.cpp | 11 + .../src/common/PilotServerPublishHelper.cpp | 35 + safe_dds/server/src/common/RuntimeConfig.cpp | 27 + safe_dds/server/src/common/TopicNames.cpp | 26 + safe_dds/server/src/nodes/ServerNode.cpp | 535 ++++ .../server/test/test_server_integration.cpp | 518 +++ 32 files changed, 11903 insertions(+), 1 deletion(-) delete mode 100644 common_server/.gitkeep create mode 100644 common_server/CMakeLists.txt create mode 100644 common_server/src/PilotServerClient.cpp create mode 100644 common_server/src/PilotServerPayloadParser.cpp create mode 100755 common_server/test/launch_server_common_test.sh create mode 100644 common_server/test/test_server_common.cpp create mode 100644 idl/common.idl create mode 100644 idl/edge.idl create mode 100644 idl/pilot_server.idl delete mode 100644 safe_dds/.gitkeep create mode 100644 safe_dds/edge/CMakeLists.txt create mode 100644 safe_dds/edge/src/apps/edge_gateway_main.cpp create mode 100644 safe_dds/edge/src/common/HeaderFactory.cpp create mode 100644 safe_dds/edge/src/common/RuntimeConfig.cpp create mode 100644 safe_dds/edge/src/common/TopicNames.cpp create mode 100644 safe_dds/edge/src/logic/EdgeAdvisor.cpp create mode 100644 safe_dds/edge/src/nodes/EdgeGatewayNode.cpp create mode 100644 safe_dds/edge/test/test_edge_integration.cpp create mode 100644 safe_dds/idl/SafeDDSGenAux.hpp create mode 100644 safe_dds/idl/SafeDDSGenTypeTraits.hpp create mode 100644 safe_dds/idl/SafeDDSGenUtils.hpp create mode 100644 safe_dds/idl/common.hpp create mode 100644 safe_dds/idl/edge.hpp create mode 100644 safe_dds/idl/pilot_server.hpp create mode 100644 safe_dds/server/CMakeLists.txt create mode 100644 safe_dds/server/src/apps/server_main.cpp create mode 100644 safe_dds/server/src/common/PilotServerPublishHelper.cpp create mode 100644 safe_dds/server/src/common/RuntimeConfig.cpp create mode 100644 safe_dds/server/src/common/TopicNames.cpp create mode 100644 safe_dds/server/src/nodes/ServerNode.cpp create mode 100644 safe_dds/server/test/test_server_integration.cpp diff --git a/.gitignore b/.gitignore index 185b23e..cd4e379 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,12 @@ compile_commands.json *.a *.so *.d -*.dep \ No newline at end of file +*.dep +*build* +*install* +*include* +!*build*.sh + +# Generated QNX artifacts +qnx/targets/*/output/ +scripts/logs/ diff --git a/common_server/.gitkeep b/common_server/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/common_server/CMakeLists.txt b/common_server/CMakeLists.txt new file mode 100644 index 0000000..f7dc8a3 --- /dev/null +++ b/common_server/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_server_common_tests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(CURL REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_SERVER_COMMON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") + +# --------------------------------------------------------------------------- +# GTest +# --------------------------------------------------------------------------- +find_package(GTest QUIET) + +if(NOT GTest_FOUND) + message(STATUS "GTest not found — fetching from source") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + if(NOT TARGET GTest::gtest_main) + add_library(GTest::gtest_main ALIAS gtest_main) + endif() +endif() + +# --------------------------------------------------------------------------- +# Test binary +# --------------------------------------------------------------------------- +add_executable( + test_server_common + src/PilotServerClient.cpp + src/PilotServerPayloadParser.cpp + test/test_server_common.cpp +) + +target_include_directories( + test_server_common + PRIVATE + "${SAFE_EDGE_SERVER_COMMON_INCLUDE_DIR}" +) + +target_compile_options( + test_server_common + PRIVATE + -Wall -Wextra -Wpedantic +) + +target_link_libraries( + test_server_common + PRIVATE + CURL::libcurl + GTest::gtest_main + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +enable_testing() +add_test(NAME server_common COMMAND test_server_common) + +install( + TARGETS test_server_common + RUNTIME DESTINATION bin +) diff --git a/common_server/src/PilotServerClient.cpp b/common_server/src/PilotServerClient.cpp new file mode 100644 index 0000000..e078447 --- /dev/null +++ b/common_server/src/PilotServerClient.cpp @@ -0,0 +1,161 @@ +#include + +#include + +#include +#include +#include + +namespace safe_edge { +namespace server { +namespace common { + +namespace { + +static size_t write_callback(void* ptr, size_t size, size_t nmemb, void* userdata) +{ + static_cast(userdata)->append( + static_cast(ptr), size * nmemb); + return size * nmemb; +} + +} // namespace + +PilotServerClient::PilotServerClient( + const std::string& base_url, + const std::string& ini_path) + : base_url_(base_url) +{ + // curl_global_init / curl_global_cleanup are called once per PilotServerClient instance. + // This is safe because exactly one PilotServerClient exists in the process (owned by ServerNode). + curl_global_init(CURL_GLOBAL_DEFAULT); + ready_ = load_api_key(ini_path); +} + +PilotServerClient::~PilotServerClient() +{ + curl_global_cleanup(); +} + +bool PilotServerClient::load_api_key( + const std::string& ini_path) +{ + std::ifstream file(ini_path); + if (!file.is_open()) + { + std::cerr << "[pilot_client] Cannot open config file: " << ini_path << std::endl; + return false; + } + + bool in_section = false; + std::string line; + while (std::getline(file, line)) + { + // Strip leading whitespace + const size_t start = line.find_first_not_of(" \t\r\n"); + if (start == std::string::npos || line[start] == '#' || line[start] == ';') + { + continue; // blank line or comment + } + const std::string trimmed = line.substr(start); + + if (trimmed == "[pilot_server]") + { + in_section = true; + continue; + } + if (trimmed[0] == '[') + { + in_section = false; // different section + continue; + } + + if (!in_section) + { + continue; + } + + const size_t eq = trimmed.find('='); + if (eq == std::string::npos) + { + continue; + } + + // Trim key + const std::string raw_key = trimmed.substr(0, eq); + const size_t key_end = raw_key.find_last_not_of(" \t"); + const std::string key = (key_end != std::string::npos) + ? raw_key.substr(0, key_end + 1) : raw_key; + + // Trim value + const std::string raw_val = trimmed.substr(eq + 1); + const size_t val_start = raw_val.find_first_not_of(" \t"); + if (val_start == std::string::npos) + { + continue; + } + const size_t val_end = raw_val.find_last_not_of(" \t\r\n"); + const std::string value = raw_val.substr(val_start, val_end - val_start + 1); + + if (key == "api_key") + { + api_key_ = value; + return true; + } + } + + std::cerr << "[pilot_client] api_key not found in [pilot_server] section of " + << ini_path << std::endl; + return false; +} + +std::string PilotServerClient::fetch( + const std::string& endpoint) noexcept +{ + if (!ready_) + { + std::cerr << "[pilot_client] Not ready — check config file" << std::endl; + return {}; + } + + CURL* curl = curl_easy_init(); + if (nullptr == curl) + { + std::cerr << "[pilot_client] curl_easy_init failed" << std::endl; + return {}; + } + + const std::string url = base_url_ + endpoint; + // api_key_ must NOT appear in any log line + const std::string auth = "X-API-KEY: " + api_key_; + struct curl_slist* headers = curl_slist_append(nullptr, auth.c_str()); + + std::string body; + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + + const CURLcode res = curl_easy_perform(curl); + + curl_slist_free_all(headers); + curl_easy_cleanup(curl); + + if (res != CURLE_OK) + { + std::cerr << "[pilot_client] GET " << endpoint + << " failed: " << curl_easy_strerror(res) << std::endl; + return {}; + } + + std::cout << "[pilot_client] GET " << endpoint + << " bytes=" << body.size() << std::endl; + return body; +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/common_server/src/PilotServerPayloadParser.cpp b/common_server/src/PilotServerPayloadParser.cpp new file mode 100644 index 0000000..8ff35ad --- /dev/null +++ b/common_server/src/PilotServerPayloadParser.cpp @@ -0,0 +1,240 @@ +#include + +#include +#include +#include + +// Hand-rolled JSON extractor — no external library, no exceptions. +// Compatible with -fno-exceptions / -fno-rtti. + +namespace safe_edge { +namespace server { +namespace common { + +namespace { + +// Returns the index one past the closing '}' matching the '{' at obj_start, +// or std::string::npos on mismatch. +static size_t find_object_end(const std::string& s, size_t obj_start) +{ + int depth = 0; + bool in_string = false; + bool escape = false; + + for (size_t i = obj_start; i < s.size(); ++i) + { + const char c = s[i]; + if (escape) + { + escape = false; + continue; + } + if (c == '\\' && in_string) + { + escape = true; + continue; + } + if (c == '"') + { + in_string = !in_string; + continue; + } + if (in_string) + { + continue; + } + if (c == '{') + { + ++depth; + } + else if (c == '}') + { + --depth; + if (depth == 0) + { + return i + 1U; + } + } + } + return std::string::npos; +} + +static bool extract_string(const std::string& obj, const char* key, std::string& out) +{ + const std::string token = std::string("\"") + key + "\""; + const size_t kpos = obj.find(token); + if (kpos == std::string::npos) + { + return false; + } + size_t pos = obj.find(':', kpos + token.size()); + if (pos == std::string::npos) + { + return false; + } + pos = obj.find('"', pos + 1U); + if (pos == std::string::npos) + { + return false; + } + const size_t start = pos + 1U; + bool escape = false; + size_t end = start; + while (end < obj.size()) + { + if (escape) + { + escape = false; + } + else if (obj[end] == '\\') + { + escape = true; + } + else if (obj[end] == '"') + { + break; + } + ++end; + } + if (end >= obj.size()) + { + return false; + } + out = obj.substr(start, end - start); + return true; +} + +static bool extract_int(const std::string& obj, const char* key, int& out) +{ + const std::string token = std::string("\"") + key + "\""; + const size_t kpos = obj.find(token); + if (kpos == std::string::npos) + { + return false; + } + size_t pos = obj.find(':', kpos + token.size()); + if (pos == std::string::npos) + { + return false; + } + ++pos; + while (pos < obj.size() && (obj[pos] == ' ' || obj[pos] == '\t' || obj[pos] == '\n')) + { + ++pos; + } + if (pos >= obj.size()) + { + return false; + } + errno = 0; + char* endp = nullptr; + const long val = strtol(obj.c_str() + pos, &endp, 10); + if (endp == obj.c_str() + pos || errno != 0) + { + return false; + } + out = static_cast(val); + return true; +} + +static bool extract_float(const std::string& obj, const char* key, float& out) +{ + const std::string token = std::string("\"") + key + "\""; + const size_t kpos = obj.find(token); + if (kpos == std::string::npos) + { + return false; + } + size_t pos = obj.find(':', kpos + token.size()); + if (pos == std::string::npos) + { + return false; + } + ++pos; + while (pos < obj.size() && (obj[pos] == ' ' || obj[pos] == '\t' || obj[pos] == '\n')) + { + ++pos; + } + if (pos >= obj.size()) + { + return false; + } + errno = 0; + char* endp = nullptr; + const double val = strtod(obj.c_str() + pos, &endp); + if (endp == obj.c_str() + pos || errno != 0) + { + return false; + } + out = static_cast(val); + return true; +} + +} // namespace + +std::vector +PilotServerPayloadParser::parse_charger_locations( + const std::string& body) +{ + std::vector result; + + size_t pos = 0U; + while (pos < body.size()) + { + const size_t obj_start = body.find('{', pos); + if (obj_start == std::string::npos) + { + break; + } + const size_t obj_end = find_object_end(body, obj_start); + if (obj_end == std::string::npos) + { + std::cerr << "[payload_parser] parse_charger_locations: unmatched '{'" << std::endl; + break; + } + + const std::string obj = body.substr(obj_start, obj_end - obj_start); + + ParsedChargerLocation loc; + + extract_int(obj, "id", loc.id); + if (!extract_string(obj, "charger_name", loc.name)) + { + extract_string(obj, "name", loc.name); // fallback + } + + // Try flat fields first, then nested "position" object + const bool has_flat = + extract_float(obj, "latitude", loc.latitude) && + extract_float(obj, "longitude", loc.longitude); + + if (!has_flat) + { + const size_t pos_key = obj.find("\"position\""); + if (pos_key != std::string::npos) + { + const size_t inner_start = obj.find('{', pos_key); + if (inner_start != std::string::npos) + { + const size_t inner_end = find_object_end(obj, inner_start); + if (inner_end != std::string::npos) + { + const std::string inner = obj.substr(inner_start, + inner_end - inner_start); + extract_float(inner, "latitude", loc.latitude); + extract_float(inner, "longitude", loc.longitude); + } + } + } + } + + result.push_back(loc); + pos = obj_end; + } + + return result; +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/common_server/test/launch_server_common_test.sh b/common_server/test/launch_server_common_test.sh new file mode 100755 index 0000000..7842e98 --- /dev/null +++ b/common_server/test/launch_server_common_test.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Build and run server_common unit tests on Linux/Ubuntu. +# +# Usage: +# bash launch_server_common_test.sh +# +# Prerequisites: +# - cmake >= 3.16 +# - libcurl development headers: sudo apt install libcurl4-openssl-dev +# - Internet access at configure time (GTest fetched if not installed) +# +# Environment variables: +# CMAKE_BUILD_TYPE Release|Debug (default: Debug) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SERVER_COMMON_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +: "${CMAKE_BUILD_TYPE:=Debug}" + +BUILD_DIR="${SERVER_COMMON_DIR}/build/linux-${CMAKE_BUILD_TYPE}" +CACHE_FILE="${BUILD_DIR}/CMakeCache.txt" + +if [[ -f "${CACHE_FILE}" ]]; then + CACHED_SOURCE_DIR="$(sed -n 's/^CMAKE_HOME_DIRECTORY:INTERNAL=//p' "${CACHE_FILE}")" + if [[ -n "${CACHED_SOURCE_DIR}" && "${CACHED_SOURCE_DIR}" != "${SERVER_COMMON_DIR}" ]]; then + echo "Removing stale CMake cache from copied build directory..." + rm -rf "${BUILD_DIR}" + fi +fi + +echo "Configuring server_common tests..." +cmake \ + -S "${SERVER_COMMON_DIR}" \ + -B "${BUILD_DIR}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + +echo "Building..." +cmake --build "${BUILD_DIR}" --parallel + +echo "" +echo "Running test_server_common..." +echo "---------------------------------------------" + +TEST_RC=0 +"${BUILD_DIR}/test_server_common" || TEST_RC=$? + +echo "---------------------------------------------" + +if [[ "${TEST_RC}" -eq 0 ]]; then + echo "PASSED" +else + echo "FAILED (exit code ${TEST_RC})" + exit "${TEST_RC}" +fi diff --git a/common_server/test/test_server_common.cpp b/common_server/test/test_server_common.cpp new file mode 100644 index 0000000..1d45d95 --- /dev/null +++ b/common_server/test/test_server_common.cpp @@ -0,0 +1,316 @@ +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using safe_edge::server::common::ParsedChargerLocation; +using safe_edge::server::common::PilotServerPayloadParser; +using safe_edge::server::common::PilotServerClient; + +static const char* const PILOT_SERVER_BASE_URL = "https://pilot2.dumitru-alexandru.work"; +static const char* const PILOT_SERVER_INI_PATH = "/etc/safe-edge/server.ini"; +static const char* const CHARGER_LOCATIONS_EP = "/api/chargers/locations"; +static const char* const CHARGER_TYPES_EP = "/api/chargers/types"; +static const char* const CHARGING_SESSIONS_EP = "/api/chargers/sessions"; +static const char* const TRANSIT_HEALTH_EP = "/api/transit/health"; +static const char* const TRANSIT_METRICS_EP = "/api/transit/metrics"; + +static bool looks_like_json_payload(const std::string& body) +{ + for (char ch : body) + { + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') + { + continue; + } + return ch == '{' || ch == '['; + } + return false; +} + +static void skip_if_degraded_real_response( + const std::string& body, + const char* endpoint, + const char* expected_marker) +{ + if (body.empty()) + { + GTEST_SKIP() << endpoint << " returned an empty response"; + } + + if (!looks_like_json_payload(body)) + { + GTEST_SKIP() << endpoint << " returned a non-JSON payload of " << body.size() << " bytes"; + } + + if (body.find(expected_marker) == std::string::npos) + { + GTEST_SKIP() << endpoint << " returned a schema-mismatched payload of " << body.size() + << " bytes; expected marker '" << expected_marker << "'"; + } +} + +// ============================================================================ +// PilotServerPayloadParser +// ============================================================================ + +TEST(PilotServerPayloadParserTest, FlatFields) +{ + const std::string body = + R"([{"id":1,"name":"Hub A","latitude":40.4637,"longitude":-3.7492},)" + R"( {"id":2,"name":"Hub B","latitude":41.3851,"longitude":2.1734}])"; + + const auto result = PilotServerPayloadParser::parse_charger_locations(body); + + ASSERT_EQ(2U, result.size()); + EXPECT_EQ(1, result[0].id); + EXPECT_EQ("Hub A", result[0].name); + EXPECT_FLOAT_EQ(40.4637F, result[0].latitude); + EXPECT_FLOAT_EQ(-3.7492F, result[0].longitude); + EXPECT_EQ(2, result[1].id); + EXPECT_EQ("Hub B", result[1].name); +} + +TEST(PilotServerPayloadParserTest, NestedPosition) +{ + const std::string body = + R"([{"id":5,"name":"Depot","position":{"latitude":39.4699,"longitude":-0.3763}}])"; + + const auto result = PilotServerPayloadParser::parse_charger_locations(body); + + ASSERT_EQ(1U, result.size()); + EXPECT_EQ(5, result[0].id); + EXPECT_EQ("Depot", result[0].name); + EXPECT_FLOAT_EQ(39.4699F, result[0].latitude); + EXPECT_FLOAT_EQ(-0.3763F, result[0].longitude); +} + +TEST(PilotServerPayloadParserTest, EmptyPayload) +{ + const auto result = PilotServerPayloadParser::parse_charger_locations(""); + EXPECT_TRUE(result.empty()); +} + +TEST(PilotServerPayloadParserTest, InvalidJson) +{ + const auto result = PilotServerPayloadParser::parse_charger_locations("not json at all"); + EXPECT_TRUE(result.empty()); +} + +TEST(PilotServerPayloadParserTest, MissingFields) +{ + // Object present but only id — name, latitude, longitude absent + const std::string body = R"([{"id":7}])"; + + const auto result = PilotServerPayloadParser::parse_charger_locations(body); + + ASSERT_EQ(1U, result.size()); + EXPECT_EQ(7, result[0].id); + EXPECT_EQ("", result[0].name); + EXPECT_FLOAT_EQ(0.0F, result[0].latitude); + EXPECT_FLOAT_EQ(0.0F, result[0].longitude); +} + +// ============================================================================ +// PilotServerClient +// ============================================================================ + +static const char* const CONFIG_PATH = "/tmp/test_pilotclient.ini"; + +static void write_config(const char* content) +{ + std::ofstream f(CONFIG_PATH); + f << content; +} + +static void remove_config() +{ + ::unlink(CONFIG_PATH); +} + +TEST(PilotServerClientTest, MissingConfigFile) +{ + remove_config(); + PilotServerClient client("http://127.0.0.1:18099", CONFIG_PATH); + EXPECT_TRUE(client.fetch("/any").empty()); +} + +TEST(PilotServerClientTest, MissingApiKey) +{ + write_config("[pilot_server]\n# no api_key\n"); + PilotServerClient client("http://127.0.0.1:18099", CONFIG_PATH); + EXPECT_TRUE(client.fetch("/any").empty()); + remove_config(); +} + +TEST(PilotServerClientTest, WrongSection) +{ + write_config("[other_section]\napi_key = secret\n"); + PilotServerClient client("http://127.0.0.1:18099", CONFIG_PATH); + EXPECT_TRUE(client.fetch("/any").empty()); + remove_config(); +} + +TEST(PilotServerClientTest, SuccessPathMockServer) +{ + static constexpr uint16_t PORT = 18099U; + static const char* const BODY = + R"([{"id":1,"name":"Mock","latitude":1.0,"longitude":2.0}])"; + + std::thread server_thread([&]() + { + const int srv = ::socket(AF_INET, SOCK_STREAM, 0); + if (srv < 0) { return; } + + const int opt = 1; + ::setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + struct sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(PORT); + + if (::bind(srv, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + ::close(srv); + return; + } + ::listen(srv, 1); + + const int conn = ::accept(srv, nullptr, nullptr); + if (conn >= 0) + { + char buf[4096]; + ::recv(conn, buf, sizeof(buf), 0); + + const std::string body = BODY; + const std::string response = + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Content-Length: " + std::to_string(body.size()) + "\r\n" + "Connection: close\r\n" + "\r\n" + body; + ::send(conn, response.c_str(), response.size(), 0); + ::close(conn); + } + ::close(srv); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + write_config("[pilot_server]\napi_key = testkey\n"); + PilotServerClient client( + "http://127.0.0.1:" + std::to_string(PORT), CONFIG_PATH); + + const std::string result = client.fetch("/locations"); + remove_config(); + server_thread.join(); + + EXPECT_FALSE(result.empty()); + EXPECT_NE(std::string::npos, result.find("Mock")); +} + +// ============================================================================ +// Real Pilot Server tests +// Skipped automatically if /etc/safe-edge/server.ini is not present. +// ============================================================================ + +TEST(RealPilotServerTest, ChargerLocationsReturnsParsedData) +{ + if (::access(PILOT_SERVER_INI_PATH, F_OK) != 0) + { + GTEST_SKIP() << PILOT_SERVER_INI_PATH << " not found — skipping real server test"; + } + + PilotServerClient client(PILOT_SERVER_BASE_URL, PILOT_SERVER_INI_PATH); + + const std::string body = client.fetch(CHARGER_LOCATIONS_EP); + skip_if_degraded_real_response(body, CHARGER_LOCATIONS_EP, "id"); + + const auto locations = PilotServerPayloadParser::parse_charger_locations(body); + if (locations.empty()) + { + GTEST_SKIP() << CHARGER_LOCATIONS_EP + << " returned JSON but no parseable charger locations"; + } + + std::cout << " [real] Received " << locations.size() << " charger location(s)\n"; + for (const auto& loc : locations) + { + std::cout << " [real] id=" << loc.id + << " name=" << loc.name + << " lat=" << loc.latitude + << " lng=" << loc.longitude << "\n"; + } +} + +TEST(RealPilotServerTest, ChargerTypesReturnsData) +{ + if (::access(PILOT_SERVER_INI_PATH, F_OK) != 0) + { + GTEST_SKIP() << PILOT_SERVER_INI_PATH << " not found — skipping real server test"; + } + + PilotServerClient client(PILOT_SERVER_BASE_URL, PILOT_SERVER_INI_PATH); + const std::string body = client.fetch(CHARGER_TYPES_EP); + + skip_if_degraded_real_response(body, CHARGER_TYPES_EP, "charger_type"); + + std::cout << " [real] charger_types response bytes=" << body.size() << "\n"; +} + +TEST(RealPilotServerTest, ChargingSessionsReturnsData) +{ + if (::access(PILOT_SERVER_INI_PATH, F_OK) != 0) + { + GTEST_SKIP() << PILOT_SERVER_INI_PATH << " not found — skipping real server test"; + } + + PilotServerClient client(PILOT_SERVER_BASE_URL, PILOT_SERVER_INI_PATH); + const std::string body = client.fetch(CHARGING_SESSIONS_EP); + + skip_if_degraded_real_response(body, CHARGING_SESSIONS_EP, "station_id"); + + std::cout << " [real] charging_sessions response bytes=" << body.size() << "\n"; +} + +TEST(RealPilotServerTest, TransitHealthReturnsStatus) +{ + if (::access(PILOT_SERVER_INI_PATH, F_OK) != 0) + { + GTEST_SKIP() << PILOT_SERVER_INI_PATH << " not found — skipping real server test"; + } + + PilotServerClient client(PILOT_SERVER_BASE_URL, PILOT_SERVER_INI_PATH); + const std::string body = client.fetch(TRANSIT_HEALTH_EP); + + skip_if_degraded_real_response(body, TRANSIT_HEALTH_EP, "status"); + + std::cout << " [real] transit_health response: " << body << "\n"; +} + +TEST(RealPilotServerTest, TransitMetricsReturnsByRoute) +{ + if (::access(PILOT_SERVER_INI_PATH, F_OK) != 0) + { + GTEST_SKIP() << PILOT_SERVER_INI_PATH << " not found — skipping real server test"; + } + + PilotServerClient client(PILOT_SERVER_BASE_URL, PILOT_SERVER_INI_PATH); + const std::string body = client.fetch(TRANSIT_METRICS_EP); + + skip_if_degraded_real_response(body, TRANSIT_METRICS_EP, "by_route"); + + std::cout << " [real] transit_metrics response bytes=" << body.size() << "\n"; +} diff --git a/idl/common.idl b/idl/common.idl new file mode 100644 index 0000000..43a4e5e --- /dev/null +++ b/idl/common.idl @@ -0,0 +1,44 @@ +module safe_edge { +module common { + +struct Header +{ + string source; + unsigned long long timestamp_ms; + string trace_id; +}; + +struct GeoPoint +{ + double latitude; + double longitude; +}; + +enum HealthStatus +{ + HEALTH_UNKNOWN, + HEALTH_OK, + HEALTH_DEGRADED, + HEALTH_ERROR +}; + +struct ServiceHeartbeat +{ + Header header_st; + string service_name; + HealthStatus status; + string detail; +}; + +enum PolicyMode +{ + POLICY_UNKNOWN, + POLICY_NOMINAL, + POLICY_LOW_SOC, + POLICY_EDGE_AUTONOMOUS, + POLICY_DEGRADED_SERVER_DOWN, + POLICY_DEGRADED_COMPLETE +}; + +}; +}; diff --git a/idl/edge.idl b/idl/edge.idl new file mode 100644 index 0000000..2901579 --- /dev/null +++ b/idl/edge.idl @@ -0,0 +1,33 @@ +#include "common.idl" + +module safe_edge { +module edge { + +struct VehicleEdgeSummary +{ + safe_edge::common::Header header; + float soc_pct; + safe_edge::common::PolicyMode current_mode; + safe_edge::common::HealthStatus vehicle_health; + boolean v2g_ready; +}; + +struct EnergyAdvisory +{ + safe_edge::common::Header header; + safe_edge::common::PolicyMode suggested_mode; + string advisory_reason; + long recommended_charger_id; + float target_soc_pct; +}; + +struct EdgeGatewayStatus +{ + safe_edge::common::Header header; + safe_edge::common::HealthStatus status; + unsigned long long last_server_sync_ms; + string detail; +}; + +}; +}; diff --git a/idl/pilot_server.idl b/idl/pilot_server.idl new file mode 100644 index 0000000..e6db4cc --- /dev/null +++ b/idl/pilot_server.idl @@ -0,0 +1,77 @@ +#include "common.idl" + +module safe_edge { +module pilot_server { + +const unsigned long MAX_CHARGER_LOCATIONS = 200; +const unsigned long MAX_CHARGER_TYPES = 64; +const unsigned long MAX_CHARGING_SESSIONS = 1000; +const unsigned long MAX_ROUTE_METRICS = 512; + +struct ChargerLocation +{ + long id; + string name; + safe_edge::common::GeoPoint position; +}; + +typedef sequence ChargerLocationSeq; + +struct ChargerType +{ + long id; + string charger_type; +}; + +typedef sequence ChargerTypeSeq; + +struct ChargingSession +{ + long id; + long station_id; + string charger_type_id; + double consume_wh; + long duration_min; + string date_iso8601; + string ingested_at_iso8601; +}; + +typedef sequence ChargingSessionSeq; + +struct TransitHealth +{ + string status; + double last_fetch_ts; +}; + +struct RouteMetric +{ + string route_id; + long updates_count; +}; + +typedef sequence RouteMetricSeq; + +struct TransitMetrics +{ + RouteMetricSeq by_route; + long vehicles_seen; +}; + +enum RequestedDataType +{ + CHARGER_LOCATION, + CHARGER_TYPE, + CHARGING_SESSION, + TRANSIT_HEALTH, + TRANSIT_METRICS +}; + +struct ServerQuery +{ + string requested_by; + RequestedDataType requested_data_type; +}; + +}; +}; diff --git a/safe_dds/.gitkeep b/safe_dds/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/safe_dds/edge/CMakeLists.txt b/safe_dds/edge/CMakeLists.txt new file mode 100644 index 0000000..6b05c65 --- /dev/null +++ b/safe_dds/edge/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_edge LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(safedds REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_EDGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_EDGE_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -fno-exceptions + -fno-rtti + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_edge_common + STATIC + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp + src/common/HeaderFactory.cpp + src/logic/EdgeAdvisor.cpp +) +safe_edge_apply_common_build_settings(safe_edge_edge_common) +target_include_directories( + safe_edge_edge_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_edge_common + PUBLIC + safedds +) + +add_executable( + safe_edge_edge_gateway + src/apps/edge_gateway_main.cpp + src/nodes/EdgeGatewayNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_edge_gateway) +target_include_directories( + safe_edge_edge_gateway + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_edge_gateway + PRIVATE + safe_edge_edge_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_edge_gateway + RUNTIME DESTINATION bin +) + +option(SAFE_EDGE_BUILD_TESTS "Build integration tests" ON) + +if(SAFE_EDGE_BUILD_TESTS) + find_package(GTest QUIET) + + if(NOT GTest_FOUND) + message(STATUS "GTest not found — fetching from source") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + if(NOT TARGET GTest::gtest) + add_library(GTest::gtest ALIAS gtest) + endif() + endif() + + add_executable( + test_edge_integration + test/test_edge_integration.cpp + ) + + # Tests need exceptions and RTTI (GTest requirement). + # Do not reuse safe_edge_apply_common_build_settings which disables them. + target_include_directories( + test_edge_integration + PRIVATE + "${SAFE_EDGE_EDGE_INCLUDE_DIR}" + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + ) + target_compile_options( + test_edge_integration + PRIVATE + -Wall -Wextra -Wpedantic + ) + target_link_libraries( + test_edge_integration + PRIVATE + safe_edge_edge_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} + GTest::gtest + ) + + enable_testing() + add_test(NAME edge_integration COMMAND test_edge_integration) + + install( + TARGETS test_edge_integration + RUNTIME DESTINATION bin + ) +endif() diff --git a/safe_dds/edge/src/apps/edge_gateway_main.cpp b/safe_dds/edge/src/apps/edge_gateway_main.cpp new file mode 100644 index 0000000..69026fa --- /dev/null +++ b/safe_dds/edge/src/apps/edge_gateway_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::edge_module::common::RuntimeConfig config = + safe_edge::edge_module::common::make_edge_gateway_runtime_config(); + + safe_edge::edge_module::nodes::EdgeGatewayNode node(config); + return node.run(); +} diff --git a/safe_dds/edge/src/common/HeaderFactory.cpp b/safe_dds/edge/src/common/HeaderFactory.cpp new file mode 100644 index 0000000..06b3581 --- /dev/null +++ b/safe_dds/edge/src/common/HeaderFactory.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +HeaderFactory::HeaderFactory(std::string source_name) + : source_name_(std::move(source_name)) +{ +} + +safe_edge::common::Header HeaderFactory::make_header(const char* trace_suffix) +{ + safe_edge::common::Header header; + header.source = source_name_; + header.timestamp_ms = now_ms(); + header.trace_id = source_name_; + header.trace_id += "-"; + + std::array buf{}; + std::snprintf(buf.data(), buf.size(), "%llu", static_cast(counter_++)); + header.trace_id += buf.data(); + + if (nullptr != trace_suffix && trace_suffix[0] != '\0') + { + header.trace_id += "-"; + header.trace_id += trace_suffix; + } + + return header; +} + +uint64_t HeaderFactory::now_ms() noexcept +{ + const auto now = std::chrono::system_clock::now(); + const auto since_epoch = now.time_since_epoch(); + return static_cast( + std::chrono::duration_cast(since_epoch).count()); +} + +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/safe_dds/edge/src/common/RuntimeConfig.cpp b/safe_dds/edge/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..ee6eb27 --- /dev/null +++ b/safe_dds/edge/src/common/RuntimeConfig.cpp @@ -0,0 +1,21 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +RuntimeConfig make_edge_gateway_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeEdgeGatewayParticipant"; + config.service_name = "edge_gateway"; + config.source_name = "edge_gateway"; + config.domain_id = 0U; + config.participant_port = 8030U; + config.status_interval_sec = 5U; + return config; +} + +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/safe_dds/edge/src/common/TopicNames.cpp b/safe_dds/edge/src/common/TopicNames.cpp new file mode 100644 index 0000000..dc1a5d4 --- /dev/null +++ b/safe_dds/edge/src/common/TopicNames.cpp @@ -0,0 +1,36 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace common { +namespace topic_names { + +const char* vehicle_edge_summary() noexcept +{ + return "safe_edge.edge.vehicle_edge_summary"; +} + +const char* energy_advisory() noexcept +{ + return "safe_edge.edge.energy_advisory"; +} + +const char* edge_gateway_status() noexcept +{ + return "safe_edge.edge.edge_gateway_status"; +} + +const char* charger_locations() noexcept +{ + return "safe_edge.pilot_server.charger_locations"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +} // namespace topic_names +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/safe_dds/edge/src/logic/EdgeAdvisor.cpp b/safe_dds/edge/src/logic/EdgeAdvisor.cpp new file mode 100644 index 0000000..f6c22e8 --- /dev/null +++ b/safe_dds/edge/src/logic/EdgeAdvisor.cpp @@ -0,0 +1,41 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace logic { + +safe_edge::edge::EnergyAdvisory EdgeAdvisor::evaluate( + const safe_edge::edge::VehicleEdgeSummary& summary, + const safe_edge::pilot_server::ChargerLocation* chargers, + int32_t charger_count) +{ + safe_edge::edge::EnergyAdvisory advisory; + + if (summary.soc_pct < 20.0F) + { + advisory.suggested_mode = safe_edge::common::PolicyMode::POLICY_LOW_SOC; + advisory.advisory_reason = "Low battery -- charge now"; + advisory.recommended_charger_id = (charger_count > 0) ? chargers[0].id : 1; + advisory.target_soc_pct = 80.0F; + } + else if (summary.v2g_ready) + { + advisory.suggested_mode = safe_edge::common::PolicyMode::POLICY_EDGE_AUTONOMOUS; + advisory.advisory_reason = "V2G available"; + advisory.recommended_charger_id = 0; + advisory.target_soc_pct = 90.0F; + } + else + { + advisory.suggested_mode = safe_edge::common::PolicyMode::POLICY_NOMINAL; + advisory.advisory_reason = "Normal operation"; + advisory.recommended_charger_id = 0; + advisory.target_soc_pct = 80.0F; + } + + return advisory; +} + +} // namespace logic +} // namespace edge_module +} // namespace safe_edge diff --git a/safe_dds/edge/src/nodes/EdgeGatewayNode.cpp b/safe_dds/edge/src/nodes/EdgeGatewayNode.cpp new file mode 100644 index 0000000..b3a2938 --- /dev/null +++ b/safe_dds/edge/src/nodes/EdgeGatewayNode.cpp @@ -0,0 +1,549 @@ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace edge_module { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod STATUS_INTERVAL = {5, 0}; + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[edge_gateway] Failed to register type: " << label << std::endl; + return false; + } + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& topic_name, + TypeSupportT& type_support) +{ + eprosima::safedds::dds::TopicQos topic_qos{}; + return participant.create_topic( + topic_name, + type_support.get_type_name(), + topic_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +} // namespace + +EdgeGatewayNode::ParticipantListener::ParticipantListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void EdgeGatewayNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +EdgeGatewayNode::VehicleEdgeSummaryListener::VehicleEdgeSummaryListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::VehicleEdgeSummaryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = + eprosima::safedds::dds::TypedDataReader::downcast(reader); + + if (nullptr == typed_reader) + { + std::cerr << "[edge_gateway] Failed to downcast vehicle edge summary reader" << std::endl; + return; + } + + safe_edge::edge::VehicleEdgeSummary sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_vehicle_edge_summary_received(sample); + } + } +} + +EdgeGatewayNode::ChargerLocationListener::ChargerLocationListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::ChargerLocationListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = + eprosima::safedds::dds::TypedDataReader::downcast(reader); + + if (nullptr == typed_reader) + { + std::cerr << "[edge_gateway] Failed to downcast charger location reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_charger_location_received(sample); + } + } +} + +EdgeGatewayNode::HeartbeatListener::HeartbeatListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = + eprosima::safedds::dds::TypedDataReader::downcast(reader); + + if (nullptr == typed_reader) + { + std::cerr << "[edge_gateway] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_server_heartbeat_received(sample); + } + } +} + +EdgeGatewayNode::EdgeGatewayNode(const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , vehicle_edge_summary_listener_(*this) + , charger_location_listener_(*this) + , heartbeat_listener_(*this) + , status_timer_(STATUS_INTERVAL) +{ +} + +int EdgeGatewayNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[edge_gateway] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (status_timer_.is_triggered_and_reset()) + { + publish_edge_gateway_status(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool EdgeGatewayNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool EdgeGatewayNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[edge_gateway] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::register_types() +{ + return register_type(*participant_, vehicle_edge_summary_type_support_, "VehicleEdgeSummary") && + register_type(*participant_, energy_advisory_type_support_, "EnergyAdvisory") && + register_type(*participant_, edge_gateway_status_type_support_, "EdgeGatewayStatus") && + register_type(*participant_, charger_location_type_support_, "ChargerLocation") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat"); +} + +bool EdgeGatewayNode::create_topics() +{ + vehicle_edge_summary_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::vehicle_edge_summary()); + vehicle_edge_summary_topic_ = create_topic( + *participant_, vehicle_edge_summary_topic_name_, vehicle_edge_summary_type_support_); + if (nullptr == vehicle_edge_summary_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: vehicle_edge_summary" << std::endl; + return false; + } + + energy_advisory_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::energy_advisory()); + energy_advisory_topic_ = create_topic( + *participant_, energy_advisory_topic_name_, energy_advisory_type_support_); + if (nullptr == energy_advisory_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: energy_advisory" << std::endl; + return false; + } + + edge_gateway_status_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::edge_gateway_status()); + edge_gateway_status_topic_ = create_topic( + *participant_, edge_gateway_status_topic_name_, edge_gateway_status_type_support_); + if (nullptr == edge_gateway_status_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: edge_gateway_status" << std::endl; + return false; + } + + charger_location_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::charger_locations()); + charger_location_topic_ = create_topic( + *participant_, charger_location_topic_name_, charger_location_type_support_); + if (nullptr == charger_location_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: charger_locations" << std::endl; + return false; + } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic( + *participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: service_heartbeat" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[edge_gateway] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + energy_advisory_datawriter_ = publisher_->create_datawriter( + *energy_advisory_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + edge_gateway_status_datawriter_ = publisher_->create_datawriter( + *edge_gateway_status_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + energy_advisory_writer_ = downcast_writer( + energy_advisory_datawriter_); + edge_gateway_status_writer_ = downcast_writer( + edge_gateway_status_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + vehicle_edge_summary_datareader_ = subscriber_->create_datareader( + *vehicle_edge_summary_topic_, + reader_qos, + &vehicle_edge_summary_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + vehicle_edge_summary_reader_ = downcast_reader( + vehicle_edge_summary_datareader_); + + charger_location_datareader_ = subscriber_->create_datareader( + *charger_location_topic_, + reader_qos, + &charger_location_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charger_location_reader_ = downcast_reader( + charger_location_datareader_); + + service_heartbeat_datareader_ = subscriber_->create_datareader( + *service_heartbeat_topic_, + reader_qos, + &heartbeat_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_reader_ = downcast_reader( + service_heartbeat_datareader_); + + const bool success = nullptr != energy_advisory_writer_ && + nullptr != edge_gateway_status_writer_ && + nullptr != vehicle_edge_summary_reader_ && + nullptr != charger_location_reader_ && + nullptr != heartbeat_reader_; + + if (!success) + { + std::cerr << "[edge_gateway] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == energy_advisory_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == edge_gateway_status_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == vehicle_edge_summary_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_location_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[edge_gateway] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool EdgeGatewayNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[edge_gateway] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void EdgeGatewayNode::start_timers() noexcept +{ + status_timer_.start(); +} + +void EdgeGatewayNode::on_vehicle_edge_summary_received( + const safe_edge::edge::VehicleEdgeSummary& summary) +{ + std::cout << "[edge_gateway] Received VehicleEdgeSummary soc=" << summary.soc_pct + << " v2g_ready=" << summary.v2g_ready + << " mode=" << static_cast(summary.current_mode) << std::endl; + + safe_edge::edge::EnergyAdvisory advisory = logic::EdgeAdvisor::evaluate( + summary, cached_chargers_, cached_charger_count_); + advisory.header = header_factory_.make_header("energy_advisory"); + publish_energy_advisory(advisory); +} + +void EdgeGatewayNode::on_charger_location_received( + const safe_edge::pilot_server::ChargerLocation& location) +{ + if (cached_charger_count_ < 3) + { + cached_chargers_[cached_charger_count_++] = location; + } + last_server_sync_ms_ = common::HeaderFactory::now_ms(); + + std::cout << "[edge_gateway] Received ChargerLocation id=" << location.id + << " name=" << location.name << std::endl; +} + +void EdgeGatewayNode::publish_energy_advisory(const safe_edge::edge::EnergyAdvisory& advisory) +{ + if (eprosima::safedds::dds::ReturnCode::OK != + energy_advisory_writer_->write(advisory, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[edge_gateway] Failed to publish EnergyAdvisory" << std::endl; + return; + } + + std::cout << "[edge_gateway] Published EnergyAdvisory mode=" << static_cast(advisory.suggested_mode) + << " reason=" << advisory.advisory_reason << std::endl; +} + +void EdgeGatewayNode::on_server_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + if (heartbeat.service_name != "server") + { + return; + } + last_server_hb_ms_ = common::HeaderFactory::now_ms(); + server_available_ = true; +} + +void EdgeGatewayNode::publish_edge_gateway_status() +{ + constexpr uint64_t SERVER_HB_TIMEOUT_MS = 10000U; + if (last_server_hb_ms_ > 0U && + (common::HeaderFactory::now_ms() - last_server_hb_ms_) > SERVER_HB_TIMEOUT_MS) + { + server_available_ = false; + } + + safe_edge::edge::EdgeGatewayStatus status; + status.header = header_factory_.make_header("edge_gateway_status"); + status.status = server_available_ + ? safe_edge::common::HealthStatus::HEALTH_OK + : safe_edge::common::HealthStatus::HEALTH_DEGRADED; + status.last_server_sync_ms = last_server_sync_ms_; + status.detail = server_available_ + ? "edge connected, server synced" + : "edge connected, server_down"; + + if (eprosima::safedds::dds::ReturnCode::OK != + edge_gateway_status_writer_->write(status, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[edge_gateway] Failed to publish EdgeGatewayStatus" << std::endl; + return; + } + + std::cout << "[edge_gateway] Published EdgeGatewayStatus status=" + << (server_available_ ? "OK" : "DEGRADED") << std::endl; +} + +void EdgeGatewayNode::log_subscription_match(const char* topic_name, int32_t total_count) const +{ + std::cout << "[edge_gateway] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void EdgeGatewayNode::log_publication_match(const char* topic_name, int32_t total_count) const +{ + std::cout << "[edge_gateway] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint EdgeGatewayNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, status_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace edge_module +} // namespace safe_edge diff --git a/safe_dds/edge/test/test_edge_integration.cpp b/safe_dds/edge/test/test_edge_integration.cpp new file mode 100644 index 0000000..3e942e7 --- /dev/null +++ b/safe_dds/edge/test/test_edge_integration.cpp @@ -0,0 +1,527 @@ +// test_edge_integration.cpp — Safe DDS variant +// +// Integration tests for EdgeGatewayNode health state machine. +// Starts safe_edge_edge_gateway as a subprocess, runs all suites, stops it. +// +// Usage: +// ./test_edge_integration +// ./test_edge_integration --gtest_output=xml:results.xml +// +// Environment variables: +// SAFE_EDGE_EDGE_BIN path to safe_edge_edge_gateway (default: safe_edge_edge_gateway) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Global peers: point at the edge participant (port 8030) +// --------------------------------------------------------------------------- + +static eprosima::safedds::memory::container::StaticList< + eprosima::safedds::transport::Locator, 1U> g_peers; + +static bool init_peers() +{ + g_peers.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, 8030U)); + return true; +} + +static const bool PEERS_INIT = init_peers(); + +static const char* EDGE_PID_FILE = "/tmp/safe_edge_edge_gateway_test.pid"; +static const char* EDGE_LOG_FILE = "/tmp/safe_edge_edge_gateway_test.log"; + +// Each test gets a unique port so there is no binding conflict when the OS +// has not yet released the previous socket. +static uint16_t next_test_port() +{ + static std::atomic next_port{8050U}; + return next_port.fetch_add(1U, std::memory_order_relaxed); +} + +static eprosima::safedds::dds::DomainParticipant* make_participant( + eprosima::safedds::dds::DomainParticipantFactory& factory, + const char* name, uint16_t port) +{ + eprosima::safedds::dds::DomainParticipantQos qos{}; + eprosima::safedds::memory::container::StaticString256 sname(name); + qos.participant_name() = sname; + qos.wire_protocol_qos().announced_locator = + eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, port); + qos.wire_protocol_qos().use_multicast_discovery = false; + qos.wire_protocol_qos().initial_peers = &g_peers; + return factory.create_participant(0U, qos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +static void destroy_participant( + eprosima::safedds::dds::DomainParticipantFactory& factory, + eprosima::safedds::dds::DomainParticipant*& participant) noexcept +{ + if (participant != nullptr) + { + (void)participant->delete_contained_entities(); + (void)factory.delete_participant(participant); + participant = nullptr; + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } +} + +// --------------------------------------------------------------------------- +// Global environment: lifecycle of safe_edge_edge_gateway subprocess +// --------------------------------------------------------------------------- + +class EdgeEnvironment : public ::testing::Environment +{ +public: + void SetUp() override + { + const char* bin = std::getenv("SAFE_EDGE_EDGE_BIN"); + const std::string edge_bin = (bin != nullptr ? bin : "safe_edge_edge_gateway"); + const std::string cmd = + "sh -c 'rm -f " + std::string(EDGE_PID_FILE) + + "; " + edge_bin + " >" + EDGE_LOG_FILE + " 2>&1 & echo $! > " + + EDGE_PID_FILE + "'"; + ASSERT_EQ(0, std::system(cmd.c_str())) + << "Failed to launch safe_edge_edge_gateway. " + "Set SAFE_EDGE_EDGE_BIN to the full path if needed."; + std::cout << "[env] safe_edge_edge_gateway started — waiting 5 s for init...\n"; + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::cout << "[env] Edge ready.\n"; + } + + void TearDown() override + { + std::cout << "[env] safe_edge_edge_gateway log:\n"; + std::system((std::string("sh -c 'if [ -f ") + EDGE_LOG_FILE + + " ]; then cat " + EDGE_LOG_FILE + "; else echo missing log; fi'").c_str()); + std::cout << "[env] Stopping safe_edge_edge_gateway...\n"; + const std::string stop_cmd = + "sh -c '" + "if [ -f " + std::string(EDGE_PID_FILE) + " ]; then " + "pid=$(cat " + std::string(EDGE_PID_FILE) + "); " + "kill \"$pid\" 2>/dev/null || true; " + "sleep 1; " + "kill -9 \"$pid\" 2>/dev/null || true; " + "rm -f " + std::string(EDGE_PID_FILE) + "; " + "fi; " + "pkill -f safe_edge_edge_gateway 2>/dev/null || true" + "'"; + std::system(stop_cmd.c_str()); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +}; + +// --------------------------------------------------------------------------- +// Fixture: mock server participant +// Publishes: ServiceHeartbeat, ChargerLocation +// Reads: EdgeGatewayStatus +// --------------------------------------------------------------------------- + +class MockServerFixture : public ::testing::Test +{ +protected: + void SetUp() override + { + (void)PEERS_INIT; + participant_ = make_participant(factory_, "TestMockServer", next_test_port()); + ASSERT_NE(nullptr, participant_); + + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + hb_ts_.register_type(*participant_, hb_ts_.get_type_name())); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + loc_ts_.register_type(*participant_, loc_ts_.get_type_name())); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + status_ts_.register_type(*participant_, status_ts_.get_type_name())); + + namespace TN = safe_edge::edge_module::common::topic_names; + eprosima::safedds::memory::container::StaticString256 + hb_name(TN::service_heartbeat()), + loc_name(TN::charger_locations()), + status_name(TN::edge_gateway_status()); + + hb_topic_ = participant_->create_topic(hb_name, hb_ts_.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + loc_topic_ = participant_->create_topic(loc_name, loc_ts_.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + status_topic_ = participant_->create_topic(status_name, status_ts_.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, hb_topic_); + ASSERT_NE(nullptr, loc_topic_); + ASSERT_NE(nullptr, status_topic_); + + publisher_ = participant_->create_publisher( + eprosima::safedds::dds::PublisherQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + subscriber_ = participant_->create_subscriber( + eprosima::safedds::dds::SubscriberQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, publisher_); + ASSERT_NE(nullptr, subscriber_); + + eprosima::safedds::dds::DataWriterQos wqos{}; + wqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + eprosima::safedds::dds::DataReaderQos rqos{}; + rqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + hb_wb_ = publisher_->create_datawriter(*hb_topic_, wqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + loc_wb_ = publisher_->create_datawriter(*loc_topic_, wqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + status_rb_ = subscriber_->create_datareader(*status_topic_, rqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, hb_wb_); + ASSERT_NE(nullptr, loc_wb_); + ASSERT_NE(nullptr, status_rb_); + + hb_writer_ = eprosima::safedds::dds::TypedDataWriter< + safe_edge::common::ServiceHeartbeatTypeSupport>::downcast(*hb_wb_); + loc_writer_ = eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ChargerLocationTypeSupport>::downcast(*loc_wb_); + status_reader_ = eprosima::safedds::dds::TypedDataReader< + safe_edge::edge::EdgeGatewayStatusTypeSupport>::downcast(*status_rb_); + ASSERT_NE(nullptr, hb_writer_); + ASSERT_NE(nullptr, loc_writer_); + ASSERT_NE(nullptr, status_reader_); + + // Enable participant FIRST so the transport starts before sub-entities bind. + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, participant_->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, publisher_->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, subscriber_->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, hb_wb_->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, loc_wb_->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, status_rb_->enable()); + + // Create executor so we can drive SafeDDS I/O from the test thread. + executor_ = factory_.create_default_executor(); + ASSERT_NE(nullptr, executor_); + + // Wait for discovery while spinning so SPDP exchange completes. + spin_for(std::chrono::seconds(3)); + } + + void TearDown() override + { + executor_ = nullptr; + hb_writer_ = nullptr; + loc_writer_ = nullptr; + status_reader_= nullptr; + hb_wb_ = nullptr; + loc_wb_ = nullptr; + status_rb_ = nullptr; + publisher_ = nullptr; + subscriber_ = nullptr; + hb_topic_ = nullptr; + loc_topic_ = nullptr; + status_topic_ = nullptr; + destroy_participant(factory_, participant_); + } + + // Drive the SafeDDS executor for the given duration (single-threaded I/O). + void spin_for(std::chrono::milliseconds dur) + { + const auto end = std::chrono::steady_clock::now() + dur; + while (std::chrono::steady_clock::now() < end) + { + if (executor_ != nullptr) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + void send_heartbeat() + { + safe_edge::common::ServiceHeartbeat hb{}; + hb.service_name = "server"; + hb_writer_->write(hb, eprosima::safedds::dds::HANDLE_NIL); + } + + bool wait_for_status(safe_edge::common::HealthStatus expected, int timeout_s) + { + const auto deadline = + std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); + while (std::chrono::steady_clock::now() < deadline) + { + if (executor_ != nullptr) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + } + + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::safedds::dds::SampleInfo info{}; + if (status_reader_->take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK && info.valid_data) + { + const bool is_ok = + sample.status == safe_edge::common::HealthStatus::HEALTH_OK; + std::cout << " [dds] EdgeGatewayStatus=" + << (is_ok ? "OK" : "DEGRADED") << "\n"; + if (sample.status == expected) + { + return true; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; + } + + eprosima::safedds::dds::DomainParticipantFactory factory_; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::dds::Topic* hb_topic_ = nullptr; + eprosima::safedds::dds::Topic* loc_topic_ = nullptr; + eprosima::safedds::dds::Topic* status_topic_= nullptr; + eprosima::safedds::dds::DataWriter* hb_wb_ = nullptr; + eprosima::safedds::dds::DataWriter* loc_wb_ = nullptr; + eprosima::safedds::dds::DataReader* status_rb_ = nullptr; + + safe_edge::common::ServiceHeartbeatTypeSupport hb_ts_; + safe_edge::pilot_server::ChargerLocationTypeSupport loc_ts_; + safe_edge::edge::EdgeGatewayStatusTypeSupport status_ts_; + + eprosima::safedds::dds::TypedDataWriter< + safe_edge::common::ServiceHeartbeatTypeSupport>* hb_writer_ = nullptr; + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ChargerLocationTypeSupport>* loc_writer_ = nullptr; + eprosima::safedds::dds::TypedDataReader< + safe_edge::edge::EdgeGatewayStatusTypeSupport>* status_reader_= nullptr; +}; + +// --------------------------------------------------------------------------- +// 1. DEGRADED when no server present +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeStatusIsDegradedWithoutServer) +{ + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 12)) + << "Expected DEGRADED within 12 s with no server heartbeat"; +} + +// --------------------------------------------------------------------------- +// 2. OK when server publishes heartbeats +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeStatusIsOkWithServer) +{ + for (int i = 0; i < 3; ++i) + { + send_heartbeat(); + spin_for(std::chrono::seconds(2)); + } + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_OK, 12)) + << "Expected OK within 12 s after sending heartbeats"; +} + +// --------------------------------------------------------------------------- +// 3. Recovers from DEGRADED to OK when server starts +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeRecoversDegradedToOk) +{ + ASSERT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 12)) + << "Expected initial DEGRADED state"; + + for (int i = 0; i < 3; ++i) + { + send_heartbeat(); + spin_for(std::chrono::seconds(2)); + } + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_OK, 12)) + << "Expected OK after heartbeats sent"; +} + +// --------------------------------------------------------------------------- +// 4. Transitions from OK to DEGRADED when server stops +// --------------------------------------------------------------------------- +// Single-threaded design: heartbeat sending and status polling are interleaved +// in the same spin loop to avoid concurrent access to the DDS writer. + +TEST_F(MockServerFixture, EdgeTransitionsOkToDegraded) +{ + // Phase 1: send heartbeats every 2 s for up to 12 s; expect HEALTH_OK. + const auto hb_phase_end = + std::chrono::steady_clock::now() + std::chrono::seconds(12); + auto last_hb = std::chrono::steady_clock::now() - std::chrono::seconds(3); + bool ok_seen = false; + + while (std::chrono::steady_clock::now() < hb_phase_end) + { + if (executor_ != nullptr) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + } + + const auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_hb).count() >= 2000) + { + send_heartbeat(); + last_hb = now; + } + + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::safedds::dds::SampleInfo info{}; + if (status_reader_->take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK && info.valid_data && + sample.status == safe_edge::common::HealthStatus::HEALTH_OK) + { + ok_seen = true; + std::cout << " [dds] EdgeGatewayStatus=OK\n"; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + ASSERT_TRUE(ok_seen) << "Expected HEALTH_OK while heartbeats running"; + + // Phase 2: stop sending; edge should go DEGRADED after 10 s HB timeout + ≤5 s status interval. + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 20)) + << "Expected DEGRADED within 20 s after heartbeats stopped"; +} + +// --------------------------------------------------------------------------- +// 5. Status published periodically (status_interval_sec = 5) +// --------------------------------------------------------------------------- + +TEST(EdgePublishesStatusPeriodically, AtLeastTwoSamplesIn15Seconds) +{ + (void)PEERS_INIT; + eprosima::safedds::dds::DomainParticipantFactory factory; + auto* participant = make_participant(factory, "TestPeriodic", next_test_port()); + ASSERT_NE(nullptr, participant); + + safe_edge::edge::EdgeGatewayStatusTypeSupport status_ts; + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + status_ts.register_type(*participant, status_ts.get_type_name())); + + eprosima::safedds::memory::container::StaticString256 tname( + safe_edge::edge_module::common::topic_names::edge_gateway_status()); + auto* topic = participant->create_topic(tname, status_ts.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, topic); + + auto* sub = participant->create_subscriber( + eprosima::safedds::dds::SubscriberQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, sub); + + eprosima::safedds::dds::DataReaderQos rqos{}; + rqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + auto* rb = sub->create_datareader(*topic, rqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, rb); + + auto* reader = eprosima::safedds::dds::TypedDataReader< + safe_edge::edge::EdgeGatewayStatusTypeSupport>::downcast(*rb); + ASSERT_NE(nullptr, reader); + + // Enable participant FIRST. + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, participant->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, sub->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, rb->enable()); + + auto* executor = factory.create_default_executor(); + ASSERT_NE(nullptr, executor); + + // Discovery wait (spin so SPDP exchange completes). + { + const auto disc_end = std::chrono::steady_clock::now() + std::chrono::seconds(3); + while (std::chrono::steady_clock::now() < disc_end) + { + while (executor->has_pending_work()) + { + executor->spin(eprosima::safedds::execution::TIME_ZERO); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + int count = 0; + const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(15); + while (std::chrono::steady_clock::now() < end) + { + while (executor->has_pending_work()) + { + executor->spin(eprosima::safedds::execution::TIME_ZERO); + } + + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::safedds::dds::SampleInfo info{}; + if (reader->take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK && info.valid_data) + { + ++count; + std::cout << " [dds] Status sample #" << count << "\n"; + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + EXPECT_GE(count, 2) + << "Expected >=2 EdgeGatewayStatus samples in 15 s, got " << count; + + executor = nullptr; + destroy_participant(factory, participant); +} + +// --------------------------------------------------------------------------- +// main +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new EdgeEnvironment()); + return RUN_ALL_TESTS(); +} diff --git a/safe_dds/idl/SafeDDSGenAux.hpp b/safe_dds/idl/SafeDDSGenAux.hpp new file mode 100644 index 0000000..2ae8678 --- /dev/null +++ b/safe_dds/idl/SafeDDSGenAux.hpp @@ -0,0 +1,2467 @@ +/** + * Copyright (C) 2024, Proyectos y Sistemas de Mantenimiento SL (eProsima) + * + * This program is commercial software licensed under the terms of the + * eProsima Software License Agreement Rev 03 (the "License") + * + * You may obtain a copy of the License at + * https://www.eprosima.com/licenses/LICENSE-REV03 + * + */ + +/** + * @file SafeDDSGenAux.hpp + */ + +#ifndef SAFEDDS_SAFEDDSGEN_SAFEDDSGENAUX_HPP +#define SAFEDDS_SAFEDDSGEN_SAFEDDSGENAUX_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace eprosima { +namespace safedds { +namespace gen { + +//! Forward declarations +class SafeDDSTypeSupportSerializer; +class SafeDDSTypeSupportDeserializer; + +/** + * @brief SafeDDSTypeSupportSerializer class. + */ +class SafeDDSTypeSupportSerializer : + public safedds::serialization::cdr::Serializer +{ +public: + + /** + * @brief Constructor + * + * @param buffer The byte array view used for serialization. + * @param endianness The endianness to use for serialization. + * @param xcdr_version The XCDR version to use for serialization. + */ + SafeDDSTypeSupportSerializer( + safedds::memory::IByteArrayView& buffer, + safedds::platform::Endianness endianness, + safedds::serialization::cdr::XCDRVersion xcdr_version) noexcept + : safedds::serialization::cdr::Serializer(buffer, endianness, xcdr_version) + , endianness_(endianness) + , encoding_algorithm_( + safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + EncodingAlgorithm::PLAIN_CDR : + EncodingAlgorithm::PLAIN_CDR2) + { + } + + /** + * @brief Constructor for emulated serialization. + * + * @param endianness The endianness to use for serialization. + * @param xcdr_version The XCDR version to use for serialization. + */ + SafeDDSTypeSupportSerializer( + safedds::platform::Endianness endianness, + safedds::serialization::cdr::XCDRVersion xcdr_version) noexcept + : safedds::serialization::cdr::Serializer(endianness, xcdr_version) + , endianness_(endianness) + , encoding_algorithm_( + safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + EncodingAlgorithm::PLAIN_CDR : + EncodingAlgorithm::PLAIN_CDR2) + { + } + + /** + * @brief Get XCDR version + * + * @return XCDRVersion + */ + safedds::serialization::cdr::XCDRVersion get_xcdr_version() const + { + return xcdr_version_; + } + + /** + * @brief Retrieves the encoding algorithm. + * + * @return EncodingAlgorithm + */ + EncodingAlgorithm get_encoding_algorithm() const + { + return encoding_algorithm_; + } + + /** + * @brief Sets the encoding algorithm. + * + * @param encoding_algorithm The encoding algorithm to use for serialization. + * + * @return ReturnCode + */ + safedds::ReturnCode set_encoding_algorithm( + const EncodingAlgorithm& encoding_algorithm) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + // Check compatibility of XCDR version and encoding algorithm + if (safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version_) + { + ret = (encoding_algorithm == EncodingAlgorithm::PLAIN_CDR) || + (encoding_algorithm == EncodingAlgorithm::PL_CDR) ? + safedds::ReturnCode::OK : + safedds::ReturnCode::ERROR; + } + else if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_) + { + ret = (encoding_algorithm == EncodingAlgorithm::PLAIN_CDR2) || + (encoding_algorithm == EncodingAlgorithm::PL_CDR2) || + (encoding_algorithm == EncodingAlgorithm::DELIMITED_CDR) ? + safedds::ReturnCode::OK : + safedds::ReturnCode::ERROR; + } + else + { + ret = safedds::ReturnCode::ERROR; + } + + if (safedds::ReturnCode::OK == ret) + { + encoding_algorithm_ = encoding_algorithm; + } + + return ret; + } + + /** + * @brief Begins the serialization of a type. + * + * @return ReturnCode + */ + safedds::ReturnCode begin_serialize_type() + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (EncodingAlgorithm::PL_CDR2 == encoding_algorithm_ || + EncodingAlgorithm::DELIMITED_CDR == encoding_algorithm_) + { + ret = serializer_align_to(4U); + + if (safedds::ReturnCode::OK == ret) + { + if (!emulated_serialization_) + { + object_dheader_view_.mutable_set(&buffer_[position_], sizeof(uint32_t)); + } + + ret = serializer_skip(sizeof(uint32_t)); + } + + object_dheader_begin_position_ = position_; + } + + return ret; + } + + /** + * @brief Ends the serialization of a type. + * + * @return ReturnCode + */ + safedds::ReturnCode end_serialize_type() + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (EncodingAlgorithm::PL_CDR == encoding_algorithm_) + { + uint8_t* dummy = nullptr; + ret = serialize_parameter(PID_LIST_END, dummy); + } + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_ && + (EncodingAlgorithm::PL_CDR2 == encoding_algorithm_ || + EncodingAlgorithm::DELIMITED_CDR == encoding_algorithm_)) + { + uint32_t dheader_length = position_ - object_dheader_begin_position_; + SafeDDSTypeSupportSerializer dheader_length_serializer(object_dheader_view_, endianness_, xcdr_version_); + ret = dheader_length_serializer.serialize(dheader_length); + } + + return ret; + } + + /** + * @brief Serializes a type. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + safedds::ReturnCode serialize_type( + const uint32_t& id, + const T& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (EncodingAlgorithm::PL_CDR == encoding_algorithm_ || EncodingAlgorithm::PL_CDR2 == encoding_algorithm_) + { + ret = serialize_parameter(id, &value); + } + else if (EncodingAlgorithm::PLAIN_CDR == encoding_algorithm_ || + EncodingAlgorithm::PLAIN_CDR2 == encoding_algorithm_ || + EncodingAlgorithm::DELIMITED_CDR == encoding_algorithm_) + { + ret = serialize_plain_type(id, value); + } + else + { + ret = safedds::ReturnCode::ERROR; + } + + return ret; + } + + /** + * @brief Specialization for Optional type. + */ + template + safedds::ReturnCode serialize_type( + const uint32_t& id, + const memory::Optional& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if ((EncodingAlgorithm::PL_CDR == encoding_algorithm_ || EncodingAlgorithm::PL_CDR2 == encoding_algorithm_) && + value.has_value()) + { + ret = serialize_parameter(id, &value.value()); + } + else if (EncodingAlgorithm::PLAIN_CDR == encoding_algorithm_) + { + ret = serialize_parameter(id, value.has_value() ? &value.value() : nullptr); + } + else if (EncodingAlgorithm::PLAIN_CDR2 == encoding_algorithm_ || + EncodingAlgorithm::DELIMITED_CDR == encoding_algorithm_) + { + ret = serialize(value.has_value()); + + if (safedds::ReturnCode::OK == ret && value.has_value()) + { + ret = serialize_type(id, value.value()); + } + } + else if (value.has_value()) + { + ret = safedds::ReturnCode::ERROR; + } + + return ret; + } + + /** + * @brief Serializes a type. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + safedds::ReturnCode serialize_type_key( + const uint32_t& id, + const T& value) + { + safedds::ReturnCode ret = + (EncodingAlgorithm::PLAIN_CDR2 == + encoding_algorithm_) ? safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + + if (safedds::ReturnCode::OK == ret) + { + serializing_key_ = true; + ret = serialize_plain_type(id, value); + serializing_key_ = false; + } + + return ret; + } + +private: + + /** + * @brief SerializerState struct. + */ + struct SerializerState + { + //! Attributes from base class + uint32_t position; //!< Position in the view. + uint32_t alignment_position; //!< Alignment position in the view. + bool native_endianness; //!< Working on native endianness + safedds::serialization::cdr::XCDRVersion xcdr_version; //!< XCDR version to use. + bool emulated_serialization; //!< Emulated serialization flag. + + //! Attributes from derived class + EncodingAlgorithm encoding_algorithm_; //!< The encoding algorithm to use. + safedds::platform::Endianness endianness_; //!< The endianness to use. + memory::byte_array::ByteArrayView object_dheader_view_; //!< The view for the dheader. + uint32_t object_dheader_begin_position_; //!< Initial position for the dheader. + bool serializing_key_; //!< Flag indicating if the serializer is serializing a key. + }; + + /** + * @brief Get the current state of the serializer. + * + * @return SerializerState + */ + SerializerState get_state() const + { + SerializerState state; + + state.position = position_; + state.alignment_position = alignment_position_; + state.native_endianness = native_endianness_; + state.xcdr_version = xcdr_version_; + state.emulated_serialization = emulated_serialization_; + state.encoding_algorithm_ = encoding_algorithm_; + state.endianness_ = endianness_; + state.object_dheader_view_ = object_dheader_view_; + state.object_dheader_begin_position_ = object_dheader_begin_position_; + state.serializing_key_ = serializing_key_; + + return state; + } + + /** + * @brief Set the state of the serializer. + * + * @param state The state to set. + */ + void set_state( + const SerializerState& state) + { + position_ = state.position; + alignment_position_ = state.alignment_position; + native_endianness_ = state.native_endianness; + xcdr_version_ = state.xcdr_version; + emulated_serialization_ = state.emulated_serialization; + encoding_algorithm_ = state.encoding_algorithm_; + endianness_ = state.endianness_; + object_dheader_view_ = state.object_dheader_view_; + object_dheader_begin_position_ = state.object_dheader_begin_position_; + serializing_key_ = state.serializing_key_; + } + + /** + * @brief Serializes a plain type, generic implementation. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + typename std::enable_if::value && !is_safe_dds_serialization_basic_type::value, + safedds::ReturnCode>::type serialize_plain_type( + const uint32_t& /* id */, + const T& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + // Store the state of the serializer to use it as a copy + SerializerState state = get_state(); + + if (serializing_key_) + { + ret = T::SerializationStruct::serialize_key(*this, value); + } + else + { + // Serialize the value + ret = T::SerializationStruct::serialize(*this, value); + } + + if (safedds::ReturnCode::OK == ret) + { + state.position = position_; + + // IMPORTANT: This reset at parameter payload is related with an open discussion about PUSH/POP(Origin=0) in OMG Standard + state.alignment_position = alignment_position_; + } + + // Restore the state of the serializer + set_state(state); + + return ret; + } + + /** + * @brief Serializes a plain type, specialization for optional types. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const memory::Optional& value) + { + return serialize_parameter(id, value.has_value() ? &value.value() : nullptr); + } + + /** + * @brief Serializes a plain type, specialization for map types. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const std::map& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + memory::byte_array::ByteArrayView local_map_dheader_view = {}; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && map_require_dheader::value) + { + ret = serializer_align_to(4U); + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_) + { + local_map_dheader_view.mutable_set(&buffer_[position_], sizeof(uint32_t)); + } + + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(sizeof(uint32_t)); + } + } + + const uint32_t begin_position = position_; + + if (safedds::ReturnCode::OK == ret) + { + ret = serialize(static_cast(value.size())); + } + + if (safedds::ReturnCode::OK == ret) + { + for (const auto& element : value) + { + ret = serialize_type(id, element.first); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + + ret = serialize_type(id, element.second); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + } + } + + uint32_t actual_size = position_ - begin_position; + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_ && + safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + map_require_dheader::value) + { + SafeDDSTypeSupportSerializer dheader_length_serializer(local_map_dheader_view, endianness_, + xcdr_version_); + ret = dheader_length_serializer.serialize(actual_size); + } + + return ret; + } + + /** + * @brief Util for serializing a raw container. Generic container implementation. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::serialize_container_by_element, + safedds::ReturnCode>::type serialize_raw_container( + const uint32_t& id, + const V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + for (uint32_t i = 0; i < container.size(); ++i) + { + ret = serialize_type(id, container.data()[i]); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + } + + return ret; + } + + /** + * @brief Util for serializing a raw container. Specialization for vectors of bool types. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::serialize_bool_vector_by_element, + safedds::ReturnCode>::type serialize_raw_container( + const uint32_t& id, + const V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + for (const auto& element : container) + { + ret = serialize_type(id, element); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + } + + return ret; + } + + /** + * @brief Util for serializing a raw container. Specialization for basic type container. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::serialize_basic_type, + safedds::ReturnCode>::type serialize_raw_container( + const uint32_t& /* id */, + const V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (!container.empty()) + { + ret = serialize_array(container.data(), static_cast(container.size())); + } + + return ret; + } + + /** + * @brief Serializes a plain type, specialization for array types. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const std::array& value) + { + return serialize_plain_array_type(id, value); + } + + /** + * @brief Serializes a plain array type. + * + * @note Array can be defined by a pointer to the first element and a size. + */ + template + typename std::enable_if< + is_array::value || is_external_array::value, + safedds::ReturnCode>::type serialize_plain_array_type( + const uint32_t& id, + const V value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + memory::byte_array::ByteArrayView local_array_dheader_view = {}; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + ret = serializer_align_to(4U); + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_) + { + local_array_dheader_view.mutable_set(&buffer_[position_], sizeof(uint32_t)); + } + + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(sizeof(uint32_t)); + } + } + + const uint32_t begin_position = position_; + + // Use templatized raw container serialization + ret = serialize_raw_container(id, value); + + uint32_t actual_size = position_ - begin_position; + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_ && + safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + SafeDDSTypeSupportSerializer dheader_length_serializer(local_array_dheader_view, endianness_, + xcdr_version_); + ret = dheader_length_serializer.serialize(actual_size); + } + + return ret; + } + + /** + * @brief Serializes a plain type, specialization for vector types. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const std::vector& value) + { + return serialize_plain_sequence_type(id, value); + } + + /** + * @brief Serializes a plain sequence type + * + * @note Sequence can be defined by a pointer to the first element and a size. + */ + template + typename std::enable_if< + is_sequence::value || is_external_sequence::value, + safedds::ReturnCode>::type serialize_plain_sequence_type( + const uint32_t& id, + const V value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + memory::byte_array::ByteArrayView local_sequence_dheader_view = {}; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + ret = serializer_align_to(4U); + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_) + { + local_sequence_dheader_view.mutable_set(&buffer_[position_], sizeof(uint32_t)); + } + + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(sizeof(uint32_t)); + } + } + + const uint32_t begin_position = position_; + + if (safedds::ReturnCode::OK == ret) + { + ret = serialize(static_cast(value.size())); + } + + if (safedds::ReturnCode::OK == ret) + { + // Use templatized raw container serialization + ret = serialize_raw_container(id, value); + } + + uint32_t actual_size = position_ - begin_position; + + if (safedds::ReturnCode::OK == ret && !emulated_serialization_ && + safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + SafeDDSTypeSupportSerializer dheader_length_serializer(local_sequence_dheader_view, endianness_, + xcdr_version_); + ret = dheader_length_serializer.serialize(actual_size); + } + + return ret; + } + + /** + * @brief Serializes a plain type, specialization for enum types. + */ + template + typename std::enable_if::value, safedds::ReturnCode>::type serialize_plain_type( + const uint32_t& /* id */, + const E& value) + { + return serialize(static_cast::type>(value)); + } + + /** + * @brief Serializes a Safe DDS serialization basic types + */ + template + typename std::enable_if::value, + safedds::ReturnCode>::type serialize_plain_type( + const uint32_t& /* id */, + const T& value) + { + return serialize(value); + } + + /** + * @brief Serializes a plain type, specialization for strings + */ + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const std::string& value) + { + return serialize_plain_string_type(id, value); + } + + /** + * @brief Serializes a plain string type. + */ + template + typename std::enable_if::value || is_external_string::value, + safedds::ReturnCode>::type serialize_plain_string_type( + const uint32_t& /* id */, + const T& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + ret = serialize(static_cast(value.size() + 1)); + + if (ret == safedds::ReturnCode::OK && value.size() > 0) + { + ret = serialize_array(const_cast(value.data()), static_cast(value.size())); + } + + if (ret == safedds::ReturnCode::OK) + { + ret = serialize(static_cast(0U)); + } + + return ret; + } + + /** + * @brief Serializes an external type. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const External& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != value.member) + { + ret = serialize_plain_type(id, *(value.member)); + } + + return ret; + } + + /** + * @brief Serializes an external sequence. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const ExternalSequence& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != value.data()) + { + ret = serialize_plain_sequence_type(id, value); + } + + return ret; + } + + /** + * @brief Serializes an external array. + */ + template + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const ExternalArray& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != value.data()) + { + ret = serialize_plain_array_type(id, value); + } + + return ret; + } + + /** + * @brief Serializes an external string. + */ + safedds::ReturnCode serialize_plain_type( + const uint32_t& id, + const ExternalString& value) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != value.data()) + { + ret = serialize_plain_string_type(id, value); + } + + return ret; + } + + /** + * @brief Serializes a parameter header. + * + * @note Will not serialize the length field. + * + * @param id The parameter ID. + * + * @return ReturnCode + */ + safedds::ReturnCode serialize_parameter_header( + const uint32_t& id) + { + // Align to 4 bytes + safedds::ReturnCode ret = serializer_align_to(4U); + + // Serialize param_id + if (safedds::ReturnCode::OK == ret) + { + ret = serialize(static_cast(id)); + } + + return ret; + } + + /** + * @brief Serializes an extended parameter header. + * + * @note Will not serialize the extended length field. + * + * @param id The parameter ID. + * + * @return ReturnCode + */ + safedds::ReturnCode serialize_parameter_header_extended( + const uint32_t& id) + { + // Align to 4 bytes + safedds::ReturnCode ret = serializer_align_to(4U); + + // Serialize extension header + if (safedds::ReturnCode::OK == ret) + { + ret = serialize_parameter_header(PID_EXTENDED); + } + + // Serialize extension header length + if (safedds::ReturnCode::OK == ret) + { + ret = serialize(static_cast(8U)); + } + + // Serialize 32 bits id + if (safedds::ReturnCode::OK == ret) + { + ret = serialize(id); + } + + return ret; + } + + /** + * @brief Serializes a parameter assuming XCDR1 encoding. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + safedds::ReturnCode serialize_parameter_xcdr1( + const uint32_t& id, + const T* value) + { + const bool extended_parameter = (id > PID_MAX_SHORT) && (id != PID_LIST_END); + + // Check preconditions + safedds::ReturnCode ret = safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version_ ? + safedds::ReturnCode::OK : + safedds::ReturnCode::ERROR; + + // Parameter header is aligned to 4 bytes + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_align_to(4U); + } + + // Serialize param_id + if (safedds::ReturnCode::OK == ret) + { + if (extended_parameter) + { + ret = serialize_parameter_header_extended(id); + } + else + { + ret = serialize_parameter_header(id); + } + } + + // Prepare actual parameter length serialization taking into account extensible parameters + const uint32_t length_field_size = extended_parameter ? + safedds::portable::safe_sizeof() : + safedds::portable::safe_sizeof(); + + uint8_t* length_position = emulated_serialization_ ? nullptr : &buffer_[position_]; + + // Skip the length field size + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(length_field_size); + } + + // Serialize the payload if there is enough space + uint32_t payload_size = 0; + + // IMPORTANT: This reset at parameter payload is related with an open discussion about PUSH/POP(Origin=0) in OMG Standard + reset_alignment(); + + // Create a clean serializer for the parameter payload taking into account the emulation mode + if (safedds::ReturnCode::OK == ret && (nullptr != value)) + { + if (emulated_serialization_) + { + SafeDDSTypeSupportSerializer payload_serializer(endianness_, xcdr_version_); + ret = payload_serializer.serialize_plain_type(id, *value); + payload_size = payload_serializer.serializer_used_size(); + } + else + { + memory::byte_array::ByteArrayView payload_view(&buffer_[position_], serializer_remaining_size()); + SafeDDSTypeSupportSerializer payload_serializer(payload_view, endianness_, xcdr_version_); + ret = payload_serializer.serialize_plain_type(id, *value); + payload_size = payload_serializer.serializer_used_size(); + } + } + + // Skip the used size in the current serializer + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(payload_size); + } + + // Serialize length + if (safedds::ReturnCode::OK == ret && !emulated_serialization_) + { + // Create a serializer for the length field stored previously + memory::byte_array::ByteArrayView length_view{length_position, length_field_size}; + safedds::serialization::cdr::Serializer length_serializer(length_view, endianness_, xcdr_version_); + + if (extended_parameter) + { + ret = length_serializer.serialize(static_cast(payload_size)); + } + else + { + ret = length_serializer.serialize(static_cast(payload_size)); + } + } + + return ret; + } + + /** + * @brief Serializes a parameter assuming XCDR2 encoding. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + safedds::ReturnCode serialize_parameter_xcdr2( + const uint32_t& id, + const T* value) + { + // Check preconditions + safedds::ReturnCode ret = safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ ? + safedds::ReturnCode::OK : + safedds::ReturnCode::ERROR; + + // Parameter header is aligned to 4 bytes + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_align_to(4U); + } + + // Check max bound of id + if (id > PID_MAX_CDRV2) + { + ret = safedds::ReturnCode::ERROR; + } + + // Serialize emheader + const uint8_t lc = get_xcdr2_emheader_length_code(*value); + // Explicit nextint field is only needed if the LC == 4. + // LC > 4 is for sequences and strings which already have their associated length at the beginning of the payload, which is the same as the nextint field. + const bool needs_explicit_nextint = (lc == 4); + + if (safedds::ReturnCode::OK == ret) + { + const bool must_understand = false; + const uint32_t member_id = id & 0x0FFFFFFF; + uint32_t emheader = (must_understand ? 0x80000000 : 0) | (lc << 28) | member_id; + + ret = serialize(emheader); + } + + // Store nextint position + uint8_t* nextint_position = + (!needs_explicit_nextint || emulated_serialization_) ? nullptr : &buffer_[position_]; + + // Skip the nextint field size if needed + if ((safedds::ReturnCode::OK == ret) && needs_explicit_nextint) + { + ret = serializer_skip(sizeof(uint32_t)); + } + + // Serialize the payload if there is enough space + uint32_t payload_size = 0; + + // Create a clean serializer for the parameter payload taking into account the emulation mode + if (safedds::ReturnCode::OK == ret && (nullptr != value)) + { + if (emulated_serialization_) + { + SafeDDSTypeSupportSerializer payload_serializer(endianness_, xcdr_version_); + ret = payload_serializer.serialize_plain_type(id, *value); + payload_size = payload_serializer.serializer_used_size(); + } + else + { + memory::byte_array::ByteArrayView payload_view(&buffer_[position_], serializer_remaining_size()); + SafeDDSTypeSupportSerializer payload_serializer(payload_view, endianness_, xcdr_version_); + ret = payload_serializer.serialize_plain_type(id, *value); + payload_size = payload_serializer.serializer_used_size(); + } + } + + // Skip the used size in the current serializer + if (safedds::ReturnCode::OK == ret) + { + ret = serializer_skip(payload_size); + } + + // Serialize nextint + if (safedds::ReturnCode::OK == ret && needs_explicit_nextint && !emulated_serialization_) + { + // Create a serializer for the nextint field stored previously + memory::byte_array::ByteArrayView nextint_view{nextint_position, sizeof(uint32_t)}; + safedds::serialization::cdr::Serializer nextint_serializer(nextint_view, endianness_, xcdr_version_); + + ret = nextint_serializer.serialize(payload_size); + } + + return ret; + } + + /** + * @brief Serializes a parameter. + * + * @param id The parameter ID. + * @param value The value to serialize. + * + * @return ReturnCode + */ + template + safedds::ReturnCode serialize_parameter( + const uint32_t& id, + const T* value) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version_) + { + ret = serialize_parameter_xcdr1(id, value); + } + else if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_) + { + ret = serialize_parameter_xcdr2(id, value); + } + else + { + ret = safedds::ReturnCode::ERROR; + } + + return ret; + } + +private: + + /** + * @brief Get XCDR2 EMHEADER length code (LC) generic implementation. + * + * @note If the size of the type is not supported, it will default to 4. + * + * @param value The value to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value && !is_external_sequence::value && !is_array::value && !is_external_array::value && !is_map::value && !is_string::value && !is_external_string::value && !is_idl_union::value && !is_external::value && is_idl_extensibility_final::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& /* value */) + { + const uint32_t element_size = portable::safe_sizeof(); + + return (element_size == 1 ? 0 : + element_size == 2 ? 1 : + element_size == 4 ? 2 : + element_size == 8 ? 3 : + 4) & 0x07; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for external types. + * + * @note If the size of the type is not supported, it will default to 4. + * + * @param value The value to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& value) + { + return get_xcdr2_emheader_length_code(*value.member); + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for sequences (are always final). + * + * @param value The sequence to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value || is_external_sequence::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& value) + { + uint8_t length_code = 0; + + if (!is_arithmetic_or_enum::value) + { + // Sequences's dheader is always 4 bytes + length_code = 5 & 0x07; + } + // When having arithmetic and enum inner types the dheader can be optimized + else + { + const uint32_t element_size = portable::safe_sizeof(); + const uint32_t sequence_size = value.size(); + + length_code = (element_size == 1 ? 5 : + element_size == 4 ? 6 : + element_size == 8 ? 7 : + sequence_size == 0 ? 2 : + 4) & 0x07; + } + + return length_code; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for arrays (are always final). + * + * @param value The array to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value || is_external_array::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& value) + { + uint8_t length_code = 0; + + if (sequence_or_array_require_dheader::value) + { + // Array's dheader is always 4 bytes and it specifies the size of the array + length_code = 5 & 0x07; + } + else + { + const uint32_t element_size = portable::safe_sizeof(); + const uint32_t array_size = value.size(); + const uint32_t total_size = array_size * element_size; + length_code = (total_size == 1 ? 0 : + total_size == 2 ? 1 : + total_size == 4 ? 2 : + total_size == 8 ? 3 : + 4) & 0x07; + } + + return length_code; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for maps (are always final). + * + * @param value The map to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value, uint8_t>::type get_xcdr2_emheader_length_code( + const T& value) + { + uint8_t length_code = 0; + + if (map_require_dheader::value) + { + // Map's dheader is always 4 bytes and it specifies the size of the map + length_code = 5 & 0x07; + } + else + { + const uint32_t element_size = portable::safe_sizeof() + + portable::safe_sizeof(); + const uint32_t map_size = value.size(); + const uint32_t total_size = map_size * element_size + portable::safe_sizeof(); + + length_code = (map_size == 0 ? 2 : + total_size == 2 ? 1 : + total_size == 4 ? 2 : + total_size == 8 ? 3 : + 4) & 0x07; + } + + return length_code; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for strings (are always final). + * + * @param value The string to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value || is_external_string::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& /* value */) + { + return 5 & 0x07; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for non-final types. + * + * @note Always returns 5. + * + * @return length code value + */ + template + const typename std::enable_if::value, uint8_t>::type get_xcdr2_emheader_length_code( + const T& /* value */) + { + return 5 & 0x07; + } + + /** + * @brief Get XCDR2 EMHEADER length code (LC) specialization for union types. + * + * @note If the size of the type is not supported, it will default to 4. + * + * @param value The value to get the length code from. + * + * @return length code value + */ + template + const typename std::enable_if::value && is_idl_union::value, + uint8_t>::type get_xcdr2_emheader_length_code( + const T& value) + { + const uint32_t element_size = value.get_current_union_size(); + + return (element_size == 1 ? 0 : + element_size == 2 ? 1 : + element_size == 4 ? 2 : + element_size == 8 ? 3 : + 4) & 0x07; + } + + safedds::platform::Endianness endianness_; //!< The endianness to use. + EncodingAlgorithm encoding_algorithm_; //!< The encoding algorithm to use. + memory::byte_array::ByteArrayView object_dheader_view_; //!< The view for the dheader. + uint32_t object_dheader_begin_position_; //!< Initial position for the dheader. + bool serializing_key_ = false; //!< Flag indicating if the serializer is serializing a key. +}; + +/** + * @brief SafeDDSTypeSupportDeserializer class. + */ +class SafeDDSTypeSupportDeserializer : + public safedds::serialization::cdr::Deserializer +{ + +public: + + /** + * @brief Constructs a SafeDDSTypeSupportDeserializer object. + * + * @param buffer The memory buffer containing the data to be deserialized. + * @param endianness The endianness to use for deserialization. + * @param xcdr_version The XCDR version to use for deserialization. + */ + SafeDDSTypeSupportDeserializer( + const memory::IConstByteArrayView& buffer, + safedds::platform::Endianness endianness, + safedds::serialization::cdr::XCDRVersion xcdr_version) noexcept + : safedds::serialization::cdr::Deserializer(buffer, endianness, xcdr_version) + , current_encoding_algorithm_( + safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + EncodingAlgorithm::PLAIN_CDR : + EncodingAlgorithm::PLAIN_CDR2) + { + } + + /** + * @brief Get XCDR version + * + * @return XCDRVersion + */ + safedds::serialization::cdr::XCDRVersion get_xcdr_version() const + { + return xcdr_version_; + } + + /** + * @brief Deserializes a type. + * + * @param encoding_algorithm The encoding algorithm to use for deserialization. + * @param callback The callback to be called for each member. + * + * @return ReturnCode + */ + template + safedds::ReturnCode deserialize_type( + const safedds::gen::EncodingAlgorithm& encoding_algorithm, + Callback callback) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + const EncodingAlgorithm previous_encoding_algorithm = current_encoding_algorithm_; + current_encoding_algorithm_ = encoding_algorithm; + + if (EncodingAlgorithm::PLAIN_CDR == encoding_algorithm) + { + ret = deserialize_type_plaincdr(callback); + } + else if (EncodingAlgorithm::PL_CDR == encoding_algorithm) + { + ret = deserialize_type_parameterlistcdr(callback); + } + else if (EncodingAlgorithm::PLAIN_CDR2 == encoding_algorithm) + { + ret = deserialize_type_plaincdr(callback); + } + else if (EncodingAlgorithm::DELIMITED_CDR == encoding_algorithm) + { + ret = deserialize_type_delimitedcdr(callback); + } + else if (EncodingAlgorithm::PL_CDR2 == encoding_algorithm) + { + ret = deserialize_type_parameterlistcdr2(callback); + } + + current_encoding_algorithm_ = previous_encoding_algorithm; + + return ret; + } + + /** + * @brief Deserializes a member. Generic implementation. + * + * @param member The member to deserialize. + * + * @return ReturnCode + */ + template + typename std::enable_if::value && !is_safe_dds_serialization_basic_type::value, + safedds::ReturnCode>::type deserialize_member( + T& member) + { + return T::SerializationStruct::deserialize(*this, member); + } + + /** + * @brief Deserializes a member. Specialization for optional types. + */ + template + safedds::ReturnCode deserialize_member( + memory::Optional& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + member.reset(); + + // When dealing with an optional member, if we are in PLAIN_CDR mode, we need to check if the parameter is present + // by means of the parameter header. + if (EncodingAlgorithm::PLAIN_CDR == current_encoding_algorithm_) + { + uint32_t id = 0; + uint32_t length = 0; + + ret = deserialize_parameter_header(id, length); + + const bool extended_header = get_pid_value(id, false) == PID_EXTENDED; + + if (safedds::ReturnCode::OK == ret && extended_header) + { + ret = deserialize_parameter_header_extended(id, length); + } + + // IMPORTANT: This reset at parameter payload is related with an open discussion about PUSH/POP(Origin=0) in OMG Standard + reset_alignment(); + + if (0U != length && safedds::ReturnCode::OK == ret) + { + // Create a new deserializer for the payload in order to take into account parameter payload alignment + memory::byte_array::ByteArrayView payload_view; + ret = deserializer_skip(length, payload_view); + SafeDDSTypeSupportDeserializer payload_deserializer(payload_view, endianness_, xcdr_version_); + payload_deserializer.current_encoding_algorithm_ = current_encoding_algorithm_; + + V& value = member.inner_reference(); + + if (safedds::ReturnCode::OK == ret) + { + ret = payload_deserializer.deserialize_member(value); + } + + if (safedds::ReturnCode::OK == ret) + { + member.set(value); + } + } + } + // In the case of being in a PL_CDR mode or PL_CDR2, we can assume that the parameter header has been already processed and only the + // payload needs to be deserialized. + else if (EncodingAlgorithm::PL_CDR == current_encoding_algorithm_ || + EncodingAlgorithm::PL_CDR2 == current_encoding_algorithm_) + { + V& value = member.inner_reference(); + ret = deserialize_member(value); + + if (safedds::ReturnCode::OK == ret) + { + member.set(value); + } + } + // In the case of being in a PLAIN_CDR2 or DELIMITED_CDR mode, we need to check if the parameter is present + else if (EncodingAlgorithm::PLAIN_CDR2 == current_encoding_algorithm_ || + EncodingAlgorithm::DELIMITED_CDR == current_encoding_algorithm_) + { + bool is_set = false; + ret = deserialize(is_set); + + if (safedds::ReturnCode::OK == ret && is_set) + { + V& value = member.inner_reference(); + ret = deserialize_member(value); + + if (safedds::ReturnCode::OK == ret) + { + member.set(value); + } + } + } + else + { + ret = safedds::ReturnCode::ERROR; + } + + return ret; + } + + /** + * @brief Util for deserializing a raw container. Generic container implementation. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::deserialize_container_by_element, + safedds::ReturnCode>::type deserialize_raw_container( + V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + for (uint32_t i = 0; i < container.size(); ++i) + { + ret = deserialize_member(container.data()[i]); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + } + + return ret; + } + + /** + * @brief Util for deserializing a raw container. Specialization for vectors of bool types. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::deserialize_bool_vector_by_element, + safedds::ReturnCode>::type deserialize_raw_container( + V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + for (size_t i = 0; i < container.size(); ++i) + { + bool temp = false; + ret = deserialize(temp); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + + container[i] = temp; + } + + return ret; + } + + /** + * @brief Util for deserializing a raw container. Specialization for basic type containers. + */ + template + typename std::enable_if< + safe_dds_serialization_container_utils::deserialize_basic_type, + safedds::ReturnCode>::type deserialize_raw_container( + V& container) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (!container.empty()) + { + ret = deserialize_array(container.data(), static_cast(container.size())); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for array types. + */ + template + safedds::ReturnCode deserialize_member( + std::array& member) + { + return deserialize_array_member(member); + } + + /** + * @brief Deserializes an array member. + * + * @note Array can also be defined by a pointer to the first element and a size. + */ + template + typename std::enable_if< + is_array::value || is_external_array::value, + safedds::ReturnCode>::type deserialize_array_member( + V& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + // Deserialize dheader if required + uint32_t dheader = 0; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + ret = deserialize(dheader); + } + + const uint32_t begin_position = position_; + + if (safedds::ReturnCode::OK == ret) + { + // Use templatized raw container deserialization + ret = deserialize_raw_container(member); + } + + uint32_t actual_size = position_ - begin_position; + + // Check if the dheader is present and skip the remaining bytes + if (safedds::ReturnCode::OK == ret && safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value && dheader > actual_size) + { + ret = deserializer_skip(dheader - actual_size); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for vector types. + */ + template + safedds::ReturnCode deserialize_member( + std::vector& member) + { + member.clear(); + + return deserialize_sequence_member(member); + } + + /** + * @brief Deserializes a sequence member. + * + * @note Sequence can be defined by a pointer to the first element and a size. + */ + template + typename std::enable_if< + is_sequence::value || is_external_sequence::value, + safedds::ReturnCode>::type deserialize_sequence_member( + V& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + // Deserialize dheader if required + uint32_t dheader = 0; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value) + { + ret = deserialize(dheader); + } + + const uint32_t begin_position = position_; + uint32_t size = 0; + + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize(size); + } + + if (safedds::ReturnCode::OK == ret) + { + ret = resize_container(member, size); + } + + if (safedds::ReturnCode::OK == ret) + { + // Use templatized raw container deserialization + ret = deserialize_raw_container(member); + } + + uint32_t actual_size = position_ - begin_position; + + // Check if the dheader is present and skip the remaining bytes + if (safedds::ReturnCode::OK == ret && safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && + sequence_or_array_require_dheader::value && dheader > actual_size) + { + ret = deserializer_skip(dheader - actual_size); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for map types. + */ + template + safedds::ReturnCode deserialize_member( + std::map& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + member.clear(); + + // Deserialize dheader if required + uint32_t dheader = 0; + + if (safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version_ && map_require_dheader::value) + { + ret = deserialize(dheader); + } + + uint32_t size = 0; + + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize(size); + } + + if (safedds::ReturnCode::OK == ret) + { + while (size-- > 0) + { + K key; + V value; + + ret = deserialize_member(key); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + + ret = deserialize_member(value); + + if (ret != safedds::ReturnCode::OK) + { + break; + } + + member[key] = value; + } + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for enum types. + */ + template + typename std::enable_if::value, safedds::ReturnCode>::type deserialize_member( + E& member) + { + typename std::underlying_type::type value; + safedds::ReturnCode ret = deserialize(value); + + if (safedds::ReturnCode::OK == ret) + { + member = static_cast(value); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for Safe DDS serialization basic types. + */ + template + typename std::enable_if::value, + safedds::ReturnCode>::type deserialize_member( + T& member) + { + return deserialize(member); + } + + /** + * @brief Deserializes a member. Specialization for strings. + */ + safedds::ReturnCode deserialize_member( + std::string& member) + { + member.clear(); + + return deserialize_string_member(member); + } + + /** + * @brief Deserializes a string member. + */ + template + typename std::enable_if::value || is_external_string::value, + safedds::ReturnCode>::type deserialize_string_member( + T& member) + { + uint32_t length = 0; + safedds::ReturnCode ret = deserialize(length); + + if ((safedds::ReturnCode::OK == ret) && (length > 1)) + { + ret = resize_container(member, length - 1); + + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize_array(const_cast(member.data()), length - 1); + } + } + + if (safedds::ReturnCode::OK == ret) + { + char null_char = 0; + ret = deserialize(null_char); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for external types. + */ + template + safedds::ReturnCode deserialize_member( + External& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != member.member) + { + ret = deserialize_member(*(member.member)); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for external sequences. + */ + template + safedds::ReturnCode deserialize_member( + ExternalSequence& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != member.data()) + { + ret = deserialize_sequence_member(member); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for external arrays. + */ + template + safedds::ReturnCode deserialize_member( + ExternalArray& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != member.data()) + { + ret = deserialize_array_member(member); + } + + return ret; + } + + /** + * @brief Deserializes a member. Specialization for external strings. + */ + safedds::ReturnCode deserialize_member( + ExternalString& member) + { + safedds::ReturnCode ret = safedds::ReturnCode::ERROR; + + if (nullptr != member.data()) + { + ret = deserialize_string_member(member); + } + + return ret; + } + +private: + + /** + * @brief Deserializes plain CDRv1 and plain CDRv2 type + * + * @param callback The callback to be called for each element. + * + * @return ReturnCode + */ + template + safedds::ReturnCode deserialize_type_plaincdr( + Callback callback) + { + safedds::ReturnCode ret = + (EncodingAlgorithm::PLAIN_CDR == current_encoding_algorithm_ || + EncodingAlgorithm::PLAIN_CDR2 == current_encoding_algorithm_) ? + safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + + if (safedds::ReturnCode::OK == ret) + { + uint32_t index = 0; + + while (deserializer_remaining_size() > 0 && safedds::ReturnCode::OK == ret) + { + ret = callback(*this, index++); + } + + // Unimplemented means end of the list + if (safedds::ReturnCode::UNIMPLEMENTED == ret) + { + ret = safedds::ReturnCode::OK; + } + } + + return ret; + } + + /** + * @brief Deserializes a Parameter list CDRv1 type + * + * @param callback The callback to be called for each element. + * + * @return ReturnCode + */ + template + safedds::ReturnCode deserialize_type_parameterlistcdr( + Callback callback) + { + safedds::ReturnCode ret = + (EncodingAlgorithm::PL_CDR == current_encoding_algorithm_) ? + safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + + if (safedds::ReturnCode::OK == ret) + { + bool list_end = false; + + while (deserializer_remaining_size() > 0 && safedds::ReturnCode::OK == ret && !list_end) + { + uint32_t id = 0; + uint32_t length = 0; + + ret = deserialize_parameter_header(id, length); + + const bool extended_header = get_pid_value(id, false) == PID_EXTENDED; + + if (safedds::ReturnCode::OK == ret && extended_header) + { + ret = deserialize_parameter_header_extended(id, length); + } + + // Reset alginment after header deserialization + reset_alignment(); + + // Get id flags + const bool implementation_extension = is_pid_implementation_extension(id, extended_header); + const bool must_understand = is_pid_must_understand(id, extended_header); + + id = get_pid_value(id, extended_header); + const bool ignore = implementation_extension || (id == PID_IGNORE); + list_end = id == PID_LIST_END; + + if (safedds::ReturnCode::OK == ret && !list_end && length > 0) + { + // Create an aux deserializer for the payload + memory::byte_array::ByteArrayView payload_view; + ret = deserializer_skip(length, payload_view); + SafeDDSTypeSupportDeserializer payload_deserializer(payload_view, endianness_, xcdr_version_); + payload_deserializer.current_encoding_algorithm_ = current_encoding_algorithm_; + + if (!ignore && safedds::ReturnCode::OK == ret) + { + ret = callback(payload_deserializer, id); + } + + // Handle unknown parameters + if (safedds::ReturnCode::UNIMPLEMENTED == ret) + { + ret = !must_understand ? safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + } + } + } + } + + return ret; + } + + /** + * @brief Deserializes a delimitated CDRv2 type + * + * @param callback The callback to be called for each element. + * + * @return ReturnCode + */ + template + safedds::ReturnCode deserialize_type_delimitedcdr( + Callback callback) + { + safedds::ReturnCode ret = + (EncodingAlgorithm::DELIMITED_CDR == current_encoding_algorithm_) ? + safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + + if (safedds::ReturnCode::OK == ret) + { + // Deserialize dheader + uint32_t dheader = 0; + + ret = deserialize(dheader); + + // Check remaining size + if (safedds::ReturnCode::OK == ret && deserializer_remaining_size() < dheader) + { + ret = safedds::ReturnCode::SERIALIZATION_INVALID_BUFFER_LENGTH; + } + + const uint32_t begin_position = position_; + + if (safedds::ReturnCode::OK == ret) + { + uint32_t index = 0; + + while ((position_ - begin_position) < dheader && safedds::ReturnCode::OK == ret) + { + ret = callback(*this, index++); + } + + // Unimplemented means end of the list + if (safedds::ReturnCode::UNIMPLEMENTED == ret) + { + ret = safedds::ReturnCode::OK; + } + } + + uint32_t actual_size = position_ - begin_position; + + // Skip the remaining bytes if dheader is greater than the actual size + if (safedds::ReturnCode::OK == ret && dheader > actual_size) + { + ret = deserializer_skip(dheader - actual_size); + } + } + + return ret; + } + + /** + * @brief Deserializes a Parameter list CDRv2 type + * + * @param callback The callback to be called for each element. + * + * @return ReturnCode + */ + template + safedds::ReturnCode deserialize_type_parameterlistcdr2( + Callback callback) + { + safedds::ReturnCode ret = + (EncodingAlgorithm::PL_CDR2 == current_encoding_algorithm_) ? + safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + + // Deserialize dheader + uint32_t dheader = 0; + + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize(dheader); + } + + // Check remaining size + if (safedds::ReturnCode::OK == ret && deserializer_remaining_size() < dheader) + { + ret = safedds::ReturnCode::SERIALIZATION_INVALID_BUFFER_LENGTH; + } + + const uint32_t begin_position = position_; + + if (safedds::ReturnCode::OK == ret) + { + while ((position_ - begin_position) < dheader && safedds::ReturnCode::OK == ret) + { + uint32_t emheader = 0; + ret = deserialize(emheader); + + uint32_t id = emheader & 0x0FFFFFFF; + const uint8_t lc = (emheader >> 28) & 0x07; + const bool must_understand = (emheader & 0x80000000) != 0; + + uint32_t length = 0; + + if (lc < 4U) + { + length = 1U << lc; + } + else if (lc == 4U) + { + deserialize(length); + } + else + { + // See the future and do not skip the nextint field + memory::byte_array::ByteArrayView length_view = {}; + length_view.const_mutable_set(&buffer_[position_], sizeof(uint32_t)); + SafeDDSTypeSupportDeserializer length_deserializer(length_view, endianness_, xcdr_version_); + length_deserializer.deserialize(length); + + // Apply multiplier + length *= (lc == 6U) ? 4U : (lc == 7U) ? 8U : 1U; + + // Add nextint field size + length += sizeof(uint32_t); + } + + if (safedds::ReturnCode::OK == ret && length > 0) + { + // Create an aux deserializer for the payload + memory::byte_array::ByteArrayView payload_view; + ret = deserializer_skip(length, payload_view); + SafeDDSTypeSupportDeserializer payload_deserializer(payload_view, endianness_, xcdr_version_); + payload_deserializer.current_encoding_algorithm_ = current_encoding_algorithm_; + + if (safedds::ReturnCode::OK == ret) + { + ret = callback(payload_deserializer, id); + } + + // Handle unknown parameters + if (safedds::ReturnCode::UNIMPLEMENTED == ret) + { + ret = !must_understand ? safedds::ReturnCode::OK : safedds::ReturnCode::ERROR; + } + } + } + } + + uint32_t actual_size = position_ - begin_position; + + // Skip the remaining bytes if dheader is greater than the actual size + if (safedds::ReturnCode::OK == ret && dheader > actual_size) + { + ret = deserializer_skip(dheader - actual_size); + } + + return ret; + } + + /** + * @brief Aligns the deserializer to the given alignment. + * + * @param alignment The alignment to align the deserializer to. + */ + safedds::ReturnCode deserializer_align_to( + uint32_t type_size) noexcept + { + safedds::ReturnCode ret = safedds::ReturnCode::SERIALIZATION_INVALID_BUFFER_LENGTH; + const uint32_t padding = xcdr_padding_needed(alignment_position_, type_size); + + if ((position_ + padding) <= buffer_.const_size()) + { + position_ += padding; + alignment_position_ += padding; + ret = safedds::ReturnCode::OK; + } + + return ret; + } + + /** + * @brief Deserializes a parameter header. + * + * @param id The parameter ID. + * @param length The parameter length. + * + * @return ReturnCode + */ + safedds::ReturnCode deserialize_parameter_header( + uint32_t& id, + uint32_t& length) + { + // Align to 4 bytes + safedds::ReturnCode ret = deserializer_align_to(4U); + + // Deserialize param_id + if (safedds::ReturnCode::OK == ret) + { + uint16_t param_id = 0; + ret = deserialize(param_id); + + if (safedds::ReturnCode::OK == ret) + { + id = param_id; + } + } + + // Deserialize length + if (safedds::ReturnCode::OK == ret) + { + uint16_t length_value = 0; + ret = deserialize(length_value); + + if (safedds::ReturnCode::OK == ret) + { + length = length_value; + } + } + + return ret; + } + + /** + * @brief Deserializes an extended parameter header. + * + * @param id The parameter ID. + * @param length The parameter length. + * + * @return ReturnCode + */ + safedds::ReturnCode deserialize_parameter_header_extended( + uint32_t& id, + uint32_t& length) + { + // Align to 4 bytes + safedds::ReturnCode ret = deserializer_align_to(4U); + + // Deserialize param_id + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize(id); + } + + // Deserialize length + if (safedds::ReturnCode::OK == ret) + { + ret = deserialize(length); + } + + return ret; + } + +private: + + EncodingAlgorithm current_encoding_algorithm_; //!< The encoding algorithm in use. +}; + +/** + * @brief SafeDDSTypeSupportComparator class + */ +struct SafeDDSTypeSupportComparator +{ + /** + * @brief Compares a member. + * + * @param first The first value to compare. + * @param second The second value to compare. + * + * @return true if the values are equal, false otherwise. + */ + template + static typename std::enable_if::value && !std::is_same::value && !std::is_enum::value, bool>::type + equal( + const T& first, + const T& second) + { + return T::SerializationStruct::compare(first, second); + } + + /** + * @brief Compares two values. Specialization for arithmetic types, strings and enums. + */ + template + static typename std::enable_if::value || std::is_same::value || std::is_enum::value, bool>::type + equal( + const T& first, + const T& second) + { + return first == second; + } + + /** + * @brief Compares two values. Specialization for optional types. + */ + template + static bool equal( + const memory::Optional& first, + const memory::Optional& second) + { + bool ret = true; + + ret = ret && (first.has_value() == second.has_value()); + + if (first.has_value()) + { + ret = ret && equal(first.value(), second.value()); + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for array types. + */ + template + static bool equal( + const std::array& first, + const std::array& second) + { + bool ret = true; + + for (size_t i = 0; i < N; ++i) + { + ret = ret && equal(first[i], second[i]); + + if (!ret) + { + break; + } + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for vector types. + */ + template + static bool equal( + const std::vector& first, + const std::vector& second) + { + bool ret = true; + + ret = ret && (first.size() == second.size()); + + for (size_t i = 0; ret && i < first.size(); ++i) + { + ret = ret && equal(first[i], second[i]); + + if (!ret) + { + break; + } + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for map types. + */ + template + static bool equal( + const std::map& first, + const std::map& second) + { + bool ret = true; + + ret = ret && (first.size() == second.size()); + + for (auto it = first.begin(); ret && it != first.end(); ++it) + { + auto it2 = second.find(it->first); + + if (it2 == second.end()) + { + ret = false; + } + else + { + ret = ret && equal(it->second, it2->second); + } + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for external types. + */ + template + static bool equal( + const External& first, + const External& second) + { + bool ret = false; + + if (first.member == second.member) + { + ret = true; + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for external sequences. + */ + template + static bool equal( + const ExternalSequence& first, + const ExternalSequence& second) + { + bool ret = false; + + if (first.member == second.member) + { + if (first.member != nullptr) + { + ret = first.len == second.len; + } + else + { + ret = true; + } + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for external arrays. + */ + template + static bool equal( + const ExternalArray& first, + const ExternalArray& second) + { + bool ret = false; + + if (first.member == second.member) + { + ret = true; + } + + return ret; + } + + /** + * @brief Compares two values. Specialization for external strings. + */ + static bool equal( + const ExternalString& first, + const ExternalString& second) + { + bool ret = false; + + if (first.member == second.member) + { + if (first.member != nullptr) + { + ret = first.len == second.len; + } + else + { + ret = true; + } + } + + return ret; + } + +}; + +} // namespace gen +} // namespace safedds +} // namespace eprosima + +#endif // SAFEDDS_SAFEDDSGEN_SAFEDDSGENAUX_HPP diff --git a/safe_dds/idl/SafeDDSGenTypeTraits.hpp b/safe_dds/idl/SafeDDSGenTypeTraits.hpp new file mode 100644 index 0000000..4a34bb9 --- /dev/null +++ b/safe_dds/idl/SafeDDSGenTypeTraits.hpp @@ -0,0 +1,270 @@ +/** + * Copyright (C) 2024, Proyectos y Sistemas de Mantenimiento SL (eProsima) + * + * This program is commercial software licensed under the terms of the + * eProsima Software License Agreement Rev 03 (the "License") + * + * You may obtain a copy of the License at + * https://www.eprosima.com/licenses/LICENSE-REV03 + * + */ + +/** + * @file SafeDDSGenTypeTraits.hpp + */ + +#ifndef SAFEDDS_SAFEDDSGEN_SAFEDDSGENTYPETRAITS_HPP +#define SAFEDDS_SAFEDDSGEN_SAFEDDSGENTYPETRAITS_HPP + +#include + +#include +#include +#include +#include + +namespace eprosima { +namespace safedds { +namespace gen { + +/* + * @section Basic type traits + */ + +//! Is arithmetic or enum +template +struct is_arithmetic_or_enum : + std::integral_constant::value || std::is_enum::value> {}; + +//! Is array (false by default) +template +struct is_array : + std::false_type {}; + +//! Is array (true for std::array) +template +struct is_array> : + std::true_type {}; + +//! Is sequence (false by default) +template +struct is_sequence : + std::false_type {}; + +//! Is sequence (true for std::vector) +template +struct is_sequence> : + std::true_type {}; + +//! Is map (false by default) +template +struct is_map : + std::false_type {}; + +//! Is map (true for std::map) +template +struct is_map> : + std::true_type {}; + +//! Is string +template +struct is_string : + std::is_same {}; + +//! Is optional (false by default) +template +struct is_optional : + std::false_type {}; + +//! Is optional (true for safedds::memory::Optional) +template +struct is_optional> : + std::true_type {}; + +//! Is external type (false by default) +template +struct is_external : + std::false_type {}; + +//! Is external type +template +struct External; +template +struct is_external> : + std::true_type {}; + +//! Is external array type (false by default) +template +struct is_external_array : + std::false_type {}; + +//! Is external array type +template +struct ExternalArray; +template +struct is_external_array> : + std::true_type {}; + +//! Is external sequence type (false by default) +template +struct is_external_sequence : + std::false_type {}; + +//! Is external sequence type +template +struct ExternalSequence; +template +struct is_external_sequence> : + std::true_type {}; + +//! Is external string type (false by default) +template +struct is_external_string : + std::false_type {}; + +//! Is external string type +struct ExternalString; +template<> +struct is_external_string : + std::true_type {}; + +//! Is IDLUnion (not C/C++ union, false by default) +struct IDLUnion; +template +struct is_idl_union : + std::is_base_of {}; + +/* + * @section Dheader type traits + */ + +//! By default all types require dheader (included sequences of sequences) +template::value || is_array::value || is_external_sequence::value || is_external_array::value>::type, + typename Enable = void> +struct sequence_or_array_require_dheader : + std::true_type {}; + +//! Specialization for arithmetic types and enums do not require dheader +template +struct sequence_or_array_require_dheader::value>::type> : + std::false_type {}; + +//! Specialization for array types +template +struct sequence_or_array_require_dheader::value>::type> : + sequence_or_array_require_dheader {}; + +//! Specialization for maps +template +struct map_require_dheader : + std::true_type {}; + +//! Specialization for maps with arithmetic types +template +struct map_require_dheader::value && is_arithmetic_or_enum::value>::type> : + std::false_type {}; + +/* + * @section IDL extensibility kind type traits + */ + +//! @brief IDL extensibility kind +enum class IDLExtensibilityKind : uint8_t +{ + FINAL, + APPENDABLE, + MUTABLE +}; + +//! By default get the IDL extensibility kind and check if it is FINAL +template +struct is_idl_extensibility_final +{ + static constexpr bool value = T::IDL_EXTENSIBILITY == IDLExtensibilityKind::FINAL; +}; + +/* + * @brief Specialization for primitive and container types that are considered extensibility FINAL + * + * @note The following types are considered extensibility FINAL: + * - Arithmetic types + * - Enums + * - Arrays + * - Sequences + * - Maps + * - Strings + * - Optional + * - External types + */ +template +struct is_idl_extensibility_final::value || is_array::value || is_sequence::value || is_map::value || is_string::value || is_optional::value || is_external::value || is_external_array::value || is_external_sequence::value || is_external_string::value>::type> +{ + static constexpr bool value = true; +}; + +//! Is Safe DDS serialization basic type +template +struct is_safe_dds_serialization_basic_type : + std::false_type {}; + +//! Is Safe DDS serialization basic type (true for all char, bool, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, float and double) +template +struct is_safe_dds_serialization_basic_type::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value>::type + > : + std::true_type {}; + +template::value || is_array::value || is_external_sequence::value || is_external_array::value, + int>::type = 0> +struct safe_dds_serialization_container_utils +{ + // Serialize complex types (except bool vectors) by element + static constexpr bool serialize_container_by_element = + !is_safe_dds_serialization_basic_type::value && + !std::is_same>::value; + + // Serialize bool vectors by element + static constexpr bool serialize_bool_vector_by_element = + std::is_same>::value; + + // Serialize basic types + static constexpr bool serialize_basic_type = + !serialize_container_by_element && !serialize_bool_vector_by_element; + + // Deserialize complex types (except bool vectors) by element + static constexpr bool deserialize_container_by_element = + !is_safe_dds_serialization_basic_type::value && + !std::is_same>::value; + + // Deserialize bool vectors by element + static constexpr bool deserialize_bool_vector_by_element = + std::is_same>::value; + + // Deserilize basic types + static constexpr bool deserialize_basic_type = + !deserialize_container_by_element && !deserialize_bool_vector_by_element; +}; + +} // namespace gen +} // namespace safedds +} // namespace eprosima + +#endif // SAFEDDS_SAFEDDSGEN_SAFEDDSGENTYPETRAITS_HPP diff --git a/safe_dds/idl/SafeDDSGenUtils.hpp b/safe_dds/idl/SafeDDSGenUtils.hpp new file mode 100644 index 0000000..04863b2 --- /dev/null +++ b/safe_dds/idl/SafeDDSGenUtils.hpp @@ -0,0 +1,225 @@ +/** + * Copyright (C) 2024, Proyectos y Sistemas de Mantenimiento SL (eProsima) + * + * This program is commercial software licensed under the terms of the + * eProsima Software License Agreement Rev 03 (the "License") + * + * You may obtain a copy of the License at + * https://www.eprosima.com/licenses/LICENSE-REV03 + * + */ + +/** + * @file SafeDDSGenUtils.hpp + */ + +#ifndef SAFEDDS_SAFEDDSGEN_SAFEDDSGENUTILS_HPP +#define SAFEDDS_SAFEDDSGEN_SAFEDDSGENUTILS_HPP + +#include + +#include +#include + +namespace eprosima { +namespace safedds { +namespace gen { + +template +struct ExternalSequence +{ + static_assert(is_safe_dds_serialization_basic_type::value || std::is_enum::value, + "T must be a basic or enum type"); + + using value_type = T; + + uint32_t size() const + { + return len; + } + + T* data() const + { + return member; + } + + bool empty() const + { + return (member == nullptr) || (len == 0); + } + + T* member = nullptr; + uint32_t len = 0; + uint32_t capacity = 0; +}; + +template +struct ExternalArray +{ + static_assert(is_safe_dds_serialization_basic_type::value || std::is_enum::value, + "T must be a basic or enum type"); + + using value_type = T; + + uint32_t size() const + { + return N; + } + + T* data() const + { + return member; + } + + bool empty() const + { + return (member == nullptr) || (N == 0); + } + + T* member = nullptr; +}; + +template +struct External +{ + static_assert(is_safe_dds_serialization_basic_type::value || std::is_enum::value, + "T must be a basic or enum type"); + + using value_type = T; + + T* member = nullptr; +}; + +struct ExternalString +{ + uint32_t size() const + { + return len; + } + + const char* data() const + { + return member; + } + + bool empty() const + { + return (member == nullptr) || (len == 0); + } + + const char* member = nullptr; + uint32_t len = 0; + uint32_t capacity = 0; +}; + +//! @brief Union type marker +struct IDLUnion +{ + virtual uint32_t get_current_union_size() const = 0; +}; + +/** + * @brief EncodingAlgorithm enumeration. + */ +enum class EncodingAlgorithm +{ + PLAIN_CDR, + PL_CDR, + PLAIN_CDR2, + DELIMITED_CDR, + PL_CDR2 +}; + +//! @brief DDS-XTypes Reserved Parameter IDs +constexpr uint16_t PID_EXTENDED = 0x3F01; +constexpr uint16_t PID_LIST_END = 0x3F02; +constexpr uint16_t PID_IGNORE = 0x3F03; + +//! @brief Maximum short Parameter ID +constexpr uint16_t PID_MAX_SHORT = 0x3F00; +constexpr uint32_t PID_MAX_CDRV2 = 0x0FFFFFFF; + +/** + * @brief Retrieves the value of the parameter ID. + * + * @param pid The parameter ID. + * @param extended Flag indicating if the parameter ID is extended. + * + * @return The value of the parameter ID. + */ +constexpr uint32_t get_pid_value( + uint32_t pid, + bool extended) +{ + // Take into account the size of the parameter ID + return extended ? (pid & 0x3FFFFFFF) : (pid & 0x3FFF); +} + +/** + * @brief Checks if the parameter ID is an implementation extension. + * + * @param pid The parameter ID. + * @param extended Flag indicating if the parameter ID is extended. + * + * @return true if the parameter ID is an implementation extension, false otherwise. + */ +constexpr bool is_pid_implementation_extension( + uint32_t pid, + bool extended) +{ + return extended ? (pid & 0x80000000) : 0 != (pid & 0x8000); +} + +/** + * @brief Checks if the parameter ID must be understood. + * + * @param pid The parameter ID. + * @param extended Flag indicating if the parameter ID is extended. + * + * @return true if the parameter ID must be understood, false otherwise. + */ +constexpr bool is_pid_must_understand( + uint16_t pid, + bool extended) +{ + return extended ? (pid & 0x40000000) : 0 != (pid & 0x4000); +} + +/** + * @brief Resize a sequence to a given size. + */ +template +typename std::enable_if::value || is_string::value, safedds::ReturnCode>::type resize_container( + V& container, + const uint32_t& size) +{ + container.resize(size); + + return safedds::ReturnCode::OK; +} + +template +typename std::enable_if::value || is_external_string::value, + safedds::ReturnCode>::type resize_container( + V& container, + const uint32_t& size) +{ + safedds::ReturnCode ret = safedds::ReturnCode::OK; + + if (size > container.capacity) + { + ret = safedds::ReturnCode::ERROR; + } + else + { + container.len = size; + } + + return ret; +} + +} // namespace gen +} // namespace safedds +} // namespace eprosima + +#endif // SAFEDDS_SAFEDDSGEN_SAFEDDSGENUTILS_HPP diff --git a/safe_dds/idl/common.hpp b/safe_dds/idl/common.hpp new file mode 100644 index 0000000..0240553 --- /dev/null +++ b/safe_dds/idl/common.hpp @@ -0,0 +1,1228 @@ +// This file was generated by Safe DDS code generator, which is +// Copyright (C) 2023, Proyectos y Sistemas de Mantenimiento SL (eProsima) +// +// This program is commercial software licensed under the terms of the +// eProsima Software License Agreement Rev 03 (the "License") +// +// You may obtain a copy of the License at +// https://www.eprosima.com/licenses/LICENSE-REV03 + +/*! + * @file common.hpp + * This header file contains the declaration of the types described in the IDL file. + * + * This file was generated by Safe-DDS-gen. + */ + +#ifndef SAFEDDS_GENERATED__SAFE_EDGE_COMMON_COMMON_HPP +#define SAFEDDS_GENERATED__SAFE_EDGE_COMMON_COMMON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + + +namespace safe_edge { + namespace common { + + // Forward declaration + struct HeaderSerialization; + + /*! + * @brief This struct represents the structure Header defined by the user in the IDL file. + * @ingroup common + */ + struct Header + { + using SerializationStruct = HeaderSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member source + std::string source ; + /// Member timestamp_ms + uint64_t timestamp_ms = 0; + /// Member trace_id + std::string trace_id ; + + // Ensure bounds + static_assert(255 != 0, "source shall be bounded or --default_unbounded_max_size shall be used"); + + static_assert(255 != 0, "trace_id shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct HeaderSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<532ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const Header& sample) + { + using namespace safe_edge::common; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.source.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.trace_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.source) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.timestamp_ms) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.trace_id) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + Header& sample) + { + using namespace safe_edge::common; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.source); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.timestamp_ms); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.trace_id); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const Header& sample) + { + using namespace safe_edge::common; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.source.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.trace_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.source) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.timestamp_ms) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.trace_id) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const Header& first, + const Header& second) + { + using namespace safe_edge::common; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.source, second.source); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.timestamp_ms, second.timestamp_ms); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.trace_id, second.trace_id); + + return ret; + } + }; + + + struct HeaderTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport
+ { + using DataType = Header; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::common::Header"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return HeaderSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const Header& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const Header& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = HeaderSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + Header& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = HeaderSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const Header& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const Header& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = HeaderSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct GeoPointSerialization; + + /*! + * @brief This struct represents the structure GeoPoint defined by the user in the IDL file. + * @ingroup common + */ + struct GeoPoint + { + using SerializationStruct = GeoPointSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member latitude + double latitude = 0.0; + /// Member longitude + double longitude = 0.0; + + // Ensure bounds + }; + + struct GeoPointSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<24ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const GeoPoint& sample) + { + using namespace safe_edge::common; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.latitude) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.longitude) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + GeoPoint& sample) + { + using namespace safe_edge::common; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.latitude); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.longitude); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const GeoPoint& sample) + { + using namespace safe_edge::common; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.latitude) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.longitude) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const GeoPoint& first, + const GeoPoint& second) + { + using namespace safe_edge::common; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.latitude, second.latitude); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.longitude, second.longitude); + + return ret; + } + }; + + + struct GeoPointTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = GeoPoint; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::common::GeoPoint"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return GeoPointSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const GeoPoint& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const GeoPoint& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = GeoPointSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + GeoPoint& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = GeoPointSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const GeoPoint& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const GeoPoint& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = GeoPointSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + enum class HealthStatus : int32_t + { + HEALTH_UNKNOWN , + HEALTH_OK , + HEALTH_DEGRADED , + HEALTH_ERROR + }; + + + // Forward declaration + struct ServiceHeartbeatSerialization; + + /*! + * @brief This struct represents the structure ServiceHeartbeat defined by the user in the IDL file. + * @ingroup common + */ + struct ServiceHeartbeat + { + using SerializationStruct = ServiceHeartbeatSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header_st + safe_edge::common::Header header_st ; + /// Member service_name + std::string service_name ; + /// Member status + safe_edge::common::HealthStatus status = safe_edge::common::HealthStatus::HEALTH_UNKNOWN; + /// Member detail + std::string detail ; + + // Ensure bounds + static_assert(255 != 0, "service_name shall be bounded or --default_unbounded_max_size shall be used"); + + static_assert(255 != 0, "detail shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct ServiceHeartbeatSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<1064ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServiceHeartbeat& sample) + { + using namespace safe_edge::common; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.service_name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header_st) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.service_name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.detail) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ServiceHeartbeat& sample) + { + using namespace safe_edge::common; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header_st); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.service_name); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.status); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.detail); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServiceHeartbeat& sample) + { + using namespace safe_edge::common; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.service_name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header_st) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.service_name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.detail) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ServiceHeartbeat& first, + const ServiceHeartbeat& second) + { + using namespace safe_edge::common; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header_st, second.header_st); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.service_name, second.service_name); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.status, second.status); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.detail, second.detail); + + return ret; + } + }; + + + struct ServiceHeartbeatTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ServiceHeartbeat; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::common::ServiceHeartbeat"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ServiceHeartbeatSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ServiceHeartbeat& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ServiceHeartbeat& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ServiceHeartbeatSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ServiceHeartbeat& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ServiceHeartbeatSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ServiceHeartbeat& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ServiceHeartbeat& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ServiceHeartbeatSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + enum class PolicyMode : int32_t + { + POLICY_UNKNOWN , + POLICY_NOMINAL , + POLICY_LOW_SOC , + POLICY_EDGE_AUTONOMOUS , + POLICY_DEGRADED_SERVER_DOWN , + POLICY_DEGRADED_COMPLETE + }; + + } // namespace common +} // namespace safe_edge + +#endif // SAFEDDS_GENERATED__SAFE_EDGE_COMMON_COMMON_HPP \ No newline at end of file diff --git a/safe_dds/idl/edge.hpp b/safe_dds/idl/edge.hpp new file mode 100644 index 0000000..447980f --- /dev/null +++ b/safe_dds/idl/edge.hpp @@ -0,0 +1,1250 @@ +// This file was generated by Safe DDS code generator, which is +// Copyright (C) 2023, Proyectos y Sistemas de Mantenimiento SL (eProsima) +// +// This program is commercial software licensed under the terms of the +// eProsima Software License Agreement Rev 03 (the "License") +// +// You may obtain a copy of the License at +// https://www.eprosima.com/licenses/LICENSE-REV03 + +/*! + * @file edge.hpp + * This header file contains the declaration of the types described in the IDL file. + * + * This file was generated by Safe-DDS-gen. + */ + +#ifndef SAFEDDS_GENERATED__SAFE_EDGE_EDGE_EDGE_HPP +#define SAFEDDS_GENERATED__SAFE_EDGE_EDGE_EDGE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#include "common.hpp" + +namespace safe_edge { + namespace edge { + + // Forward declaration + struct VehicleEdgeSummarySerialization; + + /*! + * @brief This struct represents the structure VehicleEdgeSummary defined by the user in the IDL file. + * @ingroup edge + */ + struct VehicleEdgeSummary + { + using SerializationStruct = VehicleEdgeSummarySerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member soc_pct + float soc_pct = 0.0; + /// Member current_mode + safe_edge::common::PolicyMode current_mode = safe_edge::common::PolicyMode::POLICY_UNKNOWN; + /// Member vehicle_health + safe_edge::common::HealthStatus vehicle_health = safe_edge::common::HealthStatus::HEALTH_UNKNOWN; + /// Member v2g_ready + bool v2g_ready = false; + + // Ensure bounds + }; + + struct VehicleEdgeSummarySerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<553ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const VehicleEdgeSummary& sample) + { + using namespace safe_edge::edge; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.soc_pct) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.current_mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.vehicle_health) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.v2g_ready) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + VehicleEdgeSummary& sample) + { + using namespace safe_edge::edge; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.soc_pct); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.current_mode); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.vehicle_health); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.v2g_ready); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const VehicleEdgeSummary& sample) + { + using namespace safe_edge::edge; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.soc_pct) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.current_mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.vehicle_health) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.v2g_ready) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const VehicleEdgeSummary& first, + const VehicleEdgeSummary& second) + { + using namespace safe_edge::edge; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.soc_pct, second.soc_pct); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.current_mode, second.current_mode); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.vehicle_health, second.vehicle_health); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.v2g_ready, second.v2g_ready); + + return ret; + } + }; + + + struct VehicleEdgeSummaryTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = VehicleEdgeSummary; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::edge::VehicleEdgeSummary"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return VehicleEdgeSummarySerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const VehicleEdgeSummary& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const VehicleEdgeSummary& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = VehicleEdgeSummarySerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + VehicleEdgeSummary& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = VehicleEdgeSummarySerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const VehicleEdgeSummary& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const VehicleEdgeSummary& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = VehicleEdgeSummarySerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct EnergyAdvisorySerialization; + + /*! + * @brief This struct represents the structure EnergyAdvisory defined by the user in the IDL file. + * @ingroup edge + */ + struct EnergyAdvisory + { + using SerializationStruct = EnergyAdvisorySerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member suggested_mode + safe_edge::common::PolicyMode suggested_mode = safe_edge::common::PolicyMode::POLICY_UNKNOWN; + /// Member advisory_reason + std::string advisory_reason ; + /// Member recommended_charger_id + int32_t recommended_charger_id = 0; + /// Member target_soc_pct + float target_soc_pct = 0.0; + + // Ensure bounds + static_assert(255 != 0, "advisory_reason shall be bounded or --default_unbounded_max_size shall be used"); + + + }; + + struct EnergyAdvisorySerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<812ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const EnergyAdvisory& sample) + { + using namespace safe_edge::edge; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.advisory_reason.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.suggested_mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.advisory_reason) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.recommended_charger_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.target_soc_pct) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + EnergyAdvisory& sample) + { + using namespace safe_edge::edge; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.suggested_mode); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.advisory_reason); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.recommended_charger_id); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.target_soc_pct); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const EnergyAdvisory& sample) + { + using namespace safe_edge::edge; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.advisory_reason.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.suggested_mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.advisory_reason) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.recommended_charger_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.target_soc_pct) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const EnergyAdvisory& first, + const EnergyAdvisory& second) + { + using namespace safe_edge::edge; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.suggested_mode, second.suggested_mode); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.advisory_reason, second.advisory_reason); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.recommended_charger_id, second.recommended_charger_id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.target_soc_pct, second.target_soc_pct); + + return ret; + } + }; + + + struct EnergyAdvisoryTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = EnergyAdvisory; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::edge::EnergyAdvisory"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return EnergyAdvisorySerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const EnergyAdvisory& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const EnergyAdvisory& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = EnergyAdvisorySerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + EnergyAdvisory& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = EnergyAdvisorySerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const EnergyAdvisory& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const EnergyAdvisory& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = EnergyAdvisorySerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct EdgeGatewayStatusSerialization; + + /*! + * @brief This struct represents the structure EdgeGatewayStatus defined by the user in the IDL file. + * @ingroup edge + */ + struct EdgeGatewayStatus + { + using SerializationStruct = EdgeGatewayStatusSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member status + safe_edge::common::HealthStatus status = safe_edge::common::HealthStatus::HEALTH_UNKNOWN; + /// Member last_server_sync_ms + uint64_t last_server_sync_ms = 0; + /// Member detail + std::string detail ; + + // Ensure bounds + static_assert(255 != 0, "detail shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct EdgeGatewayStatusSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<812ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const EdgeGatewayStatus& sample) + { + using namespace safe_edge::edge; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.last_server_sync_ms) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.detail) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + EdgeGatewayStatus& sample) + { + using namespace safe_edge::edge; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.status); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.last_server_sync_ms); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.detail); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const EdgeGatewayStatus& sample) + { + using namespace safe_edge::edge; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.last_server_sync_ms) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.detail) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const EdgeGatewayStatus& first, + const EdgeGatewayStatus& second) + { + using namespace safe_edge::edge; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.status, second.status); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.last_server_sync_ms, second.last_server_sync_ms); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.detail, second.detail); + + return ret; + } + }; + + + struct EdgeGatewayStatusTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = EdgeGatewayStatus; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::edge::EdgeGatewayStatus"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return EdgeGatewayStatusSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const EdgeGatewayStatus& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const EdgeGatewayStatus& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = EdgeGatewayStatusSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + EdgeGatewayStatus& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = EdgeGatewayStatusSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const EdgeGatewayStatus& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const EdgeGatewayStatus& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = EdgeGatewayStatusSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + } // namespace edge +} // namespace safe_edge + +#endif // SAFEDDS_GENERATED__SAFE_EDGE_EDGE_EDGE_HPP \ No newline at end of file diff --git a/safe_dds/idl/pilot_server.hpp b/safe_dds/idl/pilot_server.hpp new file mode 100644 index 0000000..a29173c --- /dev/null +++ b/safe_dds/idl/pilot_server.hpp @@ -0,0 +1,2781 @@ +// This file was generated by Safe DDS code generator, which is +// Copyright (C) 2023, Proyectos y Sistemas de Mantenimiento SL (eProsima) +// +// This program is commercial software licensed under the terms of the +// eProsima Software License Agreement Rev 03 (the "License") +// +// You may obtain a copy of the License at +// https://www.eprosima.com/licenses/LICENSE-REV03 + +/*! + * @file pilot_server.hpp + * This header file contains the declaration of the types described in the IDL file. + * + * This file was generated by Safe-DDS-gen. + */ + +#ifndef SAFEDDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP +#define SAFEDDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#include "common.hpp" + +namespace safe_edge { + namespace pilot_server { + const uint32_t MAX_CHARGER_LOCATIONS = 200; + + const uint32_t MAX_CHARGER_TYPES = 64; + + const uint32_t MAX_CHARGING_SESSIONS = 1000; + + const uint32_t MAX_ROUTE_METRICS = 512; + + + // Forward declaration + struct ChargerLocationSerialization; + + /*! + * @brief This struct represents the structure ChargerLocation defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct ChargerLocation + { + using SerializationStruct = ChargerLocationSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member id + int32_t id = 0; + /// Member name + std::string name ; + /// Member position + safe_edge::common::GeoPoint position ; + + // Ensure bounds + static_assert(255 != 0, "name shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct ChargerLocationSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<288ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargerLocation& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.position) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ChargerLocation& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.id); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.name); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.position); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargerLocation& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.position) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ChargerLocation& first, + const ChargerLocation& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.id, second.id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.name, second.name); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.position, second.position); + + return ret; + } + }; + + + struct ChargerLocationTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ChargerLocation; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::ChargerLocation"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ChargerLocationSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ChargerLocation& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ChargerLocation& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ChargerLocationSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ChargerLocation& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ChargerLocationSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ChargerLocation& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ChargerLocation& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ChargerLocationSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + typedef std::vector ChargerLocationSeq; + + + + // Forward declaration + struct ChargerTypeSerialization; + + /*! + * @brief This struct represents the structure ChargerType defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct ChargerType + { + using SerializationStruct = ChargerTypeSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member id + int32_t id = 0; + /// Member charger_type + std::string charger_type ; + + // Ensure bounds + static_assert(255 != 0, "charger_type shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct ChargerTypeSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<268ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargerType& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.charger_type.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.charger_type) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ChargerType& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.id); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.charger_type); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargerType& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.charger_type.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.charger_type) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ChargerType& first, + const ChargerType& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.id, second.id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.charger_type, second.charger_type); + + return ret; + } + }; + + + struct ChargerTypeTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ChargerType; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::ChargerType"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ChargerTypeSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ChargerType& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ChargerType& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ChargerTypeSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ChargerType& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ChargerTypeSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ChargerType& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ChargerType& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ChargerTypeSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + typedef std::vector ChargerTypeSeq; + + + + // Forward declaration + struct ChargingSessionSerialization; + + /*! + * @brief This struct represents the structure ChargingSession defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct ChargingSession + { + using SerializationStruct = ChargingSessionSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member id + int32_t id = 0; + /// Member station_id + int32_t station_id = 0; + /// Member charger_type_id + std::string charger_type_id ; + /// Member consume_wh + double consume_wh = 0.0; + /// Member duration_min + int32_t duration_min = 0; + /// Member date_iso8601 + std::string date_iso8601 ; + /// Member ingested_at_iso8601 + std::string ingested_at_iso8601 ; + + // Ensure bounds + static_assert(255 != 0, "charger_type_id shall be bounded or --default_unbounded_max_size shall be used"); + + + static_assert(255 != 0, "date_iso8601 shall be bounded or --default_unbounded_max_size shall be used"); + static_assert(255 != 0, "ingested_at_iso8601 shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct ChargingSessionSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<804ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingSession& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.charger_type_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.date_iso8601.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.ingested_at_iso8601.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.station_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.charger_type_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.consume_wh) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.duration_min) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(5, sample.date_iso8601) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(6, sample.ingested_at_iso8601) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ChargingSession& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.id); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.station_id); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.charger_type_id); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.consume_wh); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.duration_min); + break; + + case 5: + ret_value = inner_deser.deserialize_member(sample.date_iso8601); + break; + + case 6: + ret_value = inner_deser.deserialize_member(sample.ingested_at_iso8601); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingSession& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.charger_type_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.date_iso8601.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.ingested_at_iso8601.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.station_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.charger_type_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.consume_wh) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.duration_min) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(5, sample.date_iso8601) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(6, sample.ingested_at_iso8601) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ChargingSession& first, + const ChargingSession& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.id, second.id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.station_id, second.station_id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.charger_type_id, second.charger_type_id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.consume_wh, second.consume_wh); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.duration_min, second.duration_min); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.date_iso8601, second.date_iso8601); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.ingested_at_iso8601, second.ingested_at_iso8601); + + return ret; + } + }; + + + struct ChargingSessionTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ChargingSession; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::ChargingSession"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ChargingSessionSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ChargingSession& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ChargingSession& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ChargingSessionSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ChargingSession& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ChargingSessionSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ChargingSession& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ChargingSession& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ChargingSessionSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + typedef std::vector ChargingSessionSeq; + + + + // Forward declaration + struct TransitHealthSerialization; + + /*! + * @brief This struct represents the structure TransitHealth defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct TransitHealth + { + using SerializationStruct = TransitHealthSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member status + std::string status ; + /// Member last_fetch_ts + double last_fetch_ts = 0.0; + + // Ensure bounds + static_assert(255 != 0, "status shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct TransitHealthSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<272ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const TransitHealth& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.status.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.last_fetch_ts) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + TransitHealth& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.status); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.last_fetch_ts); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const TransitHealth& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.status.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.last_fetch_ts) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const TransitHealth& first, + const TransitHealth& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.status, second.status); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.last_fetch_ts, second.last_fetch_ts); + + return ret; + } + }; + + + struct TransitHealthTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = TransitHealth; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::TransitHealth"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return TransitHealthSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const TransitHealth& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const TransitHealth& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = TransitHealthSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + TransitHealth& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = TransitHealthSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const TransitHealth& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const TransitHealth& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = TransitHealthSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct RouteMetricSerialization; + + /*! + * @brief This struct represents the structure RouteMetric defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct RouteMetric + { + using SerializationStruct = RouteMetricSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member route_id + std::string route_id ; + /// Member updates_count + int32_t updates_count = 0; + + // Ensure bounds + static_assert(255 != 0, "route_id shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct RouteMetricSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<268ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteMetric& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.route_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.route_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.updates_count) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + RouteMetric& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.route_id); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.updates_count); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteMetric& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.route_id.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.route_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.updates_count) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const RouteMetric& first, + const RouteMetric& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.route_id, second.route_id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.updates_count, second.updates_count); + + return ret; + } + }; + + + struct RouteMetricTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = RouteMetric; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::RouteMetric"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return RouteMetricSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const RouteMetric& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const RouteMetric& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = RouteMetricSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + RouteMetric& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = RouteMetricSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const RouteMetric& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const RouteMetric& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = RouteMetricSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + typedef std::vector RouteMetricSeq; + + + + // Forward declaration + struct TransitMetricsSerialization; + + /*! + * @brief This struct represents the structure TransitMetrics defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct TransitMetrics + { + using SerializationStruct = TransitMetricsSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member by_route + safe_edge::pilot_server::RouteMetricSeq by_route ; + /// Member vehicles_seen + int32_t vehicles_seen = 0; + + // Ensure bounds + static_assert(512 != 0, "by_route shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct TransitMetricsSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<137232ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const TransitMetrics& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.by_route.size() <= 512)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.by_route) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.vehicles_seen) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + TransitMetrics& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.by_route); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.vehicles_seen); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const TransitMetrics& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.by_route.size() <= 512)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.by_route) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.vehicles_seen) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const TransitMetrics& first, + const TransitMetrics& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.by_route, second.by_route); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.vehicles_seen, second.vehicles_seen); + + return ret; + } + }; + + + struct TransitMetricsTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = TransitMetrics; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::TransitMetrics"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return TransitMetricsSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const TransitMetrics& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const TransitMetrics& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = TransitMetricsSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + TransitMetrics& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = TransitMetricsSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const TransitMetrics& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const TransitMetrics& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = TransitMetricsSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + enum class RequestedDataType : int32_t + { + CHARGER_LOCATION , + CHARGER_TYPE , + CHARGING_SESSION , + TRANSIT_HEALTH , + TRANSIT_METRICS + }; + + + // Forward declaration + struct ServerQuerySerialization; + + /*! + * @brief This struct represents the structure ServerQuery defined by the user in the IDL file. + * @ingroup pilot_server + */ + struct ServerQuery + { + using SerializationStruct = ServerQuerySerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member requested_by + std::string requested_by ; + /// Member requested_data_type + safe_edge::pilot_server::RequestedDataType requested_data_type = safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION; + + // Ensure bounds + static_assert(255 != 0, "requested_by shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct ServerQuerySerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<268ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServerQuery& sample) + { + using namespace safe_edge::pilot_server; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.requested_by.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.requested_by) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.requested_data_type) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ServerQuery& sample) + { + using namespace safe_edge::pilot_server; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.requested_by); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.requested_data_type); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServerQuery& sample) + { + using namespace safe_edge::pilot_server; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.requested_by.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.requested_by) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.requested_data_type) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ServerQuery& first, + const ServerQuery& second) + { + using namespace safe_edge::pilot_server; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.requested_by, second.requested_by); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.requested_data_type, second.requested_data_type); + + return ret; + } + }; + + + struct ServerQueryTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ServerQuery; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::pilot_server::ServerQuery"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ServerQuerySerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ServerQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ServerQuery& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ServerQuerySerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ServerQuery& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ServerQuerySerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ServerQuery& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ServerQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ServerQuerySerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + } // namespace pilot_server +} // namespace safe_edge + +#endif // SAFEDDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP \ No newline at end of file diff --git a/safe_dds/server/CMakeLists.txt b/safe_dds/server/CMakeLists.txt new file mode 100644 index 0000000..81bad19 --- /dev/null +++ b/safe_dds/server/CMakeLists.txt @@ -0,0 +1,149 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_server LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(safedds REQUIRED) +find_package(CURL REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_SERVER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") +set(SAFE_EDGE_SERVER_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../common_server") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_SERVER_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -fno-exceptions + -fno-rtti + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_server_common + STATIC + ${SAFE_EDGE_SERVER_COMMON_DIR}/src/PilotServerClient.cpp + ${SAFE_EDGE_SERVER_COMMON_DIR}/src/PilotServerPayloadParser.cpp + src/common/PilotServerPublishHelper.cpp + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp +) +safe_edge_apply_common_build_settings(safe_edge_server_common) +target_include_directories( + safe_edge_server_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + "${SAFE_EDGE_SERVER_COMMON_DIR}/include" +) +target_link_libraries( + safe_edge_server_common + PUBLIC + safedds + CURL::libcurl +) + +add_executable( + safe_edge_server + src/apps/server_main.cpp + src/nodes/ServerNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_server) +target_include_directories( + safe_edge_server + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_server + PRIVATE + safe_edge_server_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_server + RUNTIME DESTINATION bin +) + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- +option(SAFE_EDGE_BUILD_TESTS "Build integration tests" ON) + +if(SAFE_EDGE_BUILD_TESTS) + # Try system GTest first; fall back to fetching from source. + find_package(GTest QUIET) + + if(NOT GTest_FOUND) + message(STATUS "GTest not found — fetching from source") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + # Do not install GTest alongside the project + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + # Alias to the same target name used when found via find_package + if(NOT TARGET GTest::gtest) + add_library(GTest::gtest ALIAS gtest) + endif() + endif() + + add_executable( + test_server_integration + test/test_server_integration.cpp + ) + + # Tests need exceptions and RTTI (GTest requirement). + # Do not reuse safe_edge_apply_common_build_settings which disables them. + target_include_directories( + test_server_integration + PRIVATE + "${SAFE_EDGE_SERVER_INCLUDE_DIR}" + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + ) + target_compile_options( + test_server_integration + PRIVATE + -Wall -Wextra -Wpedantic + ) + target_link_libraries( + test_server_integration + PRIVATE + safe_edge_server_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} + GTest::gtest + ) + + enable_testing() + add_test(NAME server_integration COMMAND test_server_integration) + + install( + TARGETS test_server_integration + RUNTIME DESTINATION bin + ) +endif() diff --git a/safe_dds/server/src/apps/server_main.cpp b/safe_dds/server/src/apps/server_main.cpp new file mode 100644 index 0000000..b8fa6db --- /dev/null +++ b/safe_dds/server/src/apps/server_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::server::common::RuntimeConfig config = + safe_edge::server::common::make_server_runtime_config(); + + safe_edge::server::nodes::ServerNode node(config); + return node.run(); +} diff --git a/safe_dds/server/src/common/PilotServerPublishHelper.cpp b/safe_dds/server/src/common/PilotServerPublishHelper.cpp new file mode 100644 index 0000000..8d05cd8 --- /dev/null +++ b/safe_dds/server/src/common/PilotServerPublishHelper.cpp @@ -0,0 +1,35 @@ +#include + +#include + +#include + +namespace safe_edge { +namespace server { +namespace common { + +void PilotServerPublishHelper::publish_charger_locations( + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ChargerLocationTypeSupport>& writer, + const std::vector& parsed) +{ + for (size_t i = 0U; i < parsed.size(); ++i) + { + safe_edge::pilot_server::ChargerLocation loc; + loc.id = parsed[i].id; + loc.name = parsed[i].name.c_str(); + loc.position.latitude = parsed[i].latitude; + loc.position.longitude = parsed[i].longitude; + + if (eprosima::safedds::dds::ReturnCode::OK != + writer.write(loc, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[publish_helper] Failed to write ChargerLocation id=" + << parsed[i].id << std::endl; + } + } +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/safe_dds/server/src/common/RuntimeConfig.cpp b/safe_dds/server/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..0034c96 --- /dev/null +++ b/safe_dds/server/src/common/RuntimeConfig.cpp @@ -0,0 +1,27 @@ +#include + +namespace safe_edge { +namespace server { +namespace common { + +RuntimeConfig make_server_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeServerParticipant"; + config.domain_id = 0U; + config.participant_port = 8020U; + + config.pilot_server_base_url = "https://pilot2.dumitru-alexandru.work"; + // api_key moved to /etc/safe-edge/server.ini — do not hardcode here + config.charger_locations_endpoint = "/api/chargers/locations"; + config.charger_types_endpoint = "/api/chargers/types"; + config.charging_sessions_endpoint = "/api/chargers/sessions"; + config.transit_health_endpoint = "/api/transit/health"; + config.transit_metrics_endpoint = "/api/transit/metrics"; + + return config; +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/safe_dds/server/src/common/TopicNames.cpp b/safe_dds/server/src/common/TopicNames.cpp new file mode 100644 index 0000000..2c20bea --- /dev/null +++ b/safe_dds/server/src/common/TopicNames.cpp @@ -0,0 +1,26 @@ +#include + +namespace safe_edge { +namespace server { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept +{ + return "safe_edge.pilot_server.charger_locations"; +} + +const char* server_query() noexcept +{ + return "safe_edge.pilot_server.server_query"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +} // namespace topic_names +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/safe_dds/server/src/nodes/ServerNode.cpp b/safe_dds/server/src/nodes/ServerNode.cpp new file mode 100644 index 0000000..716396c --- /dev/null +++ b/safe_dds/server/src/nodes/ServerNode.cpp @@ -0,0 +1,535 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace server { +namespace nodes { + +namespace { + +static safe_edge::pilot_server::ChargerLocationSeq build_charger_locations() +{ + safe_edge::pilot_server::ChargerLocationSeq locations; + + safe_edge::pilot_server::ChargerLocation loc1; + loc1.id = 1; + loc1.name = "Madrid North Hub"; + loc1.position.latitude = 40.4637F; + loc1.position.longitude = -3.7492F; + locations.push_back(loc1); + + safe_edge::pilot_server::ChargerLocation loc2; + loc2.id = 2; + loc2.name = "Barcelona Port Station"; + loc2.position.latitude = 41.3851F; + loc2.position.longitude = 2.1734F; + locations.push_back(loc2); + + safe_edge::pilot_server::ChargerLocation loc3; + loc3.id = 3; + loc3.name = "Valencia Depot"; + loc3.position.latitude = 39.4699F; + loc3.position.longitude = -0.3763F; + locations.push_back(loc3); + + return locations; +} + +static eprosima::safedds::memory::container::StaticList< + eprosima::safedds::transport::Locator, 2U> SERVER_INITIAL_PEERS; + +static bool init_server_peers() +{ + static constexpr uint16_t ports[] = { 8011U, 8030U }; + for (uint16_t port : ports) + { + SERVER_INITIAL_PEERS.add( + eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, port)); + } + return true; +} + +static const bool SERVER_PEERS_INITIALIZED = init_server_peers(); + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[server] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& topic_name, + TypeSupportT& type_support) +{ + return participant.create_topic( + topic_name, + type_support.get_type_name(), + eprosima::safedds::dds::TopicQos{}, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +} // namespace + +ServerNode::ParticipantListener::ParticipantListener( + ServerNode& owner) + : owner_(owner) +{ +} + +void ServerNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void ServerNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); + + if (&writer == owner_.charger_locations_datawriter_ && info.total_count_change > 0) + { + owner_.request_pilot_server_data(safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION); + } +} + +ServerNode::ServerQueryListener::ServerQueryListener( + ServerNode& owner) + : owner_(owner) +{ +} + +void ServerNode::ServerQueryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[server] Failed to downcast server_query reader" << std::endl; + return; + } + + safe_edge::pilot_server::ServerQuery sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_server_query_received(sample); + } + } +} + +ServerNode::ServerNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , pilot_client_(runtime_config.pilot_server_base_url, "/etc/safe-edge/server.ini") + , participant_listener_(*this) + , server_query_listener_(*this) + , heartbeat_timer_({5, 0}) + , refresh_timer_({30, 0}) + , uptime_timer_({300, 0}) + , start_time_(std::chrono::steady_clock::now()) +{ +} + +void ServerNode::configure_participant_qos( + eprosima::safedds::dds::DomainParticipantQos& qos) noexcept +{ + qos.wire_protocol_qos().use_multicast_discovery = false; + qos.wire_protocol_qos().initial_peers = &SERVER_INITIAL_PEERS; +} + +int ServerNode::run() +{ + if (!initialize()) + { + return 1; + } + + heartbeat_timer_.start(); + refresh_timer_.start(); + uptime_timer_.start(); + + std::cout << "[server] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + std::cout << "[server] PilotServer base_url=" << runtime_config_.pilot_server_base_url + << " api_key=***" << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + } + + if (refresh_timer_.is_triggered_and_reset()) + { + periodic_refresh_all_resources(); + } + + if (uptime_timer_.is_triggered_and_reset()) + { + log_uptime(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool ServerNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool ServerNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + + configure_participant_qos(participant_qos); + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[server] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool ServerNode::register_types() +{ + return register_type(*participant_, charger_locations_type_support_, "ChargerLocation") && + register_type(*participant_, server_query_type_support_, "ServerQuery") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat"); +} + +bool ServerNode::create_topics() +{ + charger_locations_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charger_locations()); + charger_locations_topic_ = create_topic(*participant_, charger_locations_topic_name_, charger_locations_type_support_); + if (nullptr == charger_locations_topic_) { std::cerr << "[server] Failed to create topic: charger_locations" << std::endl; return false; } + + server_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::server_query()); + server_query_topic_ = create_topic(*participant_, server_query_topic_name_, server_query_type_support_); + if (nullptr == server_query_topic_) { std::cerr << "[server] Failed to create topic: server_query" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[server] Failed to create topic: service_heartbeat" << std::endl; return false; } + + return true; +} + +bool ServerNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[server] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + charger_locations_datawriter_ = publisher_->create_datawriter(*charger_locations_topic_, writer_qos, nullptr, eprosima::safedds::dds::NONE_STATUS_MASK); + charger_locations_writer_ = downcast_writer(charger_locations_datawriter_); + + service_heartbeat_datawriter_ = publisher_->create_datawriter(*service_heartbeat_topic_, writer_qos, nullptr, eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + server_query_datareader_ = subscriber_->create_datareader( + *server_query_topic_, + reader_qos, + &server_query_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + server_query_reader_ = downcast_reader(server_query_datareader_); + + const bool success = + nullptr != charger_locations_writer_ && + nullptr != service_heartbeat_writer_ && + nullptr != server_query_reader_; + + if (!success) + { + std::cerr << "[server] Failed to create endpoints" << std::endl; + } + + return success; +} + +bool ServerNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_locations_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == server_query_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[server] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool ServerNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[server] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void ServerNode::on_server_query_received( + const safe_edge::pilot_server::ServerQuery& query) +{ + std::cout << "[server] DDS query from=" << query.requested_by + << " type=" << static_cast(query.requested_data_type) << std::endl; + request_pilot_server_data(query.requested_data_type); +} + +void ServerNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[server] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void ServerNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[server] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +const char* ServerNode::resolve_endpoint( + safe_edge::pilot_server::RequestedDataType resource) const noexcept +{ + switch (resource) + { + case safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION: + return runtime_config_.charger_locations_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE: + return runtime_config_.charger_types_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION: + return runtime_config_.charging_sessions_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH: + return runtime_config_.transit_health_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS: + return runtime_config_.transit_metrics_endpoint.c_str(); + default: + return nullptr; + } +} + +void ServerNode::request_pilot_server_data( + safe_edge::pilot_server::RequestedDataType resource) noexcept +{ + const char* endpoint = resolve_endpoint(resource); + if (nullptr == endpoint) + { + std::cerr << "[server] Unsupported resource=" << static_cast(resource) << std::endl; + return; + } + + std::cout << "[server] Request resource=" << static_cast(resource) + << " endpoint=" << endpoint << std::endl; + + const std::string body = pilot_client_.fetch(endpoint); + if (body.empty()) + { + std::cerr << "[server] HTTP fetch failed endpoint=" << endpoint << std::endl; + } + else if (resource == safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION + && nullptr != charger_locations_writer_) + { + const auto parsed = + safe_edge::server::common::PilotServerPayloadParser::parse_charger_locations(body); + safe_edge::server::common::PilotServerPublishHelper::publish_charger_locations( + *charger_locations_writer_, parsed); + std::cout << "[server] Published charger_locations count=" << parsed.size() + << " (from HTTP)" << std::endl; + } + else if (!body.empty()) + { + // TODO: add writers and parsers for other resource types + std::cout << "[server] HTTP response received resource=" + << static_cast(resource) + << " bytes=" << body.size() + << " (parse not yet implemented)" << std::endl; + } + + // Stub: preserve existing charger_locations publication until HTTP response is confirmed correct + if (resource == safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION + && nullptr != charger_locations_writer_) + { + const auto locations = build_charger_locations(); + for (size_t i = 0; i < locations.size(); ++i) + { + charger_locations_writer_->write(locations[i], eprosima::safedds::dds::HANDLE_NIL); + } + std::cout << "[server] Published charger_locations count=" << locations.size() + << " (stub)" << std::endl; + } +} + +void ServerNode::periodic_refresh_all_resources() noexcept +{ + static constexpr safe_edge::pilot_server::RequestedDataType all_resources[] = { + safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION, + safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE, + safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION, + safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH, + safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS, + }; + + for (const auto resource : all_resources) + { + std::cout << "[server] Periodic refresh resource=" << static_cast(resource) << std::endl; + request_pilot_server_data(resource); + } +} + +void ServerNode::log_uptime() const noexcept +{ + const auto elapsed = std::chrono::steady_clock::now() - start_time_; + const auto seconds = std::chrono::duration_cast(elapsed).count(); + std::cout << "[server] Uptime=" << seconds << "s" << std::endl; +} + +void ServerNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat{}; + heartbeat.header_st.source = "server"; + heartbeat.service_name = "server"; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != + service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[server] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[server] Published ServiceHeartbeat" << std::endl; +} + +eprosima::safedds::execution::TimePoint ServerNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + next = eprosima::safedds::execution::TimePoint::min(next, refresh_timer_.next_trigger()); + next = eprosima::safedds::execution::TimePoint::min(next, uptime_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace server +} // namespace safe_edge diff --git a/safe_dds/server/test/test_server_integration.cpp b/safe_dds/server/test/test_server_integration.cpp new file mode 100644 index 0000000..61f681d --- /dev/null +++ b/safe_dds/server/test/test_server_integration.cpp @@ -0,0 +1,518 @@ +// test_server_integration.cpp +// +// Single binary integration test for safe_edge_server. +// Starts the server as a subprocess, runs all test suites, stops the server. +// +// Usage: +// ./test_server_integration +// ./test_server_integration --gtest_output=xml:results.xml (CI / JUnit) +// +// Environment variables: +// SAFE_EDGE_SERVER_BIN path to safe_edge_server binary (default: safe_edge_server) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +static eprosima::safedds::memory::container::StaticList< + eprosima::safedds::transport::Locator, 1U> g_peers; + +static bool init_peers() +{ + g_peers.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, 8020U)); + return true; +} + +static const bool PEERS_INIT = init_peers(); +static const char* SERVER_PID_FILE = "/tmp/safe_edge_server_test.pid"; +static const char* SERVER_LOG_FILE = "/tmp/safe_edge_server_test.log"; + +static eprosima::safedds::dds::DomainParticipant* make_participant( + eprosima::safedds::dds::DomainParticipantFactory& factory, + const char* name, + uint16_t port) +{ + eprosima::safedds::dds::DomainParticipantQos qos{}; + eprosima::safedds::memory::container::StaticString256 sname(name); + qos.participant_name() = sname; + qos.wire_protocol_qos().announced_locator = + eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, port); + qos.wire_protocol_qos().use_multicast_discovery = false; + qos.wire_protocol_qos().initial_peers = &g_peers; + return factory.create_participant(0U, qos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +static void destroy_participant( + eprosima::safedds::dds::DomainParticipantFactory& factory, + eprosima::safedds::dds::DomainParticipant*& participant, + eprosima::safedds::dds::Publisher*& publisher, + eprosima::safedds::dds::Subscriber*& subscriber, + eprosima::safedds::dds::Topic*& topic_a, + eprosima::safedds::dds::Topic*& topic_b, + eprosima::safedds::dds::DataWriter*& writer, + eprosima::safedds::dds::DataReader*& reader) noexcept +{ + writer = nullptr; + reader = nullptr; + + if (participant != nullptr) + { + (void)participant->delete_contained_entities(); + (void)factory.delete_participant(participant); + participant = nullptr; + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + + publisher = nullptr; + subscriber = nullptr; + topic_a = nullptr; + topic_b = nullptr; +} + +class ServerEnvironment : public ::testing::Environment +{ +public: + void SetUp() override + { + const char* bin = std::getenv("SAFE_EDGE_SERVER_BIN"); + const std::string server_bin = (bin != nullptr ? bin : "safe_edge_server"); + const std::string cmd = + "sh -c 'rm -f " + std::string(SERVER_PID_FILE) + + "; " + server_bin + " >" + SERVER_LOG_FILE + " 2>&1 & echo $! > " + + SERVER_PID_FILE + "'"; + ASSERT_EQ(0, std::system(cmd.c_str())) + << "Failed to launch safe_edge_server. " + "Set SAFE_EDGE_SERVER_BIN to the full path if needed."; + + std::cout << "[env] safe_edge_server started — waiting 5 s for init...\n"; + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::cout << "[env] Server ready.\n"; + } + + void TearDown() override + { + std::cout << "[env] safe_edge_server log:\n"; + std::system((std::string("sh -c 'if [ -f ") + SERVER_LOG_FILE + + " ]; then cat " + SERVER_LOG_FILE + "; else echo missing log; fi'").c_str()); + std::cout << "[env] Stopping safe_edge_server...\n"; + const std::string stop_cmd = + "sh -c '" + "if [ -f " + std::string(SERVER_PID_FILE) + " ]; then " + "pid=$(cat " + std::string(SERVER_PID_FILE) + "); " + "kill \"$pid\" 2>/dev/null || true; " + "sleep 1; " + "kill -9 \"$pid\" 2>/dev/null || true; " + "rm -f " + std::string(SERVER_PID_FILE) + "; " + "fi; " + "pkill -f safe_edge_server 2>/dev/null || true" + "'"; + std::system(stop_cmd.c_str()); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +}; + +static bool wait_for_charger_location( + eprosima::safedds::execution::ISpinnable& executor, + eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>& reader, + int timeout_s) +{ + const auto deadline = + std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); + + while (std::chrono::steady_clock::now() < deadline) + { + while (executor.has_pending_work()) + { + executor.spin(eprosima::safedds::execution::TIME_ZERO); + } + + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::safedds::dds::SampleInfo info{}; + if (reader.take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK && info.valid_data) + { + std::cout << "[dds] Received ChargerLocation id=" << sample.id + << " name=" << sample.name << "\n"; + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; +} + +static void drain_charger_locations( + eprosima::safedds::execution::ISpinnable& executor, + eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>& reader) +{ + while (executor.has_pending_work()) + { + executor.spin(eprosima::safedds::execution::TIME_ZERO); + } + + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (reader.take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK) + { + } +} + +static bool wait_for_query_response( + eprosima::safedds::execution::ISpinnable& executor, + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ServerQueryTypeSupport>& writer, + eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>& reader, + safe_edge::pilot_server::RequestedDataType type, + int timeout_s) +{ + const auto deadline = + std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); + + while (std::chrono::steady_clock::now() < deadline) + { + safe_edge::pilot_server::ServerQuery q{}; + q.requested_by = "test_query_dispatch"; + q.requested_data_type = type; + if (writer.write(q, eprosima::safedds::dds::HANDLE_NIL) == + eprosima::safedds::dds::ReturnCode::OK && + wait_for_charger_location(executor, reader, 1)) + { + return true; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + + return false; +} + +static void spin_for( + eprosima::safedds::execution::ISpinnable& executor, + std::chrono::milliseconds duration) +{ + const auto deadline = std::chrono::steady_clock::now() + duration; + + while (std::chrono::steady_clock::now() < deadline) + { + while (executor.has_pending_work()) + { + executor.spin(eprosima::safedds::execution::TIME_ZERO); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } +} + +struct QueryDispatchContext +{ + eprosima::safedds::dds::DomainParticipantFactory factory; + eprosima::safedds::dds::DomainParticipant* participant = nullptr; + eprosima::safedds::dds::Publisher* publisher = nullptr; + eprosima::safedds::dds::Subscriber* subscriber = nullptr; + eprosima::safedds::dds::Topic* charger_topic = nullptr; + eprosima::safedds::dds::Topic* query_topic = nullptr; + eprosima::safedds::dds::DataWriter* query_writer_base = nullptr; + eprosima::safedds::dds::DataReader* charger_reader_base = nullptr; + eprosima::safedds::execution::ISpinnable* executor = nullptr; + safe_edge::pilot_server::ChargerLocationTypeSupport charger_ts; + safe_edge::pilot_server::ServerQueryTypeSupport query_ts; + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ServerQueryTypeSupport>* query_writer = nullptr; + eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>* charger_reader = nullptr; +}; + +static QueryDispatchContext& query_dispatch_context() +{ + static QueryDispatchContext* ctx = new QueryDispatchContext(); + return *ctx; +} + +static void create_query_dispatch_context(QueryDispatchContext& ctx); +static void destroy_query_dispatch_context(QueryDispatchContext& ctx) noexcept; + +class QueryDispatchSuite : public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + create_query_dispatch_context(query_dispatch_context()); + } + + static void TearDownTestSuite() + { + destroy_query_dispatch_context(query_dispatch_context()); + } +}; + +static void create_query_dispatch_context( + QueryDispatchContext& ctx) +{ + (void)PEERS_INIT; + + ctx.participant = make_participant(ctx.factory, "TestQueryDispatch", 8011U); + ASSERT_NE(nullptr, ctx.participant); + + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + ctx.charger_ts.register_type(*ctx.participant, ctx.charger_ts.get_type_name())); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + ctx.query_ts.register_type(*ctx.participant, ctx.query_ts.get_type_name())); + + eprosima::safedds::memory::container::StaticString256 cname( + safe_edge::server::common::topic_names::charger_locations()); + eprosima::safedds::memory::container::StaticString256 qname( + safe_edge::server::common::topic_names::server_query()); + + ctx.charger_topic = ctx.participant->create_topic( + cname, ctx.charger_ts.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ctx.query_topic = ctx.participant->create_topic( + qname, ctx.query_ts.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, ctx.charger_topic); + ASSERT_NE(nullptr, ctx.query_topic); + + ctx.publisher = ctx.participant->create_publisher( + eprosima::safedds::dds::PublisherQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ctx.subscriber = ctx.participant->create_subscriber( + eprosima::safedds::dds::SubscriberQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, ctx.publisher); + ASSERT_NE(nullptr, ctx.subscriber); + + eprosima::safedds::dds::DataWriterQos wqos{}; + wqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + eprosima::safedds::dds::DataReaderQos rqos{}; + rqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + ctx.query_writer_base = ctx.publisher->create_datawriter( + *ctx.query_topic, wqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ctx.charger_reader_base = ctx.subscriber->create_datareader( + *ctx.charger_topic, rqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, ctx.query_writer_base); + ASSERT_NE(nullptr, ctx.charger_reader_base); + + ctx.query_writer = eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ServerQueryTypeSupport>::downcast(*ctx.query_writer_base); + ctx.charger_reader = eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>::downcast(*ctx.charger_reader_base); + ASSERT_NE(nullptr, ctx.query_writer); + ASSERT_NE(nullptr, ctx.charger_reader); + + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, ctx.publisher->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, ctx.subscriber->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, ctx.query_writer_base->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, ctx.charger_reader_base->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, ctx.participant->enable()); + + ctx.executor = ctx.factory.create_default_executor(); + ASSERT_NE(nullptr, ctx.executor); + + spin_for(*ctx.executor, std::chrono::seconds(3)); +} + +static void destroy_query_dispatch_context( + QueryDispatchContext& ctx) noexcept +{ + if (nullptr == ctx.participant) + { + return; + } + + destroy_participant(ctx.factory, ctx.participant, ctx.publisher, ctx.subscriber, + ctx.charger_topic, ctx.query_topic, ctx.query_writer_base, ctx.charger_reader_base); + ctx.executor = nullptr; + ctx.query_writer = nullptr; + ctx.charger_reader = nullptr; +} + +static void send_query( + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ServerQueryTypeSupport>& writer, + safe_edge::pilot_server::RequestedDataType type) +{ + safe_edge::pilot_server::ServerQuery q{}; + q.requested_by = "test_query_dispatch"; + q.requested_data_type = type; + EXPECT_EQ(eprosima::safedds::dds::ReturnCode::OK, + writer.write(q, eprosima::safedds::dds::HANDLE_NIL)); +} + +TEST_F(QueryDispatchSuite, ChargerLocationReturnsData) +{ + auto& ctx = query_dispatch_context(); + drain_charger_locations(*ctx.executor, *ctx.charger_reader); + EXPECT_TRUE(wait_for_query_response( + *ctx.executor, + *ctx.query_writer, + *ctx.charger_reader, + safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION, + 10)) << "No ChargerLocation received after query within 10 s"; +} + +TEST_F(QueryDispatchSuite, ChargerTypeHandledWithoutCrash) +{ + auto& ctx = query_dispatch_context(); + send_query(*ctx.query_writer, + safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE); + spin_for(*ctx.executor, std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchSuite, ChargingSessionHandledWithoutCrash) +{ + auto& ctx = query_dispatch_context(); + send_query(*ctx.query_writer, + safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION); + spin_for(*ctx.executor, std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchSuite, TransitHealthHandledWithoutCrash) +{ + auto& ctx = query_dispatch_context(); + send_query(*ctx.query_writer, + safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH); + spin_for(*ctx.executor, std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchSuite, TransitMetricsHandledWithoutCrash) +{ + auto& ctx = query_dispatch_context(); + send_query(*ctx.query_writer, + safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS); + spin_for(*ctx.executor, std::chrono::milliseconds(500)); +} + +TEST(PeriodicRefresh, TwoBurstsIn35Seconds) +{ + (void)PEERS_INIT; + + eprosima::safedds::dds::DomainParticipantFactory factory; + auto* participant = make_participant(factory, "TestPeriodicRefresh", 8030U); + ASSERT_NE(nullptr, participant); + + safe_edge::pilot_server::ChargerLocationTypeSupport charger_ts; + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, + charger_ts.register_type(*participant, charger_ts.get_type_name())); + + eprosima::safedds::memory::container::StaticString256 tname( + safe_edge::server::common::topic_names::charger_locations()); + auto* topic = participant->create_topic( + tname, charger_ts.get_type_name(), + eprosima::safedds::dds::TopicQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, topic); + + auto* sub = participant->create_subscriber( + eprosima::safedds::dds::SubscriberQos{}, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, sub); + + eprosima::safedds::dds::DataReaderQos rqos{}; + rqos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + auto* reader_base = sub->create_datareader( + *topic, rqos, nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + ASSERT_NE(nullptr, reader_base); + + auto* reader = eprosima::safedds::dds::TypedDataReader< + safe_edge::pilot_server::ChargerLocationTypeSupport>::downcast(*reader_base); + ASSERT_NE(nullptr, reader); + + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, sub->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, reader_base->enable()); + ASSERT_EQ(eprosima::safedds::dds::ReturnCode::OK, participant->enable()); + + auto* executor = factory.create_default_executor(); + ASSERT_NE(nullptr, executor); + + spin_for(*executor, std::chrono::seconds(3)); + + int burst_count = 0; + const auto t0 = std::chrono::steady_clock::now(); + const auto end = t0 + std::chrono::seconds(45); + auto last_sample = t0 - std::chrono::seconds(10); + + while (std::chrono::steady_clock::now() < end) + { + while (executor->has_pending_work()) + { + executor->spin(eprosima::safedds::execution::TIME_ZERO); + } + + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::safedds::dds::SampleInfo info{}; + if (reader->take_next_sample(sample, info) == + eprosima::safedds::dds::ReturnCode::OK && info.valid_data) + { + const auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_sample).count() > 2000) + { + burst_count++; + std::cout << "[dds] Burst " << burst_count << " at t=" + << std::chrono::duration_cast( + now - t0).count() << " s\n"; + } + last_sample = now; + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + EXPECT_GE(burst_count, 2) + << "Expected >=2 bursts (publication_match + periodic refresh), " + << "got " << burst_count; + + eprosima::safedds::dds::Publisher* unused_publisher = nullptr; + eprosima::safedds::dds::DataWriter* unused_writer = nullptr; + destroy_participant(factory, participant, unused_publisher, sub, + topic, topic, unused_writer, reader_base); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new ServerEnvironment()); + return RUN_ALL_TESTS(); +} From 4224b3f5c03f98d7ffc780735682fcf33a8000bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 28 May 2026 20:04:55 +0200 Subject: [PATCH 03/17] Add QNX env and setup info. Also added scripts (tests abd builders) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 27 ++ README.md | 235 ++++++++++++++++ qnx/targets/qemu-qnx800-x86_64/local/options | 119 ++++++++ .../local/snippets/data_files.custom | 2 + .../local/snippets/ifs_files.custom | 2 + .../local/snippets/profile.custom | 2 + .../qemu-qnx800-x86_64/local/valgrind.files | 12 + .../qemu-qnx800-x86_64/mkqnximage-wrapper.sh | 9 + qnx/toolchains/qnx8.cmake | 53 ++++ scripts/.gitkeep | 0 scripts/build_qnx.sh | 236 ++++++++++++++++ scripts/build_safedds_qnx.sh | 66 +++++ scripts/check_setup.sh | 130 +++++++++ scripts/env.example | 14 + scripts/launch_tpi_2_1_test.sh | 254 ++++++++++++++++++ scripts/launch_tpi_2_2_test.sh | 254 ++++++++++++++++++ scripts/launch_tpi_2_3_test.sh | 14 + 17 files changed, 1429 insertions(+) create mode 100644 qnx/targets/qemu-qnx800-x86_64/local/options create mode 100644 qnx/targets/qemu-qnx800-x86_64/local/snippets/data_files.custom create mode 100644 qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_files.custom create mode 100644 qnx/targets/qemu-qnx800-x86_64/local/snippets/profile.custom create mode 100644 qnx/targets/qemu-qnx800-x86_64/local/valgrind.files create mode 100755 qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh create mode 100644 qnx/toolchains/qnx8.cmake delete mode 100644 scripts/.gitkeep create mode 100755 scripts/build_qnx.sh create mode 100755 scripts/build_safedds_qnx.sh create mode 100755 scripts/check_setup.sh create mode 100644 scripts/env.example create mode 100755 scripts/launch_tpi_2_1_test.sh create mode 100755 scripts/launch_tpi_2_2_test.sh create mode 100755 scripts/launch_tpi_2_3_test.sh diff --git a/.gitignore b/.gitignore index cd4e379..d9d2b88 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,32 @@ compile_commands.json !*build*.sh # Generated QNX artifacts +qnx/build/ +qnx/install/ qnx/targets/*/output/ scripts/logs/ + +# Repo-local QNX assets are intentionally versionable even though the generic +# build/install/include patterns above would otherwise hide parts of them. +!qnx/ +!qnx/toolchains/ +!qnx/toolchains/** +!qnx/targets/ +!qnx/targets/qemu-qnx800-*/ +!qnx/targets/qemu-qnx800-*/mkqnximage-wrapper.sh +!qnx/targets/qemu-qnx800-*/local/ +!qnx/targets/qemu-qnx800-*/local/options +!qnx/targets/qemu-qnx800-*/local/valgrind.files +!qnx/targets/qemu-qnx800-*/local/misc_files/ +!qnx/targets/qemu-qnx800-*/local/snippets/ +!qnx/targets/qemu-qnx800-*/local/snippets/data_files.custom +!qnx/targets/qemu-qnx800-*/local/snippets/ifs_files.custom +!qnx/targets/qemu-qnx800-*/local/snippets/profile.custom + +# Local QNX keys/credentials and generated snippets are rewritten by QNX tooling +# or by the TPI launch scripts and must not be committed. +qnx/targets/qemu-qnx800-*/local/misc_files/* +qnx/targets/qemu-qnx800-*/local/ssh-ident +qnx/targets/qemu-qnx800-*/local/snippets/ifs_start.custom +qnx/targets/qemu-qnx800-*/local/snippets/post_start.custom +qnx/targets/qemu-qnx800-*/local/snippets/system_files.custom diff --git a/README.md b/README.md index e69de29..b6f75fd 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,235 @@ +# SafeEDGE + +This repository is self-contained for SafeEDGE source code, QNX target definitions, build scripts, and test launchers. +It does not require the old `~/Safe/SAFE-EDGE` repository. + +It does not vendor third-party SDKs or source trees. QNX SDP 8 and Safe-DDS source code must be installed/provided outside this repository and pointed to with environment variables. + +## Customer Quick Start + +For Ubuntu/Debian hosts, install the host packages that can be installed automatically: + +```bash +bash scripts/install_host_deps.sh +``` + +Provide the two external inputs that are not stored in this repository: + +```bash +export QNX_SDP_ROOT="/path/to/qnx800" +export QNX_HOST="$QNX_SDP_ROOT/host/linux/x86_64" +export QNX_TARGET="$QNX_SDP_ROOT/target/qnx" +export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" +``` + +`scripts/env.example` contains the same variables as a source-able template. + +Check the environment: + +```bash +bash scripts/check_setup.sh +``` + +Build and test: + +```bash +bash scripts/build_safedds_qnx.sh -- -j2 +bash scripts/build_qnx.sh -- -j2 +bash scripts/launch_tpi_2_3_test.sh +bash scripts/launch_tpi_2_1_test.sh +bash scripts/launch_tpi_2_2_test.sh +``` + +For Linux-only validation of the common server component: + +```bash +bash scripts/install_host_deps.sh --linux-only +bash scripts/check_setup.sh --linux-only +bash scripts/launch_tpi_2_3_test.sh +``` + +## Repository Paths + +- Shared IDL sources: `idl/` +- Safe DDS generated headers: `safe_dds/idl/` +- Shared server code: `common_server/` +- Safe DDS server: `safe_dds/server/` +- Safe DDS edge: `safe_dds/edge/` +- QNX toolchain file: `qnx/toolchains/qnx8.cmake` +- Safe DDS QNX build script: `scripts/build_safedds_qnx.sh` +- Generated Safe DDS QNX package: `qnx/install/safedds-qnx8-x86_64/safedds` +- Bundled QNX QEMU target: `qnx/targets/qemu-qnx800-x86_64` +- Scripts: `scripts/` +- Test logs: `scripts/logs/` + +## Versioned QNX Assets + +These files are needed by the build and QNX test scripts and are intended to be kept in this repository: + +- `qnx/toolchains/qnx8.cmake` +- `scripts/build_safedds_qnx.sh` +- `qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh` +- `qnx/targets/qemu-qnx800-x86_64/local/options` +- `qnx/targets/qemu-qnx800-x86_64/local/valgrind.files` +- stable snippets under `qnx/targets/qemu-qnx800-x86_64/local/snippets/` + +These files are generated and should not be committed: + +- `qnx/build/` +- `qnx/install/` +- `qnx/targets/qemu-qnx800-x86_64/output/` +- `scripts/logs/` +- `qnx/targets/qemu-qnx800-x86_64/local/misc_files/*` +- `qnx/targets/qemu-qnx800-x86_64/local/ssh-ident` +- `qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_start.custom` +- `qnx/targets/qemu-qnx800-x86_64/local/snippets/post_start.custom` +- `qnx/targets/qemu-qnx800-x86_64/local/snippets/system_files.custom` + +The `misc_files` entries include QNX VM keys, `shadow`, and other local image files. The three generated snippets above are rewritten by `launch_tpi_2_1_test.sh` and `launch_tpi_2_2_test.sh`. + +## External Prerequisites + +### Required to build QNX targets + +- QNX SDP 8 installed on the host +- Safe-DDS source tree available on the host +- `SAFE_DDS_PATH` exported and pointing to the Safe-DDS source tree +- Default SDK path used by scripts if `QNX_SDP_ROOT` is not set: `/home/$USER/qnx800` +- Required SDK files/directories: + - `$QNX_SDP_ROOT/qnxsdp-env.sh` + - `$QNX_SDP_ROOT/host/linux/x86_64` + - `$QNX_SDP_ROOT/target/qnx` +- `cmake` +- a C/C++ build toolchain usable by CMake on the host +- Internet access during configure time if GTest is not already available locally + +`scripts/install_host_deps.sh` installs the host packages listed above where possible. It does not install QNX SDP 8 or Safe-DDS sources. + +### Required to run QNX VM tests + +- `qemu-system-x86_64` +- `sshpass` +- `brctl` from `bridge-utils` +- `file` + +### Required to run the Linux test + +- `cmake` +- `libcurl` development package + - Ubuntu/Debian: `sudo apt install libcurl4-openssl-dev` + +## Environment Variables + +Required for QNX builds: + +```bash +export QNX_SDP_ROOT="/home/$USER/qnx800" +export QNX_HOST="$QNX_SDP_ROOT/host/linux/x86_64" +export QNX_TARGET="$QNX_SDP_ROOT/target/qnx" +export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" +``` + +Optional variables: + +```bash +export QNX_ARCH="x86_64" +export CMAKE_BUILD_TYPE="Release" +``` + +## Setup Check + +Install Ubuntu/Debian host packages: + +```bash +bash scripts/install_host_deps.sh +``` + +For only the Linux test dependencies: + +```bash +bash scripts/install_host_deps.sh --linux-only +``` + +Run: + +```bash +bash scripts/check_setup.sh +``` + +What it does: + +- checks for required host tools +- validates the expected QNX SDK path +- validates repo paths under `qnx/` +- validates that `SAFE_DDS_PATH` is defined for QNX builds +- reports local/generated QNX folders if they have not been created yet + +For a Linux-only check: + +```bash +bash scripts/check_setup.sh --linux-only +``` + +## Build + +Build and install Safe DDS for QNX if `qnx/install/safedds-qnx8-x86_64/safedds` is missing: + +```bash +bash scripts/build_safedds_qnx.sh -- -j2 +``` + +Build all QNX targets from this repository: + +```bash +bash scripts/build_qnx.sh -- -j2 +``` + +This configures and installs: + +- `common_server` +- `safe_dds/server` +- `safe_dds/edge` + +Installed binaries end up in: + +- `common_server/install/server-common-qnx8-x86_64-Release/bin` +- `safe_dds/install/server-qnx8-x86_64-Release/bin` +- `safe_dds/install/edge-qnx8-x86_64-Release/bin` + +## Test + +### KPI/TPI 2.3: Linux test + +```bash +bash scripts/launch_tpi_2_3_test.sh +``` + +Log file: + +- `scripts/logs/launch_tpi_2_3.log` + +### TPI 2.1: QNX server test + +```bash +bash scripts/launch_tpi_2_1_test.sh +``` + +Log file: + +- `scripts/logs/launch_tpi_2_1.log` + +### TPI 2.2: QNX edge test + +```bash +bash scripts/launch_tpi_2_2_test.sh +``` + +Log file: + +- `scripts/logs/launch_tpi_2_2.log` + +## Notes + +- The QNX tests rebuild the QEMU image from `qnx/targets/qemu-qnx800-x86_64`. +- Generated target output is recreated under `qnx/targets/qemu-qnx800-x86_64/output/` and is ignored by git. +- The Linux test may execute real Pilot Server checks if `/etc/safe-edge/server.ini` exists on the host. diff --git a/qnx/targets/qemu-qnx800-x86_64/local/options b/qnx/targets/qemu-qnx800-x86_64/local/options new file mode 100644 index 0000000..d4c6eb8 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/local/options @@ -0,0 +1,119 @@ +OPT_AARCH64_VERSION='8' +DEF_OPT_AARCH64_VERSION='8' +OPT_ABLELOCK='yes' +DEF_OPT_ABLELOCK='yes' +OPT_ARCH='x86_64' +DEF_OPT_ARCH='x86_64' +OPT_ASLR='yes' +DEF_OPT_ASLR='yes' +OPT_ASSUMED_IP='none' +DEF_OPT_ASSUMED_IP='none' +OPT_BOOT_SIZE='0' +DEF_OPT_BOOT_SIZE='0' +OPT_BUILD='all' +DEF_OPT_BUILD='all' +OPT_CERTICOM='no' +DEF_OPT_CERTICOM='no' +OPT_COPY='all' +DEF_OPT_COPY='all' +OPT_COPY_DEST='none' +DEF_OPT_COPY_DEST='none' +OPT_CPU='2' +DEF_OPT_CPU='2' +OPT_CRYPTODEV='no' +DEF_OPT_CRYPTODEV='no' +OPT_DATA_INODES='3000' +DEF_OPT_DATA_INODES='3000' +OPT_DATA_SIZE='60' +DEF_OPT_DATA_SIZE='60' +OPT_EXTRA_DIRS='none' +DEF_OPT_EXTRA_DIRS='none' +OPT_GRAPHICS='no' +DEF_OPT_GRAPHICS='no' +OPT_GUEST='none' +DEF_OPT_GUEST='none' +OPT_HOSTNAME='qemu-qnx800-x86_64' +DEF_OPT_HOSTNAME='noname' +OPT_IO_SOCK_DIAG='no' +DEF_OPT_IO_SOCK_DIAG='no' +OPT_IP='dhcp' +DEF_OPT_IP='dhcp' +OPT_MACADDR='52:54:00:38:3a:92' +DEF_OPT_MACADDR='generate' +OPT_NFS='no' +DEF_OPT_NFS='no' +OPT_PART_SIZES='full' +DEF_OPT_PART_SIZES='full' +OPT_PATHTRUST='no' +DEF_OPT_PATHTRUST='no' +OPT_PERL='no' +DEF_OPT_PERL='no' +OPT_PKCS11='no' +DEF_OPT_PKCS11='no' +OPT_POLICY='none' +DEF_OPT_POLICY='none' +OPT_PROC='no' +DEF_OPT_PROC='no' +OPT_PYTHON='no' +DEF_OPT_PYTHON='no' +OPT_QAUDIT='no' +DEF_OPT_QAUDIT='no' +OPT_QCFS='no' +DEF_OPT_QCFS='no' +OPT_QFIM='no' +DEF_OPT_QFIM='no' +OPT_QH_CONFIG='no' +DEF_OPT_QH_CONFIG='no' +OPT_QTD='no' +DEF_OPT_QTD='no' +OPT_QTSAFEFS='no' +DEF_OPT_QTSAFEFS='no' +OPT_QVM='no' +DEF_OPT_QVM='no' +OPT_RAM='1G' +DEF_OPT_RAM='1G' +OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +DEF_OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +OPT_ROOT='no' +DEF_OPT_ROOT='no' +OPT_SANITIZERS='no' +DEF_OPT_SANITIZERS='no' +OPT_SECPOL='no' +DEF_OPT_SECPOL='no' +OPT_SECURE_DATA='no' +DEF_OPT_SECURE_DATA='no' +OPT_SECURE_PROCFS='yes' +DEF_OPT_SECURE_PROCFS='yes' +OPT_SLM='no' +DEF_OPT_SLM='no' +OPT_SSHD_PREGEN='yes' +DEF_OPT_SSHD_PREGEN='yes' +OPT_SSH_IDENT='prompt' +DEF_OPT_SSH_IDENT='prompt' +OPT_SYS_INODES='1000' +DEF_OPT_SYS_INODES='1000' +OPT_SYS_SIZE='20' +DEF_OPT_SYS_SIZE='20' +OPT_TCG='no' +DEF_OPT_TCG='no' +OPT_TIME_SERVERS='pool.ntp.org' +DEF_OPT_TIME_SERVERS='pool.ntp.org' +OPT_TOMCRYPT='no' +DEF_OPT_TOMCRYPT='no' +OPT_TYPE='qemu' +DEF_OPT_TYPE='qemu' +OPT_TZ='UTC0' +DEF_OPT_TZ='UTC0' +OPT_UNION='yes' +DEF_OPT_UNION='yes' +OPT_UPDATE='all' +DEF_OPT_UPDATE='all' +OPT_USB='yes' +DEF_OPT_USB='yes' +OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +DEF_OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +OPT_VALGRIND='no' +DEF_OPT_VALGRIND='no' +OPT_ZONEINFO='no' +DEF_OPT_ZONEINFO='no' +VERSION=3 diff --git a/qnx/targets/qemu-qnx800-x86_64/local/snippets/data_files.custom b/qnx/targets/qemu-qnx800-x86_64/local/snippets/data_files.custom new file mode 100644 index 0000000..3ee0a4c --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/local/snippets/data_files.custom @@ -0,0 +1,2 @@ +# local/snippets/data_files.custom +# Placeholder for local list of files to add to data partition diff --git a/qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_files.custom b/qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_files.custom new file mode 100644 index 0000000..f54033c --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_files.custom @@ -0,0 +1,2 @@ +# local/snippets/ifs_files.custom +# Placeholder for local list of files to add to ifs diff --git a/qnx/targets/qemu-qnx800-x86_64/local/snippets/profile.custom b/qnx/targets/qemu-qnx800-x86_64/local/snippets/profile.custom new file mode 100644 index 0000000..61b37bb --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/local/snippets/profile.custom @@ -0,0 +1,2 @@ +# local/snippets/profile.custom +# Contents are included at the end of /etc/profile diff --git a/qnx/targets/qemu-qnx800-x86_64/local/valgrind.files b/qnx/targets/qemu-qnx800-x86_64/local/valgrind.files new file mode 100644 index 0000000..65cf691 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/local/valgrind.files @@ -0,0 +1,12 @@ +# Add the names of the binaries you want to add a symbols to the target +# to debug through valgrind. +# +# e.g sbin/devb-ram +# +# Binaries that should be picked up from SDP/target_symbols should be +# suffixed with .sym else the stage or normal SDP binary will be picked up +# +# e.g. sbin/devb-ram.sym +# +# Note: libc and ldqnx are added automatically. +# diff --git a/qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh b/qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh new file mode 100755 index 0000000..087aa69 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" +exec mkqnximage "$@" diff --git a/qnx/toolchains/qnx8.cmake b/qnx/toolchains/qnx8.cmake new file mode 100644 index 0000000..8c85e24 --- /dev/null +++ b/qnx/toolchains/qnx8.cmake @@ -0,0 +1,53 @@ +set(CMAKE_SYSTEM_NAME QNX) +set(CMAKE_SYSTEM_VERSION 8.0.0) + +# QNX cross-builds cannot execute target binaries during configure. +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +if(DEFINED QNX_HOST) + set(_qnx_host "${QNX_HOST}") +elseif(DEFINED ENV{QNX_HOST}) + set(_qnx_host "$ENV{QNX_HOST}") +else() + message(FATAL_ERROR "QNX_HOST is not set. Export QNX_HOST before running CMake.") +endif() + +if(DEFINED QNX_TARGET) + set(_qnx_target "${QNX_TARGET}") +elseif(DEFINED ENV{QNX_TARGET}) + set(_qnx_target "$ENV{QNX_TARGET}") +else() + message(FATAL_ERROR "QNX_TARGET is not set. Export QNX_TARGET before running CMake.") +endif() + +set(QNX_ARCH "x86_64" CACHE STRING "QNX target architecture (x86_64 or aarch64le)") +set_property(CACHE QNX_ARCH PROPERTY STRINGS x86_64 aarch64le) + +if(QNX_ARCH STREQUAL "x86_64") + set(_qnx_tool_prefix "ntox86_64") + set(CMAKE_SYSTEM_PROCESSOR "x86_64") +elseif(QNX_ARCH STREQUAL "aarch64le") + set(_qnx_tool_prefix "ntoaarch64") + set(CMAKE_SYSTEM_PROCESSOR "aarch64") +else() + message(FATAL_ERROR "Unsupported QNX_ARCH='${QNX_ARCH}'. Use x86_64 or aarch64le.") +endif() + +set(ENV{QNX_HOST} "${_qnx_host}") +set(ENV{QNX_TARGET} "${_qnx_target}") +set(ENV{PATH} "${_qnx_host}/usr/bin:$ENV{PATH}") + +set(CMAKE_SYSROOT "${_qnx_target}" CACHE PATH "QNX target sysroot" FORCE) +set(CMAKE_C_COMPILER "${_qnx_host}/usr/bin/${_qnx_tool_prefix}-gcc" CACHE FILEPATH "QNX C compiler" FORCE) +set(CMAKE_CXX_COMPILER "${_qnx_host}/usr/bin/${_qnx_tool_prefix}-g++" CACHE FILEPATH "QNX C++ compiler" FORCE) +set(CMAKE_AR "${_qnx_host}/usr/bin/${_qnx_tool_prefix}-ar" CACHE FILEPATH "QNX archiver" FORCE) +set(CMAKE_RANLIB "${_qnx_host}/usr/bin/${_qnx_tool_prefix}-ranlib" CACHE FILEPATH "QNX ranlib" FORCE) +set(CMAKE_STRIP "${_qnx_host}/usr/bin/${_qnx_tool_prefix}-strip" CACHE FILEPATH "QNX strip" FORCE) +set(CMAKE_C_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} -D_QNX_SOURCE") +set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} -D_QNX_SOURCE") + +set(CMAKE_FIND_ROOT_PATH "${_qnx_target}/${QNX_ARCH}" "${_qnx_target}" CACHE STRING "QNX root paths for find_*" FORCE) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/scripts/.gitkeep b/scripts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/build_qnx.sh b/scripts/build_qnx.sh new file mode 100755 index 0000000..696ae3c --- /dev/null +++ b/scripts/build_qnx.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_HOST:=${QNX_SDP_ROOT}/host/linux/x86_64}" +: "${QNX_TARGET:=${QNX_SDP_ROOT}/target/qnx}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${CMAKE_GENERATOR:=Unix Makefiles}" +: "${QNX_COMMON_CXXFLAGS:=-D_QNX_SOURCE}" +: "${QNX_TOOLCHAIN_FILE:=${WORKSPACE_ROOT}/qnx/toolchains/qnx8.cmake}" +: "${COMMON_SERVER_BUILD_FOLDER:=${WORKSPACE_ROOT}/common_server/build/server-common-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${COMMON_SERVER_INSTALL_FOLDER:=${WORKSPACE_ROOT}/common_server/install/server-common-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${SERVER_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/server-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${SERVER_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/server-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${EDGE_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${EDGE_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${SAFE_DDS_IDL_GENERATOR:-}" +: "${SAFE_DDS_IDL_GENERATOR_ARGS:-}" +: "${SAFEDDS_DIR:=${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}/safedds}" + +COMMON_IDL_SOURCE_DIR="${WORKSPACE_ROOT}/idl" +SAFE_DDS_IDL_DIR="${WORKSPACE_ROOT}/safe_dds/idl" + +REGEN_IDL=0 +EXTRA_BUILD_ARGS=() + +usage() { + cat <] + +Options: + -i, --idl regenerate safe_dds/idl from the shared idl/*.idl sources + -h, --help show this help message + +Environment variables: + SAFE_DDS_IDL_GENERATOR command used to regenerate Safe DDS IDL artifacts + SAFE_DDS_IDL_GENERATOR_ARGS extra args appended to the IDL generator command + QNX_TOOLCHAIN_FILE path to the QNX toolchain file + SAFEDDS_DIR path to cross-compiled Safe DDS CMake package for QNX + QNX_ARCH x86_64 or aarch64le (default: x86_64) + CMAKE_BUILD_TYPE CMake configuration type (default: Release) + +The script always configures and builds: + - common_server + - safe_dds/server + - safe_dds/edge + +If there are no source changes, CMake will skip recompilation internally. +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -i|--idl) + REGEN_IDL=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + EXTRA_BUILD_ARGS=("$@") + break + ;; + *) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + esac +done + +find_idl_generator() { + if [[ -n "${SAFE_DDS_IDL_GENERATOR:-}" ]]; then + local generator_bin="${SAFE_DDS_IDL_GENERATOR%% *}" + if command -v "${generator_bin}" >/dev/null 2>&1; then + echo "${SAFE_DDS_IDL_GENERATOR}" + return 0 + fi + fi + + local candidates=(safeddsgen safedds-idl-gen safedds-gen eprosima_safeddsgen) + for prog in "${candidates[@]}"; do + if command -v "${prog}" >/dev/null 2>&1; then + echo "${prog}" + return 0 + fi + done + + return 1 +} + +regenerate_idl() { + local generator + generator="$(find_idl_generator)" || { + echo "No IDL generator found. Set SAFE_DDS_IDL_GENERATOR to a valid command." >&2 + exit 1 + } + + if [[ ! -d "${COMMON_IDL_SOURCE_DIR}" ]]; then + echo "Shared IDL source directory not found at '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 + fi + + mkdir -p "${SAFE_DDS_IDL_DIR}" + + local cleanup_links=() + local idl_file + for idl_file in "${COMMON_IDL_SOURCE_DIR}"/*.idl; do + if [[ ! -e "${idl_file}" ]]; then + echo "No .idl files found in '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 + fi + + local link_target="${SAFE_DDS_IDL_DIR}/$(basename "${idl_file}")" + ln -sfn "${idl_file}" "${link_target}" + cleanup_links+=("${link_target}") + done + + echo "Regenerating Safe DDS IDL artifacts" + echo " source : ${COMMON_IDL_SOURCE_DIR}" + echo " generated: ${SAFE_DDS_IDL_DIR}" + echo " generator: ${generator} ${SAFE_DDS_IDL_GENERATOR_ARGS:-}" + + pushd "${SAFE_DDS_IDL_DIR}" >/dev/null + if ! bash -lc "${generator} ${SAFE_DDS_IDL_GENERATOR_ARGS:-}"; then + popd >/dev/null + rm -f "${cleanup_links[@]}" + exit 1 + fi + popd >/dev/null + + rm -f "${cleanup_links[@]}" +} + +configure_and_build() { + local src_dir="$1" + local build_dir="$2" + local install_dir="$3" + local name="$4" + shift 4 + local extra_cmake_args=("$@") + local cache_file="${build_dir}/CMakeCache.txt" + + if [[ -f "${cache_file}" ]]; then + local cached_source + cached_source="$(sed -n 's/^CMAKE_HOME_DIRECTORY:INTERNAL=//p' "${cache_file}")" + if [[ -n "${cached_source}" && "${cached_source}" != "${src_dir}" ]]; then + echo "Removing stale CMake cache for ${name}" + echo " cached source: ${cached_source}" + rm -rf "${build_dir}" + mkdir -p "${build_dir}" + fi + fi + + local cmake_args=( + -S "${src_dir}" + -B "${build_dir}" + -G "${CMAKE_GENERATOR}" + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DCMAKE_TOOLCHAIN_FILE="${QNX_TOOLCHAIN_FILE}" + -DQNX_ARCH="${QNX_ARCH}" + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS:-${QNX_COMMON_CXXFLAGS}}" + -DCMAKE_INSTALL_PREFIX="${install_dir}" + ) + + cmake_args+=("-Dsafedds_DIR=${SAFEDDS_DIR}") + cmake_args+=("${extra_cmake_args[@]}") + + echo "Configuring ${name}" + echo " source : ${src_dir}" + echo " build : ${build_dir}" + echo " install: ${install_dir}" + cmake "${cmake_args[@]}" + cmake --build "${build_dir}" --target install "${EXTRA_BUILD_ARGS[@]}" + echo -e "Finished building ${name}\n" +} + +case "${QNX_ARCH}" in + x86_64|aarch64le) ;; + *) + echo "Unsupported QNX_ARCH='${QNX_ARCH}'. Use x86_64 or aarch64le." >&2 + exit 1 + ;; +esac + +if [[ ! -d "${QNX_HOST}" || ! -d "${QNX_TARGET}" ]]; then + echo "QNX SDK paths not found. QNX_HOST='${QNX_HOST}', QNX_TARGET='${QNX_TARGET}'" >&2 + exit 1 +fi + +if [[ ! -f "${QNX_TOOLCHAIN_FILE}" ]]; then + echo "QNX toolchain file not found. QNX_TOOLCHAIN_FILE='${QNX_TOOLCHAIN_FILE}'" >&2 + exit 1 +fi + +if [[ ! -d "${SAFEDDS_DIR}" ]]; then + echo "Safe DDS QNX install not found at '${SAFEDDS_DIR}'" >&2 + echo "Build it first with: bash scripts/build_safedds_qnx.sh -- -j2" >&2 + exit 1 +fi + +if [[ ! -d "${COMMON_IDL_SOURCE_DIR}" ]]; then + echo "Shared IDL source directory not found at '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 +fi + +if [[ ! -d "${SAFE_DDS_IDL_DIR}" ]]; then + echo "Safe DDS generated IDL directory not found at '${SAFE_DDS_IDL_DIR}'" >&2 + exit 1 +fi + +export QNX_HOST +export QNX_TARGET +export PATH="${QNX_HOST}/usr/bin:${PATH}" + +if (( REGEN_IDL )); then + regenerate_idl +fi + +mkdir -p "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" \ + "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" \ + "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" + +configure_and_build "${WORKSPACE_ROOT}/common_server" "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" "common_server" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/server" "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" "safe_dds/server" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/edge" "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" "safe_dds/edge" + +echo "All SafeEDGE QNX targets built successfully." diff --git a/scripts/build_safedds_qnx.sh b/scripts/build_safedds_qnx.sh new file mode 100755 index 0000000..b1580a7 --- /dev/null +++ b/scripts/build_safedds_qnx.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_HOST:=${QNX_SDP_ROOT}/host/linux/x86_64}" +: "${QNX_TARGET:=${QNX_SDP_ROOT}/target/qnx}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${CMAKE_GENERATOR:=Unix Makefiles}" +: "${QNX_COMMON_CXXFLAGS:=-D_QNX_SOURCE}" +: "${QNX_BUILD_SAFEDDS_FOLDER:=${WORKSPACE_ROOT}/qnx/build/safedds-qnx8-${QNX_ARCH}}" +: "${QNX_INSTALL_SAFEDDS_FOLDER:=${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}}" + +if [[ -z "${SAFE_DDS_PATH:-}" ]]; then + echo "SAFE_DDS_PATH is not defined." >&2 + echo "Set it to the Safe-DDS source tree, for example:" >&2 + echo " export SAFE_DDS_PATH=/path/to/Safe-DDS-source-release" >&2 + exit 1 +fi + +if [[ ! -d "${SAFE_DDS_PATH}" ]]; then + echo "Safe-DDS source path not found: ${SAFE_DDS_PATH}" >&2 + exit 1 +fi + +if [[ ! -d "${QNX_HOST}" || ! -d "${QNX_TARGET}" ]]; then + echo "QNX SDK paths not found. QNX_HOST='${QNX_HOST}', QNX_TARGET='${QNX_TARGET}'" >&2 + exit 1 +fi + +case "${QNX_ARCH}" in + x86_64|aarch64le) ;; + *) + echo "Unsupported QNX_ARCH='${QNX_ARCH}'. Use x86_64 or aarch64le." >&2 + exit 1 + ;; +esac + +export QNX_HOST +export QNX_TARGET +export PATH="${QNX_HOST}/usr/bin:${PATH}" + +mkdir -p "${QNX_BUILD_SAFEDDS_FOLDER}" "${QNX_INSTALL_SAFEDDS_FOLDER}" + +echo "Configuring Safe-DDS for QNX 8" +echo " source : ${SAFE_DDS_PATH}" +echo " build : ${QNX_BUILD_SAFEDDS_FOLDER}" +echo " install: ${QNX_INSTALL_SAFEDDS_FOLDER}" +echo " arch : ${QNX_ARCH}" + +cmake \ + -S "${SAFE_DDS_PATH}" \ + -B "${QNX_BUILD_SAFEDDS_FOLDER}" \ + -G "${CMAKE_GENERATOR}" \ + -DCMAKE_TOOLCHAIN_FILE="${WORKSPACE_ROOT}/qnx/toolchains/qnx8.cmake" \ + -DQNX_ARCH="${QNX_ARCH}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS:-${QNX_COMMON_CXXFLAGS}}" \ + -DCMAKE_INSTALL_PREFIX="${QNX_INSTALL_SAFEDDS_FOLDER}" \ + -DSAFEDDS_LOG_LEVEL="WARNING" + +cmake --build "${QNX_BUILD_SAFEDDS_FOLDER}" --target install "$@" diff --git a/scripts/check_setup.sh b/scripts/check_setup.sh new file mode 100755 index 0000000..5896ca1 --- /dev/null +++ b/scripts/check_setup.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_HOST:=${QNX_SDP_ROOT}/host/linux/x86_64}" +: "${QNX_TARGET:=${QNX_SDP_ROOT}/target/qnx}" +: "${QNX_ARCH:=x86_64}" + +LINUX_ONLY=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +require_cmd() { + local cmd="$1" + if ! command -v "${cmd}" >/dev/null 2>&1; then + echo "Missing command: ${cmd}" >&2 + echo "For Ubuntu/Debian host packages, run: bash scripts/install_host_deps.sh" >&2 + return 1 + fi +} + +require_path() { + local path="$1" + if [[ ! -e "${path}" ]]; then + echo "Missing path: ${path}" >&2 + return 1 + fi +} + +echo "Checking common prerequisites..." +require_cmd bash +require_cmd cmake +require_cmd tee +require_cmd curl-config +require_path "${WORKSPACE_ROOT}/README.md" +require_path "${WORKSPACE_ROOT}/common_server/CMakeLists.txt" +require_path "${WORKSPACE_ROOT}/safe_dds/server/CMakeLists.txt" +require_path "${WORKSPACE_ROOT}/safe_dds/edge/CMakeLists.txt" +require_path "${WORKSPACE_ROOT}/scripts/build_qnx.sh" +require_path "${WORKSPACE_ROOT}/scripts/build_safedds_qnx.sh" +require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_1_test.sh" +require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_2_test.sh" +require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_3_test.sh" + +if (( LINUX_ONLY )); then + echo "Linux-only setup check completed." + exit 0 +fi + +echo "Checking QNX SDK..." +if [[ ! -e "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "Missing QNX SDK environment file: ${QNX_SDP_ROOT}/qnxsdp-env.sh" >&2 + echo "Install QNX SDP 8 and/or set QNX_SDP_ROOT to its installation path." >&2 + exit 1 +fi +require_path "${QNX_HOST}" +require_path "${QNX_TARGET}" + +echo "Checking QNX host tools..." +require_cmd qemu-system-x86_64 +require_cmd sshpass +require_cmd brctl +require_cmd file + +echo "Checking repo-local QNX assets..." +require_path "${WORKSPACE_ROOT}/qnx/toolchains/qnx8.cmake" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/mkqnximage-wrapper.sh" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/options" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/valgrind.files" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/snippets" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/snippets/data_files.custom" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/snippets/ifs_files.custom" +require_path "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/snippets/profile.custom" + +if [[ ! -d "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/misc_files" ]]; then + echo "QNX local misc_files directory not found yet. Test launchers create it when needed." +fi + +if [[ -e "${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}/local/ssh-ident" ]]; then + echo "Found local QNX ssh-ident file. It is local/generated and intentionally ignored by git." +fi + +echo "Checking Safe-DDS source configuration..." +if [[ -z "${SAFE_DDS_PATH:-}" ]]; then + echo "SAFE_DDS_PATH is not defined." >&2 + echo "Set it to the Safe-DDS source tree before building SafeDDS for QNX." >&2 + echo "Example: export SAFE_DDS_PATH=/path/to/Safe-DDS-source-release" >&2 + exit 1 +fi +require_path "${SAFE_DDS_PATH}" + +if [[ ! -d "${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}/safedds" ]]; then + echo "Safe DDS QNX install not found yet." + echo "Build it with: bash scripts/build_safedds_qnx.sh -- -j2" +fi + +echo "Setup check completed." +echo "QNX_SDP_ROOT=${QNX_SDP_ROOT}" +echo "QNX_HOST=${QNX_HOST}" +echo "QNX_TARGET=${QNX_TARGET}" +echo "SAFE_DDS_PATH=${SAFE_DDS_PATH}" +echo "QNX target=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}" diff --git a/scripts/env.example b/scripts/env.example new file mode 100644 index 0000000..3106564 --- /dev/null +++ b/scripts/env.example @@ -0,0 +1,14 @@ +# SafeEDGE environment example. +# Copy values into your shell or source a local copy adapted to your machine. + +# QNX SDP 8 installation root. This tree is external and is not stored in this repository. +export QNX_SDP_ROOT="/path/to/qnx800" +export QNX_HOST="$QNX_SDP_ROOT/host/linux/x86_64" +export QNX_TARGET="$QNX_SDP_ROOT/target/qnx" + +# Safe-DDS source tree. This tree is external and is not stored in this repository. +export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" + +# Optional build settings. +export QNX_ARCH="x86_64" +export CMAKE_BUILD_TYPE="Release" diff --git a/scripts/launch_tpi_2_1_test.sh b/scripts/launch_tpi_2_1_test.sh new file mode 100755 index 0000000..283c4c8 --- /dev/null +++ b/scripts/launch_tpi_2_1_test.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env bash +# Run the Safe DDS server integration test on a QNX VM. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_tpi_2_1.log" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" +: "${SERVER_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/server-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" + +mkdir -p "${LOG_DIR}" +exec > >(tee "${LOG_FILE}") 2>&1 + +OPT_NO_REBUILD=0 +OPT_STOP=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 + exit 1 +fi + +if [[ ! -d "${TARGET_DIR}" ]]; then + echo "QNX target directory not found: ${TARGET_DIR}" >&2 + exit 1 +fi + +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2 + exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2 + exit 1 +fi + +if ! command -v brctl >/dev/null 2>&1; then + echo "brctl not found. Install with: sudo apt install bridge-utils" >&2 + exit 1 +fi + +_get_ip_address() { + local max_tries=20 + local ip + local i + + for ((i = 0; i < max_tries; i++)); do + set +e + ip="$(mkqnximage --getip 2>/dev/null)" + set -e + if [[ -n "${ip}" ]]; then + echo "${ip}" + return 0 + fi + sleep 2 + done + + echo "Timed out waiting for VM IP address." >&2 + return 1 +} + +_ssh_run() { + local ip="$1" + local cmd="$2" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_wait_for_ssh() { + local ip="$1" + local max_tries=45 + local i + + for ((i = 1; i <= max_tries; i++)); do + if _ssh_run "${ip}" "true" >/dev/null 2>&1; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " waiting for SSH (${i}/${max_tries})..." + fi + sleep 2 + done + + echo "Timed out waiting for SSH on ${ip}." >&2 + return 1 +} + +_validate_qnx_binary() { + local bin="$1" + local description + + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_refresh_test_system_files_snippet() { + local snippet="${TARGET_DIR}/local/snippets/system_files.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" < "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_tpi_2_1_test.sh +EOF +} + +_refresh_test_post_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/post_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_tpi_2_1_test.sh +route add -net 224.0.0.0/4 vtnet0 +EOF +} + +_reset_generated_target_output() { + rm -rf "${TARGET_DIR}/output" +} + +_prepare_local_target_dirs() { + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" +} + +_prepare_local_target_dirs +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + for bin in \ + "${SERVER_BIN_DIR}/safe_edge_server" \ + "${SERVER_BIN_DIR}/test_server_integration"; do + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2 + exit 1 + fi + _validate_qnx_binary "${bin}" + done + + _refresh_test_ifs_start_snippet + _refresh_test_post_start_snippet + _refresh_test_system_files_snippet + _reset_generated_target_output +fi + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 +else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for VM IP..." +VM_IP="$(_get_ip_address)" +echo "VM is up: ${VM_IP}" +echo "Waiting for SSH..." +_wait_for_ssh "${VM_IP}" +echo "VM is reachable." + +echo "" +echo "Running test_server_integration on QNX..." +echo "---------------------------------------------" + +TEST_RC=0 +_ssh_run "${VM_IP}" \ + "SAFE_EDGE_SERVER_BIN=/system/bin/safe_edge_server /system/bin/test_server_integration" \ + || TEST_RC=$? + +echo "---------------------------------------------" +echo "" +echo "Stopping QNX VM..." +mkqnximage --stop 2>/dev/null || true + +if [[ "${TEST_RC}" -eq 0 ]]; then + echo "PASSED" +else + echo "FAILED (exit code ${TEST_RC})" + exit "${TEST_RC}" +fi diff --git a/scripts/launch_tpi_2_2_test.sh b/scripts/launch_tpi_2_2_test.sh new file mode 100755 index 0000000..4e6b41e --- /dev/null +++ b/scripts/launch_tpi_2_2_test.sh @@ -0,0 +1,254 @@ +#!/usr/bin/env bash +# Run the Safe DDS edge integration test on a QNX VM. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_tpi_2_2.log" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" +: "${EDGE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" + +mkdir -p "${LOG_DIR}" +exec > >(tee "${LOG_FILE}") 2>&1 + +OPT_NO_REBUILD=0 +OPT_STOP=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 + exit 1 +fi + +if [[ ! -d "${TARGET_DIR}" ]]; then + echo "QNX target directory not found: ${TARGET_DIR}" >&2 + exit 1 +fi + +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2 + exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2 + exit 1 +fi + +if ! command -v brctl >/dev/null 2>&1; then + echo "brctl not found. Install with: sudo apt install bridge-utils" >&2 + exit 1 +fi + +_get_ip_address() { + local max_tries=20 + local ip + local i + + for ((i = 0; i < max_tries; i++)); do + set +e + ip="$(mkqnximage --getip 2>/dev/null)" + set -e + if [[ -n "${ip}" ]]; then + echo "${ip}" + return 0 + fi + sleep 2 + done + + echo "Timed out waiting for VM IP address." >&2 + return 1 +} + +_ssh_run() { + local ip="$1" + local cmd="$2" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_wait_for_ssh() { + local ip="$1" + local max_tries=45 + local i + + for ((i = 1; i <= max_tries; i++)); do + if _ssh_run "${ip}" "true" >/dev/null 2>&1; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " waiting for SSH (${i}/${max_tries})..." + fi + sleep 2 + done + + echo "Timed out waiting for SSH on ${ip}." >&2 + return 1 +} + +_validate_qnx_binary() { + local bin="$1" + local description + + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_refresh_test_system_files_snippet() { + local snippet="${TARGET_DIR}/local/snippets/system_files.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" < "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_tpi_2_2_test.sh +EOF +} + +_refresh_test_post_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/post_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_tpi_2_2_test.sh +route add -net 224.0.0.0/4 vtnet0 +EOF +} + +_reset_generated_target_output() { + rm -rf "${TARGET_DIR}/output" +} + +_prepare_local_target_dirs() { + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" +} + +_prepare_local_target_dirs +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + for bin in \ + "${EDGE_BIN_DIR}/safe_edge_edge_gateway" \ + "${EDGE_BIN_DIR}/test_edge_integration"; do + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2 + exit 1 + fi + _validate_qnx_binary "${bin}" + done + + _refresh_test_ifs_start_snippet + _refresh_test_post_start_snippet + _refresh_test_system_files_snippet + _reset_generated_target_output +fi + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 +else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for VM IP..." +VM_IP="$(_get_ip_address)" +echo "VM is up: ${VM_IP}" +echo "Waiting for SSH..." +_wait_for_ssh "${VM_IP}" +echo "VM is reachable." + +echo "" +echo "Running test_edge_integration on QNX..." +echo "---------------------------------------------" + +TEST_RC=0 +_ssh_run "${VM_IP}" \ + "SAFE_EDGE_EDGE_BIN=/system/bin/safe_edge_edge_gateway /system/bin/test_edge_integration" \ + || TEST_RC=$? + +echo "---------------------------------------------" +echo "" +echo "Stopping QNX VM..." +mkqnximage --stop 2>/dev/null || true + +if [[ "${TEST_RC}" -eq 0 ]]; then + echo "PASSED" +else + echo "FAILED (exit code ${TEST_RC})" + exit "${TEST_RC}" +fi diff --git a/scripts/launch_tpi_2_3_test.sh b/scripts/launch_tpi_2_3_test.sh new file mode 100755 index 0000000..08f5851 --- /dev/null +++ b/scripts/launch_tpi_2_3_test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Run KPI/TPI 2.3 on Linux/Ubuntu. +# This KPI validates the shared common_server component and does not require QNX. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_tpi_2_3.log" + +mkdir -p "${LOG_DIR}" +exec > >(tee "${LOG_FILE}") 2>&1 + +bash "${WORKSPACE_ROOT}/common_server/test/launch_server_common_test.sh" "$@" From feae50677f879e67733bb5e6e3f1e19d8491291c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 28 May 2026 21:06:23 +0200 Subject: [PATCH 04/17] Add FastDDS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 7 + README.md | 135 +- fast_dds/.gitkeep | 0 fast_dds/docker/edge.Dockerfile | 31 + fast_dds/docker/edge.test.Dockerfile | 31 + fast_dds/docker/server.Dockerfile | 32 + fast_dds/docker/server.test.Dockerfile | 32 + fast_dds/edge/CMakeLists.txt | 142 ++ .../edge_module/common/HeaderFactory.hpp | 33 + .../edge_module/common/RuntimeConfig.hpp | 27 + .../edge_module/common/TopicNames.hpp | 20 + .../edge_module/logic/EdgeAdvisor.hpp | 25 + .../edge_module/nodes/EdgeGatewayNode.hpp | 169 ++ fast_dds/edge/src/apps/edge_gateway_main.cpp | 11 + fast_dds/edge/src/common/HeaderFactory.cpp | 49 + fast_dds/edge/src/common/RuntimeConfig.cpp | 21 + fast_dds/edge/src/common/TopicNames.cpp | 36 + fast_dds/edge/src/logic/EdgeAdvisor.cpp | 41 + fast_dds/edge/src/nodes/EdgeGatewayNode.cpp | 467 +++++ fast_dds/edge/test/test_edge_integration.cpp | 363 ++++ fast_dds/idl/common.hpp | 730 ++++++++ fast_dds/idl/commonCdrAux.hpp | 62 + fast_dds/idl/commonCdrAux.ipp | 367 ++++ fast_dds/idl/commonPubSubTypes.cxx | 474 +++++ fast_dds/idl/commonPubSubTypes.hpp | 285 +++ fast_dds/idl/commonTypeObjectSupport.cxx | 583 +++++++ fast_dds/idl/commonTypeObjectSupport.hpp | 114 ++ fast_dds/idl/edge.hpp | 892 ++++++++++ fast_dds/idl/edgeCdrAux.hpp | 63 + fast_dds/idl/edgeCdrAux.ipp | 433 +++++ fast_dds/idl/edgePubSubTypes.cxx | 474 +++++ fast_dds/idl/edgePubSubTypes.hpp | 286 +++ fast_dds/idl/edgeTypeObjectSupport.cxx | 554 ++++++ fast_dds/idl/edgeTypeObjectSupport.hpp | 89 + fast_dds/idl/pilot_server.hpp | 1534 +++++++++++++++++ fast_dds/idl/pilot_serverCdrAux.hpp | 98 ++ fast_dds/idl/pilot_serverCdrAux.ipp | 778 +++++++++ fast_dds/idl/pilot_serverPubSubTypes.cxx | 1046 +++++++++++ fast_dds/idl/pilot_serverPubSubTypes.hpp | 606 +++++++ .../idl/pilot_serverTypeObjectSupport.cxx | 1299 ++++++++++++++ .../idl/pilot_serverTypeObjectSupport.hpp | 214 +++ fast_dds/server/CMakeLists.txt | 147 ++ .../common/PilotServerPublishHelper.hpp | 25 + .../safe_edge/server/common/RuntimeConfig.hpp | 32 + .../safe_edge/server/common/TopicNames.hpp | 18 + .../safe_edge/server/nodes/ServerNode.hpp | 130 ++ fast_dds/server/src/apps/server_main.cpp | 11 + .../src/common/PilotServerPublishHelper.cpp | 39 + fast_dds/server/src/common/RuntimeConfig.cpp | 27 + fast_dds/server/src/common/TopicNames.cpp | 26 + fast_dds/server/src/nodes/ServerNode.cpp | 483 ++++++ .../server/test/test_server_integration.cpp | 315 ++++ scripts/build_qnx.sh | 4 +- scripts/build_ubuntu.sh | 95 + scripts/check_setup.sh | 6 +- scripts/launch_fast_edge_test.sh | 57 + scripts/launch_fast_server_test.sh | 56 + 57 files changed, 14091 insertions(+), 33 deletions(-) delete mode 100644 fast_dds/.gitkeep create mode 100644 fast_dds/docker/edge.Dockerfile create mode 100644 fast_dds/docker/edge.test.Dockerfile create mode 100644 fast_dds/docker/server.Dockerfile create mode 100644 fast_dds/docker/server.test.Dockerfile create mode 100644 fast_dds/edge/CMakeLists.txt create mode 100644 fast_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp create mode 100644 fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp create mode 100644 fast_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp create mode 100644 fast_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp create mode 100644 fast_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp create mode 100644 fast_dds/edge/src/apps/edge_gateway_main.cpp create mode 100644 fast_dds/edge/src/common/HeaderFactory.cpp create mode 100644 fast_dds/edge/src/common/RuntimeConfig.cpp create mode 100644 fast_dds/edge/src/common/TopicNames.cpp create mode 100644 fast_dds/edge/src/logic/EdgeAdvisor.cpp create mode 100644 fast_dds/edge/src/nodes/EdgeGatewayNode.cpp create mode 100644 fast_dds/edge/test/test_edge_integration.cpp create mode 100644 fast_dds/idl/common.hpp create mode 100644 fast_dds/idl/commonCdrAux.hpp create mode 100644 fast_dds/idl/commonCdrAux.ipp create mode 100644 fast_dds/idl/commonPubSubTypes.cxx create mode 100644 fast_dds/idl/commonPubSubTypes.hpp create mode 100644 fast_dds/idl/commonTypeObjectSupport.cxx create mode 100644 fast_dds/idl/commonTypeObjectSupport.hpp create mode 100644 fast_dds/idl/edge.hpp create mode 100644 fast_dds/idl/edgeCdrAux.hpp create mode 100644 fast_dds/idl/edgeCdrAux.ipp create mode 100644 fast_dds/idl/edgePubSubTypes.cxx create mode 100644 fast_dds/idl/edgePubSubTypes.hpp create mode 100644 fast_dds/idl/edgeTypeObjectSupport.cxx create mode 100644 fast_dds/idl/edgeTypeObjectSupport.hpp create mode 100644 fast_dds/idl/pilot_server.hpp create mode 100644 fast_dds/idl/pilot_serverCdrAux.hpp create mode 100644 fast_dds/idl/pilot_serverCdrAux.ipp create mode 100644 fast_dds/idl/pilot_serverPubSubTypes.cxx create mode 100644 fast_dds/idl/pilot_serverPubSubTypes.hpp create mode 100644 fast_dds/idl/pilot_serverTypeObjectSupport.cxx create mode 100644 fast_dds/idl/pilot_serverTypeObjectSupport.hpp create mode 100644 fast_dds/server/CMakeLists.txt create mode 100644 fast_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp create mode 100644 fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp create mode 100644 fast_dds/server/include/safe_edge/server/common/TopicNames.hpp create mode 100644 fast_dds/server/include/safe_edge/server/nodes/ServerNode.hpp create mode 100644 fast_dds/server/src/apps/server_main.cpp create mode 100644 fast_dds/server/src/common/PilotServerPublishHelper.cpp create mode 100644 fast_dds/server/src/common/RuntimeConfig.cpp create mode 100644 fast_dds/server/src/common/TopicNames.cpp create mode 100644 fast_dds/server/src/nodes/ServerNode.cpp create mode 100644 fast_dds/server/test/test_server_integration.cpp create mode 100755 scripts/build_ubuntu.sh create mode 100755 scripts/launch_fast_edge_test.sh create mode 100755 scripts/launch_fast_server_test.sh diff --git a/.gitignore b/.gitignore index d9d2b88..504c345 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,10 @@ qnx/targets/qemu-qnx800-*/local/ssh-ident qnx/targets/qemu-qnx800-*/local/snippets/ifs_start.custom qnx/targets/qemu-qnx800-*/local/snippets/post_start.custom qnx/targets/qemu-qnx800-*/local/snippets/system_files.custom + +# fast_dds/ FastDDS components — source and generated IDL files are intentionally versioned. +# The broad patterns above (*include*, *build*, *install*) would otherwise hide them. +!fast_dds/ +!fast_dds/** +fast_dds/build/ +fast_dds/install/ diff --git a/README.md b/README.md index b6f75fd..52c69b3 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,25 @@ It does not require the old `~/Safe/SAFE-EDGE` repository. It does not vendor third-party SDKs or source trees. QNX SDP 8 and Safe-DDS source code must be installed/provided outside this repository and pointed to with environment variables. +## Running scripts + +All scripts live under `scripts/` and resolve paths relative to their own location. +**Always run them from inside the `scripts/` directory:** + +```bash +cd scripts +bash .sh [options] +``` + +They also work when invoked from the repository root with a prefix (`bash scripts/.sh`), but the canonical form is from inside `scripts/`. + ## Customer Quick Start For Ubuntu/Debian hosts, install the host packages that can be installed automatically: ```bash -bash scripts/install_host_deps.sh +cd scripts +bash install_host_deps.sh ``` Provide the two external inputs that are not stored in this repository: @@ -27,25 +40,33 @@ export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" Check the environment: ```bash -bash scripts/check_setup.sh +bash check_setup.sh +``` + +Build and test (QNX): + +```bash +bash build_safedds_qnx.sh -- -j2 +bash build_qnx.sh -- -j2 +bash launch_tpi_2_3_test.sh +bash launch_tpi_2_1_test.sh +bash launch_tpi_2_2_test.sh ``` -Build and test: +Build and test (FastDDS / Docker): ```bash -bash scripts/build_safedds_qnx.sh -- -j2 -bash scripts/build_qnx.sh -- -j2 -bash scripts/launch_tpi_2_3_test.sh -bash scripts/launch_tpi_2_1_test.sh -bash scripts/launch_tpi_2_2_test.sh +bash build_ubuntu.sh --tests +bash launch_fast_server_test.sh +bash launch_fast_edge_test.sh ``` For Linux-only validation of the common server component: ```bash -bash scripts/install_host_deps.sh --linux-only -bash scripts/check_setup.sh --linux-only -bash scripts/launch_tpi_2_3_test.sh +bash install_host_deps.sh --linux-only +bash check_setup.sh --linux-only +bash launch_tpi_2_3_test.sh ``` ## Repository Paths @@ -55,6 +76,10 @@ bash scripts/launch_tpi_2_3_test.sh - Shared server code: `common_server/` - Safe DDS server: `safe_dds/server/` - Safe DDS edge: `safe_dds/edge/` +- FastDDS server: `fast_dds/server/` +- FastDDS edge: `fast_dds/edge/` +- FastDDS generated headers: `fast_dds/idl/` +- FastDDS Dockerfiles: `fast_dds/docker/` - QNX toolchain file: `qnx/toolchains/qnx8.cmake` - Safe DDS QNX build script: `scripts/build_safedds_qnx.sh` - Generated Safe DDS QNX package: `qnx/install/safedds-qnx8-x86_64/safedds` @@ -112,6 +137,12 @@ The `misc_files` entries include QNX VM keys, `shadow`, and other local image fi - `brctl` from `bridge-utils` - `file` +### Required to build and run FastDDS Docker tests + +- Docker + +FastDDS is provided by the base Docker image (`eprosima/vulcanexus:kilted-base`); no separate FastDDS installation is required on the host. + ### Required to run the Linux test - `cmake` @@ -141,19 +172,20 @@ export CMAKE_BUILD_TYPE="Release" Install Ubuntu/Debian host packages: ```bash -bash scripts/install_host_deps.sh +cd scripts +bash install_host_deps.sh ``` For only the Linux test dependencies: ```bash -bash scripts/install_host_deps.sh --linux-only +bash install_host_deps.sh --linux-only ``` Run: ```bash -bash scripts/check_setup.sh +bash check_setup.sh ``` What it does: @@ -167,21 +199,24 @@ What it does: For a Linux-only check: ```bash -bash scripts/check_setup.sh --linux-only +bash check_setup.sh --linux-only ``` ## Build +### QNX targets + Build and install Safe DDS for QNX if `qnx/install/safedds-qnx8-x86_64/safedds` is missing: ```bash -bash scripts/build_safedds_qnx.sh -- -j2 +cd scripts +bash build_safedds_qnx.sh -- -j2 ``` Build all QNX targets from this repository: ```bash -bash scripts/build_qnx.sh -- -j2 +bash build_qnx.sh -- -j2 ``` This configures and installs: @@ -196,40 +231,84 @@ Installed binaries end up in: - `safe_dds/install/server-qnx8-x86_64-Release/bin` - `safe_dds/install/edge-qnx8-x86_64-Release/bin` +### FastDDS Docker images + +Build the runtime images: + +```bash +cd scripts +bash build_ubuntu.sh +``` + +Build runtime and test images: + +```bash +bash build_ubuntu.sh --tests +``` + +Images produced: + +- `safe-edge-server:fast` +- `safe-edge-edge:fast` +- `safe-edge-server:fast-test` (with `--tests`) +- `safe-edge-edge:fast-test` (with `--tests`) + ## Test +All test scripts log their output under `scripts/logs/`. + ### KPI/TPI 2.3: Linux test ```bash -bash scripts/launch_tpi_2_3_test.sh +cd scripts +bash launch_tpi_2_3_test.sh ``` -Log file: - -- `scripts/logs/launch_tpi_2_3.log` +Log: `scripts/logs/launch_tpi_2_3.log` ### TPI 2.1: QNX server test ```bash -bash scripts/launch_tpi_2_1_test.sh +cd scripts +bash launch_tpi_2_1_test.sh ``` -Log file: - -- `scripts/logs/launch_tpi_2_1.log` +Log: `scripts/logs/launch_tpi_2_1.log` ### TPI 2.2: QNX edge test ```bash -bash scripts/launch_tpi_2_2_test.sh +cd scripts +bash launch_tpi_2_2_test.sh +``` + +Log: `scripts/logs/launch_tpi_2_2.log` + +### FastDDS server integration test + +```bash +cd scripts +bash launch_fast_server_test.sh +``` + +Log: `scripts/logs/launch_fast_server_test.log` + +Builds `safe-edge-server:fast-test` automatically if the image is not present. + +### FastDDS edge integration test + +```bash +cd scripts +bash launch_fast_edge_test.sh ``` -Log file: +Log: `scripts/logs/launch_fast_edge_test.log` -- `scripts/logs/launch_tpi_2_2.log` +Builds `safe-edge-edge:fast-test` automatically if the image is not present. ## Notes - The QNX tests rebuild the QEMU image from `qnx/targets/qemu-qnx800-x86_64`. - Generated target output is recreated under `qnx/targets/qemu-qnx800-x86_64/output/` and is ignored by git. - The Linux test may execute real Pilot Server checks if `/etc/safe-edge/server.ini` exists on the host. +- The FastDDS Docker tests are self-contained: each test image spawns the component under test as a subprocess and communicates with it via DDS over the loopback interface. diff --git a/fast_dds/.gitkeep b/fast_dds/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/fast_dds/docker/edge.Dockerfile b/fast_dds/docker/edge.Dockerfile new file mode 100644 index 0000000..a5c693a --- /dev/null +++ b/fast_dds/docker/edge.Dockerfile @@ -0,0 +1,31 @@ +ARG FASTDDS_BASE_IMAGE=eprosima/vulcanexus:kilted-base +FROM ${FASTDDS_BASE_IMAGE} AS build + +ARG CMAKE_BUILD_TYPE=Release +ARG CMAKE_PREFIX_PATH=/opt/ros/kilted +ARG EDGE_INSTALL_PREFIX=/opt/safe-edge/edge + +WORKDIR /workspace + +COPY fast_dds/idl /workspace/fast_dds/idl +COPY fast_dds/edge /workspace/fast_dds/edge + +RUN cmake -S /workspace/fast_dds/edge \ + -B /workspace/build/edge \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ + -DCMAKE_INSTALL_PREFIX=${EDGE_INSTALL_PREFIX} \ + && cmake --build /workspace/build/edge --target install -j"$(nproc)" + +FROM ${FASTDDS_BASE_IMAGE} AS runtime + +ARG EDGE_INSTALL_PREFIX=/opt/safe-edge/edge + +ENV LD_LIBRARY_PATH=/opt/ros/kilted/lib +ENV FASTDDS_BUILTIN_TRANSPORTS=UDPv4 + +COPY --from=build ${EDGE_INSTALL_PREFIX} ${EDGE_INSTALL_PREFIX} + +WORKDIR ${EDGE_INSTALL_PREFIX} + +ENTRYPOINT ["/opt/safe-edge/edge/bin/safe_edge_edge_gateway"] diff --git a/fast_dds/docker/edge.test.Dockerfile b/fast_dds/docker/edge.test.Dockerfile new file mode 100644 index 0000000..ede70d4 --- /dev/null +++ b/fast_dds/docker/edge.test.Dockerfile @@ -0,0 +1,31 @@ +ARG FASTDDS_BASE_IMAGE=eprosima/vulcanexus:kilted-base +FROM ${FASTDDS_BASE_IMAGE} AS build + +ARG CMAKE_BUILD_TYPE=Release +ARG CMAKE_PREFIX_PATH=/opt/ros/kilted +ARG EDGE_INSTALL_PREFIX=/opt/safe-edge/edge + +WORKDIR /workspace + +COPY fast_dds/idl /workspace/fast_dds/idl +COPY fast_dds/edge /workspace/fast_dds/edge + +RUN cmake -S /workspace/fast_dds/edge \ + -B /workspace/build/edge \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ + -DCMAKE_INSTALL_PREFIX=${EDGE_INSTALL_PREFIX} \ + -DSAFE_EDGE_BUILD_TESTS=ON \ + && cmake --build /workspace/build/edge --target install -j"$(nproc)" + +FROM ${FASTDDS_BASE_IMAGE} AS test + +ARG EDGE_INSTALL_PREFIX=/opt/safe-edge/edge + +ENV LD_LIBRARY_PATH=/opt/ros/kilted/lib +ENV FASTDDS_BUILTIN_TRANSPORTS=UDPv4 +ENV SAFE_EDGE_FAST_EDGE_BIN=${EDGE_INSTALL_PREFIX}/bin/safe_edge_edge_gateway + +COPY --from=build ${EDGE_INSTALL_PREFIX} ${EDGE_INSTALL_PREFIX} + +ENTRYPOINT ["/opt/safe-edge/edge/bin/test_edge_integration"] diff --git a/fast_dds/docker/server.Dockerfile b/fast_dds/docker/server.Dockerfile new file mode 100644 index 0000000..5b510f0 --- /dev/null +++ b/fast_dds/docker/server.Dockerfile @@ -0,0 +1,32 @@ +ARG FASTDDS_BASE_IMAGE=eprosima/vulcanexus:kilted-base +FROM ${FASTDDS_BASE_IMAGE} AS build + +ARG CMAKE_BUILD_TYPE=Release +ARG CMAKE_PREFIX_PATH=/opt/ros/kilted +ARG SERVER_INSTALL_PREFIX=/opt/safe-edge/server + +WORKDIR /workspace + +COPY fast_dds/idl /workspace/fast_dds/idl +COPY fast_dds/server /workspace/fast_dds/server +COPY common_server /workspace/common_server + +RUN cmake -S /workspace/fast_dds/server \ + -B /workspace/build/server \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ + -DCMAKE_INSTALL_PREFIX=${SERVER_INSTALL_PREFIX} \ + && cmake --build /workspace/build/server --target install -j"$(nproc)" + +FROM ${FASTDDS_BASE_IMAGE} AS runtime + +ARG SERVER_INSTALL_PREFIX=/opt/safe-edge/server + +ENV LD_LIBRARY_PATH=/opt/ros/kilted/lib +ENV FASTDDS_BUILTIN_TRANSPORTS=UDPv4 + +COPY --from=build ${SERVER_INSTALL_PREFIX} ${SERVER_INSTALL_PREFIX} + +WORKDIR ${SERVER_INSTALL_PREFIX} + +ENTRYPOINT ["/opt/safe-edge/server/bin/safe_edge_server"] diff --git a/fast_dds/docker/server.test.Dockerfile b/fast_dds/docker/server.test.Dockerfile new file mode 100644 index 0000000..cf2dcf5 --- /dev/null +++ b/fast_dds/docker/server.test.Dockerfile @@ -0,0 +1,32 @@ +ARG FASTDDS_BASE_IMAGE=eprosima/vulcanexus:kilted-base +FROM ${FASTDDS_BASE_IMAGE} AS build + +ARG CMAKE_BUILD_TYPE=Release +ARG CMAKE_PREFIX_PATH=/opt/ros/kilted +ARG SERVER_INSTALL_PREFIX=/opt/safe-edge/server + +WORKDIR /workspace + +COPY fast_dds/idl /workspace/fast_dds/idl +COPY fast_dds/server /workspace/fast_dds/server +COPY common_server /workspace/common_server + +RUN cmake -S /workspace/fast_dds/server \ + -B /workspace/build/server \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ + -DCMAKE_INSTALL_PREFIX=${SERVER_INSTALL_PREFIX} \ + -DSAFE_EDGE_BUILD_TESTS=ON \ + && cmake --build /workspace/build/server --target install -j"$(nproc)" + +FROM ${FASTDDS_BASE_IMAGE} AS test + +ARG SERVER_INSTALL_PREFIX=/opt/safe-edge/server + +ENV LD_LIBRARY_PATH=/opt/ros/kilted/lib +ENV FASTDDS_BUILTIN_TRANSPORTS=UDPv4 +ENV SAFE_EDGE_FAST_SERVER_BIN=${SERVER_INSTALL_PREFIX}/bin/safe_edge_server + +COPY --from=build ${SERVER_INSTALL_PREFIX} ${SERVER_INSTALL_PREFIX} + +ENTRYPOINT ["/opt/safe-edge/server/bin/test_server_integration"] diff --git a/fast_dds/edge/CMakeLists.txt b/fast_dds/edge/CMakeLists.txt new file mode 100644 index 0000000..0899426 --- /dev/null +++ b/fast_dds/edge/CMakeLists.txt @@ -0,0 +1,142 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_edge LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(fastdds REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_EDGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_EDGE_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_edge_common + STATIC + ${SAFE_EDGE_IDL_INCLUDE_DIR}/commonPubSubTypes.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/commonTypeObjectSupport.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/edgePubSubTypes.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/edgeTypeObjectSupport.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/pilot_serverPubSubTypes.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/pilot_serverTypeObjectSupport.cxx + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp + src/common/HeaderFactory.cpp + src/logic/EdgeAdvisor.cpp +) +safe_edge_apply_common_build_settings(safe_edge_edge_common) +target_include_directories( + safe_edge_edge_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_edge_common + PUBLIC + fastdds +) + +add_executable( + safe_edge_edge_gateway + src/apps/edge_gateway_main.cpp + src/nodes/EdgeGatewayNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_edge_gateway) +target_include_directories( + safe_edge_edge_gateway + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_edge_gateway + PRIVATE + safe_edge_edge_common + fastdds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_edge_gateway + RUNTIME DESTINATION bin +) + +option(SAFE_EDGE_BUILD_TESTS "Build integration tests" ON) + +if(SAFE_EDGE_BUILD_TESTS) + find_package(GTest QUIET) + + if(NOT GTest_FOUND) + message(STATUS "GTest not found — fetching from source") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + if(NOT TARGET GTest::gtest) + add_library(GTest::gtest ALIAS gtest) + endif() + endif() + + add_executable( + test_edge_integration + test/test_edge_integration.cpp + ) + + # Tests need exceptions and RTTI (GTest requirement). + # Do not reuse safe_edge_apply_common_build_settings which disables them. + target_include_directories( + test_edge_integration + PRIVATE + "${SAFE_EDGE_EDGE_INCLUDE_DIR}" + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + ) + target_compile_options( + test_edge_integration + PRIVATE + -Wall -Wextra -Wpedantic + ) + target_link_libraries( + test_edge_integration + PRIVATE + safe_edge_edge_common + fastdds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} + GTest::gtest + ) + + enable_testing() + add_test(NAME edge_integration COMMAND test_edge_integration) + + install( + TARGETS test_edge_integration + RUNTIME DESTINATION bin + ) +endif() diff --git a/fast_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp b/fast_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp new file mode 100644 index 0000000..fa53aa9 --- /dev/null +++ b/fast_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp @@ -0,0 +1,33 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP + +#include + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +class HeaderFactory +{ +public: + + explicit HeaderFactory(std::string source_name); + + safe_edge::common::Header make_header(const char* trace_suffix = nullptr); + + static uint64_t now_ms() noexcept; + +private: + + std::string source_name_; + uint64_t counter_ = 0U; +}; + +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP diff --git a/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp b/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp new file mode 100644 index 0000000..88a357c --- /dev/null +++ b/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp @@ -0,0 +1,27 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + std::string service_name; + std::string source_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + uint32_t status_interval_sec = 5U; +}; + +RuntimeConfig make_edge_gateway_runtime_config(); + +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP diff --git a/fast_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp b/fast_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp new file mode 100644 index 0000000..eef164e --- /dev/null +++ b/fast_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp @@ -0,0 +1,20 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace edge_module { +namespace common { +namespace topic_names { + +const char* vehicle_edge_summary() noexcept; +const char* energy_advisory() noexcept; +const char* edge_gateway_status() noexcept; +const char* charger_locations() noexcept; +const char* service_heartbeat() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP diff --git a/fast_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp b/fast_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp new file mode 100644 index 0000000..c6d7c7d --- /dev/null +++ b/fast_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp @@ -0,0 +1,25 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP +#define SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace logic { + +class EdgeAdvisor +{ +public: + + static safe_edge::edge::EnergyAdvisory evaluate( + const safe_edge::edge::VehicleEdgeSummary& summary, + const safe_edge::pilot_server::ChargerLocation* chargers, + int32_t charger_count); +}; + +} // namespace logic +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP diff --git a/fast_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp b/fast_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp new file mode 100644 index 0000000..f18646a --- /dev/null +++ b/fast_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp @@ -0,0 +1,169 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP +#define SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace nodes { + +class EdgeGatewayNode +{ +public: + + explicit EdgeGatewayNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::fastdds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(EdgeGatewayNode& owner); + + void on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::fastdds::dds::DataWriter* writer, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class VehicleEdgeSummaryListener : + public eprosima::fastdds::dds::DataReaderListener + { + public: + + explicit VehicleEdgeSummaryListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class ChargerLocationListener : + public eprosima::fastdds::dds::DataReaderListener + { + public: + + explicit ChargerLocationListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class HeartbeatListener : + public eprosima::fastdds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + + void on_vehicle_edge_summary_received(const safe_edge::edge::VehicleEdgeSummary& summary); + void on_charger_location_received(const safe_edge::pilot_server::ChargerLocation& location); + void on_server_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void publish_energy_advisory(const safe_edge::edge::EnergyAdvisory& advisory); + void publish_edge_gateway_status(); + + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + VehicleEdgeSummaryListener vehicle_edge_summary_listener_; + ChargerLocationListener charger_location_listener_; + HeartbeatListener heartbeat_listener_; + + eprosima::fastdds::dds::TypeSupport vehicle_edge_summary_type_support_; + eprosima::fastdds::dds::TypeSupport energy_advisory_type_support_; + eprosima::fastdds::dds::TypeSupport edge_gateway_status_type_support_; + eprosima::fastdds::dds::TypeSupport charger_location_type_support_; + eprosima::fastdds::dds::TypeSupport service_heartbeat_type_support_; + + eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; + eprosima::fastdds::dds::Publisher* publisher_ = nullptr; + eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; + + std::string vehicle_edge_summary_topic_name_; + std::string energy_advisory_topic_name_; + std::string edge_gateway_status_topic_name_; + std::string charger_location_topic_name_; + std::string service_heartbeat_topic_name_; + + eprosima::fastdds::dds::Topic* vehicle_edge_summary_topic_ = nullptr; + eprosima::fastdds::dds::Topic* energy_advisory_topic_ = nullptr; + eprosima::fastdds::dds::Topic* edge_gateway_status_topic_ = nullptr; + eprosima::fastdds::dds::Topic* charger_location_topic_ = nullptr; + eprosima::fastdds::dds::Topic* service_heartbeat_topic_ = nullptr; + + eprosima::fastdds::dds::DataWriter* energy_advisory_datawriter_ = nullptr; + eprosima::fastdds::dds::DataWriter* edge_gateway_status_datawriter_ = nullptr; + + eprosima::fastdds::dds::DataReader* vehicle_edge_summary_datareader_ = nullptr; + eprosima::fastdds::dds::DataReader* charger_location_datareader_ = nullptr; + eprosima::fastdds::dds::DataReader* service_heartbeat_datareader_ = nullptr; + + safe_edge::pilot_server::ChargerLocation cached_chargers_[3]; + int32_t cached_charger_count_ = 0; + uint64_t last_server_sync_ms_ = 0U; + uint64_t last_server_hb_ms_ = 0U; + bool server_available_ = false; + + std::chrono::steady_clock::time_point next_status_fire_; +}; + +} // namespace nodes +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP diff --git a/fast_dds/edge/src/apps/edge_gateway_main.cpp b/fast_dds/edge/src/apps/edge_gateway_main.cpp new file mode 100644 index 0000000..69026fa --- /dev/null +++ b/fast_dds/edge/src/apps/edge_gateway_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::edge_module::common::RuntimeConfig config = + safe_edge::edge_module::common::make_edge_gateway_runtime_config(); + + safe_edge::edge_module::nodes::EdgeGatewayNode node(config); + return node.run(); +} diff --git a/fast_dds/edge/src/common/HeaderFactory.cpp b/fast_dds/edge/src/common/HeaderFactory.cpp new file mode 100644 index 0000000..632189d --- /dev/null +++ b/fast_dds/edge/src/common/HeaderFactory.cpp @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +HeaderFactory::HeaderFactory(std::string source_name) + : source_name_(std::move(source_name)) +{ +} + +safe_edge::common::Header HeaderFactory::make_header(const char* trace_suffix) +{ + std::string trace_id = source_name_; + trace_id += "-"; + + std::array buf{}; + std::snprintf(buf.data(), buf.size(), "%llu", static_cast(counter_++)); + trace_id += buf.data(); + + if (nullptr != trace_suffix && trace_suffix[0] != '\0') + { + trace_id += "-"; + trace_id += trace_suffix; + } + + safe_edge::common::Header header; + header.source(source_name_); + header.timestamp_ms(now_ms()); + header.trace_id(trace_id); + return header; +} + +uint64_t HeaderFactory::now_ms() noexcept +{ + const auto now = std::chrono::system_clock::now(); + const auto since_epoch = now.time_since_epoch(); + return static_cast( + std::chrono::duration_cast(since_epoch).count()); +} + +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/fast_dds/edge/src/common/RuntimeConfig.cpp b/fast_dds/edge/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..ee6eb27 --- /dev/null +++ b/fast_dds/edge/src/common/RuntimeConfig.cpp @@ -0,0 +1,21 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +RuntimeConfig make_edge_gateway_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeEdgeGatewayParticipant"; + config.service_name = "edge_gateway"; + config.source_name = "edge_gateway"; + config.domain_id = 0U; + config.participant_port = 8030U; + config.status_interval_sec = 5U; + return config; +} + +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/fast_dds/edge/src/common/TopicNames.cpp b/fast_dds/edge/src/common/TopicNames.cpp new file mode 100644 index 0000000..dc1a5d4 --- /dev/null +++ b/fast_dds/edge/src/common/TopicNames.cpp @@ -0,0 +1,36 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace common { +namespace topic_names { + +const char* vehicle_edge_summary() noexcept +{ + return "safe_edge.edge.vehicle_edge_summary"; +} + +const char* energy_advisory() noexcept +{ + return "safe_edge.edge.energy_advisory"; +} + +const char* edge_gateway_status() noexcept +{ + return "safe_edge.edge.edge_gateway_status"; +} + +const char* charger_locations() noexcept +{ + return "safe_edge.pilot_server.charger_locations"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +} // namespace topic_names +} // namespace common +} // namespace edge_module +} // namespace safe_edge diff --git a/fast_dds/edge/src/logic/EdgeAdvisor.cpp b/fast_dds/edge/src/logic/EdgeAdvisor.cpp new file mode 100644 index 0000000..1761832 --- /dev/null +++ b/fast_dds/edge/src/logic/EdgeAdvisor.cpp @@ -0,0 +1,41 @@ +#include + +namespace safe_edge { +namespace edge_module { +namespace logic { + +safe_edge::edge::EnergyAdvisory EdgeAdvisor::evaluate( + const safe_edge::edge::VehicleEdgeSummary& summary, + const safe_edge::pilot_server::ChargerLocation* chargers, + int32_t charger_count) +{ + safe_edge::edge::EnergyAdvisory advisory; + + if (summary.soc_pct() < 20.0F) + { + advisory.suggested_mode(safe_edge::common::PolicyMode::POLICY_LOW_SOC); + advisory.advisory_reason("Low battery -- charge now"); + advisory.recommended_charger_id((charger_count > 0) ? chargers[0].id() : 1); + advisory.target_soc_pct(80.0F); + } + else if (summary.v2g_ready()) + { + advisory.suggested_mode(safe_edge::common::PolicyMode::POLICY_EDGE_AUTONOMOUS); + advisory.advisory_reason("V2G available"); + advisory.recommended_charger_id(0); + advisory.target_soc_pct(90.0F); + } + else + { + advisory.suggested_mode(safe_edge::common::PolicyMode::POLICY_NOMINAL); + advisory.advisory_reason("Normal operation"); + advisory.recommended_charger_id(0); + advisory.target_soc_pct(80.0F); + } + + return advisory; +} + +} // namespace logic +} // namespace edge_module +} // namespace safe_edge diff --git a/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp b/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp new file mode 100644 index 0000000..b453cf6 --- /dev/null +++ b/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp @@ -0,0 +1,467 @@ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace nodes { + +namespace { + +bool register_type( + eprosima::fastdds::dds::DomainParticipant* participant, + eprosima::fastdds::dds::TypeSupport& type_support, + const char* label) +{ + if (eprosima::fastdds::dds::RETCODE_OK != type_support.register_type(participant)) + { + std::cerr << "[edge_gateway] Failed to register type: " << label << std::endl; + return false; + } + return true; +} + +eprosima::fastdds::dds::Topic* create_topic( + eprosima::fastdds::dds::DomainParticipant* participant, + const std::string& topic_name, + eprosima::fastdds::dds::TypeSupport& type_support) +{ + eprosima::fastdds::dds::TopicQos topic_qos{}; + return participant->create_topic( + topic_name, + type_support.get_type_name(), + topic_qos, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); +} + +} // namespace + +EdgeGatewayNode::ParticipantListener::ParticipantListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::ParticipantListener::on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match( + reader->get_topicdescription()->get_name().c_str(), info.total_count); +} + +void EdgeGatewayNode::ParticipantListener::on_publication_matched( + eprosima::fastdds::dds::DataWriter* writer, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match( + writer->get_topic()->get_name().c_str(), info.total_count); +} + +EdgeGatewayNode::VehicleEdgeSummaryListener::VehicleEdgeSummaryListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::VehicleEdgeSummaryListener::on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept +{ + safe_edge::edge::VehicleEdgeSummary sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + + while (eprosima::fastdds::dds::RETCODE_OK == reader->take_next_sample(&sample, &info)) + { + if (info.valid_data) + { + owner_.on_vehicle_edge_summary_received(sample); + } + } +} + +EdgeGatewayNode::ChargerLocationListener::ChargerLocationListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::ChargerLocationListener::on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept +{ + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + + while (eprosima::fastdds::dds::RETCODE_OK == reader->take_next_sample(&sample, &info)) + { + if (info.valid_data) + { + owner_.on_charger_location_received(sample); + } + } +} + +EdgeGatewayNode::HeartbeatListener::HeartbeatListener(EdgeGatewayNode& owner) + : owner_(owner) +{ +} + +void EdgeGatewayNode::HeartbeatListener::on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept +{ + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + + while (eprosima::fastdds::dds::RETCODE_OK == reader->take_next_sample(&sample, &info)) + { + if (info.valid_data) + { + owner_.on_server_heartbeat_received(sample); + } + } +} + +EdgeGatewayNode::EdgeGatewayNode(const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , vehicle_edge_summary_listener_(*this) + , charger_location_listener_(*this) + , heartbeat_listener_(*this) + , vehicle_edge_summary_type_support_(new safe_edge::edge::VehicleEdgeSummaryPubSubType()) + , energy_advisory_type_support_(new safe_edge::edge::EnergyAdvisoryPubSubType()) + , edge_gateway_status_type_support_(new safe_edge::edge::EdgeGatewayStatusPubSubType()) + , charger_location_type_support_(new safe_edge::pilot_server::ChargerLocationPubSubType()) + , service_heartbeat_type_support_(new safe_edge::common::ServiceHeartbeatPubSubType()) +{ +} + +int EdgeGatewayNode::run() +{ + if (!initialize()) + { + return 1; + } + + next_status_fire_ = std::chrono::steady_clock::now() + + std::chrono::seconds(runtime_config_.status_interval_sec); + + std::cout << "[edge_gateway] [START] Running with participant port " + << runtime_config_.participant_port << std::endl; + + while (true) + { + std::this_thread::sleep_until(next_status_fire_); + publish_edge_gateway_status(); + next_status_fire_ += std::chrono::seconds(runtime_config_.status_interval_sec); + } + + return 0; +} + +bool EdgeGatewayNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities(); +} + +bool EdgeGatewayNode::create_participant() +{ + eprosima::fastdds::dds::DomainParticipantQos participant_qos{}; + participant_qos.name(runtime_config_.participant_name); + + eprosima::fastdds::rtps::Locator_t locator; + eprosima::fastdds::rtps::IPLocator::setIPv4(locator, "127.0.0.1"); + locator.port = runtime_config_.participant_port; + participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(locator); + + eprosima::fastdds::dds::StatusMask participant_mask = + eprosima::fastdds::dds::StatusMask::publication_matched(); + participant_mask |= eprosima::fastdds::dds::StatusMask::subscription_matched(); + + participant_ = eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + participant_mask); + + if (nullptr == participant_) + { + std::cerr << "[edge_gateway] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::register_types() +{ + return register_type(participant_, vehicle_edge_summary_type_support_, "VehicleEdgeSummary") && + register_type(participant_, energy_advisory_type_support_, "EnergyAdvisory") && + register_type(participant_, edge_gateway_status_type_support_, "EdgeGatewayStatus") && + register_type(participant_, charger_location_type_support_, "ChargerLocation") && + register_type(participant_, service_heartbeat_type_support_, "ServiceHeartbeat"); +} + +bool EdgeGatewayNode::create_topics() +{ + vehicle_edge_summary_topic_name_ = common::topic_names::vehicle_edge_summary(); + vehicle_edge_summary_topic_ = create_topic( + participant_, vehicle_edge_summary_topic_name_, vehicle_edge_summary_type_support_); + if (nullptr == vehicle_edge_summary_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: vehicle_edge_summary" << std::endl; + return false; + } + + energy_advisory_topic_name_ = common::topic_names::energy_advisory(); + energy_advisory_topic_ = create_topic( + participant_, energy_advisory_topic_name_, energy_advisory_type_support_); + if (nullptr == energy_advisory_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: energy_advisory" << std::endl; + return false; + } + + edge_gateway_status_topic_name_ = common::topic_names::edge_gateway_status(); + edge_gateway_status_topic_ = create_topic( + participant_, edge_gateway_status_topic_name_, edge_gateway_status_type_support_); + if (nullptr == edge_gateway_status_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: edge_gateway_status" << std::endl; + return false; + } + + charger_location_topic_name_ = common::topic_names::charger_locations(); + charger_location_topic_ = create_topic( + participant_, charger_location_topic_name_, charger_location_type_support_); + if (nullptr == charger_location_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: charger_locations" << std::endl; + return false; + } + + service_heartbeat_topic_name_ = common::topic_names::service_heartbeat(); + service_heartbeat_topic_ = create_topic( + participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) + { + std::cerr << "[edge_gateway] Failed to create topic: service_heartbeat" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::create_endpoints() +{ + eprosima::fastdds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + eprosima::fastdds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[edge_gateway] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::fastdds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + energy_advisory_datawriter_ = publisher_->create_datawriter( + energy_advisory_topic_, + writer_qos, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + edge_gateway_status_datawriter_ = publisher_->create_datawriter( + edge_gateway_status_topic_, + writer_qos, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + eprosima::fastdds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + vehicle_edge_summary_datareader_ = subscriber_->create_datareader( + vehicle_edge_summary_topic_, + reader_qos, + &vehicle_edge_summary_listener_, + eprosima::fastdds::dds::StatusMask::data_available()); + charger_location_datareader_ = subscriber_->create_datareader( + charger_location_topic_, + reader_qos, + &charger_location_listener_, + eprosima::fastdds::dds::StatusMask::data_available()); + service_heartbeat_datareader_ = subscriber_->create_datareader( + service_heartbeat_topic_, + reader_qos, + &heartbeat_listener_, + eprosima::fastdds::dds::StatusMask::data_available()); + + const bool success = + nullptr != energy_advisory_datawriter_ && + nullptr != edge_gateway_status_datawriter_ && + nullptr != vehicle_edge_summary_datareader_ && + nullptr != charger_location_datareader_ && + nullptr != service_heartbeat_datareader_; + + if (!success) + { + std::cerr << "[edge_gateway] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool EdgeGatewayNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == publisher_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == energy_advisory_datawriter_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == edge_gateway_status_datawriter_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == subscriber_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == vehicle_edge_summary_datareader_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == charger_location_datareader_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == service_heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[edge_gateway] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +void EdgeGatewayNode::on_vehicle_edge_summary_received( + const safe_edge::edge::VehicleEdgeSummary& summary) +{ + std::cout << "[edge_gateway] Received VehicleEdgeSummary soc=" << summary.soc_pct() + << " v2g_ready=" << summary.v2g_ready() + << " mode=" << static_cast(summary.current_mode()) << std::endl; + + safe_edge::edge::EnergyAdvisory advisory = logic::EdgeAdvisor::evaluate( + summary, cached_chargers_, cached_charger_count_); + advisory.header(header_factory_.make_header("energy_advisory")); + publish_energy_advisory(advisory); +} + +void EdgeGatewayNode::on_charger_location_received( + const safe_edge::pilot_server::ChargerLocation& location) +{ + if (cached_charger_count_ < 3) + { + cached_chargers_[cached_charger_count_++] = location; + } + last_server_sync_ms_ = common::HeaderFactory::now_ms(); + + std::cout << "[edge_gateway] Received ChargerLocation id=" << location.id() + << " name=" << location.name() << std::endl; +} + +void EdgeGatewayNode::publish_energy_advisory(const safe_edge::edge::EnergyAdvisory& advisory) +{ + if (eprosima::fastdds::dds::RETCODE_OK != + energy_advisory_datawriter_->write( + const_cast(&advisory), + eprosima::fastdds::dds::HANDLE_NIL)) + { + std::cerr << "[edge_gateway] Failed to publish EnergyAdvisory" << std::endl; + return; + } + + std::cout << "[edge_gateway] Published EnergyAdvisory mode=" + << static_cast(advisory.suggested_mode()) + << " reason=" << advisory.advisory_reason() << std::endl; +} + +void EdgeGatewayNode::on_server_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + if (heartbeat.service_name() != "server") + { + return; + } + last_server_hb_ms_ = common::HeaderFactory::now_ms(); + server_available_ = true; +} + +void EdgeGatewayNode::publish_edge_gateway_status() +{ + constexpr uint64_t SERVER_HB_TIMEOUT_MS = 10000U; + if (last_server_hb_ms_ > 0U && + (common::HeaderFactory::now_ms() - last_server_hb_ms_) > SERVER_HB_TIMEOUT_MS) + { + server_available_ = false; + } + + safe_edge::edge::EdgeGatewayStatus status; + status.header(header_factory_.make_header("edge_gateway_status")); + status.status(server_available_ + ? safe_edge::common::HealthStatus::HEALTH_OK + : safe_edge::common::HealthStatus::HEALTH_DEGRADED); + status.last_server_sync_ms(last_server_sync_ms_); + status.detail(server_available_ + ? "edge connected, server synced" + : "edge connected, server_down"); + + if (eprosima::fastdds::dds::RETCODE_OK != + edge_gateway_status_datawriter_->write(&status, eprosima::fastdds::dds::HANDLE_NIL)) + { + std::cerr << "[edge_gateway] Failed to publish EdgeGatewayStatus" << std::endl; + return; + } + + std::cout << "[edge_gateway] Published EdgeGatewayStatus status=" + << (server_available_ ? "OK" : "DEGRADED") << std::endl; +} + +void EdgeGatewayNode::log_subscription_match(const char* topic_name, int32_t total_count) const +{ + std::cout << "[edge_gateway] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void EdgeGatewayNode::log_publication_match(const char* topic_name, int32_t total_count) const +{ + std::cout << "[edge_gateway] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +} // namespace nodes +} // namespace edge_module +} // namespace safe_edge diff --git a/fast_dds/edge/test/test_edge_integration.cpp b/fast_dds/edge/test/test_edge_integration.cpp new file mode 100644 index 0000000..5bcbf7b --- /dev/null +++ b/fast_dds/edge/test/test_edge_integration.cpp @@ -0,0 +1,363 @@ +// test_edge_integration.cpp — Fast DDS variant +// +// Integration tests for EdgeGatewayNode health state machine. +// Starts safe_edge_edge_gateway as a subprocess, runs all suites, stops it. +// +// Usage: +// ./test_edge_integration +// ./test_edge_integration --gtest_output=xml:results.xml +// +// Environment variables: +// SAFE_EDGE_FAST_EDGE_BIN path to safe_edge_edge_gateway (default: safe_edge_edge_gateway) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +// Edge participant port (from RuntimeConfig) +static constexpr uint16_t EDGE_PORT = 8030U; + +// --------------------------------------------------------------------------- +// Helper: participant peered with edge at EDGE_PORT +// --------------------------------------------------------------------------- + +static eprosima::fastdds::dds::DomainParticipant* make_participant( + const char* name, uint16_t port) +{ + eprosima::fastdds::dds::DomainParticipantQos qos{}; + qos.name(name); + + eprosima::fastdds::rtps::Locator_t announced; + eprosima::fastdds::rtps::IPLocator::setIPv4(announced, "127.0.0.1"); + announced.port = port; + qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(announced); + + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, "127.0.0.1"); + peer.port = EDGE_PORT; + qos.wire_protocol().builtin.initialPeersList.push_back(peer); + + return eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->create_participant(0U, qos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); +} + +// --------------------------------------------------------------------------- +// Global environment: lifecycle of safe_edge_edge_gateway subprocess +// --------------------------------------------------------------------------- + +class EdgeEnvironment : public ::testing::Environment +{ +public: + void SetUp() override + { + const char* bin = std::getenv("SAFE_EDGE_FAST_EDGE_BIN"); + const std::string cmd = + std::string(bin != nullptr ? bin : "safe_edge_edge_gateway") + " &"; + ASSERT_EQ(0, std::system(cmd.c_str())) + << "Failed to launch safe_edge_edge_gateway. " + "Set SAFE_EDGE_FAST_EDGE_BIN to the full path if needed."; + std::cout << "[env] safe_edge_edge_gateway started — waiting 5 s for init...\n"; + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::cout << "[env] Edge ready.\n"; + } + + void TearDown() override + { + std::cout << "[env] Stopping safe_edge_edge_gateway...\n"; + std::system("pkill -f safe_edge_edge_gateway 2>/dev/null || true"); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +}; + +// --------------------------------------------------------------------------- +// Fixture: mock server participant (port 8050, peered with edge at 8030) +// Publishes: ServiceHeartbeat, ChargerLocation +// Reads: EdgeGatewayStatus +// --------------------------------------------------------------------------- + +class MockServerFixture : public ::testing::Test +{ +protected: + void SetUp() override + { + participant_ = make_participant("TestMockServer", 8050U); + ASSERT_NE(nullptr, participant_); + + eprosima::fastdds::dds::TypeSupport hb_ts( + new safe_edge::common::ServiceHeartbeatPubSubType()); + eprosima::fastdds::dds::TypeSupport loc_ts( + new safe_edge::pilot_server::ChargerLocationPubSubType()); + eprosima::fastdds::dds::TypeSupport status_ts( + new safe_edge::edge::EdgeGatewayStatusPubSubType()); + + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, hb_ts.register_type(participant_)); + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, loc_ts.register_type(participant_)); + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, status_ts.register_type(participant_)); + + namespace TN = safe_edge::edge_module::common::topic_names; + hb_topic_ = participant_->create_topic( + TN::service_heartbeat(), hb_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + loc_topic_ = participant_->create_topic( + TN::charger_locations(), loc_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + status_topic_ = participant_->create_topic( + TN::edge_gateway_status(), status_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + ASSERT_NE(nullptr, hb_topic_); + ASSERT_NE(nullptr, loc_topic_); + ASSERT_NE(nullptr, status_topic_); + + publisher_ = participant_->create_publisher( + eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + subscriber_ = participant_->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, publisher_); + ASSERT_NE(nullptr, subscriber_); + + eprosima::fastdds::dds::DataWriterQos wqos = + eprosima::fastdds::dds::DATAWRITER_QOS_DEFAULT; + wqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + eprosima::fastdds::dds::DataReaderQos rqos = + eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + hb_writer_ = publisher_->create_datawriter( + hb_topic_, wqos, nullptr, eprosima::fastdds::dds::StatusMask::none()); + loc_writer_ = publisher_->create_datawriter( + loc_topic_, wqos, nullptr, eprosima::fastdds::dds::StatusMask::none()); + status_reader_ = subscriber_->create_datareader( + status_topic_, rqos, nullptr, eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, hb_writer_); + ASSERT_NE(nullptr, loc_writer_); + ASSERT_NE(nullptr, status_reader_); + + std::this_thread::sleep_for(std::chrono::seconds(3)); // discovery + } + + void TearDown() override + { + if (participant_ != nullptr) + { + participant_->delete_contained_entities(); + eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->delete_participant(participant_); + participant_ = nullptr; + } + } + + void send_heartbeat() + { + safe_edge::common::ServiceHeartbeat hb{}; + hb.service_name("server"); + EXPECT_EQ(eprosima::fastdds::dds::RETCODE_OK, + hb_writer_->write(&hb, eprosima::fastdds::dds::HANDLE_NIL)); + } + + bool wait_for_status(safe_edge::common::HealthStatus expected, int timeout_s) + { + const auto deadline = + std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); + while (std::chrono::steady_clock::now() < deadline) + { + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + if (status_reader_->take_next_sample(&sample, &info) == + eprosima::fastdds::dds::RETCODE_OK && info.valid_data) + { + const bool is_ok = + sample.status() == safe_edge::common::HealthStatus::HEALTH_OK; + std::cout << "[dds] EdgeGatewayStatus=" + << (is_ok ? "OK" : "DEGRADED") << "\n"; + if (sample.status() == expected) + { + return true; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + return false; + } + + eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; + eprosima::fastdds::dds::Publisher* publisher_ = nullptr; + eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; + eprosima::fastdds::dds::Topic* hb_topic_ = nullptr; + eprosima::fastdds::dds::Topic* loc_topic_ = nullptr; + eprosima::fastdds::dds::Topic* status_topic_ = nullptr; + eprosima::fastdds::dds::DataWriter* hb_writer_ = nullptr; + eprosima::fastdds::dds::DataWriter* loc_writer_ = nullptr; + eprosima::fastdds::dds::DataReader* status_reader_= nullptr; +}; + +// --------------------------------------------------------------------------- +// 1. DEGRADED when no server present +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeStatusIsDegradedWithoutServer) +{ + // server_available_ starts false → first status (≤5 s) is DEGRADED + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 12)) + << "Expected DEGRADED within 12 s with no server heartbeat"; +} + +// --------------------------------------------------------------------------- +// 2. OK when server publishes heartbeats +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeStatusIsOkWithServer) +{ + for (int i = 0; i < 3; ++i) + { + send_heartbeat(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_OK, 12)) + << "Expected OK within 12 s after sending heartbeats"; +} + +// --------------------------------------------------------------------------- +// 3. Recovers from DEGRADED to OK when server starts +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeRecoversDegradedToOk) +{ + ASSERT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 12)) + << "Expected initial DEGRADED state"; + + for (int i = 0; i < 3; ++i) + { + send_heartbeat(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_OK, 12)) + << "Expected OK after heartbeats sent"; +} + +// --------------------------------------------------------------------------- +// 4. Transitions from OK to DEGRADED when server stops +// --------------------------------------------------------------------------- + +TEST_F(MockServerFixture, EdgeTransitionsOkToDegraded) +{ + std::atomic hb_running{true}; + std::thread hb_thread([&]() + { + while (hb_running.load()) + { + send_heartbeat(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } + }); + + ASSERT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_OK, 12)) + << "Expected OK while heartbeats are running"; + + // Stop heartbeats — DEGRADED after SERVER_HB_TIMEOUT_MS (10 s) + + // next status interval (≤5 s) → allow 20 s + hb_running.store(false); + hb_thread.join(); + + EXPECT_TRUE(wait_for_status(safe_edge::common::HealthStatus::HEALTH_DEGRADED, 20)) + << "Expected DEGRADED within 20 s after heartbeats stopped"; +} + +// --------------------------------------------------------------------------- +// 5. Status published periodically (status_interval_sec = 5) +// --------------------------------------------------------------------------- + +TEST(EdgePublishesStatusPeriodically, AtLeastTwoSamplesIn15Seconds) +{ + auto* participant = make_participant("TestPeriodic", 8051U); + ASSERT_NE(nullptr, participant); + + eprosima::fastdds::dds::TypeSupport status_ts( + new safe_edge::edge::EdgeGatewayStatusPubSubType()); + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, status_ts.register_type(participant)); + + auto* topic = participant->create_topic( + safe_edge::edge_module::common::topic_names::edge_gateway_status(), + status_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + ASSERT_NE(nullptr, topic); + + auto* sub = participant->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, sub); + + eprosima::fastdds::dds::DataReaderQos rqos = + eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + auto* reader = sub->create_datareader( + topic, rqos, nullptr, eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, reader); + + std::this_thread::sleep_for(std::chrono::seconds(3)); // discovery + + int count = 0; + const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(15); + while (std::chrono::steady_clock::now() < end) + { + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + if (reader->take_next_sample(&sample, &info) == + eprosima::fastdds::dds::RETCODE_OK && info.valid_data) + { + ++count; + std::cout << "[dds] Status sample #" << count << "\n"; + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + } + + EXPECT_GE(count, 2) + << "Expected >=2 EdgeGatewayStatus samples in 15 s, got " << count; +} + +// --------------------------------------------------------------------------- +// main +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new EdgeEnvironment()); + return RUN_ALL_TESTS(); +} diff --git a/fast_dds/idl/common.hpp b/fast_dds/idl/common.hpp new file mode 100644 index 0000000..a331aaa --- /dev/null +++ b/fast_dds/idl/common.hpp @@ -0,0 +1,730 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file common.hpp + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_HPP + +#include +#include +#include +#include + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#if defined(COMMON_SOURCE) +#define COMMON_DllAPI __declspec( dllexport ) +#else +#define COMMON_DllAPI __declspec( dllimport ) +#endif // COMMON_SOURCE +#else +#define COMMON_DllAPI +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define COMMON_DllAPI +#endif // _WIN32 + +namespace safe_edge { + +namespace common { + +/*! + * @brief This class represents the structure Header defined by the user in the IDL file. + * @ingroup common + */ +class Header +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport Header() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~Header() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object Header that will be copied. + */ + eProsima_user_DllExport Header( + const Header& x) + { + m_source = x.m_source; + + m_timestamp_ms = x.m_timestamp_ms; + + m_trace_id = x.m_trace_id; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object Header that will be copied. + */ + eProsima_user_DllExport Header( + Header&& x) noexcept + { + m_source = std::move(x.m_source); + m_timestamp_ms = x.m_timestamp_ms; + m_trace_id = std::move(x.m_trace_id); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object Header that will be copied. + */ + eProsima_user_DllExport Header& operator =( + const Header& x) + { + + m_source = x.m_source; + + m_timestamp_ms = x.m_timestamp_ms; + + m_trace_id = x.m_trace_id; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object Header that will be copied. + */ + eProsima_user_DllExport Header& operator =( + Header&& x) noexcept + { + + m_source = std::move(x.m_source); + m_timestamp_ms = x.m_timestamp_ms; + m_trace_id = std::move(x.m_trace_id); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x Header object to compare. + */ + eProsima_user_DllExport bool operator ==( + const Header& x) const + { + return (m_source == x.m_source && + m_timestamp_ms == x.m_timestamp_ms && + m_trace_id == x.m_trace_id); + } + + /*! + * @brief Comparison operator. + * @param x Header object to compare. + */ + eProsima_user_DllExport bool operator !=( + const Header& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member source + * @param _source New value to be copied in member source + */ + eProsima_user_DllExport void source( + const std::string& _source) + { + m_source = _source; + } + + /*! + * @brief This function moves the value in member source + * @param _source New value to be moved in member source + */ + eProsima_user_DllExport void source( + std::string&& _source) + { + m_source = std::move(_source); + } + + /*! + * @brief This function returns a constant reference to member source + * @return Constant reference to member source + */ + eProsima_user_DllExport const std::string& source() const + { + return m_source; + } + + /*! + * @brief This function returns a reference to member source + * @return Reference to member source + */ + eProsima_user_DllExport std::string& source() + { + return m_source; + } + + + /*! + * @brief This function sets a value in member timestamp_ms + * @param _timestamp_ms New value for member timestamp_ms + */ + eProsima_user_DllExport void timestamp_ms( + uint64_t _timestamp_ms) + { + m_timestamp_ms = _timestamp_ms; + } + + /*! + * @brief This function returns the value of member timestamp_ms + * @return Value of member timestamp_ms + */ + eProsima_user_DllExport uint64_t timestamp_ms() const + { + return m_timestamp_ms; + } + + /*! + * @brief This function returns a reference to member timestamp_ms + * @return Reference to member timestamp_ms + */ + eProsima_user_DllExport uint64_t& timestamp_ms() + { + return m_timestamp_ms; + } + + + /*! + * @brief This function copies the value in member trace_id + * @param _trace_id New value to be copied in member trace_id + */ + eProsima_user_DllExport void trace_id( + const std::string& _trace_id) + { + m_trace_id = _trace_id; + } + + /*! + * @brief This function moves the value in member trace_id + * @param _trace_id New value to be moved in member trace_id + */ + eProsima_user_DllExport void trace_id( + std::string&& _trace_id) + { + m_trace_id = std::move(_trace_id); + } + + /*! + * @brief This function returns a constant reference to member trace_id + * @return Constant reference to member trace_id + */ + eProsima_user_DllExport const std::string& trace_id() const + { + return m_trace_id; + } + + /*! + * @brief This function returns a reference to member trace_id + * @return Reference to member trace_id + */ + eProsima_user_DllExport std::string& trace_id() + { + return m_trace_id; + } + + + +private: + + std::string m_source; + uint64_t m_timestamp_ms{0}; + std::string m_trace_id; + +}; +/*! + * @brief This class represents the structure GeoPoint defined by the user in the IDL file. + * @ingroup common + */ +class GeoPoint +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport GeoPoint() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~GeoPoint() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object GeoPoint that will be copied. + */ + eProsima_user_DllExport GeoPoint( + const GeoPoint& x) + { + m_latitude = x.m_latitude; + + m_longitude = x.m_longitude; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object GeoPoint that will be copied. + */ + eProsima_user_DllExport GeoPoint( + GeoPoint&& x) noexcept + { + m_latitude = x.m_latitude; + m_longitude = x.m_longitude; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object GeoPoint that will be copied. + */ + eProsima_user_DllExport GeoPoint& operator =( + const GeoPoint& x) + { + + m_latitude = x.m_latitude; + + m_longitude = x.m_longitude; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object GeoPoint that will be copied. + */ + eProsima_user_DllExport GeoPoint& operator =( + GeoPoint&& x) noexcept + { + + m_latitude = x.m_latitude; + m_longitude = x.m_longitude; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x GeoPoint object to compare. + */ + eProsima_user_DllExport bool operator ==( + const GeoPoint& x) const + { + return (m_latitude == x.m_latitude && + m_longitude == x.m_longitude); + } + + /*! + * @brief Comparison operator. + * @param x GeoPoint object to compare. + */ + eProsima_user_DllExport bool operator !=( + const GeoPoint& x) const + { + return !(*this == x); + } + + /*! + * @brief This function sets a value in member latitude + * @param _latitude New value for member latitude + */ + eProsima_user_DllExport void latitude( + double _latitude) + { + m_latitude = _latitude; + } + + /*! + * @brief This function returns the value of member latitude + * @return Value of member latitude + */ + eProsima_user_DllExport double latitude() const + { + return m_latitude; + } + + /*! + * @brief This function returns a reference to member latitude + * @return Reference to member latitude + */ + eProsima_user_DllExport double& latitude() + { + return m_latitude; + } + + + /*! + * @brief This function sets a value in member longitude + * @param _longitude New value for member longitude + */ + eProsima_user_DllExport void longitude( + double _longitude) + { + m_longitude = _longitude; + } + + /*! + * @brief This function returns the value of member longitude + * @return Value of member longitude + */ + eProsima_user_DllExport double longitude() const + { + return m_longitude; + } + + /*! + * @brief This function returns a reference to member longitude + * @return Reference to member longitude + */ + eProsima_user_DllExport double& longitude() + { + return m_longitude; + } + + + +private: + + double m_latitude{0.0}; + double m_longitude{0.0}; + +}; +/*! + * @brief This class represents the enumeration HealthStatus defined by the user in the IDL file. + * @ingroup common + */ +enum class HealthStatus : int32_t +{ + HEALTH_UNKNOWN, + HEALTH_OK, + HEALTH_DEGRADED, + HEALTH_ERROR +}; +/*! + * @brief This class represents the structure ServiceHeartbeat defined by the user in the IDL file. + * @ingroup common + */ +class ServiceHeartbeat +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport ServiceHeartbeat() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~ServiceHeartbeat() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object ServiceHeartbeat that will be copied. + */ + eProsima_user_DllExport ServiceHeartbeat( + const ServiceHeartbeat& x) + { + m_header_st = x.m_header_st; + + m_service_name = x.m_service_name; + + m_status = x.m_status; + + m_detail = x.m_detail; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object ServiceHeartbeat that will be copied. + */ + eProsima_user_DllExport ServiceHeartbeat( + ServiceHeartbeat&& x) noexcept + { + m_header_st = std::move(x.m_header_st); + m_service_name = std::move(x.m_service_name); + m_status = x.m_status; + m_detail = std::move(x.m_detail); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object ServiceHeartbeat that will be copied. + */ + eProsima_user_DllExport ServiceHeartbeat& operator =( + const ServiceHeartbeat& x) + { + + m_header_st = x.m_header_st; + + m_service_name = x.m_service_name; + + m_status = x.m_status; + + m_detail = x.m_detail; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object ServiceHeartbeat that will be copied. + */ + eProsima_user_DllExport ServiceHeartbeat& operator =( + ServiceHeartbeat&& x) noexcept + { + + m_header_st = std::move(x.m_header_st); + m_service_name = std::move(x.m_service_name); + m_status = x.m_status; + m_detail = std::move(x.m_detail); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x ServiceHeartbeat object to compare. + */ + eProsima_user_DllExport bool operator ==( + const ServiceHeartbeat& x) const + { + return (m_header_st == x.m_header_st && + m_service_name == x.m_service_name && + m_status == x.m_status && + m_detail == x.m_detail); + } + + /*! + * @brief Comparison operator. + * @param x ServiceHeartbeat object to compare. + */ + eProsima_user_DllExport bool operator !=( + const ServiceHeartbeat& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member header_st + * @param _header_st New value to be copied in member header_st + */ + eProsima_user_DllExport void header_st( + const Header& _header_st) + { + m_header_st = _header_st; + } + + /*! + * @brief This function moves the value in member header_st + * @param _header_st New value to be moved in member header_st + */ + eProsima_user_DllExport void header_st( + Header&& _header_st) + { + m_header_st = std::move(_header_st); + } + + /*! + * @brief This function returns a constant reference to member header_st + * @return Constant reference to member header_st + */ + eProsima_user_DllExport const Header& header_st() const + { + return m_header_st; + } + + /*! + * @brief This function returns a reference to member header_st + * @return Reference to member header_st + */ + eProsima_user_DllExport Header& header_st() + { + return m_header_st; + } + + + /*! + * @brief This function copies the value in member service_name + * @param _service_name New value to be copied in member service_name + */ + eProsima_user_DllExport void service_name( + const std::string& _service_name) + { + m_service_name = _service_name; + } + + /*! + * @brief This function moves the value in member service_name + * @param _service_name New value to be moved in member service_name + */ + eProsima_user_DllExport void service_name( + std::string&& _service_name) + { + m_service_name = std::move(_service_name); + } + + /*! + * @brief This function returns a constant reference to member service_name + * @return Constant reference to member service_name + */ + eProsima_user_DllExport const std::string& service_name() const + { + return m_service_name; + } + + /*! + * @brief This function returns a reference to member service_name + * @return Reference to member service_name + */ + eProsima_user_DllExport std::string& service_name() + { + return m_service_name; + } + + + /*! + * @brief This function sets a value in member status + * @param _status New value for member status + */ + eProsima_user_DllExport void status( + HealthStatus _status) + { + m_status = _status; + } + + /*! + * @brief This function returns the value of member status + * @return Value of member status + */ + eProsima_user_DllExport HealthStatus status() const + { + return m_status; + } + + /*! + * @brief This function returns a reference to member status + * @return Reference to member status + */ + eProsima_user_DllExport HealthStatus& status() + { + return m_status; + } + + + /*! + * @brief This function copies the value in member detail + * @param _detail New value to be copied in member detail + */ + eProsima_user_DllExport void detail( + const std::string& _detail) + { + m_detail = _detail; + } + + /*! + * @brief This function moves the value in member detail + * @param _detail New value to be moved in member detail + */ + eProsima_user_DllExport void detail( + std::string&& _detail) + { + m_detail = std::move(_detail); + } + + /*! + * @brief This function returns a constant reference to member detail + * @return Constant reference to member detail + */ + eProsima_user_DllExport const std::string& detail() const + { + return m_detail; + } + + /*! + * @brief This function returns a reference to member detail + * @return Reference to member detail + */ + eProsima_user_DllExport std::string& detail() + { + return m_detail; + } + + + +private: + + Header m_header_st; + std::string m_service_name; + HealthStatus m_status{HealthStatus::HEALTH_UNKNOWN}; + std::string m_detail; + +}; +/*! + * @brief This class represents the enumeration PolicyMode defined by the user in the IDL file. + * @ingroup common + */ +enum class PolicyMode : int32_t +{ + POLICY_UNKNOWN, + POLICY_NOMINAL, + POLICY_LOW_SOC, + POLICY_EDGE_AUTONOMOUS, + POLICY_DEGRADED_SERVER_DOWN, + POLICY_DEGRADED_COMPLETE +}; + +} // namespace common + +} // namespace safe_edge + +#endif // _FAST_DDS_GENERATED_SAFE_EDGE_COMMON_COMMON_HPP_ + + diff --git a/fast_dds/idl/commonCdrAux.hpp b/fast_dds/idl/commonCdrAux.hpp new file mode 100644 index 0000000..444271f --- /dev/null +++ b/fast_dds/idl/commonCdrAux.hpp @@ -0,0 +1,62 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonCdrAux.hpp + * This source file contains some definitions of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_HPP + +#include "common.hpp" + +constexpr uint32_t safe_edge_common_ServiceHeartbeat_max_cdr_typesize {1064UL}; +constexpr uint32_t safe_edge_common_ServiceHeartbeat_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_common_GeoPoint_max_cdr_typesize {24UL}; +constexpr uint32_t safe_edge_common_GeoPoint_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_common_Header_max_cdr_typesize {532UL}; +constexpr uint32_t safe_edge_common_Header_max_key_cdr_typesize {0UL}; + + + + +namespace eprosima { +namespace fastcdr { + +class Cdr; +class CdrSizeCalculator; + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::Header& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::GeoPoint& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::ServiceHeartbeat& data); + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_HPP + diff --git a/fast_dds/idl/commonCdrAux.ipp b/fast_dds/idl/commonCdrAux.ipp new file mode 100644 index 0000000..6fe9e60 --- /dev/null +++ b/fast_dds/idl/commonCdrAux.ipp @@ -0,0 +1,367 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonCdrAux.ipp + * This source file contains some declarations of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_IPP +#define FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_IPP + +#include "commonCdrAux.hpp" + +#include +#include + + +#include +using namespace eprosima::fastcdr::exception; + +namespace eprosima { +namespace fastcdr { + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::common::Header& data, + size_t& current_alignment) +{ + using namespace safe_edge::common; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.source(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.timestamp_ms(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.trace_id(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::Header& data) +{ + using namespace safe_edge::common; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.source() + << eprosima::fastcdr::MemberId(1) << data.timestamp_ms() + << eprosima::fastcdr::MemberId(2) << data.trace_id() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::common::Header& data) +{ + using namespace safe_edge::common; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.source(); + break; + + case 1: + dcdr >> data.timestamp_ms(); + break; + + case 2: + dcdr >> data.trace_id(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::Header& data) +{ + using namespace safe_edge::common; + + static_cast(scdr); + static_cast(data); + scdr << data.source(); + + scdr << data.timestamp_ms(); + + scdr << data.trace_id(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::common::GeoPoint& data, + size_t& current_alignment) +{ + using namespace safe_edge::common; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.latitude(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.longitude(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::GeoPoint& data) +{ + using namespace safe_edge::common; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.latitude() + << eprosima::fastcdr::MemberId(1) << data.longitude() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::common::GeoPoint& data) +{ + using namespace safe_edge::common; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.latitude(); + break; + + case 1: + dcdr >> data.longitude(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::GeoPoint& data) +{ + using namespace safe_edge::common; + + static_cast(scdr); + static_cast(data); + scdr << data.latitude(); + + scdr << data.longitude(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::common::ServiceHeartbeat& data, + size_t& current_alignment) +{ + using namespace safe_edge::common; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.header_st(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.service_name(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.status(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), + data.detail(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::ServiceHeartbeat& data) +{ + using namespace safe_edge::common; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.header_st() + << eprosima::fastcdr::MemberId(1) << data.service_name() + << eprosima::fastcdr::MemberId(2) << data.status() + << eprosima::fastcdr::MemberId(3) << data.detail() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::common::ServiceHeartbeat& data) +{ + using namespace safe_edge::common; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.header_st(); + break; + + case 1: + dcdr >> data.service_name(); + break; + + case 2: + dcdr >> data.status(); + break; + + case 3: + dcdr >> data.detail(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::common::ServiceHeartbeat& data) +{ + using namespace safe_edge::common; + extern void serialize_key( + Cdr& scdr, + const safe_edge::common::Header& data); + + + + + + static_cast(scdr); + static_cast(data); + serialize_key(scdr, data.header_st()); + + scdr << data.service_name(); + + scdr << data.status(); + + scdr << data.detail(); + +} + + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMONCDRAUX_IPP + diff --git a/fast_dds/idl/commonPubSubTypes.cxx b/fast_dds/idl/commonPubSubTypes.cxx new file mode 100644 index 0000000..17eb4b6 --- /dev/null +++ b/fast_dds/idl/commonPubSubTypes.cxx @@ -0,0 +1,474 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonPubSubTypes.cpp + * This header file contains the implementation of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "commonPubSubTypes.hpp" + +#include +#include + +#include +#include + +#include "commonCdrAux.hpp" +#include "commonTypeObjectSupport.hpp" + +using SerializedPayload_t = eprosima::fastdds::rtps::SerializedPayload_t; +using InstanceHandle_t = eprosima::fastdds::rtps::InstanceHandle_t; +using DataRepresentationId_t = eprosima::fastdds::dds::DataRepresentationId_t; + +namespace safe_edge { +namespace common { +HeaderPubSubType::HeaderPubSubType() +{ + set_name("safe_edge::common::Header"); + uint32_t type_size = safe_edge_common_Header_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +HeaderPubSubType::~HeaderPubSubType() +{ +} + +bool HeaderPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::common::Header* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool HeaderPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::common::Header* p_type = + static_cast<::safe_edge::common::Header*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t HeaderPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::common::Header* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* HeaderPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::common::Header()); +} + +void HeaderPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::common::Header*>(data)); +} + +bool HeaderPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool HeaderPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void HeaderPubSubType::register_type_object_representation() +{ + register_Header_type_identifier(type_identifiers_); +} + +GeoPointPubSubType::GeoPointPubSubType() +{ + set_name("safe_edge::common::GeoPoint"); + uint32_t type_size = safe_edge_common_GeoPoint_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +GeoPointPubSubType::~GeoPointPubSubType() +{ +} + +bool GeoPointPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::common::GeoPoint* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool GeoPointPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::common::GeoPoint* p_type = + static_cast<::safe_edge::common::GeoPoint*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t GeoPointPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::common::GeoPoint* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* GeoPointPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::common::GeoPoint()); +} + +void GeoPointPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::common::GeoPoint*>(data)); +} + +bool GeoPointPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool GeoPointPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void GeoPointPubSubType::register_type_object_representation() +{ + register_GeoPoint_type_identifier(type_identifiers_); +} + +ServiceHeartbeatPubSubType::ServiceHeartbeatPubSubType() +{ + set_name("safe_edge::common::ServiceHeartbeat"); + uint32_t type_size = safe_edge_common_ServiceHeartbeat_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +ServiceHeartbeatPubSubType::~ServiceHeartbeatPubSubType() +{ +} + +bool ServiceHeartbeatPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::common::ServiceHeartbeat* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool ServiceHeartbeatPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::common::ServiceHeartbeat* p_type = + static_cast<::safe_edge::common::ServiceHeartbeat*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t ServiceHeartbeatPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::common::ServiceHeartbeat* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* ServiceHeartbeatPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::common::ServiceHeartbeat()); +} + +void ServiceHeartbeatPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::common::ServiceHeartbeat*>(data)); +} + +bool ServiceHeartbeatPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool ServiceHeartbeatPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void ServiceHeartbeatPubSubType::register_type_object_representation() +{ + register_ServiceHeartbeat_type_identifier(type_identifiers_); +} + +} // namespace common + +} // namespace safe_edge + + +// Include auxiliary functions like for serializing/deserializing. +#include "commonCdrAux.ipp" diff --git a/fast_dds/idl/commonPubSubTypes.hpp b/fast_dds/idl/commonPubSubTypes.hpp new file mode 100644 index 0000000..4a39c6a --- /dev/null +++ b/fast_dds/idl/commonPubSubTypes.hpp @@ -0,0 +1,285 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonPubSubTypes.hpp + * This header file contains the declaration of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_PUBSUBTYPES_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_PUBSUBTYPES_HPP + +#include + +#include +#include +#include +#include +#include + +#include "common.hpp" + + +#if !defined(FASTDDS_GEN_API_VER) || (FASTDDS_GEN_API_VER != 3) +#error \ + Generated common is not compatible with current installed Fast DDS. Please, regenerate it with fastddsgen. +#endif // FASTDDS_GEN_API_VER + +namespace safe_edge { +namespace common { + +/*! + * @brief This class represents the TopicDataType of the type Header defined by the user in the IDL file. + * @ingroup common + */ +class HeaderPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::common::Header type; + + eProsima_user_DllExport HeaderPubSubType(); + + eProsima_user_DllExport ~HeaderPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type GeoPoint defined by the user in the IDL file. + * @ingroup common + */ +class GeoPointPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::common::GeoPoint type; + + eProsima_user_DllExport GeoPointPubSubType(); + + eProsima_user_DllExport ~GeoPointPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return true; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type ServiceHeartbeat defined by the user in the IDL file. + * @ingroup common + */ +class ServiceHeartbeatPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::common::ServiceHeartbeat type; + + eProsima_user_DllExport ServiceHeartbeatPubSubType(); + + eProsima_user_DllExport ~ServiceHeartbeatPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +} // namespace common +} // namespace safe_edge + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_PUBSUBTYPES_HPP + diff --git a/fast_dds/idl/commonTypeObjectSupport.cxx b/fast_dds/idl/commonTypeObjectSupport.cxx new file mode 100644 index 0000000..5a9d53c --- /dev/null +++ b/fast_dds/idl/commonTypeObjectSupport.cxx @@ -0,0 +1,583 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonTypeObjectSupport.cxx + * Source file containing the implementation to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "commonTypeObjectSupport.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.hpp" + + +using namespace eprosima::fastdds::dds::xtypes; + +namespace safe_edge { +namespace common { +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_Header_type_identifier( + TypeIdentifierPair& type_ids_Header) +{ + + ReturnCode_t return_code_Header {eprosima::fastdds::dds::RETCODE_OK}; + return_code_Header = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::Header", type_ids_Header); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_Header) + { + StructTypeFlag struct_flags_Header = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_Header = "safe_edge::common::Header"; + eprosima::fastcdr::optional type_ann_builtin_Header; + eprosima::fastcdr::optional ann_custom_Header; + CompleteTypeDetail detail_Header = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_Header, ann_custom_Header, type_name_Header.to_string()); + CompleteStructHeader header_Header; + header_Header = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_Header); + CompleteStructMemberSeq member_seq_Header; + { + TypeIdentifierPair type_ids_source; + ReturnCode_t return_code_source {eprosima::fastdds::dds::RETCODE_OK}; + return_code_source = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_source); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_source) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_source)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_source = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_source = 0x00000000; + bool common_source_ec {false}; + CommonStructMember common_source {TypeObjectUtils::build_common_struct_member(member_id_source, member_flags_source, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_source, common_source_ec))}; + if (!common_source_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure source member TypeIdentifier inconsistent."); + return; + } + MemberName name_source = "source"; + eprosima::fastcdr::optional member_ann_builtin_source; + ann_custom_Header.reset(); + CompleteMemberDetail detail_source = TypeObjectUtils::build_complete_member_detail(name_source, member_ann_builtin_source, ann_custom_Header); + CompleteStructMember member_source = TypeObjectUtils::build_complete_struct_member(common_source, detail_source); + TypeObjectUtils::add_complete_struct_member(member_seq_Header, member_source); + } + { + TypeIdentifierPair type_ids_timestamp_ms; + ReturnCode_t return_code_timestamp_ms {eprosima::fastdds::dds::RETCODE_OK}; + return_code_timestamp_ms = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_uint64_t", type_ids_timestamp_ms); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_timestamp_ms) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "timestamp_ms Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_timestamp_ms = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_timestamp_ms = 0x00000001; + bool common_timestamp_ms_ec {false}; + CommonStructMember common_timestamp_ms {TypeObjectUtils::build_common_struct_member(member_id_timestamp_ms, member_flags_timestamp_ms, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_timestamp_ms, common_timestamp_ms_ec))}; + if (!common_timestamp_ms_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure timestamp_ms member TypeIdentifier inconsistent."); + return; + } + MemberName name_timestamp_ms = "timestamp_ms"; + eprosima::fastcdr::optional member_ann_builtin_timestamp_ms; + ann_custom_Header.reset(); + CompleteMemberDetail detail_timestamp_ms = TypeObjectUtils::build_complete_member_detail(name_timestamp_ms, member_ann_builtin_timestamp_ms, ann_custom_Header); + CompleteStructMember member_timestamp_ms = TypeObjectUtils::build_complete_struct_member(common_timestamp_ms, detail_timestamp_ms); + TypeObjectUtils::add_complete_struct_member(member_seq_Header, member_timestamp_ms); + } + { + TypeIdentifierPair type_ids_trace_id; + ReturnCode_t return_code_trace_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_trace_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_trace_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_trace_id) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_trace_id)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_trace_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_trace_id = 0x00000002; + bool common_trace_id_ec {false}; + CommonStructMember common_trace_id {TypeObjectUtils::build_common_struct_member(member_id_trace_id, member_flags_trace_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_trace_id, common_trace_id_ec))}; + if (!common_trace_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure trace_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_trace_id = "trace_id"; + eprosima::fastcdr::optional member_ann_builtin_trace_id; + ann_custom_Header.reset(); + CompleteMemberDetail detail_trace_id = TypeObjectUtils::build_complete_member_detail(name_trace_id, member_ann_builtin_trace_id, ann_custom_Header); + CompleteStructMember member_trace_id = TypeObjectUtils::build_complete_struct_member(common_trace_id, detail_trace_id); + TypeObjectUtils::add_complete_struct_member(member_seq_Header, member_trace_id); + } + CompleteStructType struct_type_Header = TypeObjectUtils::build_complete_struct_type(struct_flags_Header, header_Header, member_seq_Header); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_Header, type_name_Header.to_string(), type_ids_Header)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::common::Header already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_GeoPoint_type_identifier( + TypeIdentifierPair& type_ids_GeoPoint) +{ + + ReturnCode_t return_code_GeoPoint {eprosima::fastdds::dds::RETCODE_OK}; + return_code_GeoPoint = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::GeoPoint", type_ids_GeoPoint); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_GeoPoint) + { + StructTypeFlag struct_flags_GeoPoint = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_GeoPoint = "safe_edge::common::GeoPoint"; + eprosima::fastcdr::optional type_ann_builtin_GeoPoint; + eprosima::fastcdr::optional ann_custom_GeoPoint; + CompleteTypeDetail detail_GeoPoint = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_GeoPoint, ann_custom_GeoPoint, type_name_GeoPoint.to_string()); + CompleteStructHeader header_GeoPoint; + header_GeoPoint = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_GeoPoint); + CompleteStructMemberSeq member_seq_GeoPoint; + { + TypeIdentifierPair type_ids_latitude; + ReturnCode_t return_code_latitude {eprosima::fastdds::dds::RETCODE_OK}; + return_code_latitude = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_double", type_ids_latitude); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_latitude) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "latitude Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_latitude = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_latitude = 0x00000000; + bool common_latitude_ec {false}; + CommonStructMember common_latitude {TypeObjectUtils::build_common_struct_member(member_id_latitude, member_flags_latitude, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_latitude, common_latitude_ec))}; + if (!common_latitude_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure latitude member TypeIdentifier inconsistent."); + return; + } + MemberName name_latitude = "latitude"; + eprosima::fastcdr::optional member_ann_builtin_latitude; + ann_custom_GeoPoint.reset(); + CompleteMemberDetail detail_latitude = TypeObjectUtils::build_complete_member_detail(name_latitude, member_ann_builtin_latitude, ann_custom_GeoPoint); + CompleteStructMember member_latitude = TypeObjectUtils::build_complete_struct_member(common_latitude, detail_latitude); + TypeObjectUtils::add_complete_struct_member(member_seq_GeoPoint, member_latitude); + } + { + TypeIdentifierPair type_ids_longitude; + ReturnCode_t return_code_longitude {eprosima::fastdds::dds::RETCODE_OK}; + return_code_longitude = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_double", type_ids_longitude); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_longitude) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "longitude Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_longitude = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_longitude = 0x00000001; + bool common_longitude_ec {false}; + CommonStructMember common_longitude {TypeObjectUtils::build_common_struct_member(member_id_longitude, member_flags_longitude, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_longitude, common_longitude_ec))}; + if (!common_longitude_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure longitude member TypeIdentifier inconsistent."); + return; + } + MemberName name_longitude = "longitude"; + eprosima::fastcdr::optional member_ann_builtin_longitude; + ann_custom_GeoPoint.reset(); + CompleteMemberDetail detail_longitude = TypeObjectUtils::build_complete_member_detail(name_longitude, member_ann_builtin_longitude, ann_custom_GeoPoint); + CompleteStructMember member_longitude = TypeObjectUtils::build_complete_struct_member(common_longitude, detail_longitude); + TypeObjectUtils::add_complete_struct_member(member_seq_GeoPoint, member_longitude); + } + CompleteStructType struct_type_GeoPoint = TypeObjectUtils::build_complete_struct_type(struct_flags_GeoPoint, header_GeoPoint, member_seq_GeoPoint); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_GeoPoint, type_name_GeoPoint.to_string(), type_ids_GeoPoint)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::common::GeoPoint already registered in TypeObjectRegistry for a different type."); + } + } +}void register_HealthStatus_type_identifier( + TypeIdentifierPair& type_ids_HealthStatus) +{ + ReturnCode_t return_code_HealthStatus {eprosima::fastdds::dds::RETCODE_OK}; + return_code_HealthStatus = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::HealthStatus", type_ids_HealthStatus); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_HealthStatus) + { + EnumTypeFlag enum_flags_HealthStatus = 0; + BitBound bit_bound_HealthStatus = 32; + CommonEnumeratedHeader common_HealthStatus = TypeObjectUtils::build_common_enumerated_header(bit_bound_HealthStatus); + QualifiedTypeName type_name_HealthStatus = "safe_edge::common::HealthStatus"; + eprosima::fastcdr::optional type_ann_builtin_HealthStatus; + eprosima::fastcdr::optional ann_custom_HealthStatus; + CompleteTypeDetail detail_HealthStatus = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_HealthStatus, ann_custom_HealthStatus, type_name_HealthStatus.to_string()); + CompleteEnumeratedHeader header_HealthStatus = TypeObjectUtils::build_complete_enumerated_header(common_HealthStatus, detail_HealthStatus); + CompleteEnumeratedLiteralSeq literal_seq_HealthStatus; + { + EnumeratedLiteralFlag flags_HEALTH_UNKNOWN = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_HEALTH_UNKNOWN = TypeObjectUtils::build_common_enumerated_literal(0, flags_HEALTH_UNKNOWN); + eprosima::fastcdr::optional member_ann_builtin_HEALTH_UNKNOWN; + ann_custom_HealthStatus.reset(); + MemberName name_HEALTH_UNKNOWN = "HEALTH_UNKNOWN"; + CompleteMemberDetail detail_HEALTH_UNKNOWN = TypeObjectUtils::build_complete_member_detail(name_HEALTH_UNKNOWN, member_ann_builtin_HEALTH_UNKNOWN, ann_custom_HealthStatus); + CompleteEnumeratedLiteral literal_HEALTH_UNKNOWN = TypeObjectUtils::build_complete_enumerated_literal(common_HEALTH_UNKNOWN, detail_HEALTH_UNKNOWN); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_HealthStatus, literal_HEALTH_UNKNOWN); + } + { + EnumeratedLiteralFlag flags_HEALTH_OK = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_HEALTH_OK = TypeObjectUtils::build_common_enumerated_literal(1, flags_HEALTH_OK); + eprosima::fastcdr::optional member_ann_builtin_HEALTH_OK; + ann_custom_HealthStatus.reset(); + MemberName name_HEALTH_OK = "HEALTH_OK"; + CompleteMemberDetail detail_HEALTH_OK = TypeObjectUtils::build_complete_member_detail(name_HEALTH_OK, member_ann_builtin_HEALTH_OK, ann_custom_HealthStatus); + CompleteEnumeratedLiteral literal_HEALTH_OK = TypeObjectUtils::build_complete_enumerated_literal(common_HEALTH_OK, detail_HEALTH_OK); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_HealthStatus, literal_HEALTH_OK); + } + { + EnumeratedLiteralFlag flags_HEALTH_DEGRADED = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_HEALTH_DEGRADED = TypeObjectUtils::build_common_enumerated_literal(2, flags_HEALTH_DEGRADED); + eprosima::fastcdr::optional member_ann_builtin_HEALTH_DEGRADED; + ann_custom_HealthStatus.reset(); + MemberName name_HEALTH_DEGRADED = "HEALTH_DEGRADED"; + CompleteMemberDetail detail_HEALTH_DEGRADED = TypeObjectUtils::build_complete_member_detail(name_HEALTH_DEGRADED, member_ann_builtin_HEALTH_DEGRADED, ann_custom_HealthStatus); + CompleteEnumeratedLiteral literal_HEALTH_DEGRADED = TypeObjectUtils::build_complete_enumerated_literal(common_HEALTH_DEGRADED, detail_HEALTH_DEGRADED); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_HealthStatus, literal_HEALTH_DEGRADED); + } + { + EnumeratedLiteralFlag flags_HEALTH_ERROR = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_HEALTH_ERROR = TypeObjectUtils::build_common_enumerated_literal(3, flags_HEALTH_ERROR); + eprosima::fastcdr::optional member_ann_builtin_HEALTH_ERROR; + ann_custom_HealthStatus.reset(); + MemberName name_HEALTH_ERROR = "HEALTH_ERROR"; + CompleteMemberDetail detail_HEALTH_ERROR = TypeObjectUtils::build_complete_member_detail(name_HEALTH_ERROR, member_ann_builtin_HEALTH_ERROR, ann_custom_HealthStatus); + CompleteEnumeratedLiteral literal_HEALTH_ERROR = TypeObjectUtils::build_complete_enumerated_literal(common_HEALTH_ERROR, detail_HEALTH_ERROR); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_HealthStatus, literal_HEALTH_ERROR); + } + CompleteEnumeratedType enumerated_type_HealthStatus = TypeObjectUtils::build_complete_enumerated_type(enum_flags_HealthStatus, header_HealthStatus, + literal_seq_HealthStatus); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_enumerated_type_object(enumerated_type_HealthStatus, type_name_HealthStatus.to_string(), type_ids_HealthStatus)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::common::HealthStatus already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_ServiceHeartbeat_type_identifier( + TypeIdentifierPair& type_ids_ServiceHeartbeat) +{ + + ReturnCode_t return_code_ServiceHeartbeat {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ServiceHeartbeat = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::ServiceHeartbeat", type_ids_ServiceHeartbeat); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ServiceHeartbeat) + { + StructTypeFlag struct_flags_ServiceHeartbeat = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_ServiceHeartbeat = "safe_edge::common::ServiceHeartbeat"; + eprosima::fastcdr::optional type_ann_builtin_ServiceHeartbeat; + eprosima::fastcdr::optional ann_custom_ServiceHeartbeat; + CompleteTypeDetail detail_ServiceHeartbeat = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ServiceHeartbeat, ann_custom_ServiceHeartbeat, type_name_ServiceHeartbeat.to_string()); + CompleteStructHeader header_ServiceHeartbeat; + header_ServiceHeartbeat = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ServiceHeartbeat); + CompleteStructMemberSeq member_seq_ServiceHeartbeat; + { + TypeIdentifierPair type_ids_header_st; + ReturnCode_t return_code_header_st {eprosima::fastdds::dds::RETCODE_OK}; + return_code_header_st = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::Header", type_ids_header_st); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_header_st) + { + ::safe_edge::common::register_Header_type_identifier(type_ids_header_st); + } + StructMemberFlag member_flags_header_st = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_header_st = 0x00000000; + bool common_header_st_ec {false}; + CommonStructMember common_header_st {TypeObjectUtils::build_common_struct_member(member_id_header_st, member_flags_header_st, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_header_st, common_header_st_ec))}; + if (!common_header_st_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure header_st member TypeIdentifier inconsistent."); + return; + } + MemberName name_header_st = "header_st"; + eprosima::fastcdr::optional member_ann_builtin_header_st; + ann_custom_ServiceHeartbeat.reset(); + CompleteMemberDetail detail_header_st = TypeObjectUtils::build_complete_member_detail(name_header_st, member_ann_builtin_header_st, ann_custom_ServiceHeartbeat); + CompleteStructMember member_header_st = TypeObjectUtils::build_complete_struct_member(common_header_st, detail_header_st); + TypeObjectUtils::add_complete_struct_member(member_seq_ServiceHeartbeat, member_header_st); + } + { + TypeIdentifierPair type_ids_service_name; + ReturnCode_t return_code_service_name {eprosima::fastdds::dds::RETCODE_OK}; + return_code_service_name = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_service_name); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_service_name) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_service_name)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_service_name = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_service_name = 0x00000001; + bool common_service_name_ec {false}; + CommonStructMember common_service_name {TypeObjectUtils::build_common_struct_member(member_id_service_name, member_flags_service_name, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_service_name, common_service_name_ec))}; + if (!common_service_name_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure service_name member TypeIdentifier inconsistent."); + return; + } + MemberName name_service_name = "service_name"; + eprosima::fastcdr::optional member_ann_builtin_service_name; + ann_custom_ServiceHeartbeat.reset(); + CompleteMemberDetail detail_service_name = TypeObjectUtils::build_complete_member_detail(name_service_name, member_ann_builtin_service_name, ann_custom_ServiceHeartbeat); + CompleteStructMember member_service_name = TypeObjectUtils::build_complete_struct_member(common_service_name, detail_service_name); + TypeObjectUtils::add_complete_struct_member(member_seq_ServiceHeartbeat, member_service_name); + } + { + TypeIdentifierPair type_ids_status; + ReturnCode_t return_code_status {eprosima::fastdds::dds::RETCODE_OK}; + return_code_status = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::HealthStatus", type_ids_status); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_status) + { + ::safe_edge::common::register_HealthStatus_type_identifier(type_ids_status); + } + StructMemberFlag member_flags_status = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_status = 0x00000002; + bool common_status_ec {false}; + CommonStructMember common_status {TypeObjectUtils::build_common_struct_member(member_id_status, member_flags_status, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_status, common_status_ec))}; + if (!common_status_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure status member TypeIdentifier inconsistent."); + return; + } + MemberName name_status = "status"; + eprosima::fastcdr::optional member_ann_builtin_status; + ann_custom_ServiceHeartbeat.reset(); + CompleteMemberDetail detail_status = TypeObjectUtils::build_complete_member_detail(name_status, member_ann_builtin_status, ann_custom_ServiceHeartbeat); + CompleteStructMember member_status = TypeObjectUtils::build_complete_struct_member(common_status, detail_status); + TypeObjectUtils::add_complete_struct_member(member_seq_ServiceHeartbeat, member_status); + } + { + TypeIdentifierPair type_ids_detail; + ReturnCode_t return_code_detail {eprosima::fastdds::dds::RETCODE_OK}; + return_code_detail = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_detail); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_detail) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_detail)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_detail = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_detail = 0x00000003; + bool common_detail_ec {false}; + CommonStructMember common_detail {TypeObjectUtils::build_common_struct_member(member_id_detail, member_flags_detail, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_detail, common_detail_ec))}; + if (!common_detail_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure detail member TypeIdentifier inconsistent."); + return; + } + MemberName name_detail = "detail"; + eprosima::fastcdr::optional member_ann_builtin_detail; + ann_custom_ServiceHeartbeat.reset(); + CompleteMemberDetail detail_detail = TypeObjectUtils::build_complete_member_detail(name_detail, member_ann_builtin_detail, ann_custom_ServiceHeartbeat); + CompleteStructMember member_detail = TypeObjectUtils::build_complete_struct_member(common_detail, detail_detail); + TypeObjectUtils::add_complete_struct_member(member_seq_ServiceHeartbeat, member_detail); + } + CompleteStructType struct_type_ServiceHeartbeat = TypeObjectUtils::build_complete_struct_type(struct_flags_ServiceHeartbeat, header_ServiceHeartbeat, member_seq_ServiceHeartbeat); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_ServiceHeartbeat, type_name_ServiceHeartbeat.to_string(), type_ids_ServiceHeartbeat)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::common::ServiceHeartbeat already registered in TypeObjectRegistry for a different type."); + } + } +}void register_PolicyMode_type_identifier( + TypeIdentifierPair& type_ids_PolicyMode) +{ + ReturnCode_t return_code_PolicyMode {eprosima::fastdds::dds::RETCODE_OK}; + return_code_PolicyMode = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::PolicyMode", type_ids_PolicyMode); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_PolicyMode) + { + EnumTypeFlag enum_flags_PolicyMode = 0; + BitBound bit_bound_PolicyMode = 32; + CommonEnumeratedHeader common_PolicyMode = TypeObjectUtils::build_common_enumerated_header(bit_bound_PolicyMode); + QualifiedTypeName type_name_PolicyMode = "safe_edge::common::PolicyMode"; + eprosima::fastcdr::optional type_ann_builtin_PolicyMode; + eprosima::fastcdr::optional ann_custom_PolicyMode; + CompleteTypeDetail detail_PolicyMode = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_PolicyMode, ann_custom_PolicyMode, type_name_PolicyMode.to_string()); + CompleteEnumeratedHeader header_PolicyMode = TypeObjectUtils::build_complete_enumerated_header(common_PolicyMode, detail_PolicyMode); + CompleteEnumeratedLiteralSeq literal_seq_PolicyMode; + { + EnumeratedLiteralFlag flags_POLICY_UNKNOWN = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_UNKNOWN = TypeObjectUtils::build_common_enumerated_literal(0, flags_POLICY_UNKNOWN); + eprosima::fastcdr::optional member_ann_builtin_POLICY_UNKNOWN; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_UNKNOWN = "POLICY_UNKNOWN"; + CompleteMemberDetail detail_POLICY_UNKNOWN = TypeObjectUtils::build_complete_member_detail(name_POLICY_UNKNOWN, member_ann_builtin_POLICY_UNKNOWN, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_UNKNOWN = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_UNKNOWN, detail_POLICY_UNKNOWN); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_UNKNOWN); + } + { + EnumeratedLiteralFlag flags_POLICY_NOMINAL = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_NOMINAL = TypeObjectUtils::build_common_enumerated_literal(1, flags_POLICY_NOMINAL); + eprosima::fastcdr::optional member_ann_builtin_POLICY_NOMINAL; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_NOMINAL = "POLICY_NOMINAL"; + CompleteMemberDetail detail_POLICY_NOMINAL = TypeObjectUtils::build_complete_member_detail(name_POLICY_NOMINAL, member_ann_builtin_POLICY_NOMINAL, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_NOMINAL = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_NOMINAL, detail_POLICY_NOMINAL); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_NOMINAL); + } + { + EnumeratedLiteralFlag flags_POLICY_LOW_SOC = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_LOW_SOC = TypeObjectUtils::build_common_enumerated_literal(2, flags_POLICY_LOW_SOC); + eprosima::fastcdr::optional member_ann_builtin_POLICY_LOW_SOC; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_LOW_SOC = "POLICY_LOW_SOC"; + CompleteMemberDetail detail_POLICY_LOW_SOC = TypeObjectUtils::build_complete_member_detail(name_POLICY_LOW_SOC, member_ann_builtin_POLICY_LOW_SOC, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_LOW_SOC = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_LOW_SOC, detail_POLICY_LOW_SOC); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_LOW_SOC); + } + { + EnumeratedLiteralFlag flags_POLICY_EDGE_AUTONOMOUS = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_EDGE_AUTONOMOUS = TypeObjectUtils::build_common_enumerated_literal(3, flags_POLICY_EDGE_AUTONOMOUS); + eprosima::fastcdr::optional member_ann_builtin_POLICY_EDGE_AUTONOMOUS; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_EDGE_AUTONOMOUS = "POLICY_EDGE_AUTONOMOUS"; + CompleteMemberDetail detail_POLICY_EDGE_AUTONOMOUS = TypeObjectUtils::build_complete_member_detail(name_POLICY_EDGE_AUTONOMOUS, member_ann_builtin_POLICY_EDGE_AUTONOMOUS, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_EDGE_AUTONOMOUS = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_EDGE_AUTONOMOUS, detail_POLICY_EDGE_AUTONOMOUS); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_EDGE_AUTONOMOUS); + } + { + EnumeratedLiteralFlag flags_POLICY_DEGRADED_SERVER_DOWN = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_DEGRADED_SERVER_DOWN = TypeObjectUtils::build_common_enumerated_literal(4, flags_POLICY_DEGRADED_SERVER_DOWN); + eprosima::fastcdr::optional member_ann_builtin_POLICY_DEGRADED_SERVER_DOWN; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_DEGRADED_SERVER_DOWN = "POLICY_DEGRADED_SERVER_DOWN"; + CompleteMemberDetail detail_POLICY_DEGRADED_SERVER_DOWN = TypeObjectUtils::build_complete_member_detail(name_POLICY_DEGRADED_SERVER_DOWN, member_ann_builtin_POLICY_DEGRADED_SERVER_DOWN, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_DEGRADED_SERVER_DOWN = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_DEGRADED_SERVER_DOWN, detail_POLICY_DEGRADED_SERVER_DOWN); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_DEGRADED_SERVER_DOWN); + } + { + EnumeratedLiteralFlag flags_POLICY_DEGRADED_COMPLETE = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_POLICY_DEGRADED_COMPLETE = TypeObjectUtils::build_common_enumerated_literal(5, flags_POLICY_DEGRADED_COMPLETE); + eprosima::fastcdr::optional member_ann_builtin_POLICY_DEGRADED_COMPLETE; + ann_custom_PolicyMode.reset(); + MemberName name_POLICY_DEGRADED_COMPLETE = "POLICY_DEGRADED_COMPLETE"; + CompleteMemberDetail detail_POLICY_DEGRADED_COMPLETE = TypeObjectUtils::build_complete_member_detail(name_POLICY_DEGRADED_COMPLETE, member_ann_builtin_POLICY_DEGRADED_COMPLETE, ann_custom_PolicyMode); + CompleteEnumeratedLiteral literal_POLICY_DEGRADED_COMPLETE = TypeObjectUtils::build_complete_enumerated_literal(common_POLICY_DEGRADED_COMPLETE, detail_POLICY_DEGRADED_COMPLETE); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_PolicyMode, literal_POLICY_DEGRADED_COMPLETE); + } + CompleteEnumeratedType enumerated_type_PolicyMode = TypeObjectUtils::build_complete_enumerated_type(enum_flags_PolicyMode, header_PolicyMode, + literal_seq_PolicyMode); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_enumerated_type_object(enumerated_type_PolicyMode, type_name_PolicyMode.to_string(), type_ids_PolicyMode)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::common::PolicyMode already registered in TypeObjectRegistry for a different type."); + } + } +} +} // namespace common + +} // namespace safe_edge + diff --git a/fast_dds/idl/commonTypeObjectSupport.hpp b/fast_dds/idl/commonTypeObjectSupport.hpp new file mode 100644 index 0000000..9a2be3b --- /dev/null +++ b/fast_dds/idl/commonTypeObjectSupport.hpp @@ -0,0 +1,114 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file commonTypeObjectSupport.hpp + * Header file containing the API required to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_TYPE_OBJECT_SUPPORT_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_TYPE_OBJECT_SUPPORT_HPP + +#include + + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +namespace safe_edge { +namespace common { +/** + * @brief Register Header related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_Header_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register GeoPoint related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_GeoPoint_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register HealthStatus related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_HealthStatus_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register ServiceHeartbeat related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ServiceHeartbeat_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register PolicyMode related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_PolicyMode_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +} // namespace common + +} // namespace safe_edge + + +#endif // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_COMMON_COMMON_TYPE_OBJECT_SUPPORT_HPP diff --git a/fast_dds/idl/edge.hpp b/fast_dds/idl/edge.hpp new file mode 100644 index 0000000..7231574 --- /dev/null +++ b/fast_dds/idl/edge.hpp @@ -0,0 +1,892 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edge.hpp + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_HPP + +#include +#include +#include +#include +#include "common.hpp" + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#if defined(EDGE_SOURCE) +#define EDGE_DllAPI __declspec( dllexport ) +#else +#define EDGE_DllAPI __declspec( dllimport ) +#endif // EDGE_SOURCE +#else +#define EDGE_DllAPI +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define EDGE_DllAPI +#endif // _WIN32 + +namespace safe_edge { + +namespace edge { + +/*! + * @brief This class represents the structure VehicleEdgeSummary defined by the user in the IDL file. + * @ingroup edge + */ +class VehicleEdgeSummary +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport VehicleEdgeSummary() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~VehicleEdgeSummary() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object VehicleEdgeSummary that will be copied. + */ + eProsima_user_DllExport VehicleEdgeSummary( + const VehicleEdgeSummary& x) + { + m_header = x.m_header; + + m_soc_pct = x.m_soc_pct; + + m_current_mode = x.m_current_mode; + + m_vehicle_health = x.m_vehicle_health; + + m_v2g_ready = x.m_v2g_ready; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object VehicleEdgeSummary that will be copied. + */ + eProsima_user_DllExport VehicleEdgeSummary( + VehicleEdgeSummary&& x) noexcept + { + m_header = std::move(x.m_header); + m_soc_pct = x.m_soc_pct; + m_current_mode = x.m_current_mode; + m_vehicle_health = x.m_vehicle_health; + m_v2g_ready = x.m_v2g_ready; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object VehicleEdgeSummary that will be copied. + */ + eProsima_user_DllExport VehicleEdgeSummary& operator =( + const VehicleEdgeSummary& x) + { + + m_header = x.m_header; + + m_soc_pct = x.m_soc_pct; + + m_current_mode = x.m_current_mode; + + m_vehicle_health = x.m_vehicle_health; + + m_v2g_ready = x.m_v2g_ready; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object VehicleEdgeSummary that will be copied. + */ + eProsima_user_DllExport VehicleEdgeSummary& operator =( + VehicleEdgeSummary&& x) noexcept + { + + m_header = std::move(x.m_header); + m_soc_pct = x.m_soc_pct; + m_current_mode = x.m_current_mode; + m_vehicle_health = x.m_vehicle_health; + m_v2g_ready = x.m_v2g_ready; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x VehicleEdgeSummary object to compare. + */ + eProsima_user_DllExport bool operator ==( + const VehicleEdgeSummary& x) const + { + return (m_header == x.m_header && + m_soc_pct == x.m_soc_pct && + m_current_mode == x.m_current_mode && + m_vehicle_health == x.m_vehicle_health && + m_v2g_ready == x.m_v2g_ready); + } + + /*! + * @brief Comparison operator. + * @param x VehicleEdgeSummary object to compare. + */ + eProsima_user_DllExport bool operator !=( + const VehicleEdgeSummary& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member header + * @param _header New value to be copied in member header + */ + eProsima_user_DllExport void header( + const safe_edge::common::Header& _header) + { + m_header = _header; + } + + /*! + * @brief This function moves the value in member header + * @param _header New value to be moved in member header + */ + eProsima_user_DllExport void header( + safe_edge::common::Header&& _header) + { + m_header = std::move(_header); + } + + /*! + * @brief This function returns a constant reference to member header + * @return Constant reference to member header + */ + eProsima_user_DllExport const safe_edge::common::Header& header() const + { + return m_header; + } + + /*! + * @brief This function returns a reference to member header + * @return Reference to member header + */ + eProsima_user_DllExport safe_edge::common::Header& header() + { + return m_header; + } + + + /*! + * @brief This function sets a value in member soc_pct + * @param _soc_pct New value for member soc_pct + */ + eProsima_user_DllExport void soc_pct( + float _soc_pct) + { + m_soc_pct = _soc_pct; + } + + /*! + * @brief This function returns the value of member soc_pct + * @return Value of member soc_pct + */ + eProsima_user_DllExport float soc_pct() const + { + return m_soc_pct; + } + + /*! + * @brief This function returns a reference to member soc_pct + * @return Reference to member soc_pct + */ + eProsima_user_DllExport float& soc_pct() + { + return m_soc_pct; + } + + + /*! + * @brief This function sets a value in member current_mode + * @param _current_mode New value for member current_mode + */ + eProsima_user_DllExport void current_mode( + safe_edge::common::PolicyMode _current_mode) + { + m_current_mode = _current_mode; + } + + /*! + * @brief This function returns the value of member current_mode + * @return Value of member current_mode + */ + eProsima_user_DllExport safe_edge::common::PolicyMode current_mode() const + { + return m_current_mode; + } + + /*! + * @brief This function returns a reference to member current_mode + * @return Reference to member current_mode + */ + eProsima_user_DllExport safe_edge::common::PolicyMode& current_mode() + { + return m_current_mode; + } + + + /*! + * @brief This function sets a value in member vehicle_health + * @param _vehicle_health New value for member vehicle_health + */ + eProsima_user_DllExport void vehicle_health( + safe_edge::common::HealthStatus _vehicle_health) + { + m_vehicle_health = _vehicle_health; + } + + /*! + * @brief This function returns the value of member vehicle_health + * @return Value of member vehicle_health + */ + eProsima_user_DllExport safe_edge::common::HealthStatus vehicle_health() const + { + return m_vehicle_health; + } + + /*! + * @brief This function returns a reference to member vehicle_health + * @return Reference to member vehicle_health + */ + eProsima_user_DllExport safe_edge::common::HealthStatus& vehicle_health() + { + return m_vehicle_health; + } + + + /*! + * @brief This function sets a value in member v2g_ready + * @param _v2g_ready New value for member v2g_ready + */ + eProsima_user_DllExport void v2g_ready( + bool _v2g_ready) + { + m_v2g_ready = _v2g_ready; + } + + /*! + * @brief This function returns the value of member v2g_ready + * @return Value of member v2g_ready + */ + eProsima_user_DllExport bool v2g_ready() const + { + return m_v2g_ready; + } + + /*! + * @brief This function returns a reference to member v2g_ready + * @return Reference to member v2g_ready + */ + eProsima_user_DllExport bool& v2g_ready() + { + return m_v2g_ready; + } + + + +private: + + safe_edge::common::Header m_header; + float m_soc_pct{0.0}; + safe_edge::common::PolicyMode m_current_mode{safe_edge::common::PolicyMode::POLICY_UNKNOWN}; + safe_edge::common::HealthStatus m_vehicle_health{safe_edge::common::HealthStatus::HEALTH_UNKNOWN}; + bool m_v2g_ready{false}; + +}; +/*! + * @brief This class represents the structure EnergyAdvisory defined by the user in the IDL file. + * @ingroup edge + */ +class EnergyAdvisory +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport EnergyAdvisory() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~EnergyAdvisory() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object EnergyAdvisory that will be copied. + */ + eProsima_user_DllExport EnergyAdvisory( + const EnergyAdvisory& x) + { + m_header = x.m_header; + + m_suggested_mode = x.m_suggested_mode; + + m_advisory_reason = x.m_advisory_reason; + + m_recommended_charger_id = x.m_recommended_charger_id; + + m_target_soc_pct = x.m_target_soc_pct; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object EnergyAdvisory that will be copied. + */ + eProsima_user_DllExport EnergyAdvisory( + EnergyAdvisory&& x) noexcept + { + m_header = std::move(x.m_header); + m_suggested_mode = x.m_suggested_mode; + m_advisory_reason = std::move(x.m_advisory_reason); + m_recommended_charger_id = x.m_recommended_charger_id; + m_target_soc_pct = x.m_target_soc_pct; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object EnergyAdvisory that will be copied. + */ + eProsima_user_DllExport EnergyAdvisory& operator =( + const EnergyAdvisory& x) + { + + m_header = x.m_header; + + m_suggested_mode = x.m_suggested_mode; + + m_advisory_reason = x.m_advisory_reason; + + m_recommended_charger_id = x.m_recommended_charger_id; + + m_target_soc_pct = x.m_target_soc_pct; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object EnergyAdvisory that will be copied. + */ + eProsima_user_DllExport EnergyAdvisory& operator =( + EnergyAdvisory&& x) noexcept + { + + m_header = std::move(x.m_header); + m_suggested_mode = x.m_suggested_mode; + m_advisory_reason = std::move(x.m_advisory_reason); + m_recommended_charger_id = x.m_recommended_charger_id; + m_target_soc_pct = x.m_target_soc_pct; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x EnergyAdvisory object to compare. + */ + eProsima_user_DllExport bool operator ==( + const EnergyAdvisory& x) const + { + return (m_header == x.m_header && + m_suggested_mode == x.m_suggested_mode && + m_advisory_reason == x.m_advisory_reason && + m_recommended_charger_id == x.m_recommended_charger_id && + m_target_soc_pct == x.m_target_soc_pct); + } + + /*! + * @brief Comparison operator. + * @param x EnergyAdvisory object to compare. + */ + eProsima_user_DllExport bool operator !=( + const EnergyAdvisory& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member header + * @param _header New value to be copied in member header + */ + eProsima_user_DllExport void header( + const safe_edge::common::Header& _header) + { + m_header = _header; + } + + /*! + * @brief This function moves the value in member header + * @param _header New value to be moved in member header + */ + eProsima_user_DllExport void header( + safe_edge::common::Header&& _header) + { + m_header = std::move(_header); + } + + /*! + * @brief This function returns a constant reference to member header + * @return Constant reference to member header + */ + eProsima_user_DllExport const safe_edge::common::Header& header() const + { + return m_header; + } + + /*! + * @brief This function returns a reference to member header + * @return Reference to member header + */ + eProsima_user_DllExport safe_edge::common::Header& header() + { + return m_header; + } + + + /*! + * @brief This function sets a value in member suggested_mode + * @param _suggested_mode New value for member suggested_mode + */ + eProsima_user_DllExport void suggested_mode( + safe_edge::common::PolicyMode _suggested_mode) + { + m_suggested_mode = _suggested_mode; + } + + /*! + * @brief This function returns the value of member suggested_mode + * @return Value of member suggested_mode + */ + eProsima_user_DllExport safe_edge::common::PolicyMode suggested_mode() const + { + return m_suggested_mode; + } + + /*! + * @brief This function returns a reference to member suggested_mode + * @return Reference to member suggested_mode + */ + eProsima_user_DllExport safe_edge::common::PolicyMode& suggested_mode() + { + return m_suggested_mode; + } + + + /*! + * @brief This function copies the value in member advisory_reason + * @param _advisory_reason New value to be copied in member advisory_reason + */ + eProsima_user_DllExport void advisory_reason( + const std::string& _advisory_reason) + { + m_advisory_reason = _advisory_reason; + } + + /*! + * @brief This function moves the value in member advisory_reason + * @param _advisory_reason New value to be moved in member advisory_reason + */ + eProsima_user_DllExport void advisory_reason( + std::string&& _advisory_reason) + { + m_advisory_reason = std::move(_advisory_reason); + } + + /*! + * @brief This function returns a constant reference to member advisory_reason + * @return Constant reference to member advisory_reason + */ + eProsima_user_DllExport const std::string& advisory_reason() const + { + return m_advisory_reason; + } + + /*! + * @brief This function returns a reference to member advisory_reason + * @return Reference to member advisory_reason + */ + eProsima_user_DllExport std::string& advisory_reason() + { + return m_advisory_reason; + } + + + /*! + * @brief This function sets a value in member recommended_charger_id + * @param _recommended_charger_id New value for member recommended_charger_id + */ + eProsima_user_DllExport void recommended_charger_id( + int32_t _recommended_charger_id) + { + m_recommended_charger_id = _recommended_charger_id; + } + + /*! + * @brief This function returns the value of member recommended_charger_id + * @return Value of member recommended_charger_id + */ + eProsima_user_DllExport int32_t recommended_charger_id() const + { + return m_recommended_charger_id; + } + + /*! + * @brief This function returns a reference to member recommended_charger_id + * @return Reference to member recommended_charger_id + */ + eProsima_user_DllExport int32_t& recommended_charger_id() + { + return m_recommended_charger_id; + } + + + /*! + * @brief This function sets a value in member target_soc_pct + * @param _target_soc_pct New value for member target_soc_pct + */ + eProsima_user_DllExport void target_soc_pct( + float _target_soc_pct) + { + m_target_soc_pct = _target_soc_pct; + } + + /*! + * @brief This function returns the value of member target_soc_pct + * @return Value of member target_soc_pct + */ + eProsima_user_DllExport float target_soc_pct() const + { + return m_target_soc_pct; + } + + /*! + * @brief This function returns a reference to member target_soc_pct + * @return Reference to member target_soc_pct + */ + eProsima_user_DllExport float& target_soc_pct() + { + return m_target_soc_pct; + } + + + +private: + + safe_edge::common::Header m_header; + safe_edge::common::PolicyMode m_suggested_mode{safe_edge::common::PolicyMode::POLICY_UNKNOWN}; + std::string m_advisory_reason; + int32_t m_recommended_charger_id{0}; + float m_target_soc_pct{0.0}; + +}; +/*! + * @brief This class represents the structure EdgeGatewayStatus defined by the user in the IDL file. + * @ingroup edge + */ +class EdgeGatewayStatus +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport EdgeGatewayStatus() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~EdgeGatewayStatus() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object EdgeGatewayStatus that will be copied. + */ + eProsima_user_DllExport EdgeGatewayStatus( + const EdgeGatewayStatus& x) + { + m_header = x.m_header; + + m_status = x.m_status; + + m_last_server_sync_ms = x.m_last_server_sync_ms; + + m_detail = x.m_detail; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object EdgeGatewayStatus that will be copied. + */ + eProsima_user_DllExport EdgeGatewayStatus( + EdgeGatewayStatus&& x) noexcept + { + m_header = std::move(x.m_header); + m_status = x.m_status; + m_last_server_sync_ms = x.m_last_server_sync_ms; + m_detail = std::move(x.m_detail); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object EdgeGatewayStatus that will be copied. + */ + eProsima_user_DllExport EdgeGatewayStatus& operator =( + const EdgeGatewayStatus& x) + { + + m_header = x.m_header; + + m_status = x.m_status; + + m_last_server_sync_ms = x.m_last_server_sync_ms; + + m_detail = x.m_detail; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object EdgeGatewayStatus that will be copied. + */ + eProsima_user_DllExport EdgeGatewayStatus& operator =( + EdgeGatewayStatus&& x) noexcept + { + + m_header = std::move(x.m_header); + m_status = x.m_status; + m_last_server_sync_ms = x.m_last_server_sync_ms; + m_detail = std::move(x.m_detail); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x EdgeGatewayStatus object to compare. + */ + eProsima_user_DllExport bool operator ==( + const EdgeGatewayStatus& x) const + { + return (m_header == x.m_header && + m_status == x.m_status && + m_last_server_sync_ms == x.m_last_server_sync_ms && + m_detail == x.m_detail); + } + + /*! + * @brief Comparison operator. + * @param x EdgeGatewayStatus object to compare. + */ + eProsima_user_DllExport bool operator !=( + const EdgeGatewayStatus& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member header + * @param _header New value to be copied in member header + */ + eProsima_user_DllExport void header( + const safe_edge::common::Header& _header) + { + m_header = _header; + } + + /*! + * @brief This function moves the value in member header + * @param _header New value to be moved in member header + */ + eProsima_user_DllExport void header( + safe_edge::common::Header&& _header) + { + m_header = std::move(_header); + } + + /*! + * @brief This function returns a constant reference to member header + * @return Constant reference to member header + */ + eProsima_user_DllExport const safe_edge::common::Header& header() const + { + return m_header; + } + + /*! + * @brief This function returns a reference to member header + * @return Reference to member header + */ + eProsima_user_DllExport safe_edge::common::Header& header() + { + return m_header; + } + + + /*! + * @brief This function sets a value in member status + * @param _status New value for member status + */ + eProsima_user_DllExport void status( + safe_edge::common::HealthStatus _status) + { + m_status = _status; + } + + /*! + * @brief This function returns the value of member status + * @return Value of member status + */ + eProsima_user_DllExport safe_edge::common::HealthStatus status() const + { + return m_status; + } + + /*! + * @brief This function returns a reference to member status + * @return Reference to member status + */ + eProsima_user_DllExport safe_edge::common::HealthStatus& status() + { + return m_status; + } + + + /*! + * @brief This function sets a value in member last_server_sync_ms + * @param _last_server_sync_ms New value for member last_server_sync_ms + */ + eProsima_user_DllExport void last_server_sync_ms( + uint64_t _last_server_sync_ms) + { + m_last_server_sync_ms = _last_server_sync_ms; + } + + /*! + * @brief This function returns the value of member last_server_sync_ms + * @return Value of member last_server_sync_ms + */ + eProsima_user_DllExport uint64_t last_server_sync_ms() const + { + return m_last_server_sync_ms; + } + + /*! + * @brief This function returns a reference to member last_server_sync_ms + * @return Reference to member last_server_sync_ms + */ + eProsima_user_DllExport uint64_t& last_server_sync_ms() + { + return m_last_server_sync_ms; + } + + + /*! + * @brief This function copies the value in member detail + * @param _detail New value to be copied in member detail + */ + eProsima_user_DllExport void detail( + const std::string& _detail) + { + m_detail = _detail; + } + + /*! + * @brief This function moves the value in member detail + * @param _detail New value to be moved in member detail + */ + eProsima_user_DllExport void detail( + std::string&& _detail) + { + m_detail = std::move(_detail); + } + + /*! + * @brief This function returns a constant reference to member detail + * @return Constant reference to member detail + */ + eProsima_user_DllExport const std::string& detail() const + { + return m_detail; + } + + /*! + * @brief This function returns a reference to member detail + * @return Reference to member detail + */ + eProsima_user_DllExport std::string& detail() + { + return m_detail; + } + + + +private: + + safe_edge::common::Header m_header; + safe_edge::common::HealthStatus m_status{safe_edge::common::HealthStatus::HEALTH_UNKNOWN}; + uint64_t m_last_server_sync_ms{0}; + std::string m_detail; + +}; + +} // namespace edge + +} // namespace safe_edge + +#endif // _FAST_DDS_GENERATED_SAFE_EDGE_EDGE_EDGE_HPP_ + + diff --git a/fast_dds/idl/edgeCdrAux.hpp b/fast_dds/idl/edgeCdrAux.hpp new file mode 100644 index 0000000..f8e126b --- /dev/null +++ b/fast_dds/idl/edgeCdrAux.hpp @@ -0,0 +1,63 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgeCdrAux.hpp + * This source file contains some definitions of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_HPP + +#include "edge.hpp" + +constexpr uint32_t safe_edge_edge_EnergyAdvisory_max_cdr_typesize {812UL}; +constexpr uint32_t safe_edge_edge_EnergyAdvisory_max_key_cdr_typesize {0UL}; + + +constexpr uint32_t safe_edge_edge_EdgeGatewayStatus_max_cdr_typesize {812UL}; +constexpr uint32_t safe_edge_edge_EdgeGatewayStatus_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_edge_VehicleEdgeSummary_max_cdr_typesize {553UL}; +constexpr uint32_t safe_edge_edge_VehicleEdgeSummary_max_key_cdr_typesize {0UL}; + + + + +namespace eprosima { +namespace fastcdr { + +class Cdr; +class CdrSizeCalculator; + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::VehicleEdgeSummary& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EnergyAdvisory& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EdgeGatewayStatus& data); + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_HPP + diff --git a/fast_dds/idl/edgeCdrAux.ipp b/fast_dds/idl/edgeCdrAux.ipp new file mode 100644 index 0000000..4a5a4c4 --- /dev/null +++ b/fast_dds/idl/edgeCdrAux.ipp @@ -0,0 +1,433 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgeCdrAux.ipp + * This source file contains some declarations of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_IPP +#define FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_IPP + +#include "edgeCdrAux.hpp" + +#include +#include + + +#include +using namespace eprosima::fastcdr::exception; + +namespace eprosima { +namespace fastcdr { + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::edge::VehicleEdgeSummary& data, + size_t& current_alignment) +{ + using namespace safe_edge::edge; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.header(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.soc_pct(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.current_mode(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), + data.vehicle_health(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(4), + data.v2g_ready(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::VehicleEdgeSummary& data) +{ + using namespace safe_edge::edge; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.header() + << eprosima::fastcdr::MemberId(1) << data.soc_pct() + << eprosima::fastcdr::MemberId(2) << data.current_mode() + << eprosima::fastcdr::MemberId(3) << data.vehicle_health() + << eprosima::fastcdr::MemberId(4) << data.v2g_ready() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::edge::VehicleEdgeSummary& data) +{ + using namespace safe_edge::edge; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.header(); + break; + + case 1: + dcdr >> data.soc_pct(); + break; + + case 2: + dcdr >> data.current_mode(); + break; + + case 3: + dcdr >> data.vehicle_health(); + break; + + case 4: + dcdr >> data.v2g_ready(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::VehicleEdgeSummary& data) +{ + using namespace safe_edge::edge; + extern void serialize_key( + Cdr& scdr, + const safe_edge::common::Header& data); + + + + + + + static_cast(scdr); + static_cast(data); + serialize_key(scdr, data.header()); + + scdr << data.soc_pct(); + + scdr << data.current_mode(); + + scdr << data.vehicle_health(); + + scdr << data.v2g_ready(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::edge::EnergyAdvisory& data, + size_t& current_alignment) +{ + using namespace safe_edge::edge; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.header(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.suggested_mode(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.advisory_reason(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), + data.recommended_charger_id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(4), + data.target_soc_pct(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EnergyAdvisory& data) +{ + using namespace safe_edge::edge; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.header() + << eprosima::fastcdr::MemberId(1) << data.suggested_mode() + << eprosima::fastcdr::MemberId(2) << data.advisory_reason() + << eprosima::fastcdr::MemberId(3) << data.recommended_charger_id() + << eprosima::fastcdr::MemberId(4) << data.target_soc_pct() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::edge::EnergyAdvisory& data) +{ + using namespace safe_edge::edge; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.header(); + break; + + case 1: + dcdr >> data.suggested_mode(); + break; + + case 2: + dcdr >> data.advisory_reason(); + break; + + case 3: + dcdr >> data.recommended_charger_id(); + break; + + case 4: + dcdr >> data.target_soc_pct(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EnergyAdvisory& data) +{ + using namespace safe_edge::edge; + extern void serialize_key( + Cdr& scdr, + const safe_edge::common::Header& data); + + + + + + + static_cast(scdr); + static_cast(data); + serialize_key(scdr, data.header()); + + scdr << data.suggested_mode(); + + scdr << data.advisory_reason(); + + scdr << data.recommended_charger_id(); + + scdr << data.target_soc_pct(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::edge::EdgeGatewayStatus& data, + size_t& current_alignment) +{ + using namespace safe_edge::edge; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.header(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.status(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.last_server_sync_ms(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), + data.detail(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EdgeGatewayStatus& data) +{ + using namespace safe_edge::edge; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.header() + << eprosima::fastcdr::MemberId(1) << data.status() + << eprosima::fastcdr::MemberId(2) << data.last_server_sync_ms() + << eprosima::fastcdr::MemberId(3) << data.detail() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::edge::EdgeGatewayStatus& data) +{ + using namespace safe_edge::edge; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.header(); + break; + + case 1: + dcdr >> data.status(); + break; + + case 2: + dcdr >> data.last_server_sync_ms(); + break; + + case 3: + dcdr >> data.detail(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::edge::EdgeGatewayStatus& data) +{ + using namespace safe_edge::edge; + extern void serialize_key( + Cdr& scdr, + const safe_edge::common::Header& data); + + + + + + static_cast(scdr); + static_cast(data); + serialize_key(scdr, data.header()); + + scdr << data.status(); + + scdr << data.last_server_sync_ms(); + + scdr << data.detail(); + +} + + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGECDRAUX_IPP + diff --git a/fast_dds/idl/edgePubSubTypes.cxx b/fast_dds/idl/edgePubSubTypes.cxx new file mode 100644 index 0000000..64ed959 --- /dev/null +++ b/fast_dds/idl/edgePubSubTypes.cxx @@ -0,0 +1,474 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgePubSubTypes.cpp + * This header file contains the implementation of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "edgePubSubTypes.hpp" + +#include +#include + +#include +#include + +#include "edgeCdrAux.hpp" +#include "edgeTypeObjectSupport.hpp" + +using SerializedPayload_t = eprosima::fastdds::rtps::SerializedPayload_t; +using InstanceHandle_t = eprosima::fastdds::rtps::InstanceHandle_t; +using DataRepresentationId_t = eprosima::fastdds::dds::DataRepresentationId_t; + +namespace safe_edge { +namespace edge { +VehicleEdgeSummaryPubSubType::VehicleEdgeSummaryPubSubType() +{ + set_name("safe_edge::edge::VehicleEdgeSummary"); + uint32_t type_size = safe_edge_edge_VehicleEdgeSummary_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +VehicleEdgeSummaryPubSubType::~VehicleEdgeSummaryPubSubType() +{ +} + +bool VehicleEdgeSummaryPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::edge::VehicleEdgeSummary* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool VehicleEdgeSummaryPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::edge::VehicleEdgeSummary* p_type = + static_cast<::safe_edge::edge::VehicleEdgeSummary*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t VehicleEdgeSummaryPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::edge::VehicleEdgeSummary* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* VehicleEdgeSummaryPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::edge::VehicleEdgeSummary()); +} + +void VehicleEdgeSummaryPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::edge::VehicleEdgeSummary*>(data)); +} + +bool VehicleEdgeSummaryPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool VehicleEdgeSummaryPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void VehicleEdgeSummaryPubSubType::register_type_object_representation() +{ + register_VehicleEdgeSummary_type_identifier(type_identifiers_); +} + +EnergyAdvisoryPubSubType::EnergyAdvisoryPubSubType() +{ + set_name("safe_edge::edge::EnergyAdvisory"); + uint32_t type_size = safe_edge_edge_EnergyAdvisory_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +EnergyAdvisoryPubSubType::~EnergyAdvisoryPubSubType() +{ +} + +bool EnergyAdvisoryPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::edge::EnergyAdvisory* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool EnergyAdvisoryPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::edge::EnergyAdvisory* p_type = + static_cast<::safe_edge::edge::EnergyAdvisory*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t EnergyAdvisoryPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::edge::EnergyAdvisory* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* EnergyAdvisoryPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::edge::EnergyAdvisory()); +} + +void EnergyAdvisoryPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::edge::EnergyAdvisory*>(data)); +} + +bool EnergyAdvisoryPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool EnergyAdvisoryPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void EnergyAdvisoryPubSubType::register_type_object_representation() +{ + register_EnergyAdvisory_type_identifier(type_identifiers_); +} + +EdgeGatewayStatusPubSubType::EdgeGatewayStatusPubSubType() +{ + set_name("safe_edge::edge::EdgeGatewayStatus"); + uint32_t type_size = safe_edge_edge_EdgeGatewayStatus_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +EdgeGatewayStatusPubSubType::~EdgeGatewayStatusPubSubType() +{ +} + +bool EdgeGatewayStatusPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::edge::EdgeGatewayStatus* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool EdgeGatewayStatusPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::edge::EdgeGatewayStatus* p_type = + static_cast<::safe_edge::edge::EdgeGatewayStatus*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t EdgeGatewayStatusPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::edge::EdgeGatewayStatus* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* EdgeGatewayStatusPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::edge::EdgeGatewayStatus()); +} + +void EdgeGatewayStatusPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::edge::EdgeGatewayStatus*>(data)); +} + +bool EdgeGatewayStatusPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool EdgeGatewayStatusPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void EdgeGatewayStatusPubSubType::register_type_object_representation() +{ + register_EdgeGatewayStatus_type_identifier(type_identifiers_); +} + +} // namespace edge + +} // namespace safe_edge + + +// Include auxiliary functions like for serializing/deserializing. +#include "edgeCdrAux.ipp" diff --git a/fast_dds/idl/edgePubSubTypes.hpp b/fast_dds/idl/edgePubSubTypes.hpp new file mode 100644 index 0000000..737d82f --- /dev/null +++ b/fast_dds/idl/edgePubSubTypes.hpp @@ -0,0 +1,286 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgePubSubTypes.hpp + * This header file contains the declaration of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_PUBSUBTYPES_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_PUBSUBTYPES_HPP + +#include + +#include +#include +#include +#include +#include + +#include "edge.hpp" + +#include "commonPubSubTypes.hpp" + +#if !defined(FASTDDS_GEN_API_VER) || (FASTDDS_GEN_API_VER != 3) +#error \ + Generated edge is not compatible with current installed Fast DDS. Please, regenerate it with fastddsgen. +#endif // FASTDDS_GEN_API_VER + +namespace safe_edge { +namespace edge { + +/*! + * @brief This class represents the TopicDataType of the type VehicleEdgeSummary defined by the user in the IDL file. + * @ingroup edge + */ +class VehicleEdgeSummaryPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::edge::VehicleEdgeSummary type; + + eProsima_user_DllExport VehicleEdgeSummaryPubSubType(); + + eProsima_user_DllExport ~VehicleEdgeSummaryPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type EnergyAdvisory defined by the user in the IDL file. + * @ingroup edge + */ +class EnergyAdvisoryPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::edge::EnergyAdvisory type; + + eProsima_user_DllExport EnergyAdvisoryPubSubType(); + + eProsima_user_DllExport ~EnergyAdvisoryPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type EdgeGatewayStatus defined by the user in the IDL file. + * @ingroup edge + */ +class EdgeGatewayStatusPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::edge::EdgeGatewayStatus type; + + eProsima_user_DllExport EdgeGatewayStatusPubSubType(); + + eProsima_user_DllExport ~EdgeGatewayStatusPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +} // namespace edge +} // namespace safe_edge + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_PUBSUBTYPES_HPP + diff --git a/fast_dds/idl/edgeTypeObjectSupport.cxx b/fast_dds/idl/edgeTypeObjectSupport.cxx new file mode 100644 index 0000000..d42ba9f --- /dev/null +++ b/fast_dds/idl/edgeTypeObjectSupport.cxx @@ -0,0 +1,554 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgeTypeObjectSupport.cxx + * Source file containing the implementation to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "edgeTypeObjectSupport.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "edge.hpp" + +#include "common.hpp" + +using namespace eprosima::fastdds::dds::xtypes; + +namespace safe_edge { +namespace edge { +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_VehicleEdgeSummary_type_identifier( + TypeIdentifierPair& type_ids_VehicleEdgeSummary) +{ + + ReturnCode_t return_code_VehicleEdgeSummary {eprosima::fastdds::dds::RETCODE_OK}; + return_code_VehicleEdgeSummary = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::edge::VehicleEdgeSummary", type_ids_VehicleEdgeSummary); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_VehicleEdgeSummary) + { + StructTypeFlag struct_flags_VehicleEdgeSummary = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_VehicleEdgeSummary = "safe_edge::edge::VehicleEdgeSummary"; + eprosima::fastcdr::optional type_ann_builtin_VehicleEdgeSummary; + eprosima::fastcdr::optional ann_custom_VehicleEdgeSummary; + CompleteTypeDetail detail_VehicleEdgeSummary = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_VehicleEdgeSummary, ann_custom_VehicleEdgeSummary, type_name_VehicleEdgeSummary.to_string()); + CompleteStructHeader header_VehicleEdgeSummary; + header_VehicleEdgeSummary = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_VehicleEdgeSummary); + CompleteStructMemberSeq member_seq_VehicleEdgeSummary; + { + TypeIdentifierPair type_ids_header; + ReturnCode_t return_code_header {eprosima::fastdds::dds::RETCODE_OK}; + return_code_header = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::Header", type_ids_header); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_header) + { + ::safe_edge::common::register_Header_type_identifier(type_ids_header); + } + StructMemberFlag member_flags_header = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_header = 0x00000000; + bool common_header_ec {false}; + CommonStructMember common_header {TypeObjectUtils::build_common_struct_member(member_id_header, member_flags_header, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_header, common_header_ec))}; + if (!common_header_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure header member TypeIdentifier inconsistent."); + return; + } + MemberName name_header = "header"; + eprosima::fastcdr::optional member_ann_builtin_header; + ann_custom_VehicleEdgeSummary.reset(); + CompleteMemberDetail detail_header = TypeObjectUtils::build_complete_member_detail(name_header, member_ann_builtin_header, ann_custom_VehicleEdgeSummary); + CompleteStructMember member_header = TypeObjectUtils::build_complete_struct_member(common_header, detail_header); + TypeObjectUtils::add_complete_struct_member(member_seq_VehicleEdgeSummary, member_header); + } + { + TypeIdentifierPair type_ids_soc_pct; + ReturnCode_t return_code_soc_pct {eprosima::fastdds::dds::RETCODE_OK}; + return_code_soc_pct = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_float", type_ids_soc_pct); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_soc_pct) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "soc_pct Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_soc_pct = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_soc_pct = 0x00000001; + bool common_soc_pct_ec {false}; + CommonStructMember common_soc_pct {TypeObjectUtils::build_common_struct_member(member_id_soc_pct, member_flags_soc_pct, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_soc_pct, common_soc_pct_ec))}; + if (!common_soc_pct_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure soc_pct member TypeIdentifier inconsistent."); + return; + } + MemberName name_soc_pct = "soc_pct"; + eprosima::fastcdr::optional member_ann_builtin_soc_pct; + ann_custom_VehicleEdgeSummary.reset(); + CompleteMemberDetail detail_soc_pct = TypeObjectUtils::build_complete_member_detail(name_soc_pct, member_ann_builtin_soc_pct, ann_custom_VehicleEdgeSummary); + CompleteStructMember member_soc_pct = TypeObjectUtils::build_complete_struct_member(common_soc_pct, detail_soc_pct); + TypeObjectUtils::add_complete_struct_member(member_seq_VehicleEdgeSummary, member_soc_pct); + } + { + TypeIdentifierPair type_ids_current_mode; + ReturnCode_t return_code_current_mode {eprosima::fastdds::dds::RETCODE_OK}; + return_code_current_mode = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::PolicyMode", type_ids_current_mode); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_current_mode) + { + ::safe_edge::common::register_PolicyMode_type_identifier(type_ids_current_mode); + } + StructMemberFlag member_flags_current_mode = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_current_mode = 0x00000002; + bool common_current_mode_ec {false}; + CommonStructMember common_current_mode {TypeObjectUtils::build_common_struct_member(member_id_current_mode, member_flags_current_mode, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_current_mode, common_current_mode_ec))}; + if (!common_current_mode_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure current_mode member TypeIdentifier inconsistent."); + return; + } + MemberName name_current_mode = "current_mode"; + eprosima::fastcdr::optional member_ann_builtin_current_mode; + ann_custom_VehicleEdgeSummary.reset(); + CompleteMemberDetail detail_current_mode = TypeObjectUtils::build_complete_member_detail(name_current_mode, member_ann_builtin_current_mode, ann_custom_VehicleEdgeSummary); + CompleteStructMember member_current_mode = TypeObjectUtils::build_complete_struct_member(common_current_mode, detail_current_mode); + TypeObjectUtils::add_complete_struct_member(member_seq_VehicleEdgeSummary, member_current_mode); + } + { + TypeIdentifierPair type_ids_vehicle_health; + ReturnCode_t return_code_vehicle_health {eprosima::fastdds::dds::RETCODE_OK}; + return_code_vehicle_health = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::HealthStatus", type_ids_vehicle_health); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_vehicle_health) + { + ::safe_edge::common::register_HealthStatus_type_identifier(type_ids_vehicle_health); + } + StructMemberFlag member_flags_vehicle_health = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_vehicle_health = 0x00000003; + bool common_vehicle_health_ec {false}; + CommonStructMember common_vehicle_health {TypeObjectUtils::build_common_struct_member(member_id_vehicle_health, member_flags_vehicle_health, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_vehicle_health, common_vehicle_health_ec))}; + if (!common_vehicle_health_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure vehicle_health member TypeIdentifier inconsistent."); + return; + } + MemberName name_vehicle_health = "vehicle_health"; + eprosima::fastcdr::optional member_ann_builtin_vehicle_health; + ann_custom_VehicleEdgeSummary.reset(); + CompleteMemberDetail detail_vehicle_health = TypeObjectUtils::build_complete_member_detail(name_vehicle_health, member_ann_builtin_vehicle_health, ann_custom_VehicleEdgeSummary); + CompleteStructMember member_vehicle_health = TypeObjectUtils::build_complete_struct_member(common_vehicle_health, detail_vehicle_health); + TypeObjectUtils::add_complete_struct_member(member_seq_VehicleEdgeSummary, member_vehicle_health); + } + { + TypeIdentifierPair type_ids_v2g_ready; + ReturnCode_t return_code_v2g_ready {eprosima::fastdds::dds::RETCODE_OK}; + return_code_v2g_ready = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_bool", type_ids_v2g_ready); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_v2g_ready) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "v2g_ready Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_v2g_ready = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_v2g_ready = 0x00000004; + bool common_v2g_ready_ec {false}; + CommonStructMember common_v2g_ready {TypeObjectUtils::build_common_struct_member(member_id_v2g_ready, member_flags_v2g_ready, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_v2g_ready, common_v2g_ready_ec))}; + if (!common_v2g_ready_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure v2g_ready member TypeIdentifier inconsistent."); + return; + } + MemberName name_v2g_ready = "v2g_ready"; + eprosima::fastcdr::optional member_ann_builtin_v2g_ready; + ann_custom_VehicleEdgeSummary.reset(); + CompleteMemberDetail detail_v2g_ready = TypeObjectUtils::build_complete_member_detail(name_v2g_ready, member_ann_builtin_v2g_ready, ann_custom_VehicleEdgeSummary); + CompleteStructMember member_v2g_ready = TypeObjectUtils::build_complete_struct_member(common_v2g_ready, detail_v2g_ready); + TypeObjectUtils::add_complete_struct_member(member_seq_VehicleEdgeSummary, member_v2g_ready); + } + CompleteStructType struct_type_VehicleEdgeSummary = TypeObjectUtils::build_complete_struct_type(struct_flags_VehicleEdgeSummary, header_VehicleEdgeSummary, member_seq_VehicleEdgeSummary); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_VehicleEdgeSummary, type_name_VehicleEdgeSummary.to_string(), type_ids_VehicleEdgeSummary)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::edge::VehicleEdgeSummary already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_EnergyAdvisory_type_identifier( + TypeIdentifierPair& type_ids_EnergyAdvisory) +{ + + ReturnCode_t return_code_EnergyAdvisory {eprosima::fastdds::dds::RETCODE_OK}; + return_code_EnergyAdvisory = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::edge::EnergyAdvisory", type_ids_EnergyAdvisory); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_EnergyAdvisory) + { + StructTypeFlag struct_flags_EnergyAdvisory = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_EnergyAdvisory = "safe_edge::edge::EnergyAdvisory"; + eprosima::fastcdr::optional type_ann_builtin_EnergyAdvisory; + eprosima::fastcdr::optional ann_custom_EnergyAdvisory; + CompleteTypeDetail detail_EnergyAdvisory = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_EnergyAdvisory, ann_custom_EnergyAdvisory, type_name_EnergyAdvisory.to_string()); + CompleteStructHeader header_EnergyAdvisory; + header_EnergyAdvisory = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_EnergyAdvisory); + CompleteStructMemberSeq member_seq_EnergyAdvisory; + { + TypeIdentifierPair type_ids_header; + ReturnCode_t return_code_header {eprosima::fastdds::dds::RETCODE_OK}; + return_code_header = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::Header", type_ids_header); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_header) + { + ::safe_edge::common::register_Header_type_identifier(type_ids_header); + } + StructMemberFlag member_flags_header = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_header = 0x00000000; + bool common_header_ec {false}; + CommonStructMember common_header {TypeObjectUtils::build_common_struct_member(member_id_header, member_flags_header, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_header, common_header_ec))}; + if (!common_header_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure header member TypeIdentifier inconsistent."); + return; + } + MemberName name_header = "header"; + eprosima::fastcdr::optional member_ann_builtin_header; + ann_custom_EnergyAdvisory.reset(); + CompleteMemberDetail detail_header = TypeObjectUtils::build_complete_member_detail(name_header, member_ann_builtin_header, ann_custom_EnergyAdvisory); + CompleteStructMember member_header = TypeObjectUtils::build_complete_struct_member(common_header, detail_header); + TypeObjectUtils::add_complete_struct_member(member_seq_EnergyAdvisory, member_header); + } + { + TypeIdentifierPair type_ids_suggested_mode; + ReturnCode_t return_code_suggested_mode {eprosima::fastdds::dds::RETCODE_OK}; + return_code_suggested_mode = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::PolicyMode", type_ids_suggested_mode); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_suggested_mode) + { + ::safe_edge::common::register_PolicyMode_type_identifier(type_ids_suggested_mode); + } + StructMemberFlag member_flags_suggested_mode = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_suggested_mode = 0x00000001; + bool common_suggested_mode_ec {false}; + CommonStructMember common_suggested_mode {TypeObjectUtils::build_common_struct_member(member_id_suggested_mode, member_flags_suggested_mode, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_suggested_mode, common_suggested_mode_ec))}; + if (!common_suggested_mode_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure suggested_mode member TypeIdentifier inconsistent."); + return; + } + MemberName name_suggested_mode = "suggested_mode"; + eprosima::fastcdr::optional member_ann_builtin_suggested_mode; + ann_custom_EnergyAdvisory.reset(); + CompleteMemberDetail detail_suggested_mode = TypeObjectUtils::build_complete_member_detail(name_suggested_mode, member_ann_builtin_suggested_mode, ann_custom_EnergyAdvisory); + CompleteStructMember member_suggested_mode = TypeObjectUtils::build_complete_struct_member(common_suggested_mode, detail_suggested_mode); + TypeObjectUtils::add_complete_struct_member(member_seq_EnergyAdvisory, member_suggested_mode); + } + { + TypeIdentifierPair type_ids_advisory_reason; + ReturnCode_t return_code_advisory_reason {eprosima::fastdds::dds::RETCODE_OK}; + return_code_advisory_reason = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_advisory_reason); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_advisory_reason) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_advisory_reason)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_advisory_reason = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_advisory_reason = 0x00000002; + bool common_advisory_reason_ec {false}; + CommonStructMember common_advisory_reason {TypeObjectUtils::build_common_struct_member(member_id_advisory_reason, member_flags_advisory_reason, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_advisory_reason, common_advisory_reason_ec))}; + if (!common_advisory_reason_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure advisory_reason member TypeIdentifier inconsistent."); + return; + } + MemberName name_advisory_reason = "advisory_reason"; + eprosima::fastcdr::optional member_ann_builtin_advisory_reason; + ann_custom_EnergyAdvisory.reset(); + CompleteMemberDetail detail_advisory_reason = TypeObjectUtils::build_complete_member_detail(name_advisory_reason, member_ann_builtin_advisory_reason, ann_custom_EnergyAdvisory); + CompleteStructMember member_advisory_reason = TypeObjectUtils::build_complete_struct_member(common_advisory_reason, detail_advisory_reason); + TypeObjectUtils::add_complete_struct_member(member_seq_EnergyAdvisory, member_advisory_reason); + } + { + TypeIdentifierPair type_ids_recommended_charger_id; + ReturnCode_t return_code_recommended_charger_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_recommended_charger_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_recommended_charger_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_recommended_charger_id) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "recommended_charger_id Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_recommended_charger_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_recommended_charger_id = 0x00000003; + bool common_recommended_charger_id_ec {false}; + CommonStructMember common_recommended_charger_id {TypeObjectUtils::build_common_struct_member(member_id_recommended_charger_id, member_flags_recommended_charger_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_recommended_charger_id, common_recommended_charger_id_ec))}; + if (!common_recommended_charger_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure recommended_charger_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_recommended_charger_id = "recommended_charger_id"; + eprosima::fastcdr::optional member_ann_builtin_recommended_charger_id; + ann_custom_EnergyAdvisory.reset(); + CompleteMemberDetail detail_recommended_charger_id = TypeObjectUtils::build_complete_member_detail(name_recommended_charger_id, member_ann_builtin_recommended_charger_id, ann_custom_EnergyAdvisory); + CompleteStructMember member_recommended_charger_id = TypeObjectUtils::build_complete_struct_member(common_recommended_charger_id, detail_recommended_charger_id); + TypeObjectUtils::add_complete_struct_member(member_seq_EnergyAdvisory, member_recommended_charger_id); + } + { + TypeIdentifierPair type_ids_target_soc_pct; + ReturnCode_t return_code_target_soc_pct {eprosima::fastdds::dds::RETCODE_OK}; + return_code_target_soc_pct = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_float", type_ids_target_soc_pct); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_target_soc_pct) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "target_soc_pct Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_target_soc_pct = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_target_soc_pct = 0x00000004; + bool common_target_soc_pct_ec {false}; + CommonStructMember common_target_soc_pct {TypeObjectUtils::build_common_struct_member(member_id_target_soc_pct, member_flags_target_soc_pct, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_target_soc_pct, common_target_soc_pct_ec))}; + if (!common_target_soc_pct_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure target_soc_pct member TypeIdentifier inconsistent."); + return; + } + MemberName name_target_soc_pct = "target_soc_pct"; + eprosima::fastcdr::optional member_ann_builtin_target_soc_pct; + ann_custom_EnergyAdvisory.reset(); + CompleteMemberDetail detail_target_soc_pct = TypeObjectUtils::build_complete_member_detail(name_target_soc_pct, member_ann_builtin_target_soc_pct, ann_custom_EnergyAdvisory); + CompleteStructMember member_target_soc_pct = TypeObjectUtils::build_complete_struct_member(common_target_soc_pct, detail_target_soc_pct); + TypeObjectUtils::add_complete_struct_member(member_seq_EnergyAdvisory, member_target_soc_pct); + } + CompleteStructType struct_type_EnergyAdvisory = TypeObjectUtils::build_complete_struct_type(struct_flags_EnergyAdvisory, header_EnergyAdvisory, member_seq_EnergyAdvisory); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_EnergyAdvisory, type_name_EnergyAdvisory.to_string(), type_ids_EnergyAdvisory)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::edge::EnergyAdvisory already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_EdgeGatewayStatus_type_identifier( + TypeIdentifierPair& type_ids_EdgeGatewayStatus) +{ + + ReturnCode_t return_code_EdgeGatewayStatus {eprosima::fastdds::dds::RETCODE_OK}; + return_code_EdgeGatewayStatus = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::edge::EdgeGatewayStatus", type_ids_EdgeGatewayStatus); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_EdgeGatewayStatus) + { + StructTypeFlag struct_flags_EdgeGatewayStatus = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_EdgeGatewayStatus = "safe_edge::edge::EdgeGatewayStatus"; + eprosima::fastcdr::optional type_ann_builtin_EdgeGatewayStatus; + eprosima::fastcdr::optional ann_custom_EdgeGatewayStatus; + CompleteTypeDetail detail_EdgeGatewayStatus = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_EdgeGatewayStatus, ann_custom_EdgeGatewayStatus, type_name_EdgeGatewayStatus.to_string()); + CompleteStructHeader header_EdgeGatewayStatus; + header_EdgeGatewayStatus = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_EdgeGatewayStatus); + CompleteStructMemberSeq member_seq_EdgeGatewayStatus; + { + TypeIdentifierPair type_ids_header; + ReturnCode_t return_code_header {eprosima::fastdds::dds::RETCODE_OK}; + return_code_header = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::Header", type_ids_header); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_header) + { + ::safe_edge::common::register_Header_type_identifier(type_ids_header); + } + StructMemberFlag member_flags_header = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_header = 0x00000000; + bool common_header_ec {false}; + CommonStructMember common_header {TypeObjectUtils::build_common_struct_member(member_id_header, member_flags_header, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_header, common_header_ec))}; + if (!common_header_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure header member TypeIdentifier inconsistent."); + return; + } + MemberName name_header = "header"; + eprosima::fastcdr::optional member_ann_builtin_header; + ann_custom_EdgeGatewayStatus.reset(); + CompleteMemberDetail detail_header = TypeObjectUtils::build_complete_member_detail(name_header, member_ann_builtin_header, ann_custom_EdgeGatewayStatus); + CompleteStructMember member_header = TypeObjectUtils::build_complete_struct_member(common_header, detail_header); + TypeObjectUtils::add_complete_struct_member(member_seq_EdgeGatewayStatus, member_header); + } + { + TypeIdentifierPair type_ids_status; + ReturnCode_t return_code_status {eprosima::fastdds::dds::RETCODE_OK}; + return_code_status = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::HealthStatus", type_ids_status); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_status) + { + ::safe_edge::common::register_HealthStatus_type_identifier(type_ids_status); + } + StructMemberFlag member_flags_status = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_status = 0x00000001; + bool common_status_ec {false}; + CommonStructMember common_status {TypeObjectUtils::build_common_struct_member(member_id_status, member_flags_status, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_status, common_status_ec))}; + if (!common_status_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure status member TypeIdentifier inconsistent."); + return; + } + MemberName name_status = "status"; + eprosima::fastcdr::optional member_ann_builtin_status; + ann_custom_EdgeGatewayStatus.reset(); + CompleteMemberDetail detail_status = TypeObjectUtils::build_complete_member_detail(name_status, member_ann_builtin_status, ann_custom_EdgeGatewayStatus); + CompleteStructMember member_status = TypeObjectUtils::build_complete_struct_member(common_status, detail_status); + TypeObjectUtils::add_complete_struct_member(member_seq_EdgeGatewayStatus, member_status); + } + { + TypeIdentifierPair type_ids_last_server_sync_ms; + ReturnCode_t return_code_last_server_sync_ms {eprosima::fastdds::dds::RETCODE_OK}; + return_code_last_server_sync_ms = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_uint64_t", type_ids_last_server_sync_ms); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_last_server_sync_ms) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "last_server_sync_ms Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_last_server_sync_ms = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_last_server_sync_ms = 0x00000002; + bool common_last_server_sync_ms_ec {false}; + CommonStructMember common_last_server_sync_ms {TypeObjectUtils::build_common_struct_member(member_id_last_server_sync_ms, member_flags_last_server_sync_ms, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_last_server_sync_ms, common_last_server_sync_ms_ec))}; + if (!common_last_server_sync_ms_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure last_server_sync_ms member TypeIdentifier inconsistent."); + return; + } + MemberName name_last_server_sync_ms = "last_server_sync_ms"; + eprosima::fastcdr::optional member_ann_builtin_last_server_sync_ms; + ann_custom_EdgeGatewayStatus.reset(); + CompleteMemberDetail detail_last_server_sync_ms = TypeObjectUtils::build_complete_member_detail(name_last_server_sync_ms, member_ann_builtin_last_server_sync_ms, ann_custom_EdgeGatewayStatus); + CompleteStructMember member_last_server_sync_ms = TypeObjectUtils::build_complete_struct_member(common_last_server_sync_ms, detail_last_server_sync_ms); + TypeObjectUtils::add_complete_struct_member(member_seq_EdgeGatewayStatus, member_last_server_sync_ms); + } + { + TypeIdentifierPair type_ids_detail; + ReturnCode_t return_code_detail {eprosima::fastdds::dds::RETCODE_OK}; + return_code_detail = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_detail); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_detail) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_detail)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_detail = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_detail = 0x00000003; + bool common_detail_ec {false}; + CommonStructMember common_detail {TypeObjectUtils::build_common_struct_member(member_id_detail, member_flags_detail, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_detail, common_detail_ec))}; + if (!common_detail_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure detail member TypeIdentifier inconsistent."); + return; + } + MemberName name_detail = "detail"; + eprosima::fastcdr::optional member_ann_builtin_detail; + ann_custom_EdgeGatewayStatus.reset(); + CompleteMemberDetail detail_detail = TypeObjectUtils::build_complete_member_detail(name_detail, member_ann_builtin_detail, ann_custom_EdgeGatewayStatus); + CompleteStructMember member_detail = TypeObjectUtils::build_complete_struct_member(common_detail, detail_detail); + TypeObjectUtils::add_complete_struct_member(member_seq_EdgeGatewayStatus, member_detail); + } + CompleteStructType struct_type_EdgeGatewayStatus = TypeObjectUtils::build_complete_struct_type(struct_flags_EdgeGatewayStatus, header_EdgeGatewayStatus, member_seq_EdgeGatewayStatus); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_EdgeGatewayStatus, type_name_EdgeGatewayStatus.to_string(), type_ids_EdgeGatewayStatus)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::edge::EdgeGatewayStatus already registered in TypeObjectRegistry for a different type."); + } + } +} +} // namespace edge + +} // namespace safe_edge + diff --git a/fast_dds/idl/edgeTypeObjectSupport.hpp b/fast_dds/idl/edgeTypeObjectSupport.hpp new file mode 100644 index 0000000..8de7065 --- /dev/null +++ b/fast_dds/idl/edgeTypeObjectSupport.hpp @@ -0,0 +1,89 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file edgeTypeObjectSupport.hpp + * Header file containing the API required to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_TYPE_OBJECT_SUPPORT_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_TYPE_OBJECT_SUPPORT_HPP + +#include + +#include "commonTypeObjectSupport.hpp" + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +namespace safe_edge { +namespace edge { +/** + * @brief Register VehicleEdgeSummary related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_VehicleEdgeSummary_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register EnergyAdvisory related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_EnergyAdvisory_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register EdgeGatewayStatus related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_EdgeGatewayStatus_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +} // namespace edge + +} // namespace safe_edge + + +#endif // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_EDGE_EDGE_TYPE_OBJECT_SUPPORT_HPP diff --git a/fast_dds/idl/pilot_server.hpp b/fast_dds/idl/pilot_server.hpp new file mode 100644 index 0000000..f948c83 --- /dev/null +++ b/fast_dds/idl/pilot_server.hpp @@ -0,0 +1,1534 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_server.hpp + * This header file contains the declaration of the described types in the IDL file. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP + +#include +#include +#include +#include + +#include +#include "common.hpp" + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#if defined(PILOT_SERVER_SOURCE) +#define PILOT_SERVER_DllAPI __declspec( dllexport ) +#else +#define PILOT_SERVER_DllAPI __declspec( dllimport ) +#endif // PILOT_SERVER_SOURCE +#else +#define PILOT_SERVER_DllAPI +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define PILOT_SERVER_DllAPI +#endif // _WIN32 + +namespace safe_edge { + +namespace pilot_server { + +const uint32_t MAX_CHARGER_LOCATIONS = 200; +const uint32_t MAX_CHARGER_TYPES = 64; +const uint32_t MAX_CHARGING_SESSIONS = 1000; +const uint32_t MAX_ROUTE_METRICS = 512; +/*! + * @brief This class represents the structure ChargerLocation defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargerLocation +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport ChargerLocation() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~ChargerLocation() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object ChargerLocation that will be copied. + */ + eProsima_user_DllExport ChargerLocation( + const ChargerLocation& x) + { + m_id = x.m_id; + + m_name = x.m_name; + + m_position = x.m_position; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object ChargerLocation that will be copied. + */ + eProsima_user_DllExport ChargerLocation( + ChargerLocation&& x) noexcept + { + m_id = x.m_id; + m_name = std::move(x.m_name); + m_position = std::move(x.m_position); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object ChargerLocation that will be copied. + */ + eProsima_user_DllExport ChargerLocation& operator =( + const ChargerLocation& x) + { + + m_id = x.m_id; + + m_name = x.m_name; + + m_position = x.m_position; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object ChargerLocation that will be copied. + */ + eProsima_user_DllExport ChargerLocation& operator =( + ChargerLocation&& x) noexcept + { + + m_id = x.m_id; + m_name = std::move(x.m_name); + m_position = std::move(x.m_position); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x ChargerLocation object to compare. + */ + eProsima_user_DllExport bool operator ==( + const ChargerLocation& x) const + { + return (m_id == x.m_id && + m_name == x.m_name && + m_position == x.m_position); + } + + /*! + * @brief Comparison operator. + * @param x ChargerLocation object to compare. + */ + eProsima_user_DllExport bool operator !=( + const ChargerLocation& x) const + { + return !(*this == x); + } + + /*! + * @brief This function sets a value in member id + * @param _id New value for member id + */ + eProsima_user_DllExport void id( + int32_t _id) + { + m_id = _id; + } + + /*! + * @brief This function returns the value of member id + * @return Value of member id + */ + eProsima_user_DllExport int32_t id() const + { + return m_id; + } + + /*! + * @brief This function returns a reference to member id + * @return Reference to member id + */ + eProsima_user_DllExport int32_t& id() + { + return m_id; + } + + + /*! + * @brief This function copies the value in member name + * @param _name New value to be copied in member name + */ + eProsima_user_DllExport void name( + const std::string& _name) + { + m_name = _name; + } + + /*! + * @brief This function moves the value in member name + * @param _name New value to be moved in member name + */ + eProsima_user_DllExport void name( + std::string&& _name) + { + m_name = std::move(_name); + } + + /*! + * @brief This function returns a constant reference to member name + * @return Constant reference to member name + */ + eProsima_user_DllExport const std::string& name() const + { + return m_name; + } + + /*! + * @brief This function returns a reference to member name + * @return Reference to member name + */ + eProsima_user_DllExport std::string& name() + { + return m_name; + } + + + /*! + * @brief This function copies the value in member position + * @param _position New value to be copied in member position + */ + eProsima_user_DllExport void position( + const safe_edge::common::GeoPoint& _position) + { + m_position = _position; + } + + /*! + * @brief This function moves the value in member position + * @param _position New value to be moved in member position + */ + eProsima_user_DllExport void position( + safe_edge::common::GeoPoint&& _position) + { + m_position = std::move(_position); + } + + /*! + * @brief This function returns a constant reference to member position + * @return Constant reference to member position + */ + eProsima_user_DllExport const safe_edge::common::GeoPoint& position() const + { + return m_position; + } + + /*! + * @brief This function returns a reference to member position + * @return Reference to member position + */ + eProsima_user_DllExport safe_edge::common::GeoPoint& position() + { + return m_position; + } + + + +private: + + int32_t m_id{0}; + std::string m_name; + safe_edge::common::GeoPoint m_position; + +}; +typedef std::vector ChargerLocationSeq; + +/*! + * @brief This class represents the structure ChargerType defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargerType +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport ChargerType() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~ChargerType() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object ChargerType that will be copied. + */ + eProsima_user_DllExport ChargerType( + const ChargerType& x) + { + m_id = x.m_id; + + m_charger_type = x.m_charger_type; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object ChargerType that will be copied. + */ + eProsima_user_DllExport ChargerType( + ChargerType&& x) noexcept + { + m_id = x.m_id; + m_charger_type = std::move(x.m_charger_type); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object ChargerType that will be copied. + */ + eProsima_user_DllExport ChargerType& operator =( + const ChargerType& x) + { + + m_id = x.m_id; + + m_charger_type = x.m_charger_type; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object ChargerType that will be copied. + */ + eProsima_user_DllExport ChargerType& operator =( + ChargerType&& x) noexcept + { + + m_id = x.m_id; + m_charger_type = std::move(x.m_charger_type); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x ChargerType object to compare. + */ + eProsima_user_DllExport bool operator ==( + const ChargerType& x) const + { + return (m_id == x.m_id && + m_charger_type == x.m_charger_type); + } + + /*! + * @brief Comparison operator. + * @param x ChargerType object to compare. + */ + eProsima_user_DllExport bool operator !=( + const ChargerType& x) const + { + return !(*this == x); + } + + /*! + * @brief This function sets a value in member id + * @param _id New value for member id + */ + eProsima_user_DllExport void id( + int32_t _id) + { + m_id = _id; + } + + /*! + * @brief This function returns the value of member id + * @return Value of member id + */ + eProsima_user_DllExport int32_t id() const + { + return m_id; + } + + /*! + * @brief This function returns a reference to member id + * @return Reference to member id + */ + eProsima_user_DllExport int32_t& id() + { + return m_id; + } + + + /*! + * @brief This function copies the value in member charger_type + * @param _charger_type New value to be copied in member charger_type + */ + eProsima_user_DllExport void charger_type( + const std::string& _charger_type) + { + m_charger_type = _charger_type; + } + + /*! + * @brief This function moves the value in member charger_type + * @param _charger_type New value to be moved in member charger_type + */ + eProsima_user_DllExport void charger_type( + std::string&& _charger_type) + { + m_charger_type = std::move(_charger_type); + } + + /*! + * @brief This function returns a constant reference to member charger_type + * @return Constant reference to member charger_type + */ + eProsima_user_DllExport const std::string& charger_type() const + { + return m_charger_type; + } + + /*! + * @brief This function returns a reference to member charger_type + * @return Reference to member charger_type + */ + eProsima_user_DllExport std::string& charger_type() + { + return m_charger_type; + } + + + +private: + + int32_t m_id{0}; + std::string m_charger_type; + +}; +typedef std::vector ChargerTypeSeq; + +/*! + * @brief This class represents the structure ChargingSession defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargingSession +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport ChargingSession() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~ChargingSession() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object ChargingSession that will be copied. + */ + eProsima_user_DllExport ChargingSession( + const ChargingSession& x) + { + m_id = x.m_id; + + m_station_id = x.m_station_id; + + m_charger_type_id = x.m_charger_type_id; + + m_consume_wh = x.m_consume_wh; + + m_duration_min = x.m_duration_min; + + m_date_iso8601 = x.m_date_iso8601; + + m_ingested_at_iso8601 = x.m_ingested_at_iso8601; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object ChargingSession that will be copied. + */ + eProsima_user_DllExport ChargingSession( + ChargingSession&& x) noexcept + { + m_id = x.m_id; + m_station_id = x.m_station_id; + m_charger_type_id = std::move(x.m_charger_type_id); + m_consume_wh = x.m_consume_wh; + m_duration_min = x.m_duration_min; + m_date_iso8601 = std::move(x.m_date_iso8601); + m_ingested_at_iso8601 = std::move(x.m_ingested_at_iso8601); + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object ChargingSession that will be copied. + */ + eProsima_user_DllExport ChargingSession& operator =( + const ChargingSession& x) + { + + m_id = x.m_id; + + m_station_id = x.m_station_id; + + m_charger_type_id = x.m_charger_type_id; + + m_consume_wh = x.m_consume_wh; + + m_duration_min = x.m_duration_min; + + m_date_iso8601 = x.m_date_iso8601; + + m_ingested_at_iso8601 = x.m_ingested_at_iso8601; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object ChargingSession that will be copied. + */ + eProsima_user_DllExport ChargingSession& operator =( + ChargingSession&& x) noexcept + { + + m_id = x.m_id; + m_station_id = x.m_station_id; + m_charger_type_id = std::move(x.m_charger_type_id); + m_consume_wh = x.m_consume_wh; + m_duration_min = x.m_duration_min; + m_date_iso8601 = std::move(x.m_date_iso8601); + m_ingested_at_iso8601 = std::move(x.m_ingested_at_iso8601); + return *this; + } + + /*! + * @brief Comparison operator. + * @param x ChargingSession object to compare. + */ + eProsima_user_DllExport bool operator ==( + const ChargingSession& x) const + { + return (m_id == x.m_id && + m_station_id == x.m_station_id && + m_charger_type_id == x.m_charger_type_id && + m_consume_wh == x.m_consume_wh && + m_duration_min == x.m_duration_min && + m_date_iso8601 == x.m_date_iso8601 && + m_ingested_at_iso8601 == x.m_ingested_at_iso8601); + } + + /*! + * @brief Comparison operator. + * @param x ChargingSession object to compare. + */ + eProsima_user_DllExport bool operator !=( + const ChargingSession& x) const + { + return !(*this == x); + } + + /*! + * @brief This function sets a value in member id + * @param _id New value for member id + */ + eProsima_user_DllExport void id( + int32_t _id) + { + m_id = _id; + } + + /*! + * @brief This function returns the value of member id + * @return Value of member id + */ + eProsima_user_DllExport int32_t id() const + { + return m_id; + } + + /*! + * @brief This function returns a reference to member id + * @return Reference to member id + */ + eProsima_user_DllExport int32_t& id() + { + return m_id; + } + + + /*! + * @brief This function sets a value in member station_id + * @param _station_id New value for member station_id + */ + eProsima_user_DllExport void station_id( + int32_t _station_id) + { + m_station_id = _station_id; + } + + /*! + * @brief This function returns the value of member station_id + * @return Value of member station_id + */ + eProsima_user_DllExport int32_t station_id() const + { + return m_station_id; + } + + /*! + * @brief This function returns a reference to member station_id + * @return Reference to member station_id + */ + eProsima_user_DllExport int32_t& station_id() + { + return m_station_id; + } + + + /*! + * @brief This function copies the value in member charger_type_id + * @param _charger_type_id New value to be copied in member charger_type_id + */ + eProsima_user_DllExport void charger_type_id( + const std::string& _charger_type_id) + { + m_charger_type_id = _charger_type_id; + } + + /*! + * @brief This function moves the value in member charger_type_id + * @param _charger_type_id New value to be moved in member charger_type_id + */ + eProsima_user_DllExport void charger_type_id( + std::string&& _charger_type_id) + { + m_charger_type_id = std::move(_charger_type_id); + } + + /*! + * @brief This function returns a constant reference to member charger_type_id + * @return Constant reference to member charger_type_id + */ + eProsima_user_DllExport const std::string& charger_type_id() const + { + return m_charger_type_id; + } + + /*! + * @brief This function returns a reference to member charger_type_id + * @return Reference to member charger_type_id + */ + eProsima_user_DllExport std::string& charger_type_id() + { + return m_charger_type_id; + } + + + /*! + * @brief This function sets a value in member consume_wh + * @param _consume_wh New value for member consume_wh + */ + eProsima_user_DllExport void consume_wh( + double _consume_wh) + { + m_consume_wh = _consume_wh; + } + + /*! + * @brief This function returns the value of member consume_wh + * @return Value of member consume_wh + */ + eProsima_user_DllExport double consume_wh() const + { + return m_consume_wh; + } + + /*! + * @brief This function returns a reference to member consume_wh + * @return Reference to member consume_wh + */ + eProsima_user_DllExport double& consume_wh() + { + return m_consume_wh; + } + + + /*! + * @brief This function sets a value in member duration_min + * @param _duration_min New value for member duration_min + */ + eProsima_user_DllExport void duration_min( + int32_t _duration_min) + { + m_duration_min = _duration_min; + } + + /*! + * @brief This function returns the value of member duration_min + * @return Value of member duration_min + */ + eProsima_user_DllExport int32_t duration_min() const + { + return m_duration_min; + } + + /*! + * @brief This function returns a reference to member duration_min + * @return Reference to member duration_min + */ + eProsima_user_DllExport int32_t& duration_min() + { + return m_duration_min; + } + + + /*! + * @brief This function copies the value in member date_iso8601 + * @param _date_iso8601 New value to be copied in member date_iso8601 + */ + eProsima_user_DllExport void date_iso8601( + const std::string& _date_iso8601) + { + m_date_iso8601 = _date_iso8601; + } + + /*! + * @brief This function moves the value in member date_iso8601 + * @param _date_iso8601 New value to be moved in member date_iso8601 + */ + eProsima_user_DllExport void date_iso8601( + std::string&& _date_iso8601) + { + m_date_iso8601 = std::move(_date_iso8601); + } + + /*! + * @brief This function returns a constant reference to member date_iso8601 + * @return Constant reference to member date_iso8601 + */ + eProsima_user_DllExport const std::string& date_iso8601() const + { + return m_date_iso8601; + } + + /*! + * @brief This function returns a reference to member date_iso8601 + * @return Reference to member date_iso8601 + */ + eProsima_user_DllExport std::string& date_iso8601() + { + return m_date_iso8601; + } + + + /*! + * @brief This function copies the value in member ingested_at_iso8601 + * @param _ingested_at_iso8601 New value to be copied in member ingested_at_iso8601 + */ + eProsima_user_DllExport void ingested_at_iso8601( + const std::string& _ingested_at_iso8601) + { + m_ingested_at_iso8601 = _ingested_at_iso8601; + } + + /*! + * @brief This function moves the value in member ingested_at_iso8601 + * @param _ingested_at_iso8601 New value to be moved in member ingested_at_iso8601 + */ + eProsima_user_DllExport void ingested_at_iso8601( + std::string&& _ingested_at_iso8601) + { + m_ingested_at_iso8601 = std::move(_ingested_at_iso8601); + } + + /*! + * @brief This function returns a constant reference to member ingested_at_iso8601 + * @return Constant reference to member ingested_at_iso8601 + */ + eProsima_user_DllExport const std::string& ingested_at_iso8601() const + { + return m_ingested_at_iso8601; + } + + /*! + * @brief This function returns a reference to member ingested_at_iso8601 + * @return Reference to member ingested_at_iso8601 + */ + eProsima_user_DllExport std::string& ingested_at_iso8601() + { + return m_ingested_at_iso8601; + } + + + +private: + + int32_t m_id{0}; + int32_t m_station_id{0}; + std::string m_charger_type_id; + double m_consume_wh{0.0}; + int32_t m_duration_min{0}; + std::string m_date_iso8601; + std::string m_ingested_at_iso8601; + +}; +typedef std::vector ChargingSessionSeq; + +/*! + * @brief This class represents the structure TransitHealth defined by the user in the IDL file. + * @ingroup pilot_server + */ +class TransitHealth +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport TransitHealth() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~TransitHealth() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object TransitHealth that will be copied. + */ + eProsima_user_DllExport TransitHealth( + const TransitHealth& x) + { + m_status = x.m_status; + + m_last_fetch_ts = x.m_last_fetch_ts; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object TransitHealth that will be copied. + */ + eProsima_user_DllExport TransitHealth( + TransitHealth&& x) noexcept + { + m_status = std::move(x.m_status); + m_last_fetch_ts = x.m_last_fetch_ts; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object TransitHealth that will be copied. + */ + eProsima_user_DllExport TransitHealth& operator =( + const TransitHealth& x) + { + + m_status = x.m_status; + + m_last_fetch_ts = x.m_last_fetch_ts; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object TransitHealth that will be copied. + */ + eProsima_user_DllExport TransitHealth& operator =( + TransitHealth&& x) noexcept + { + + m_status = std::move(x.m_status); + m_last_fetch_ts = x.m_last_fetch_ts; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x TransitHealth object to compare. + */ + eProsima_user_DllExport bool operator ==( + const TransitHealth& x) const + { + return (m_status == x.m_status && + m_last_fetch_ts == x.m_last_fetch_ts); + } + + /*! + * @brief Comparison operator. + * @param x TransitHealth object to compare. + */ + eProsima_user_DllExport bool operator !=( + const TransitHealth& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member status + * @param _status New value to be copied in member status + */ + eProsima_user_DllExport void status( + const std::string& _status) + { + m_status = _status; + } + + /*! + * @brief This function moves the value in member status + * @param _status New value to be moved in member status + */ + eProsima_user_DllExport void status( + std::string&& _status) + { + m_status = std::move(_status); + } + + /*! + * @brief This function returns a constant reference to member status + * @return Constant reference to member status + */ + eProsima_user_DllExport const std::string& status() const + { + return m_status; + } + + /*! + * @brief This function returns a reference to member status + * @return Reference to member status + */ + eProsima_user_DllExport std::string& status() + { + return m_status; + } + + + /*! + * @brief This function sets a value in member last_fetch_ts + * @param _last_fetch_ts New value for member last_fetch_ts + */ + eProsima_user_DllExport void last_fetch_ts( + double _last_fetch_ts) + { + m_last_fetch_ts = _last_fetch_ts; + } + + /*! + * @brief This function returns the value of member last_fetch_ts + * @return Value of member last_fetch_ts + */ + eProsima_user_DllExport double last_fetch_ts() const + { + return m_last_fetch_ts; + } + + /*! + * @brief This function returns a reference to member last_fetch_ts + * @return Reference to member last_fetch_ts + */ + eProsima_user_DllExport double& last_fetch_ts() + { + return m_last_fetch_ts; + } + + + +private: + + std::string m_status; + double m_last_fetch_ts{0.0}; + +}; +/*! + * @brief This class represents the structure RouteMetric defined by the user in the IDL file. + * @ingroup pilot_server + */ +class RouteMetric +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport RouteMetric() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~RouteMetric() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object RouteMetric that will be copied. + */ + eProsima_user_DllExport RouteMetric( + const RouteMetric& x) + { + m_route_id = x.m_route_id; + + m_updates_count = x.m_updates_count; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object RouteMetric that will be copied. + */ + eProsima_user_DllExport RouteMetric( + RouteMetric&& x) noexcept + { + m_route_id = std::move(x.m_route_id); + m_updates_count = x.m_updates_count; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object RouteMetric that will be copied. + */ + eProsima_user_DllExport RouteMetric& operator =( + const RouteMetric& x) + { + + m_route_id = x.m_route_id; + + m_updates_count = x.m_updates_count; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object RouteMetric that will be copied. + */ + eProsima_user_DllExport RouteMetric& operator =( + RouteMetric&& x) noexcept + { + + m_route_id = std::move(x.m_route_id); + m_updates_count = x.m_updates_count; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x RouteMetric object to compare. + */ + eProsima_user_DllExport bool operator ==( + const RouteMetric& x) const + { + return (m_route_id == x.m_route_id && + m_updates_count == x.m_updates_count); + } + + /*! + * @brief Comparison operator. + * @param x RouteMetric object to compare. + */ + eProsima_user_DllExport bool operator !=( + const RouteMetric& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member route_id + * @param _route_id New value to be copied in member route_id + */ + eProsima_user_DllExport void route_id( + const std::string& _route_id) + { + m_route_id = _route_id; + } + + /*! + * @brief This function moves the value in member route_id + * @param _route_id New value to be moved in member route_id + */ + eProsima_user_DllExport void route_id( + std::string&& _route_id) + { + m_route_id = std::move(_route_id); + } + + /*! + * @brief This function returns a constant reference to member route_id + * @return Constant reference to member route_id + */ + eProsima_user_DllExport const std::string& route_id() const + { + return m_route_id; + } + + /*! + * @brief This function returns a reference to member route_id + * @return Reference to member route_id + */ + eProsima_user_DllExport std::string& route_id() + { + return m_route_id; + } + + + /*! + * @brief This function sets a value in member updates_count + * @param _updates_count New value for member updates_count + */ + eProsima_user_DllExport void updates_count( + int32_t _updates_count) + { + m_updates_count = _updates_count; + } + + /*! + * @brief This function returns the value of member updates_count + * @return Value of member updates_count + */ + eProsima_user_DllExport int32_t updates_count() const + { + return m_updates_count; + } + + /*! + * @brief This function returns a reference to member updates_count + * @return Reference to member updates_count + */ + eProsima_user_DllExport int32_t& updates_count() + { + return m_updates_count; + } + + + +private: + + std::string m_route_id; + int32_t m_updates_count{0}; + +}; +typedef std::vector RouteMetricSeq; + +/*! + * @brief This class represents the structure TransitMetrics defined by the user in the IDL file. + * @ingroup pilot_server + */ +class TransitMetrics +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport TransitMetrics() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~TransitMetrics() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object TransitMetrics that will be copied. + */ + eProsima_user_DllExport TransitMetrics( + const TransitMetrics& x) + { + m_by_route = x.m_by_route; + + m_vehicles_seen = x.m_vehicles_seen; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object TransitMetrics that will be copied. + */ + eProsima_user_DllExport TransitMetrics( + TransitMetrics&& x) noexcept + { + m_by_route = std::move(x.m_by_route); + m_vehicles_seen = x.m_vehicles_seen; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object TransitMetrics that will be copied. + */ + eProsima_user_DllExport TransitMetrics& operator =( + const TransitMetrics& x) + { + + m_by_route = x.m_by_route; + + m_vehicles_seen = x.m_vehicles_seen; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object TransitMetrics that will be copied. + */ + eProsima_user_DllExport TransitMetrics& operator =( + TransitMetrics&& x) noexcept + { + + m_by_route = std::move(x.m_by_route); + m_vehicles_seen = x.m_vehicles_seen; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x TransitMetrics object to compare. + */ + eProsima_user_DllExport bool operator ==( + const TransitMetrics& x) const + { + return (m_by_route == x.m_by_route && + m_vehicles_seen == x.m_vehicles_seen); + } + + /*! + * @brief Comparison operator. + * @param x TransitMetrics object to compare. + */ + eProsima_user_DllExport bool operator !=( + const TransitMetrics& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member by_route + * @param _by_route New value to be copied in member by_route + */ + eProsima_user_DllExport void by_route( + const RouteMetricSeq& _by_route) + { + m_by_route = _by_route; + } + + /*! + * @brief This function moves the value in member by_route + * @param _by_route New value to be moved in member by_route + */ + eProsima_user_DllExport void by_route( + RouteMetricSeq&& _by_route) + { + m_by_route = std::move(_by_route); + } + + /*! + * @brief This function returns a constant reference to member by_route + * @return Constant reference to member by_route + */ + eProsima_user_DllExport const RouteMetricSeq& by_route() const + { + return m_by_route; + } + + /*! + * @brief This function returns a reference to member by_route + * @return Reference to member by_route + */ + eProsima_user_DllExport RouteMetricSeq& by_route() + { + return m_by_route; + } + + + /*! + * @brief This function sets a value in member vehicles_seen + * @param _vehicles_seen New value for member vehicles_seen + */ + eProsima_user_DllExport void vehicles_seen( + int32_t _vehicles_seen) + { + m_vehicles_seen = _vehicles_seen; + } + + /*! + * @brief This function returns the value of member vehicles_seen + * @return Value of member vehicles_seen + */ + eProsima_user_DllExport int32_t vehicles_seen() const + { + return m_vehicles_seen; + } + + /*! + * @brief This function returns a reference to member vehicles_seen + * @return Reference to member vehicles_seen + */ + eProsima_user_DllExport int32_t& vehicles_seen() + { + return m_vehicles_seen; + } + + + +private: + + RouteMetricSeq m_by_route; + int32_t m_vehicles_seen{0}; + +}; +/*! + * @brief This class represents the enumeration RequestedDataType defined by the user in the IDL file. + * @ingroup pilot_server + */ +enum class RequestedDataType : int32_t +{ + CHARGER_LOCATION, + CHARGER_TYPE, + CHARGING_SESSION, + TRANSIT_HEALTH, + TRANSIT_METRICS +}; +/*! + * @brief This class represents the structure ServerQuery defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ServerQuery +{ +public: + + /*! + * @brief Default constructor. + */ + eProsima_user_DllExport ServerQuery() + { + } + + /*! + * @brief Default destructor. + */ + eProsima_user_DllExport ~ServerQuery() + { + } + + /*! + * @brief Copy constructor. + * @param x Reference to the object ServerQuery that will be copied. + */ + eProsima_user_DllExport ServerQuery( + const ServerQuery& x) + { + m_requested_by = x.m_requested_by; + + m_requested_data_type = x.m_requested_data_type; + + } + + /*! + * @brief Move constructor. + * @param x Reference to the object ServerQuery that will be copied. + */ + eProsima_user_DllExport ServerQuery( + ServerQuery&& x) noexcept + { + m_requested_by = std::move(x.m_requested_by); + m_requested_data_type = x.m_requested_data_type; + } + + /*! + * @brief Copy assignment. + * @param x Reference to the object ServerQuery that will be copied. + */ + eProsima_user_DllExport ServerQuery& operator =( + const ServerQuery& x) + { + + m_requested_by = x.m_requested_by; + + m_requested_data_type = x.m_requested_data_type; + + return *this; + } + + /*! + * @brief Move assignment. + * @param x Reference to the object ServerQuery that will be copied. + */ + eProsima_user_DllExport ServerQuery& operator =( + ServerQuery&& x) noexcept + { + + m_requested_by = std::move(x.m_requested_by); + m_requested_data_type = x.m_requested_data_type; + return *this; + } + + /*! + * @brief Comparison operator. + * @param x ServerQuery object to compare. + */ + eProsima_user_DllExport bool operator ==( + const ServerQuery& x) const + { + return (m_requested_by == x.m_requested_by && + m_requested_data_type == x.m_requested_data_type); + } + + /*! + * @brief Comparison operator. + * @param x ServerQuery object to compare. + */ + eProsima_user_DllExport bool operator !=( + const ServerQuery& x) const + { + return !(*this == x); + } + + /*! + * @brief This function copies the value in member requested_by + * @param _requested_by New value to be copied in member requested_by + */ + eProsima_user_DllExport void requested_by( + const std::string& _requested_by) + { + m_requested_by = _requested_by; + } + + /*! + * @brief This function moves the value in member requested_by + * @param _requested_by New value to be moved in member requested_by + */ + eProsima_user_DllExport void requested_by( + std::string&& _requested_by) + { + m_requested_by = std::move(_requested_by); + } + + /*! + * @brief This function returns a constant reference to member requested_by + * @return Constant reference to member requested_by + */ + eProsima_user_DllExport const std::string& requested_by() const + { + return m_requested_by; + } + + /*! + * @brief This function returns a reference to member requested_by + * @return Reference to member requested_by + */ + eProsima_user_DllExport std::string& requested_by() + { + return m_requested_by; + } + + + /*! + * @brief This function sets a value in member requested_data_type + * @param _requested_data_type New value for member requested_data_type + */ + eProsima_user_DllExport void requested_data_type( + RequestedDataType _requested_data_type) + { + m_requested_data_type = _requested_data_type; + } + + /*! + * @brief This function returns the value of member requested_data_type + * @return Value of member requested_data_type + */ + eProsima_user_DllExport RequestedDataType requested_data_type() const + { + return m_requested_data_type; + } + + /*! + * @brief This function returns a reference to member requested_data_type + * @return Reference to member requested_data_type + */ + eProsima_user_DllExport RequestedDataType& requested_data_type() + { + return m_requested_data_type; + } + + + +private: + + std::string m_requested_by; + RequestedDataType m_requested_data_type{RequestedDataType::CHARGER_LOCATION}; + +}; + +} // namespace pilot_server + +} // namespace safe_edge + +#endif // _FAST_DDS_GENERATED_SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_HPP_ + + diff --git a/fast_dds/idl/pilot_serverCdrAux.hpp b/fast_dds/idl/pilot_serverCdrAux.hpp new file mode 100644 index 0000000..fb82ca0 --- /dev/null +++ b/fast_dds/idl/pilot_serverCdrAux.hpp @@ -0,0 +1,98 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverCdrAux.hpp + * This source file contains some definitions of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_HPP + +#include "pilot_server.hpp" + +constexpr uint32_t safe_edge_pilot_server_TransitMetrics_max_cdr_typesize {137232UL}; +constexpr uint32_t safe_edge_pilot_server_TransitMetrics_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_pilot_server_ChargingSession_max_cdr_typesize {804UL}; +constexpr uint32_t safe_edge_pilot_server_ChargingSession_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_pilot_server_ChargerLocation_max_cdr_typesize {288UL}; +constexpr uint32_t safe_edge_pilot_server_ChargerLocation_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_pilot_server_RouteMetric_max_cdr_typesize {268UL}; +constexpr uint32_t safe_edge_pilot_server_RouteMetric_max_key_cdr_typesize {0UL}; + +constexpr uint32_t safe_edge_pilot_server_ServerQuery_max_cdr_typesize {268UL}; +constexpr uint32_t safe_edge_pilot_server_ServerQuery_max_key_cdr_typesize {0UL}; + + + + + +constexpr uint32_t safe_edge_pilot_server_TransitHealth_max_cdr_typesize {272UL}; +constexpr uint32_t safe_edge_pilot_server_TransitHealth_max_key_cdr_typesize {0UL}; + + + + +constexpr uint32_t safe_edge_pilot_server_ChargerType_max_cdr_typesize {268UL}; +constexpr uint32_t safe_edge_pilot_server_ChargerType_max_key_cdr_typesize {0UL}; + + + + + +namespace eprosima { +namespace fastcdr { + +class Cdr; +class CdrSizeCalculator; + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerLocation& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerType& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargingSession& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitHealth& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::RouteMetric& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitMetrics& data); + +eProsima_user_DllExport void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ServerQuery& data); + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_HPP + diff --git a/fast_dds/idl/pilot_serverCdrAux.ipp b/fast_dds/idl/pilot_serverCdrAux.ipp new file mode 100644 index 0000000..8ad7de6 --- /dev/null +++ b/fast_dds/idl/pilot_serverCdrAux.ipp @@ -0,0 +1,778 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverCdrAux.ipp + * This source file contains some declarations of CDR related functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_IPP +#define FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_IPP + +#include "pilot_serverCdrAux.hpp" + +#include +#include + + +#include +using namespace eprosima::fastcdr::exception; + +namespace eprosima { +namespace fastcdr { + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::ChargerLocation& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.name(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.position(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerLocation& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.id() + << eprosima::fastcdr::MemberId(1) << data.name() + << eprosima::fastcdr::MemberId(2) << data.position() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::ChargerLocation& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.id(); + break; + + case 1: + dcdr >> data.name(); + break; + + case 2: + dcdr >> data.position(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerLocation& data) +{ + using namespace safe_edge::pilot_server; + extern void serialize_key( + Cdr& scdr, + const safe_edge::common::GeoPoint& data); + + + static_cast(scdr); + static_cast(data); + scdr << data.id(); + + scdr << data.name(); + + serialize_key(scdr, data.position()); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::ChargerType& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.charger_type(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerType& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.id() + << eprosima::fastcdr::MemberId(1) << data.charger_type() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::ChargerType& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.id(); + break; + + case 1: + dcdr >> data.charger_type(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargerType& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.id(); + + scdr << data.charger_type(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::ChargingSession& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.station_id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(2), + data.charger_type_id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(3), + data.consume_wh(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(4), + data.duration_min(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(5), + data.date_iso8601(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(6), + data.ingested_at_iso8601(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargingSession& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.id() + << eprosima::fastcdr::MemberId(1) << data.station_id() + << eprosima::fastcdr::MemberId(2) << data.charger_type_id() + << eprosima::fastcdr::MemberId(3) << data.consume_wh() + << eprosima::fastcdr::MemberId(4) << data.duration_min() + << eprosima::fastcdr::MemberId(5) << data.date_iso8601() + << eprosima::fastcdr::MemberId(6) << data.ingested_at_iso8601() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::ChargingSession& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.id(); + break; + + case 1: + dcdr >> data.station_id(); + break; + + case 2: + dcdr >> data.charger_type_id(); + break; + + case 3: + dcdr >> data.consume_wh(); + break; + + case 4: + dcdr >> data.duration_min(); + break; + + case 5: + dcdr >> data.date_iso8601(); + break; + + case 6: + dcdr >> data.ingested_at_iso8601(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ChargingSession& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.id(); + + scdr << data.station_id(); + + scdr << data.charger_type_id(); + + scdr << data.consume_wh(); + + scdr << data.duration_min(); + + scdr << data.date_iso8601(); + + scdr << data.ingested_at_iso8601(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::TransitHealth& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.status(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.last_fetch_ts(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitHealth& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.status() + << eprosima::fastcdr::MemberId(1) << data.last_fetch_ts() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::TransitHealth& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.status(); + break; + + case 1: + dcdr >> data.last_fetch_ts(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitHealth& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.status(); + + scdr << data.last_fetch_ts(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::RouteMetric& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.route_id(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.updates_count(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::RouteMetric& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.route_id() + << eprosima::fastcdr::MemberId(1) << data.updates_count() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::RouteMetric& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.route_id(); + break; + + case 1: + dcdr >> data.updates_count(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::RouteMetric& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.route_id(); + + scdr << data.updates_count(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::TransitMetrics& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.by_route(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.vehicles_seen(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitMetrics& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.by_route() + << eprosima::fastcdr::MemberId(1) << data.vehicles_seen() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::TransitMetrics& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.by_route(); + break; + + case 1: + dcdr >> data.vehicles_seen(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::TransitMetrics& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.by_route(); + + scdr << data.vehicles_seen(); + +} + + +template<> +eProsima_user_DllExport size_t calculate_serialized_size( + eprosima::fastcdr::CdrSizeCalculator& calculator, + const safe_edge::pilot_server::ServerQuery& data, + size_t& current_alignment) +{ + using namespace safe_edge::pilot_server; + + static_cast(data); + + eprosima::fastcdr::EncodingAlgorithmFlag previous_encoding = calculator.get_encoding(); + size_t calculated_size {calculator.begin_calculate_type_serialized_size( + eprosima::fastcdr::CdrVersion::XCDRv2 == calculator.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + current_alignment)}; + + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(0), + data.requested_by(), current_alignment); + + calculated_size += calculator.calculate_member_serialized_size(eprosima::fastcdr::MemberId(1), + data.requested_data_type(), current_alignment); + + + calculated_size += calculator.end_calculate_type_serialized_size(previous_encoding, current_alignment); + + return calculated_size; +} + +template<> +eProsima_user_DllExport void serialize( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ServerQuery& data) +{ + using namespace safe_edge::pilot_server; + + eprosima::fastcdr::Cdr::state current_state(scdr); + scdr.begin_serialize_type(current_state, + eprosima::fastcdr::CdrVersion::XCDRv2 == scdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR); + + scdr + << eprosima::fastcdr::MemberId(0) << data.requested_by() + << eprosima::fastcdr::MemberId(1) << data.requested_data_type() +; + scdr.end_serialize_type(current_state); +} + +template<> +eProsima_user_DllExport void deserialize( + eprosima::fastcdr::Cdr& cdr, + safe_edge::pilot_server::ServerQuery& data) +{ + using namespace safe_edge::pilot_server; + + cdr.deserialize_type(eprosima::fastcdr::CdrVersion::XCDRv2 == cdr.get_cdr_version() ? + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2 : + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR, + [&data](eprosima::fastcdr::Cdr& dcdr, const eprosima::fastcdr::MemberId& mid) -> bool + { + bool ret_value = true; + switch (mid.id) + { + case 0: + dcdr >> data.requested_by(); + break; + + case 1: + dcdr >> data.requested_data_type(); + break; + + default: + ret_value = false; + break; + } + return ret_value; + }); +} + +void serialize_key( + eprosima::fastcdr::Cdr& scdr, + const safe_edge::pilot_server::ServerQuery& data) +{ + using namespace safe_edge::pilot_server; + + static_cast(scdr); + static_cast(data); + scdr << data.requested_by(); + + scdr << data.requested_data_type(); + +} + + + +} // namespace fastcdr +} // namespace eprosima + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVERCDRAUX_IPP + diff --git a/fast_dds/idl/pilot_serverPubSubTypes.cxx b/fast_dds/idl/pilot_serverPubSubTypes.cxx new file mode 100644 index 0000000..077e32f --- /dev/null +++ b/fast_dds/idl/pilot_serverPubSubTypes.cxx @@ -0,0 +1,1046 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverPubSubTypes.cpp + * This header file contains the implementation of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "pilot_serverPubSubTypes.hpp" + +#include +#include + +#include +#include + +#include "pilot_serverCdrAux.hpp" +#include "pilot_serverTypeObjectSupport.hpp" + +using SerializedPayload_t = eprosima::fastdds::rtps::SerializedPayload_t; +using InstanceHandle_t = eprosima::fastdds::rtps::InstanceHandle_t; +using DataRepresentationId_t = eprosima::fastdds::dds::DataRepresentationId_t; + +namespace safe_edge { +namespace pilot_server { +ChargerLocationPubSubType::ChargerLocationPubSubType() +{ + set_name("safe_edge::pilot_server::ChargerLocation"); + uint32_t type_size = safe_edge_pilot_server_ChargerLocation_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +ChargerLocationPubSubType::~ChargerLocationPubSubType() +{ +} + +bool ChargerLocationPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::ChargerLocation* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool ChargerLocationPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::ChargerLocation* p_type = + static_cast<::safe_edge::pilot_server::ChargerLocation*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t ChargerLocationPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::ChargerLocation* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* ChargerLocationPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::ChargerLocation()); +} + +void ChargerLocationPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::ChargerLocation*>(data)); +} + +bool ChargerLocationPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool ChargerLocationPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void ChargerLocationPubSubType::register_type_object_representation() +{ + register_ChargerLocation_type_identifier(type_identifiers_); +} + +ChargerTypePubSubType::ChargerTypePubSubType() +{ + set_name("safe_edge::pilot_server::ChargerType"); + uint32_t type_size = safe_edge_pilot_server_ChargerType_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +ChargerTypePubSubType::~ChargerTypePubSubType() +{ +} + +bool ChargerTypePubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::ChargerType* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool ChargerTypePubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::ChargerType* p_type = + static_cast<::safe_edge::pilot_server::ChargerType*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t ChargerTypePubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::ChargerType* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* ChargerTypePubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::ChargerType()); +} + +void ChargerTypePubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::ChargerType*>(data)); +} + +bool ChargerTypePubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool ChargerTypePubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void ChargerTypePubSubType::register_type_object_representation() +{ + register_ChargerType_type_identifier(type_identifiers_); +} + +ChargingSessionPubSubType::ChargingSessionPubSubType() +{ + set_name("safe_edge::pilot_server::ChargingSession"); + uint32_t type_size = safe_edge_pilot_server_ChargingSession_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +ChargingSessionPubSubType::~ChargingSessionPubSubType() +{ +} + +bool ChargingSessionPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::ChargingSession* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool ChargingSessionPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::ChargingSession* p_type = + static_cast<::safe_edge::pilot_server::ChargingSession*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t ChargingSessionPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::ChargingSession* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* ChargingSessionPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::ChargingSession()); +} + +void ChargingSessionPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::ChargingSession*>(data)); +} + +bool ChargingSessionPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool ChargingSessionPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void ChargingSessionPubSubType::register_type_object_representation() +{ + register_ChargingSession_type_identifier(type_identifiers_); +} + +TransitHealthPubSubType::TransitHealthPubSubType() +{ + set_name("safe_edge::pilot_server::TransitHealth"); + uint32_t type_size = safe_edge_pilot_server_TransitHealth_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +TransitHealthPubSubType::~TransitHealthPubSubType() +{ +} + +bool TransitHealthPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::TransitHealth* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool TransitHealthPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::TransitHealth* p_type = + static_cast<::safe_edge::pilot_server::TransitHealth*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t TransitHealthPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::TransitHealth* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* TransitHealthPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::TransitHealth()); +} + +void TransitHealthPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::TransitHealth*>(data)); +} + +bool TransitHealthPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool TransitHealthPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void TransitHealthPubSubType::register_type_object_representation() +{ + register_TransitHealth_type_identifier(type_identifiers_); +} + +RouteMetricPubSubType::RouteMetricPubSubType() +{ + set_name("safe_edge::pilot_server::RouteMetric"); + uint32_t type_size = safe_edge_pilot_server_RouteMetric_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +RouteMetricPubSubType::~RouteMetricPubSubType() +{ +} + +bool RouteMetricPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::RouteMetric* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool RouteMetricPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::RouteMetric* p_type = + static_cast<::safe_edge::pilot_server::RouteMetric*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t RouteMetricPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::RouteMetric* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* RouteMetricPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::RouteMetric()); +} + +void RouteMetricPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::RouteMetric*>(data)); +} + +bool RouteMetricPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool RouteMetricPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void RouteMetricPubSubType::register_type_object_representation() +{ + register_RouteMetric_type_identifier(type_identifiers_); +} + +TransitMetricsPubSubType::TransitMetricsPubSubType() +{ + set_name("safe_edge::pilot_server::TransitMetrics"); + uint32_t type_size = safe_edge_pilot_server_TransitMetrics_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +TransitMetricsPubSubType::~TransitMetricsPubSubType() +{ +} + +bool TransitMetricsPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::TransitMetrics* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool TransitMetricsPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::TransitMetrics* p_type = + static_cast<::safe_edge::pilot_server::TransitMetrics*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t TransitMetricsPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::TransitMetrics* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* TransitMetricsPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::TransitMetrics()); +} + +void TransitMetricsPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::TransitMetrics*>(data)); +} + +bool TransitMetricsPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool TransitMetricsPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void TransitMetricsPubSubType::register_type_object_representation() +{ + register_TransitMetrics_type_identifier(type_identifiers_); +} + +ServerQueryPubSubType::ServerQueryPubSubType() +{ + set_name("safe_edge::pilot_server::ServerQuery"); + uint32_t type_size = safe_edge_pilot_server_ServerQuery_max_cdr_typesize; + type_size += static_cast(eprosima::fastcdr::Cdr::alignment(type_size, 4)); /* possible submessage alignment */ + max_serialized_type_size = type_size + 4; /*encapsulation*/ + is_compute_key_provided = false; +} + +ServerQueryPubSubType::~ServerQueryPubSubType() +{ +} + +bool ServerQueryPubSubType::serialize( + const void* const data, + SerializedPayload_t& payload, + DataRepresentationId_t data_representation) +{ + const ::safe_edge::pilot_server::ServerQuery* p_type = + static_cast(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.max_size); + // Object that serializes the data. + eprosima::fastcdr::Cdr ser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 : eprosima::fastcdr::CdrVersion::XCDRv2); + payload.encapsulation = ser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + ser.set_encoding_flag( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::EncodingAlgorithmFlag::PLAIN_CDR : + eprosima::fastcdr::EncodingAlgorithmFlag::DELIMIT_CDR2); + + try + { + // Serialize encapsulation + ser.serialize_encapsulation(); + // Serialize the object. + ser << *p_type; + ser.set_dds_cdr_options({0, 0}); + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + // Get the serialized length + payload.length = static_cast(ser.get_serialized_data_length()); + return true; +} + +bool ServerQueryPubSubType::deserialize( + SerializedPayload_t& payload, + void* data) +{ + try + { + // Convert DATA to pointer of your type + ::safe_edge::pilot_server::ServerQuery* p_type = + static_cast<::safe_edge::pilot_server::ServerQuery*>(data); + + // Object that manages the raw buffer. + eprosima::fastcdr::FastBuffer fastbuffer(reinterpret_cast(payload.data), payload.length); + + // Object that deserializes the data. + eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN); + + // Deserialize encapsulation. + deser.read_encapsulation(); + payload.encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; + + // Deserialize the object. + deser >> *p_type; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return false; + } + + return true; +} + +uint32_t ServerQueryPubSubType::calculate_serialized_size( + const void* const data, + DataRepresentationId_t data_representation) +{ + try + { + eprosima::fastcdr::CdrSizeCalculator calculator( + data_representation == DataRepresentationId_t::XCDR_DATA_REPRESENTATION ? + eprosima::fastcdr::CdrVersion::XCDRv1 :eprosima::fastcdr::CdrVersion::XCDRv2); + size_t current_alignment {0}; + const ::safe_edge::pilot_server::ServerQuery* p_type = + static_cast(data); + auto calc_size = calculator.calculate_serialized_size(*p_type, current_alignment); + return static_cast(calc_size) + 4u /*encapsulation*/; + } + catch (eprosima::fastcdr::exception::Exception& /*exception*/) + { + return 0; + } +} + +void* ServerQueryPubSubType::create_data() +{ + return reinterpret_cast(new ::safe_edge::pilot_server::ServerQuery()); +} + +void ServerQueryPubSubType::delete_data( + void* data) +{ + delete(reinterpret_cast<::safe_edge::pilot_server::ServerQuery*>(data)); +} + +bool ServerQueryPubSubType::compute_key( + SerializedPayload_t& payload, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(payload); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +bool ServerQueryPubSubType::compute_key( + const void* const data, + InstanceHandle_t& handle, + bool force_md5) +{ + static_cast(data); + static_cast(handle); + static_cast(force_md5); + + return false; +} + +void ServerQueryPubSubType::register_type_object_representation() +{ + register_ServerQuery_type_identifier(type_identifiers_); +} + +} // namespace pilot_server + +} // namespace safe_edge + + +// Include auxiliary functions like for serializing/deserializing. +#include "pilot_serverCdrAux.ipp" diff --git a/fast_dds/idl/pilot_serverPubSubTypes.hpp b/fast_dds/idl/pilot_serverPubSubTypes.hpp new file mode 100644 index 0000000..9ebc4f2 --- /dev/null +++ b/fast_dds/idl/pilot_serverPubSubTypes.hpp @@ -0,0 +1,606 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverPubSubTypes.hpp + * This header file contains the declaration of the serialization functions. + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_PUBSUBTYPES_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_PUBSUBTYPES_HPP + +#include + +#include +#include +#include +#include +#include + +#include "pilot_server.hpp" + +#include "commonPubSubTypes.hpp" + +#if !defined(FASTDDS_GEN_API_VER) || (FASTDDS_GEN_API_VER != 3) +#error \ + Generated pilot_server is not compatible with current installed Fast DDS. Please, regenerate it with fastddsgen. +#endif // FASTDDS_GEN_API_VER + +namespace safe_edge { +namespace pilot_server { + +/*! + * @brief This class represents the TopicDataType of the type ChargerLocation defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargerLocationPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::ChargerLocation type; + + eProsima_user_DllExport ChargerLocationPubSubType(); + + eProsima_user_DllExport ~ChargerLocationPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +typedef std::vector ChargerLocationSeq; + +/*! + * @brief This class represents the TopicDataType of the type ChargerType defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargerTypePubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::ChargerType type; + + eProsima_user_DllExport ChargerTypePubSubType(); + + eProsima_user_DllExport ~ChargerTypePubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +typedef std::vector ChargerTypeSeq; + +/*! + * @brief This class represents the TopicDataType of the type ChargingSession defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ChargingSessionPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::ChargingSession type; + + eProsima_user_DllExport ChargingSessionPubSubType(); + + eProsima_user_DllExport ~ChargingSessionPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +typedef std::vector ChargingSessionSeq; + +/*! + * @brief This class represents the TopicDataType of the type TransitHealth defined by the user in the IDL file. + * @ingroup pilot_server + */ +class TransitHealthPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::TransitHealth type; + + eProsima_user_DllExport TransitHealthPubSubType(); + + eProsima_user_DllExport ~TransitHealthPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type RouteMetric defined by the user in the IDL file. + * @ingroup pilot_server + */ +class RouteMetricPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::RouteMetric type; + + eProsima_user_DllExport RouteMetricPubSubType(); + + eProsima_user_DllExport ~RouteMetricPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +typedef std::vector RouteMetricSeq; + +/*! + * @brief This class represents the TopicDataType of the type TransitMetrics defined by the user in the IDL file. + * @ingroup pilot_server + */ +class TransitMetricsPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::TransitMetrics type; + + eProsima_user_DllExport TransitMetricsPubSubType(); + + eProsima_user_DllExport ~TransitMetricsPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + + +/*! + * @brief This class represents the TopicDataType of the type ServerQuery defined by the user in the IDL file. + * @ingroup pilot_server + */ +class ServerQueryPubSubType : public eprosima::fastdds::dds::TopicDataType +{ +public: + + typedef ::safe_edge::pilot_server::ServerQuery type; + + eProsima_user_DllExport ServerQueryPubSubType(); + + eProsima_user_DllExport ~ServerQueryPubSubType() override; + + eProsima_user_DllExport bool serialize( + const void* const data, + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool deserialize( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + void* data) override; + + eProsima_user_DllExport uint32_t calculate_serialized_size( + const void* const data, + eprosima::fastdds::dds::DataRepresentationId_t data_representation) override; + + eProsima_user_DllExport bool compute_key( + eprosima::fastdds::rtps::SerializedPayload_t& payload, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport bool compute_key( + const void* const data, + eprosima::fastdds::rtps::InstanceHandle_t& ihandle, + bool force_md5 = false) override; + + eProsima_user_DllExport void* create_data() override; + + eProsima_user_DllExport void delete_data( + void* data) override; + + //Register TypeObject representation in Fast DDS TypeObjectRegistry + eProsima_user_DllExport void register_type_object_representation() override; + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + eProsima_user_DllExport inline bool is_bounded() const override + { + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_BOUNDED + +#ifdef TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + + eProsima_user_DllExport inline bool is_plain( + eprosima::fastdds::dds::DataRepresentationId_t data_representation) const override + { + static_cast(data_representation); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_IS_PLAIN + +#ifdef TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + eProsima_user_DllExport inline bool construct_sample( + void* memory) const override + { + static_cast(memory); + return false; + } + +#endif // TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE + +private: + +}; + +} // namespace pilot_server +} // namespace safe_edge + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_PUBSUBTYPES_HPP + diff --git a/fast_dds/idl/pilot_serverTypeObjectSupport.cxx b/fast_dds/idl/pilot_serverTypeObjectSupport.cxx new file mode 100644 index 0000000..3ac9ad9 --- /dev/null +++ b/fast_dds/idl/pilot_serverTypeObjectSupport.cxx @@ -0,0 +1,1299 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverTypeObjectSupport.cxx + * Source file containing the implementation to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#include "pilot_serverTypeObjectSupport.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pilot_server.hpp" + +#include "common.hpp" + +using namespace eprosima::fastdds::dds::xtypes; + +namespace safe_edge { +namespace pilot_server { +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_ChargerLocation_type_identifier( + TypeIdentifierPair& type_ids_ChargerLocation) +{ + + ReturnCode_t return_code_ChargerLocation {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargerLocation = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerLocation", type_ids_ChargerLocation); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerLocation) + { + StructTypeFlag struct_flags_ChargerLocation = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_ChargerLocation = "safe_edge::pilot_server::ChargerLocation"; + eprosima::fastcdr::optional type_ann_builtin_ChargerLocation; + eprosima::fastcdr::optional ann_custom_ChargerLocation; + CompleteTypeDetail detail_ChargerLocation = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargerLocation, ann_custom_ChargerLocation, type_name_ChargerLocation.to_string()); + CompleteStructHeader header_ChargerLocation; + header_ChargerLocation = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ChargerLocation); + CompleteStructMemberSeq member_seq_ChargerLocation; + { + TypeIdentifierPair type_ids_id; + ReturnCode_t return_code_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_id) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "id Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_id = 0x00000000; + bool common_id_ec {false}; + CommonStructMember common_id {TypeObjectUtils::build_common_struct_member(member_id_id, member_flags_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_id, common_id_ec))}; + if (!common_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure id member TypeIdentifier inconsistent."); + return; + } + MemberName name_id = "id"; + eprosima::fastcdr::optional member_ann_builtin_id; + ann_custom_ChargerLocation.reset(); + CompleteMemberDetail detail_id = TypeObjectUtils::build_complete_member_detail(name_id, member_ann_builtin_id, ann_custom_ChargerLocation); + CompleteStructMember member_id = TypeObjectUtils::build_complete_struct_member(common_id, detail_id); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargerLocation, member_id); + } + { + TypeIdentifierPair type_ids_name; + ReturnCode_t return_code_name {eprosima::fastdds::dds::RETCODE_OK}; + return_code_name = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_name); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_name) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_name)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_name = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_name = 0x00000001; + bool common_name_ec {false}; + CommonStructMember common_name {TypeObjectUtils::build_common_struct_member(member_id_name, member_flags_name, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_name, common_name_ec))}; + if (!common_name_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure name member TypeIdentifier inconsistent."); + return; + } + MemberName name_name = "name"; + eprosima::fastcdr::optional member_ann_builtin_name; + ann_custom_ChargerLocation.reset(); + CompleteMemberDetail detail_name = TypeObjectUtils::build_complete_member_detail(name_name, member_ann_builtin_name, ann_custom_ChargerLocation); + CompleteStructMember member_name = TypeObjectUtils::build_complete_struct_member(common_name, detail_name); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargerLocation, member_name); + } + { + TypeIdentifierPair type_ids_position; + ReturnCode_t return_code_position {eprosima::fastdds::dds::RETCODE_OK}; + return_code_position = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::common::GeoPoint", type_ids_position); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_position) + { + ::safe_edge::common::register_GeoPoint_type_identifier(type_ids_position); + } + StructMemberFlag member_flags_position = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_position = 0x00000002; + bool common_position_ec {false}; + CommonStructMember common_position {TypeObjectUtils::build_common_struct_member(member_id_position, member_flags_position, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_position, common_position_ec))}; + if (!common_position_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure position member TypeIdentifier inconsistent."); + return; + } + MemberName name_position = "position"; + eprosima::fastcdr::optional member_ann_builtin_position; + ann_custom_ChargerLocation.reset(); + CompleteMemberDetail detail_position = TypeObjectUtils::build_complete_member_detail(name_position, member_ann_builtin_position, ann_custom_ChargerLocation); + CompleteStructMember member_position = TypeObjectUtils::build_complete_struct_member(common_position, detail_position); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargerLocation, member_position); + } + CompleteStructType struct_type_ChargerLocation = TypeObjectUtils::build_complete_struct_type(struct_flags_ChargerLocation, header_ChargerLocation, member_seq_ChargerLocation); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_ChargerLocation, type_name_ChargerLocation.to_string(), type_ids_ChargerLocation)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargerLocation already registered in TypeObjectRegistry for a different type."); + } + } +}void register_ChargerLocationSeq_type_identifier( + TypeIdentifierPair& type_ids_ChargerLocationSeq) +{ + ReturnCode_t return_code_ChargerLocationSeq {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargerLocationSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerLocationSeq", type_ids_ChargerLocationSeq); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerLocationSeq) + { + AliasTypeFlag alias_flags_ChargerLocationSeq = 0; + QualifiedTypeName type_name_ChargerLocationSeq = "safe_edge::pilot_server::ChargerLocationSeq"; + eprosima::fastcdr::optional type_ann_builtin_ChargerLocationSeq; + eprosima::fastcdr::optional ann_custom_ChargerLocationSeq; + CompleteTypeDetail detail_ChargerLocationSeq = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargerLocationSeq, ann_custom_ChargerLocationSeq, type_name_ChargerLocationSeq.to_string()); + CompleteAliasHeader header_ChargerLocationSeq = TypeObjectUtils::build_complete_alias_header(detail_ChargerLocationSeq); + AliasMemberFlag related_flags_ChargerLocationSeq = 0; + return_code_ChargerLocationSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200", type_ids_ChargerLocationSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerLocationSeq) + { + return_code_ChargerLocationSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerLocation", type_ids_ChargerLocationSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerLocationSeq) + { + ::safe_edge::pilot_server::register_ChargerLocation_type_identifier(type_ids_ChargerLocationSeq); + } + bool element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200_ec {false}; + TypeIdentifier* element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 {new TypeIdentifier(TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargerLocationSeq, element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200_ec))}; + if (!element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Sequence element TypeIdentifier inconsistent."); + return; + } + EquivalenceKind equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 = EK_COMPLETE; + if (TK_NONE == type_ids_ChargerLocationSeq.type_identifier2()._d()) + { + equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 = EK_BOTH; + } + CollectionElementFlag element_flags_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 = 0; + PlainCollectionHeader header_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 = TypeObjectUtils::build_plain_collection_header(equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200, element_flags_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200); + { + SBound bound = static_cast(200); + PlainSequenceSElemDefn seq_sdefn = TypeObjectUtils::build_plain_sequence_s_elem_defn(header_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200, bound, + eprosima::fastcdr::external(element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200)); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_sequence_type_identifier(seq_sdefn, "anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200", type_ids_ChargerLocationSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_sequence_safe_edge_pilot_server_ChargerLocation_200 already registered in TypeObjectRegistry for a different type."); + } + } + } + bool common_ChargerLocationSeq_ec {false}; + CommonAliasBody common_ChargerLocationSeq {TypeObjectUtils::build_common_alias_body(related_flags_ChargerLocationSeq, + TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargerLocationSeq, common_ChargerLocationSeq_ec))}; + if (!common_ChargerLocationSeq_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "safe_edge::pilot_server::ChargerLocationSeq related TypeIdentifier inconsistent."); + return; + } + eprosima::fastcdr::optional member_ann_builtin_ChargerLocationSeq; + ann_custom_ChargerLocationSeq.reset(); + CompleteAliasBody body_ChargerLocationSeq = TypeObjectUtils::build_complete_alias_body(common_ChargerLocationSeq, + member_ann_builtin_ChargerLocationSeq, ann_custom_ChargerLocationSeq); + CompleteAliasType alias_type_ChargerLocationSeq = TypeObjectUtils::build_complete_alias_type(alias_flags_ChargerLocationSeq, + header_ChargerLocationSeq, body_ChargerLocationSeq); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_alias_type_object(alias_type_ChargerLocationSeq, + type_name_ChargerLocationSeq.to_string(), type_ids_ChargerLocationSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargerLocationSeq already registered in TypeObjectRegistry for a different type."); + } + } +} + +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_ChargerType_type_identifier( + TypeIdentifierPair& type_ids_ChargerType) +{ + + ReturnCode_t return_code_ChargerType {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargerType = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerType", type_ids_ChargerType); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerType) + { + StructTypeFlag struct_flags_ChargerType = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_ChargerType = "safe_edge::pilot_server::ChargerType"; + eprosima::fastcdr::optional type_ann_builtin_ChargerType; + eprosima::fastcdr::optional ann_custom_ChargerType; + CompleteTypeDetail detail_ChargerType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargerType, ann_custom_ChargerType, type_name_ChargerType.to_string()); + CompleteStructHeader header_ChargerType; + header_ChargerType = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ChargerType); + CompleteStructMemberSeq member_seq_ChargerType; + { + TypeIdentifierPair type_ids_id; + ReturnCode_t return_code_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_id) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "id Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_id = 0x00000000; + bool common_id_ec {false}; + CommonStructMember common_id {TypeObjectUtils::build_common_struct_member(member_id_id, member_flags_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_id, common_id_ec))}; + if (!common_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure id member TypeIdentifier inconsistent."); + return; + } + MemberName name_id = "id"; + eprosima::fastcdr::optional member_ann_builtin_id; + ann_custom_ChargerType.reset(); + CompleteMemberDetail detail_id = TypeObjectUtils::build_complete_member_detail(name_id, member_ann_builtin_id, ann_custom_ChargerType); + CompleteStructMember member_id = TypeObjectUtils::build_complete_struct_member(common_id, detail_id); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargerType, member_id); + } + { + TypeIdentifierPair type_ids_charger_type; + ReturnCode_t return_code_charger_type {eprosima::fastdds::dds::RETCODE_OK}; + return_code_charger_type = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_charger_type); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_charger_type) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_charger_type)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_charger_type = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_charger_type = 0x00000001; + bool common_charger_type_ec {false}; + CommonStructMember common_charger_type {TypeObjectUtils::build_common_struct_member(member_id_charger_type, member_flags_charger_type, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_charger_type, common_charger_type_ec))}; + if (!common_charger_type_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure charger_type member TypeIdentifier inconsistent."); + return; + } + MemberName name_charger_type = "charger_type"; + eprosima::fastcdr::optional member_ann_builtin_charger_type; + ann_custom_ChargerType.reset(); + CompleteMemberDetail detail_charger_type = TypeObjectUtils::build_complete_member_detail(name_charger_type, member_ann_builtin_charger_type, ann_custom_ChargerType); + CompleteStructMember member_charger_type = TypeObjectUtils::build_complete_struct_member(common_charger_type, detail_charger_type); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargerType, member_charger_type); + } + CompleteStructType struct_type_ChargerType = TypeObjectUtils::build_complete_struct_type(struct_flags_ChargerType, header_ChargerType, member_seq_ChargerType); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_ChargerType, type_name_ChargerType.to_string(), type_ids_ChargerType)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargerType already registered in TypeObjectRegistry for a different type."); + } + } +}void register_ChargerTypeSeq_type_identifier( + TypeIdentifierPair& type_ids_ChargerTypeSeq) +{ + ReturnCode_t return_code_ChargerTypeSeq {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargerTypeSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerTypeSeq", type_ids_ChargerTypeSeq); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerTypeSeq) + { + AliasTypeFlag alias_flags_ChargerTypeSeq = 0; + QualifiedTypeName type_name_ChargerTypeSeq = "safe_edge::pilot_server::ChargerTypeSeq"; + eprosima::fastcdr::optional type_ann_builtin_ChargerTypeSeq; + eprosima::fastcdr::optional ann_custom_ChargerTypeSeq; + CompleteTypeDetail detail_ChargerTypeSeq = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargerTypeSeq, ann_custom_ChargerTypeSeq, type_name_ChargerTypeSeq.to_string()); + CompleteAliasHeader header_ChargerTypeSeq = TypeObjectUtils::build_complete_alias_header(detail_ChargerTypeSeq); + AliasMemberFlag related_flags_ChargerTypeSeq = 0; + return_code_ChargerTypeSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_sequence_safe_edge_pilot_server_ChargerType_64", type_ids_ChargerTypeSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerTypeSeq) + { + return_code_ChargerTypeSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargerType", type_ids_ChargerTypeSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargerTypeSeq) + { + ::safe_edge::pilot_server::register_ChargerType_type_identifier(type_ids_ChargerTypeSeq); + } + bool element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerType_64_ec {false}; + TypeIdentifier* element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerType_64 {new TypeIdentifier(TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargerTypeSeq, element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerType_64_ec))}; + if (!element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerType_64_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Sequence element TypeIdentifier inconsistent."); + return; + } + EquivalenceKind equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerType_64 = EK_COMPLETE; + if (TK_NONE == type_ids_ChargerTypeSeq.type_identifier2()._d()) + { + equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerType_64 = EK_BOTH; + } + CollectionElementFlag element_flags_anonymous_sequence_safe_edge_pilot_server_ChargerType_64 = 0; + PlainCollectionHeader header_anonymous_sequence_safe_edge_pilot_server_ChargerType_64 = TypeObjectUtils::build_plain_collection_header(equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargerType_64, element_flags_anonymous_sequence_safe_edge_pilot_server_ChargerType_64); + { + SBound bound = static_cast(64); + PlainSequenceSElemDefn seq_sdefn = TypeObjectUtils::build_plain_sequence_s_elem_defn(header_anonymous_sequence_safe_edge_pilot_server_ChargerType_64, bound, + eprosima::fastcdr::external(element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargerType_64)); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_sequence_type_identifier(seq_sdefn, "anonymous_sequence_safe_edge_pilot_server_ChargerType_64", type_ids_ChargerTypeSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_sequence_safe_edge_pilot_server_ChargerType_64 already registered in TypeObjectRegistry for a different type."); + } + } + } + bool common_ChargerTypeSeq_ec {false}; + CommonAliasBody common_ChargerTypeSeq {TypeObjectUtils::build_common_alias_body(related_flags_ChargerTypeSeq, + TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargerTypeSeq, common_ChargerTypeSeq_ec))}; + if (!common_ChargerTypeSeq_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "safe_edge::pilot_server::ChargerTypeSeq related TypeIdentifier inconsistent."); + return; + } + eprosima::fastcdr::optional member_ann_builtin_ChargerTypeSeq; + ann_custom_ChargerTypeSeq.reset(); + CompleteAliasBody body_ChargerTypeSeq = TypeObjectUtils::build_complete_alias_body(common_ChargerTypeSeq, + member_ann_builtin_ChargerTypeSeq, ann_custom_ChargerTypeSeq); + CompleteAliasType alias_type_ChargerTypeSeq = TypeObjectUtils::build_complete_alias_type(alias_flags_ChargerTypeSeq, + header_ChargerTypeSeq, body_ChargerTypeSeq); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_alias_type_object(alias_type_ChargerTypeSeq, + type_name_ChargerTypeSeq.to_string(), type_ids_ChargerTypeSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargerTypeSeq already registered in TypeObjectRegistry for a different type."); + } + } +} + +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_ChargingSession_type_identifier( + TypeIdentifierPair& type_ids_ChargingSession) +{ + + ReturnCode_t return_code_ChargingSession {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargingSession = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargingSession", type_ids_ChargingSession); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargingSession) + { + StructTypeFlag struct_flags_ChargingSession = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_ChargingSession = "safe_edge::pilot_server::ChargingSession"; + eprosima::fastcdr::optional type_ann_builtin_ChargingSession; + eprosima::fastcdr::optional ann_custom_ChargingSession; + CompleteTypeDetail detail_ChargingSession = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargingSession, ann_custom_ChargingSession, type_name_ChargingSession.to_string()); + CompleteStructHeader header_ChargingSession; + header_ChargingSession = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ChargingSession); + CompleteStructMemberSeq member_seq_ChargingSession; + { + TypeIdentifierPair type_ids_id; + ReturnCode_t return_code_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_id) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "id Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_id = 0x00000000; + bool common_id_ec {false}; + CommonStructMember common_id {TypeObjectUtils::build_common_struct_member(member_id_id, member_flags_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_id, common_id_ec))}; + if (!common_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure id member TypeIdentifier inconsistent."); + return; + } + MemberName name_id = "id"; + eprosima::fastcdr::optional member_ann_builtin_id; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_id = TypeObjectUtils::build_complete_member_detail(name_id, member_ann_builtin_id, ann_custom_ChargingSession); + CompleteStructMember member_id = TypeObjectUtils::build_complete_struct_member(common_id, detail_id); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_id); + } + { + TypeIdentifierPair type_ids_station_id; + ReturnCode_t return_code_station_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_station_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_station_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_station_id) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "station_id Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_station_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_station_id = 0x00000001; + bool common_station_id_ec {false}; + CommonStructMember common_station_id {TypeObjectUtils::build_common_struct_member(member_id_station_id, member_flags_station_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_station_id, common_station_id_ec))}; + if (!common_station_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure station_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_station_id = "station_id"; + eprosima::fastcdr::optional member_ann_builtin_station_id; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_station_id = TypeObjectUtils::build_complete_member_detail(name_station_id, member_ann_builtin_station_id, ann_custom_ChargingSession); + CompleteStructMember member_station_id = TypeObjectUtils::build_complete_struct_member(common_station_id, detail_station_id); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_station_id); + } + { + TypeIdentifierPair type_ids_charger_type_id; + ReturnCode_t return_code_charger_type_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_charger_type_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_charger_type_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_charger_type_id) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_charger_type_id)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_charger_type_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_charger_type_id = 0x00000002; + bool common_charger_type_id_ec {false}; + CommonStructMember common_charger_type_id {TypeObjectUtils::build_common_struct_member(member_id_charger_type_id, member_flags_charger_type_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_charger_type_id, common_charger_type_id_ec))}; + if (!common_charger_type_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure charger_type_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_charger_type_id = "charger_type_id"; + eprosima::fastcdr::optional member_ann_builtin_charger_type_id; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_charger_type_id = TypeObjectUtils::build_complete_member_detail(name_charger_type_id, member_ann_builtin_charger_type_id, ann_custom_ChargingSession); + CompleteStructMember member_charger_type_id = TypeObjectUtils::build_complete_struct_member(common_charger_type_id, detail_charger_type_id); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_charger_type_id); + } + { + TypeIdentifierPair type_ids_consume_wh; + ReturnCode_t return_code_consume_wh {eprosima::fastdds::dds::RETCODE_OK}; + return_code_consume_wh = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_double", type_ids_consume_wh); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_consume_wh) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "consume_wh Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_consume_wh = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_consume_wh = 0x00000003; + bool common_consume_wh_ec {false}; + CommonStructMember common_consume_wh {TypeObjectUtils::build_common_struct_member(member_id_consume_wh, member_flags_consume_wh, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_consume_wh, common_consume_wh_ec))}; + if (!common_consume_wh_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure consume_wh member TypeIdentifier inconsistent."); + return; + } + MemberName name_consume_wh = "consume_wh"; + eprosima::fastcdr::optional member_ann_builtin_consume_wh; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_consume_wh = TypeObjectUtils::build_complete_member_detail(name_consume_wh, member_ann_builtin_consume_wh, ann_custom_ChargingSession); + CompleteStructMember member_consume_wh = TypeObjectUtils::build_complete_struct_member(common_consume_wh, detail_consume_wh); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_consume_wh); + } + { + TypeIdentifierPair type_ids_duration_min; + ReturnCode_t return_code_duration_min {eprosima::fastdds::dds::RETCODE_OK}; + return_code_duration_min = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_duration_min); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_duration_min) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "duration_min Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_duration_min = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_duration_min = 0x00000004; + bool common_duration_min_ec {false}; + CommonStructMember common_duration_min {TypeObjectUtils::build_common_struct_member(member_id_duration_min, member_flags_duration_min, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_duration_min, common_duration_min_ec))}; + if (!common_duration_min_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure duration_min member TypeIdentifier inconsistent."); + return; + } + MemberName name_duration_min = "duration_min"; + eprosima::fastcdr::optional member_ann_builtin_duration_min; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_duration_min = TypeObjectUtils::build_complete_member_detail(name_duration_min, member_ann_builtin_duration_min, ann_custom_ChargingSession); + CompleteStructMember member_duration_min = TypeObjectUtils::build_complete_struct_member(common_duration_min, detail_duration_min); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_duration_min); + } + { + TypeIdentifierPair type_ids_date_iso8601; + ReturnCode_t return_code_date_iso8601 {eprosima::fastdds::dds::RETCODE_OK}; + return_code_date_iso8601 = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_date_iso8601); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_date_iso8601) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_date_iso8601)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_date_iso8601 = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_date_iso8601 = 0x00000005; + bool common_date_iso8601_ec {false}; + CommonStructMember common_date_iso8601 {TypeObjectUtils::build_common_struct_member(member_id_date_iso8601, member_flags_date_iso8601, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_date_iso8601, common_date_iso8601_ec))}; + if (!common_date_iso8601_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure date_iso8601 member TypeIdentifier inconsistent."); + return; + } + MemberName name_date_iso8601 = "date_iso8601"; + eprosima::fastcdr::optional member_ann_builtin_date_iso8601; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_date_iso8601 = TypeObjectUtils::build_complete_member_detail(name_date_iso8601, member_ann_builtin_date_iso8601, ann_custom_ChargingSession); + CompleteStructMember member_date_iso8601 = TypeObjectUtils::build_complete_struct_member(common_date_iso8601, detail_date_iso8601); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_date_iso8601); + } + { + TypeIdentifierPair type_ids_ingested_at_iso8601; + ReturnCode_t return_code_ingested_at_iso8601 {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ingested_at_iso8601 = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_ingested_at_iso8601); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ingested_at_iso8601) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_ingested_at_iso8601)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_ingested_at_iso8601 = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_ingested_at_iso8601 = 0x00000006; + bool common_ingested_at_iso8601_ec {false}; + CommonStructMember common_ingested_at_iso8601 {TypeObjectUtils::build_common_struct_member(member_id_ingested_at_iso8601, member_flags_ingested_at_iso8601, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ingested_at_iso8601, common_ingested_at_iso8601_ec))}; + if (!common_ingested_at_iso8601_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure ingested_at_iso8601 member TypeIdentifier inconsistent."); + return; + } + MemberName name_ingested_at_iso8601 = "ingested_at_iso8601"; + eprosima::fastcdr::optional member_ann_builtin_ingested_at_iso8601; + ann_custom_ChargingSession.reset(); + CompleteMemberDetail detail_ingested_at_iso8601 = TypeObjectUtils::build_complete_member_detail(name_ingested_at_iso8601, member_ann_builtin_ingested_at_iso8601, ann_custom_ChargingSession); + CompleteStructMember member_ingested_at_iso8601 = TypeObjectUtils::build_complete_struct_member(common_ingested_at_iso8601, detail_ingested_at_iso8601); + TypeObjectUtils::add_complete_struct_member(member_seq_ChargingSession, member_ingested_at_iso8601); + } + CompleteStructType struct_type_ChargingSession = TypeObjectUtils::build_complete_struct_type(struct_flags_ChargingSession, header_ChargingSession, member_seq_ChargingSession); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_ChargingSession, type_name_ChargingSession.to_string(), type_ids_ChargingSession)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargingSession already registered in TypeObjectRegistry for a different type."); + } + } +}void register_ChargingSessionSeq_type_identifier( + TypeIdentifierPair& type_ids_ChargingSessionSeq) +{ + ReturnCode_t return_code_ChargingSessionSeq {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ChargingSessionSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargingSessionSeq", type_ids_ChargingSessionSeq); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargingSessionSeq) + { + AliasTypeFlag alias_flags_ChargingSessionSeq = 0; + QualifiedTypeName type_name_ChargingSessionSeq = "safe_edge::pilot_server::ChargingSessionSeq"; + eprosima::fastcdr::optional type_ann_builtin_ChargingSessionSeq; + eprosima::fastcdr::optional ann_custom_ChargingSessionSeq; + CompleteTypeDetail detail_ChargingSessionSeq = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ChargingSessionSeq, ann_custom_ChargingSessionSeq, type_name_ChargingSessionSeq.to_string()); + CompleteAliasHeader header_ChargingSessionSeq = TypeObjectUtils::build_complete_alias_header(detail_ChargingSessionSeq); + AliasMemberFlag related_flags_ChargingSessionSeq = 0; + return_code_ChargingSessionSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000", type_ids_ChargingSessionSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargingSessionSeq) + { + return_code_ChargingSessionSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ChargingSession", type_ids_ChargingSessionSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ChargingSessionSeq) + { + ::safe_edge::pilot_server::register_ChargingSession_type_identifier(type_ids_ChargingSessionSeq); + } + bool element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000_ec {false}; + TypeIdentifier* element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 {new TypeIdentifier(TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargingSessionSeq, element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000_ec))}; + if (!element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Sequence element TypeIdentifier inconsistent."); + return; + } + EquivalenceKind equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 = EK_COMPLETE; + if (TK_NONE == type_ids_ChargingSessionSeq.type_identifier2()._d()) + { + equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 = EK_BOTH; + } + CollectionElementFlag element_flags_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 = 0; + PlainCollectionHeader header_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 = TypeObjectUtils::build_plain_collection_header(equiv_kind_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000, element_flags_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000); + { + LBound bound = 1000; + PlainSequenceLElemDefn seq_ldefn = TypeObjectUtils::build_plain_sequence_l_elem_defn(header_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000, bound, + eprosima::fastcdr::external(element_identifier_anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000)); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_l_sequence_type_identifier(seq_ldefn, "anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000", type_ids_ChargingSessionSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_sequence_safe_edge_pilot_server_ChargingSession_1000 already registered in TypeObjectRegistry for a different type."); + } + } + } + bool common_ChargingSessionSeq_ec {false}; + CommonAliasBody common_ChargingSessionSeq {TypeObjectUtils::build_common_alias_body(related_flags_ChargingSessionSeq, + TypeObjectUtils::retrieve_complete_type_identifier(type_ids_ChargingSessionSeq, common_ChargingSessionSeq_ec))}; + if (!common_ChargingSessionSeq_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "safe_edge::pilot_server::ChargingSessionSeq related TypeIdentifier inconsistent."); + return; + } + eprosima::fastcdr::optional member_ann_builtin_ChargingSessionSeq; + ann_custom_ChargingSessionSeq.reset(); + CompleteAliasBody body_ChargingSessionSeq = TypeObjectUtils::build_complete_alias_body(common_ChargingSessionSeq, + member_ann_builtin_ChargingSessionSeq, ann_custom_ChargingSessionSeq); + CompleteAliasType alias_type_ChargingSessionSeq = TypeObjectUtils::build_complete_alias_type(alias_flags_ChargingSessionSeq, + header_ChargingSessionSeq, body_ChargingSessionSeq); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_alias_type_object(alias_type_ChargingSessionSeq, + type_name_ChargingSessionSeq.to_string(), type_ids_ChargingSessionSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ChargingSessionSeq already registered in TypeObjectRegistry for a different type."); + } + } +} + +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_TransitHealth_type_identifier( + TypeIdentifierPair& type_ids_TransitHealth) +{ + + ReturnCode_t return_code_TransitHealth {eprosima::fastdds::dds::RETCODE_OK}; + return_code_TransitHealth = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::TransitHealth", type_ids_TransitHealth); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_TransitHealth) + { + StructTypeFlag struct_flags_TransitHealth = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_TransitHealth = "safe_edge::pilot_server::TransitHealth"; + eprosima::fastcdr::optional type_ann_builtin_TransitHealth; + eprosima::fastcdr::optional ann_custom_TransitHealth; + CompleteTypeDetail detail_TransitHealth = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_TransitHealth, ann_custom_TransitHealth, type_name_TransitHealth.to_string()); + CompleteStructHeader header_TransitHealth; + header_TransitHealth = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_TransitHealth); + CompleteStructMemberSeq member_seq_TransitHealth; + { + TypeIdentifierPair type_ids_status; + ReturnCode_t return_code_status {eprosima::fastdds::dds::RETCODE_OK}; + return_code_status = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_status); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_status) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_status)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_status = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_status = 0x00000000; + bool common_status_ec {false}; + CommonStructMember common_status {TypeObjectUtils::build_common_struct_member(member_id_status, member_flags_status, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_status, common_status_ec))}; + if (!common_status_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure status member TypeIdentifier inconsistent."); + return; + } + MemberName name_status = "status"; + eprosima::fastcdr::optional member_ann_builtin_status; + ann_custom_TransitHealth.reset(); + CompleteMemberDetail detail_status = TypeObjectUtils::build_complete_member_detail(name_status, member_ann_builtin_status, ann_custom_TransitHealth); + CompleteStructMember member_status = TypeObjectUtils::build_complete_struct_member(common_status, detail_status); + TypeObjectUtils::add_complete_struct_member(member_seq_TransitHealth, member_status); + } + { + TypeIdentifierPair type_ids_last_fetch_ts; + ReturnCode_t return_code_last_fetch_ts {eprosima::fastdds::dds::RETCODE_OK}; + return_code_last_fetch_ts = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_double", type_ids_last_fetch_ts); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_last_fetch_ts) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "last_fetch_ts Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_last_fetch_ts = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_last_fetch_ts = 0x00000001; + bool common_last_fetch_ts_ec {false}; + CommonStructMember common_last_fetch_ts {TypeObjectUtils::build_common_struct_member(member_id_last_fetch_ts, member_flags_last_fetch_ts, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_last_fetch_ts, common_last_fetch_ts_ec))}; + if (!common_last_fetch_ts_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure last_fetch_ts member TypeIdentifier inconsistent."); + return; + } + MemberName name_last_fetch_ts = "last_fetch_ts"; + eprosima::fastcdr::optional member_ann_builtin_last_fetch_ts; + ann_custom_TransitHealth.reset(); + CompleteMemberDetail detail_last_fetch_ts = TypeObjectUtils::build_complete_member_detail(name_last_fetch_ts, member_ann_builtin_last_fetch_ts, ann_custom_TransitHealth); + CompleteStructMember member_last_fetch_ts = TypeObjectUtils::build_complete_struct_member(common_last_fetch_ts, detail_last_fetch_ts); + TypeObjectUtils::add_complete_struct_member(member_seq_TransitHealth, member_last_fetch_ts); + } + CompleteStructType struct_type_TransitHealth = TypeObjectUtils::build_complete_struct_type(struct_flags_TransitHealth, header_TransitHealth, member_seq_TransitHealth); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_TransitHealth, type_name_TransitHealth.to_string(), type_ids_TransitHealth)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::TransitHealth already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_RouteMetric_type_identifier( + TypeIdentifierPair& type_ids_RouteMetric) +{ + + ReturnCode_t return_code_RouteMetric {eprosima::fastdds::dds::RETCODE_OK}; + return_code_RouteMetric = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RouteMetric", type_ids_RouteMetric); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_RouteMetric) + { + StructTypeFlag struct_flags_RouteMetric = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_RouteMetric = "safe_edge::pilot_server::RouteMetric"; + eprosima::fastcdr::optional type_ann_builtin_RouteMetric; + eprosima::fastcdr::optional ann_custom_RouteMetric; + CompleteTypeDetail detail_RouteMetric = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_RouteMetric, ann_custom_RouteMetric, type_name_RouteMetric.to_string()); + CompleteStructHeader header_RouteMetric; + header_RouteMetric = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_RouteMetric); + CompleteStructMemberSeq member_seq_RouteMetric; + { + TypeIdentifierPair type_ids_route_id; + ReturnCode_t return_code_route_id {eprosima::fastdds::dds::RETCODE_OK}; + return_code_route_id = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_route_id); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_route_id) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_route_id)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_route_id = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_route_id = 0x00000000; + bool common_route_id_ec {false}; + CommonStructMember common_route_id {TypeObjectUtils::build_common_struct_member(member_id_route_id, member_flags_route_id, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_route_id, common_route_id_ec))}; + if (!common_route_id_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure route_id member TypeIdentifier inconsistent."); + return; + } + MemberName name_route_id = "route_id"; + eprosima::fastcdr::optional member_ann_builtin_route_id; + ann_custom_RouteMetric.reset(); + CompleteMemberDetail detail_route_id = TypeObjectUtils::build_complete_member_detail(name_route_id, member_ann_builtin_route_id, ann_custom_RouteMetric); + CompleteStructMember member_route_id = TypeObjectUtils::build_complete_struct_member(common_route_id, detail_route_id); + TypeObjectUtils::add_complete_struct_member(member_seq_RouteMetric, member_route_id); + } + { + TypeIdentifierPair type_ids_updates_count; + ReturnCode_t return_code_updates_count {eprosima::fastdds::dds::RETCODE_OK}; + return_code_updates_count = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_updates_count); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_updates_count) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "updates_count Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_updates_count = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_updates_count = 0x00000001; + bool common_updates_count_ec {false}; + CommonStructMember common_updates_count {TypeObjectUtils::build_common_struct_member(member_id_updates_count, member_flags_updates_count, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_updates_count, common_updates_count_ec))}; + if (!common_updates_count_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure updates_count member TypeIdentifier inconsistent."); + return; + } + MemberName name_updates_count = "updates_count"; + eprosima::fastcdr::optional member_ann_builtin_updates_count; + ann_custom_RouteMetric.reset(); + CompleteMemberDetail detail_updates_count = TypeObjectUtils::build_complete_member_detail(name_updates_count, member_ann_builtin_updates_count, ann_custom_RouteMetric); + CompleteStructMember member_updates_count = TypeObjectUtils::build_complete_struct_member(common_updates_count, detail_updates_count); + TypeObjectUtils::add_complete_struct_member(member_seq_RouteMetric, member_updates_count); + } + CompleteStructType struct_type_RouteMetric = TypeObjectUtils::build_complete_struct_type(struct_flags_RouteMetric, header_RouteMetric, member_seq_RouteMetric); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_RouteMetric, type_name_RouteMetric.to_string(), type_ids_RouteMetric)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::RouteMetric already registered in TypeObjectRegistry for a different type."); + } + } +}void register_RouteMetricSeq_type_identifier( + TypeIdentifierPair& type_ids_RouteMetricSeq) +{ + ReturnCode_t return_code_RouteMetricSeq {eprosima::fastdds::dds::RETCODE_OK}; + return_code_RouteMetricSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RouteMetricSeq", type_ids_RouteMetricSeq); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_RouteMetricSeq) + { + AliasTypeFlag alias_flags_RouteMetricSeq = 0; + QualifiedTypeName type_name_RouteMetricSeq = "safe_edge::pilot_server::RouteMetricSeq"; + eprosima::fastcdr::optional type_ann_builtin_RouteMetricSeq; + eprosima::fastcdr::optional ann_custom_RouteMetricSeq; + CompleteTypeDetail detail_RouteMetricSeq = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_RouteMetricSeq, ann_custom_RouteMetricSeq, type_name_RouteMetricSeq.to_string()); + CompleteAliasHeader header_RouteMetricSeq = TypeObjectUtils::build_complete_alias_header(detail_RouteMetricSeq); + AliasMemberFlag related_flags_RouteMetricSeq = 0; + return_code_RouteMetricSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_sequence_safe_edge_pilot_server_RouteMetric_512", type_ids_RouteMetricSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_RouteMetricSeq) + { + return_code_RouteMetricSeq = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RouteMetric", type_ids_RouteMetricSeq); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_RouteMetricSeq) + { + ::safe_edge::pilot_server::register_RouteMetric_type_identifier(type_ids_RouteMetricSeq); + } + bool element_identifier_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512_ec {false}; + TypeIdentifier* element_identifier_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 {new TypeIdentifier(TypeObjectUtils::retrieve_complete_type_identifier(type_ids_RouteMetricSeq, element_identifier_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512_ec))}; + if (!element_identifier_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Sequence element TypeIdentifier inconsistent."); + return; + } + EquivalenceKind equiv_kind_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 = EK_COMPLETE; + if (TK_NONE == type_ids_RouteMetricSeq.type_identifier2()._d()) + { + equiv_kind_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 = EK_BOTH; + } + CollectionElementFlag element_flags_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 = 0; + PlainCollectionHeader header_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 = TypeObjectUtils::build_plain_collection_header(equiv_kind_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512, element_flags_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512); + { + LBound bound = 512; + PlainSequenceLElemDefn seq_ldefn = TypeObjectUtils::build_plain_sequence_l_elem_defn(header_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512, bound, + eprosima::fastcdr::external(element_identifier_anonymous_sequence_safe_edge_pilot_server_RouteMetric_512)); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_l_sequence_type_identifier(seq_ldefn, "anonymous_sequence_safe_edge_pilot_server_RouteMetric_512", type_ids_RouteMetricSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_sequence_safe_edge_pilot_server_RouteMetric_512 already registered in TypeObjectRegistry for a different type."); + } + } + } + bool common_RouteMetricSeq_ec {false}; + CommonAliasBody common_RouteMetricSeq {TypeObjectUtils::build_common_alias_body(related_flags_RouteMetricSeq, + TypeObjectUtils::retrieve_complete_type_identifier(type_ids_RouteMetricSeq, common_RouteMetricSeq_ec))}; + if (!common_RouteMetricSeq_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "safe_edge::pilot_server::RouteMetricSeq related TypeIdentifier inconsistent."); + return; + } + eprosima::fastcdr::optional member_ann_builtin_RouteMetricSeq; + ann_custom_RouteMetricSeq.reset(); + CompleteAliasBody body_RouteMetricSeq = TypeObjectUtils::build_complete_alias_body(common_RouteMetricSeq, + member_ann_builtin_RouteMetricSeq, ann_custom_RouteMetricSeq); + CompleteAliasType alias_type_RouteMetricSeq = TypeObjectUtils::build_complete_alias_type(alias_flags_RouteMetricSeq, + header_RouteMetricSeq, body_RouteMetricSeq); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_alias_type_object(alias_type_RouteMetricSeq, + type_name_RouteMetricSeq.to_string(), type_ids_RouteMetricSeq)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::RouteMetricSeq already registered in TypeObjectRegistry for a different type."); + } + } +} + +// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_TransitMetrics_type_identifier( + TypeIdentifierPair& type_ids_TransitMetrics) +{ + + ReturnCode_t return_code_TransitMetrics {eprosima::fastdds::dds::RETCODE_OK}; + return_code_TransitMetrics = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::TransitMetrics", type_ids_TransitMetrics); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_TransitMetrics) + { + StructTypeFlag struct_flags_TransitMetrics = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_TransitMetrics = "safe_edge::pilot_server::TransitMetrics"; + eprosima::fastcdr::optional type_ann_builtin_TransitMetrics; + eprosima::fastcdr::optional ann_custom_TransitMetrics; + CompleteTypeDetail detail_TransitMetrics = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_TransitMetrics, ann_custom_TransitMetrics, type_name_TransitMetrics.to_string()); + CompleteStructHeader header_TransitMetrics; + header_TransitMetrics = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_TransitMetrics); + CompleteStructMemberSeq member_seq_TransitMetrics; + { + TypeIdentifierPair type_ids_by_route; + ReturnCode_t return_code_by_route {eprosima::fastdds::dds::RETCODE_OK}; + return_code_by_route = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RouteMetricSeq", type_ids_by_route); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_by_route) + { + ::safe_edge::pilot_server::register_RouteMetricSeq_type_identifier(type_ids_by_route); + } + StructMemberFlag member_flags_by_route = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_by_route = 0x00000000; + bool common_by_route_ec {false}; + CommonStructMember common_by_route {TypeObjectUtils::build_common_struct_member(member_id_by_route, member_flags_by_route, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_by_route, common_by_route_ec))}; + if (!common_by_route_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure by_route member TypeIdentifier inconsistent."); + return; + } + MemberName name_by_route = "by_route"; + eprosima::fastcdr::optional member_ann_builtin_by_route; + ann_custom_TransitMetrics.reset(); + CompleteMemberDetail detail_by_route = TypeObjectUtils::build_complete_member_detail(name_by_route, member_ann_builtin_by_route, ann_custom_TransitMetrics); + CompleteStructMember member_by_route = TypeObjectUtils::build_complete_struct_member(common_by_route, detail_by_route); + TypeObjectUtils::add_complete_struct_member(member_seq_TransitMetrics, member_by_route); + } + { + TypeIdentifierPair type_ids_vehicles_seen; + ReturnCode_t return_code_vehicles_seen {eprosima::fastdds::dds::RETCODE_OK}; + return_code_vehicles_seen = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "_int32_t", type_ids_vehicles_seen); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_vehicles_seen) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "vehicles_seen Structure member TypeIdentifier unknown to TypeObjectRegistry."); + return; + } + StructMemberFlag member_flags_vehicles_seen = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_vehicles_seen = 0x00000001; + bool common_vehicles_seen_ec {false}; + CommonStructMember common_vehicles_seen {TypeObjectUtils::build_common_struct_member(member_id_vehicles_seen, member_flags_vehicles_seen, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_vehicles_seen, common_vehicles_seen_ec))}; + if (!common_vehicles_seen_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure vehicles_seen member TypeIdentifier inconsistent."); + return; + } + MemberName name_vehicles_seen = "vehicles_seen"; + eprosima::fastcdr::optional member_ann_builtin_vehicles_seen; + ann_custom_TransitMetrics.reset(); + CompleteMemberDetail detail_vehicles_seen = TypeObjectUtils::build_complete_member_detail(name_vehicles_seen, member_ann_builtin_vehicles_seen, ann_custom_TransitMetrics); + CompleteStructMember member_vehicles_seen = TypeObjectUtils::build_complete_struct_member(common_vehicles_seen, detail_vehicles_seen); + TypeObjectUtils::add_complete_struct_member(member_seq_TransitMetrics, member_vehicles_seen); + } + CompleteStructType struct_type_TransitMetrics = TypeObjectUtils::build_complete_struct_type(struct_flags_TransitMetrics, header_TransitMetrics, member_seq_TransitMetrics); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_TransitMetrics, type_name_TransitMetrics.to_string(), type_ids_TransitMetrics)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::TransitMetrics already registered in TypeObjectRegistry for a different type."); + } + } +}void register_RequestedDataType_type_identifier( + TypeIdentifierPair& type_ids_RequestedDataType) +{ + ReturnCode_t return_code_RequestedDataType {eprosima::fastdds::dds::RETCODE_OK}; + return_code_RequestedDataType = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RequestedDataType", type_ids_RequestedDataType); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_RequestedDataType) + { + EnumTypeFlag enum_flags_RequestedDataType = 0; + BitBound bit_bound_RequestedDataType = 32; + CommonEnumeratedHeader common_RequestedDataType = TypeObjectUtils::build_common_enumerated_header(bit_bound_RequestedDataType); + QualifiedTypeName type_name_RequestedDataType = "safe_edge::pilot_server::RequestedDataType"; + eprosima::fastcdr::optional type_ann_builtin_RequestedDataType; + eprosima::fastcdr::optional ann_custom_RequestedDataType; + CompleteTypeDetail detail_RequestedDataType = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_RequestedDataType, ann_custom_RequestedDataType, type_name_RequestedDataType.to_string()); + CompleteEnumeratedHeader header_RequestedDataType = TypeObjectUtils::build_complete_enumerated_header(common_RequestedDataType, detail_RequestedDataType); + CompleteEnumeratedLiteralSeq literal_seq_RequestedDataType; + { + EnumeratedLiteralFlag flags_CHARGER_LOCATION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_CHARGER_LOCATION = TypeObjectUtils::build_common_enumerated_literal(0, flags_CHARGER_LOCATION); + eprosima::fastcdr::optional member_ann_builtin_CHARGER_LOCATION; + ann_custom_RequestedDataType.reset(); + MemberName name_CHARGER_LOCATION = "CHARGER_LOCATION"; + CompleteMemberDetail detail_CHARGER_LOCATION = TypeObjectUtils::build_complete_member_detail(name_CHARGER_LOCATION, member_ann_builtin_CHARGER_LOCATION, ann_custom_RequestedDataType); + CompleteEnumeratedLiteral literal_CHARGER_LOCATION = TypeObjectUtils::build_complete_enumerated_literal(common_CHARGER_LOCATION, detail_CHARGER_LOCATION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_RequestedDataType, literal_CHARGER_LOCATION); + } + { + EnumeratedLiteralFlag flags_CHARGER_TYPE = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_CHARGER_TYPE = TypeObjectUtils::build_common_enumerated_literal(1, flags_CHARGER_TYPE); + eprosima::fastcdr::optional member_ann_builtin_CHARGER_TYPE; + ann_custom_RequestedDataType.reset(); + MemberName name_CHARGER_TYPE = "CHARGER_TYPE"; + CompleteMemberDetail detail_CHARGER_TYPE = TypeObjectUtils::build_complete_member_detail(name_CHARGER_TYPE, member_ann_builtin_CHARGER_TYPE, ann_custom_RequestedDataType); + CompleteEnumeratedLiteral literal_CHARGER_TYPE = TypeObjectUtils::build_complete_enumerated_literal(common_CHARGER_TYPE, detail_CHARGER_TYPE); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_RequestedDataType, literal_CHARGER_TYPE); + } + { + EnumeratedLiteralFlag flags_CHARGING_SESSION = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_CHARGING_SESSION = TypeObjectUtils::build_common_enumerated_literal(2, flags_CHARGING_SESSION); + eprosima::fastcdr::optional member_ann_builtin_CHARGING_SESSION; + ann_custom_RequestedDataType.reset(); + MemberName name_CHARGING_SESSION = "CHARGING_SESSION"; + CompleteMemberDetail detail_CHARGING_SESSION = TypeObjectUtils::build_complete_member_detail(name_CHARGING_SESSION, member_ann_builtin_CHARGING_SESSION, ann_custom_RequestedDataType); + CompleteEnumeratedLiteral literal_CHARGING_SESSION = TypeObjectUtils::build_complete_enumerated_literal(common_CHARGING_SESSION, detail_CHARGING_SESSION); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_RequestedDataType, literal_CHARGING_SESSION); + } + { + EnumeratedLiteralFlag flags_TRANSIT_HEALTH = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_TRANSIT_HEALTH = TypeObjectUtils::build_common_enumerated_literal(3, flags_TRANSIT_HEALTH); + eprosima::fastcdr::optional member_ann_builtin_TRANSIT_HEALTH; + ann_custom_RequestedDataType.reset(); + MemberName name_TRANSIT_HEALTH = "TRANSIT_HEALTH"; + CompleteMemberDetail detail_TRANSIT_HEALTH = TypeObjectUtils::build_complete_member_detail(name_TRANSIT_HEALTH, member_ann_builtin_TRANSIT_HEALTH, ann_custom_RequestedDataType); + CompleteEnumeratedLiteral literal_TRANSIT_HEALTH = TypeObjectUtils::build_complete_enumerated_literal(common_TRANSIT_HEALTH, detail_TRANSIT_HEALTH); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_RequestedDataType, literal_TRANSIT_HEALTH); + } + { + EnumeratedLiteralFlag flags_TRANSIT_METRICS = TypeObjectUtils::build_enumerated_literal_flag(false); + CommonEnumeratedLiteral common_TRANSIT_METRICS = TypeObjectUtils::build_common_enumerated_literal(4, flags_TRANSIT_METRICS); + eprosima::fastcdr::optional member_ann_builtin_TRANSIT_METRICS; + ann_custom_RequestedDataType.reset(); + MemberName name_TRANSIT_METRICS = "TRANSIT_METRICS"; + CompleteMemberDetail detail_TRANSIT_METRICS = TypeObjectUtils::build_complete_member_detail(name_TRANSIT_METRICS, member_ann_builtin_TRANSIT_METRICS, ann_custom_RequestedDataType); + CompleteEnumeratedLiteral literal_TRANSIT_METRICS = TypeObjectUtils::build_complete_enumerated_literal(common_TRANSIT_METRICS, detail_TRANSIT_METRICS); + TypeObjectUtils::add_complete_enumerated_literal(literal_seq_RequestedDataType, literal_TRANSIT_METRICS); + } + CompleteEnumeratedType enumerated_type_RequestedDataType = TypeObjectUtils::build_complete_enumerated_type(enum_flags_RequestedDataType, header_RequestedDataType, + literal_seq_RequestedDataType); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_enumerated_type_object(enumerated_type_RequestedDataType, type_name_RequestedDataType.to_string(), type_ids_RequestedDataType)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::RequestedDataType already registered in TypeObjectRegistry for a different type."); + } + } +}// TypeIdentifier is returned by reference: dependent structures/unions are registered in this same method +void register_ServerQuery_type_identifier( + TypeIdentifierPair& type_ids_ServerQuery) +{ + + ReturnCode_t return_code_ServerQuery {eprosima::fastdds::dds::RETCODE_OK}; + return_code_ServerQuery = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::ServerQuery", type_ids_ServerQuery); + if (eprosima::fastdds::dds::RETCODE_OK != return_code_ServerQuery) + { + StructTypeFlag struct_flags_ServerQuery = TypeObjectUtils::build_struct_type_flag(eprosima::fastdds::dds::xtypes::ExtensibilityKind::APPENDABLE, + false, false); + QualifiedTypeName type_name_ServerQuery = "safe_edge::pilot_server::ServerQuery"; + eprosima::fastcdr::optional type_ann_builtin_ServerQuery; + eprosima::fastcdr::optional ann_custom_ServerQuery; + CompleteTypeDetail detail_ServerQuery = TypeObjectUtils::build_complete_type_detail(type_ann_builtin_ServerQuery, ann_custom_ServerQuery, type_name_ServerQuery.to_string()); + CompleteStructHeader header_ServerQuery; + header_ServerQuery = TypeObjectUtils::build_complete_struct_header(TypeIdentifier(), detail_ServerQuery); + CompleteStructMemberSeq member_seq_ServerQuery; + { + TypeIdentifierPair type_ids_requested_by; + ReturnCode_t return_code_requested_by {eprosima::fastdds::dds::RETCODE_OK}; + return_code_requested_by = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "anonymous_string_unbounded", type_ids_requested_by); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_requested_by) + { + { + SBound bound = 0; + StringSTypeDefn string_sdefn = TypeObjectUtils::build_string_s_type_defn(bound); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_s_string_type_identifier(string_sdefn, + "anonymous_string_unbounded", type_ids_requested_by)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "anonymous_string_unbounded already registered in TypeObjectRegistry for a different type."); + } + } + } + StructMemberFlag member_flags_requested_by = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_requested_by = 0x00000000; + bool common_requested_by_ec {false}; + CommonStructMember common_requested_by {TypeObjectUtils::build_common_struct_member(member_id_requested_by, member_flags_requested_by, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_requested_by, common_requested_by_ec))}; + if (!common_requested_by_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure requested_by member TypeIdentifier inconsistent."); + return; + } + MemberName name_requested_by = "requested_by"; + eprosima::fastcdr::optional member_ann_builtin_requested_by; + ann_custom_ServerQuery.reset(); + CompleteMemberDetail detail_requested_by = TypeObjectUtils::build_complete_member_detail(name_requested_by, member_ann_builtin_requested_by, ann_custom_ServerQuery); + CompleteStructMember member_requested_by = TypeObjectUtils::build_complete_struct_member(common_requested_by, detail_requested_by); + TypeObjectUtils::add_complete_struct_member(member_seq_ServerQuery, member_requested_by); + } + { + TypeIdentifierPair type_ids_requested_data_type; + ReturnCode_t return_code_requested_data_type {eprosima::fastdds::dds::RETCODE_OK}; + return_code_requested_data_type = + eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->type_object_registry().get_type_identifiers( + "safe_edge::pilot_server::RequestedDataType", type_ids_requested_data_type); + + if (eprosima::fastdds::dds::RETCODE_OK != return_code_requested_data_type) + { + ::safe_edge::pilot_server::register_RequestedDataType_type_identifier(type_ids_requested_data_type); + } + StructMemberFlag member_flags_requested_data_type = TypeObjectUtils::build_struct_member_flag(eprosima::fastdds::dds::xtypes::TryConstructFailAction::DISCARD, + false, false, false, false); + MemberId member_id_requested_data_type = 0x00000001; + bool common_requested_data_type_ec {false}; + CommonStructMember common_requested_data_type {TypeObjectUtils::build_common_struct_member(member_id_requested_data_type, member_flags_requested_data_type, TypeObjectUtils::retrieve_complete_type_identifier(type_ids_requested_data_type, common_requested_data_type_ec))}; + if (!common_requested_data_type_ec) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, "Structure requested_data_type member TypeIdentifier inconsistent."); + return; + } + MemberName name_requested_data_type = "requested_data_type"; + eprosima::fastcdr::optional member_ann_builtin_requested_data_type; + ann_custom_ServerQuery.reset(); + CompleteMemberDetail detail_requested_data_type = TypeObjectUtils::build_complete_member_detail(name_requested_data_type, member_ann_builtin_requested_data_type, ann_custom_ServerQuery); + CompleteStructMember member_requested_data_type = TypeObjectUtils::build_complete_struct_member(common_requested_data_type, detail_requested_data_type); + TypeObjectUtils::add_complete_struct_member(member_seq_ServerQuery, member_requested_data_type); + } + CompleteStructType struct_type_ServerQuery = TypeObjectUtils::build_complete_struct_type(struct_flags_ServerQuery, header_ServerQuery, member_seq_ServerQuery); + if (eprosima::fastdds::dds::RETCODE_BAD_PARAMETER == + TypeObjectUtils::build_and_register_struct_type_object(struct_type_ServerQuery, type_name_ServerQuery.to_string(), type_ids_ServerQuery)) + { + EPROSIMA_LOG_ERROR(XTYPES_TYPE_REPRESENTATION, + "safe_edge::pilot_server::ServerQuery already registered in TypeObjectRegistry for a different type."); + } + } +} +} // namespace pilot_server + +} // namespace safe_edge + diff --git a/fast_dds/idl/pilot_serverTypeObjectSupport.hpp b/fast_dds/idl/pilot_serverTypeObjectSupport.hpp new file mode 100644 index 0000000..8c106b9 --- /dev/null +++ b/fast_dds/idl/pilot_serverTypeObjectSupport.hpp @@ -0,0 +1,214 @@ +// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * @file pilot_serverTypeObjectSupport.hpp + * Header file containing the API required to register the TypeObject representation of the described types in the IDL file + * + * This file was generated by the tool fastddsgen (version: 4.3.0). + */ + +#ifndef FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_TYPE_OBJECT_SUPPORT_HPP +#define FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_TYPE_OBJECT_SUPPORT_HPP + +#include + +#include "commonTypeObjectSupport.hpp" + +#if defined(_WIN32) +#if defined(EPROSIMA_USER_DLL_EXPORT) +#define eProsima_user_DllExport __declspec( dllexport ) +#else +#define eProsima_user_DllExport +#endif // EPROSIMA_USER_DLL_EXPORT +#else +#define eProsima_user_DllExport +#endif // _WIN32 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +namespace safe_edge { +namespace pilot_server { +/** + * @brief Register ChargerLocation related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargerLocation_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register ChargerLocationSeq related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargerLocationSeq_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + + + +/** + * @brief Register ChargerType related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargerType_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register ChargerTypeSeq related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargerTypeSeq_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + + + +/** + * @brief Register ChargingSession related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargingSession_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register ChargingSessionSeq related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ChargingSessionSeq_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + + + +/** + * @brief Register TransitHealth related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_TransitHealth_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register RouteMetric related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_RouteMetric_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register RouteMetricSeq related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_RouteMetricSeq_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + + + +/** + * @brief Register TransitMetrics related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_TransitMetrics_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register RequestedDataType related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_RequestedDataType_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +/** + * @brief Register ServerQuery related TypeIdentifier. + * Fully-descriptive TypeIdentifiers are directly registered. + * Hash TypeIdentifiers require to fill the TypeObject information and hash it, consequently, the TypeObject is + * indirectly registered as well. + * + * @param[out] type_ids TypeIdentifier of the registered type. + * The returned TypeIdentifier corresponds to the complete TypeIdentifier in case of hashed TypeIdentifiers. + * Invalid TypeIdentifier is returned in case of error. + */ +eProsima_user_DllExport void register_ServerQuery_type_identifier( + eprosima::fastdds::dds::xtypes::TypeIdentifierPair& type_ids); + +} // namespace pilot_server + +} // namespace safe_edge + + +#endif // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC + +#endif // FAST_DDS_GENERATED__SAFE_EDGE_PILOT_SERVER_PILOT_SERVER_TYPE_OBJECT_SUPPORT_HPP diff --git a/fast_dds/server/CMakeLists.txt b/fast_dds/server/CMakeLists.txt new file mode 100644 index 0000000..8b7fef1 --- /dev/null +++ b/fast_dds/server/CMakeLists.txt @@ -0,0 +1,147 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_server LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(fastdds REQUIRED) +find_package(CURL REQUIRED) + + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_SERVER_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") +set(SAFE_EDGE_SERVER_COMMON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../common_server") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_SERVER_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_server_common + STATIC + ${SAFE_EDGE_IDL_INCLUDE_DIR}/commonPubSubTypes.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/commonTypeObjectSupport.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/pilot_serverPubSubTypes.cxx + ${SAFE_EDGE_IDL_INCLUDE_DIR}/pilot_serverTypeObjectSupport.cxx + ${SAFE_EDGE_SERVER_COMMON_DIR}/src/PilotServerClient.cpp + ${SAFE_EDGE_SERVER_COMMON_DIR}/src/PilotServerPayloadParser.cpp + src/common/PilotServerPublishHelper.cpp + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp +) +safe_edge_apply_common_build_settings(safe_edge_server_common) +target_include_directories( + safe_edge_server_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + "${SAFE_EDGE_SERVER_COMMON_DIR}/include" +) +target_link_libraries( + safe_edge_server_common + PUBLIC + fastdds + CURL::libcurl +) + +add_executable( + safe_edge_server + src/apps/server_main.cpp + src/nodes/ServerNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_server) +target_include_directories( + safe_edge_server + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_server + PRIVATE + safe_edge_server_common + fastdds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_server + RUNTIME DESTINATION bin +) + +option(SAFE_EDGE_BUILD_TESTS "Build integration tests" ON) + +if(SAFE_EDGE_BUILD_TESTS) + # Try system GTest first; fall back to fetching from source. + find_package(GTest QUIET) + + if(NOT GTest_FOUND) + message(STATUS "GTest not found — fetching from source") + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz + URL_HASH SHA256=8ad598c73ad796e0d8280b082cebd82a630d73e73cd3c70057938a6501bba5d7 + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + + if(NOT TARGET GTest::gtest) + add_library(GTest::gtest ALIAS gtest) + endif() + endif() + + add_executable( + test_server_integration + test/test_server_integration.cpp + ) + + # Tests need exceptions and RTTI (GTest requirement). + # Do not reuse safe_edge_apply_common_build_settings which disables them. + target_include_directories( + test_server_integration + PRIVATE + "${SAFE_EDGE_SERVER_INCLUDE_DIR}" + "${SAFE_EDGE_IDL_INCLUDE_DIR}" + ) + target_compile_options( + test_server_integration + PRIVATE + -Wall -Wextra -Wpedantic + ) + target_link_libraries( + test_server_integration + PRIVATE + safe_edge_server_common + fastdds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} + GTest::gtest + ) + + enable_testing() + add_test(NAME server_integration COMMAND test_server_integration) + + install( + TARGETS test_server_integration + RUNTIME DESTINATION bin + ) +endif() diff --git a/fast_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp b/fast_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp new file mode 100644 index 0000000..a653bca --- /dev/null +++ b/fast_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp @@ -0,0 +1,25 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP +#define SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP + +#include + +#include + +#include + +namespace safe_edge { +namespace server { +namespace common { + +struct PilotServerPublishHelper +{ + static void publish_charger_locations( + eprosima::fastdds::dds::DataWriter& writer, + const std::vector& parsed); +}; + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP diff --git a/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp b/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp new file mode 100644 index 0000000..c7577c4 --- /dev/null +++ b/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp @@ -0,0 +1,32 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP + +#include +#include + +namespace safe_edge { +namespace server { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + + std::string pilot_server_base_url; + std::string pilot_server_api_key; + std::string charger_locations_endpoint; + std::string charger_types_endpoint; + std::string charging_sessions_endpoint; + std::string transit_health_endpoint; + std::string transit_metrics_endpoint; +}; + +RuntimeConfig make_server_runtime_config(); + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP diff --git a/fast_dds/server/include/safe_edge/server/common/TopicNames.hpp b/fast_dds/server/include/safe_edge/server/common/TopicNames.hpp new file mode 100644 index 0000000..6838cf5 --- /dev/null +++ b/fast_dds/server/include/safe_edge/server/common/TopicNames.hpp @@ -0,0 +1,18 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace server { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept; +const char* server_query() noexcept; +const char* service_heartbeat() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP diff --git a/fast_dds/server/include/safe_edge/server/nodes/ServerNode.hpp b/fast_dds/server/include/safe_edge/server/nodes/ServerNode.hpp new file mode 100644 index 0000000..8a0810f --- /dev/null +++ b/fast_dds/server/include/safe_edge/server/nodes/ServerNode.hpp @@ -0,0 +1,130 @@ +#ifndef SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP +#define SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace safe_edge { +namespace server { +namespace nodes { + +class ServerNode +{ +public: + + explicit ServerNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::fastdds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(ServerNode& owner); + + void on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::fastdds::dds::DataWriter* writer, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + ServerNode& owner_; + }; + + class ServerQueryListener : + public eprosima::fastdds::dds::DataReaderListener + { + public: + + explicit ServerQueryListener(ServerNode& owner); + + void on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept override; + + private: + + ServerNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + + void on_server_query_received(const safe_edge::pilot_server::ServerQuery& query); + void publish_heartbeat(); + + void request_pilot_server_data(safe_edge::pilot_server::RequestedDataType resource) noexcept; + void periodic_refresh_all_resources() noexcept; + const char* resolve_endpoint(safe_edge::pilot_server::RequestedDataType resource) const noexcept; + void log_uptime() const noexcept; + + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + + common::RuntimeConfig runtime_config_; + common::PilotServerClient pilot_client_; + + ParticipantListener participant_listener_; + ServerQueryListener server_query_listener_; + + std::chrono::steady_clock::time_point next_heartbeat_fire_; + std::chrono::steady_clock::time_point next_refresh_fire_; + std::chrono::steady_clock::time_point next_uptime_fire_; + std::chrono::steady_clock::time_point start_time_; + + eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; + eprosima::fastdds::dds::Publisher* publisher_ = nullptr; + eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; + + eprosima::fastdds::dds::Topic* charger_locations_topic_ = nullptr; + eprosima::fastdds::dds::Topic* server_query_topic_ = nullptr; + eprosima::fastdds::dds::Topic* service_heartbeat_topic_ = nullptr; + + std::string charger_locations_topic_name_; + std::string server_query_topic_name_; + std::string service_heartbeat_topic_name_; + + eprosima::fastdds::dds::DataWriter* charger_locations_datawriter_ = nullptr; + eprosima::fastdds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + eprosima::fastdds::dds::DataReader* server_query_datareader_ = nullptr; + + eprosima::fastdds::dds::TypeSupport charger_location_type_support_; + eprosima::fastdds::dds::TypeSupport server_query_type_support_; + eprosima::fastdds::dds::TypeSupport service_heartbeat_type_support_; +}; + +} // namespace nodes +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP diff --git a/fast_dds/server/src/apps/server_main.cpp b/fast_dds/server/src/apps/server_main.cpp new file mode 100644 index 0000000..b8fa6db --- /dev/null +++ b/fast_dds/server/src/apps/server_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::server::common::RuntimeConfig config = + safe_edge::server::common::make_server_runtime_config(); + + safe_edge::server::nodes::ServerNode node(config); + return node.run(); +} diff --git a/fast_dds/server/src/common/PilotServerPublishHelper.cpp b/fast_dds/server/src/common/PilotServerPublishHelper.cpp new file mode 100644 index 0000000..b35c207 --- /dev/null +++ b/fast_dds/server/src/common/PilotServerPublishHelper.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include + +#include + +#include + +namespace safe_edge { +namespace server { +namespace common { + +void PilotServerPublishHelper::publish_charger_locations( + eprosima::fastdds::dds::DataWriter& writer, + const std::vector& parsed) +{ + for (const auto& p : parsed) + { + safe_edge::pilot_server::ChargerLocation loc; + loc.id(p.id); + loc.name(p.name); + safe_edge::common::GeoPoint pos; + pos.latitude(p.latitude); + pos.longitude(p.longitude); + loc.position(pos); + + if (eprosima::fastdds::dds::RETCODE_OK != + writer.write(&loc, eprosima::fastdds::dds::HANDLE_NIL)) + { + std::cerr << "[publish_helper] Failed to write ChargerLocation id=" + << p.id << std::endl; + } + } +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/fast_dds/server/src/common/RuntimeConfig.cpp b/fast_dds/server/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..0034c96 --- /dev/null +++ b/fast_dds/server/src/common/RuntimeConfig.cpp @@ -0,0 +1,27 @@ +#include + +namespace safe_edge { +namespace server { +namespace common { + +RuntimeConfig make_server_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeServerParticipant"; + config.domain_id = 0U; + config.participant_port = 8020U; + + config.pilot_server_base_url = "https://pilot2.dumitru-alexandru.work"; + // api_key moved to /etc/safe-edge/server.ini — do not hardcode here + config.charger_locations_endpoint = "/api/chargers/locations"; + config.charger_types_endpoint = "/api/chargers/types"; + config.charging_sessions_endpoint = "/api/chargers/sessions"; + config.transit_health_endpoint = "/api/transit/health"; + config.transit_metrics_endpoint = "/api/transit/metrics"; + + return config; +} + +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/fast_dds/server/src/common/TopicNames.cpp b/fast_dds/server/src/common/TopicNames.cpp new file mode 100644 index 0000000..2c20bea --- /dev/null +++ b/fast_dds/server/src/common/TopicNames.cpp @@ -0,0 +1,26 @@ +#include + +namespace safe_edge { +namespace server { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept +{ + return "safe_edge.pilot_server.charger_locations"; +} + +const char* server_query() noexcept +{ + return "safe_edge.pilot_server.server_query"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +} // namespace topic_names +} // namespace common +} // namespace server +} // namespace safe_edge diff --git a/fast_dds/server/src/nodes/ServerNode.cpp b/fast_dds/server/src/nodes/ServerNode.cpp new file mode 100644 index 0000000..81c4ec7 --- /dev/null +++ b/fast_dds/server/src/nodes/ServerNode.cpp @@ -0,0 +1,483 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace safe_edge { +namespace server { +namespace nodes { + +namespace { + +static std::vector build_charger_locations() +{ + std::vector locations; + + safe_edge::pilot_server::ChargerLocation loc1; + loc1.id(1); + loc1.name("Madrid North Hub"); + safe_edge::common::GeoPoint pos1; + pos1.latitude(40.4637F); + pos1.longitude(-3.7492F); + loc1.position(pos1); + locations.push_back(loc1); + + safe_edge::pilot_server::ChargerLocation loc2; + loc2.id(2); + loc2.name("Barcelona Port Station"); + safe_edge::common::GeoPoint pos2; + pos2.latitude(41.3851F); + pos2.longitude(2.1734F); + loc2.position(pos2); + locations.push_back(loc2); + + safe_edge::pilot_server::ChargerLocation loc3; + loc3.id(3); + loc3.name("Valencia Depot"); + safe_edge::common::GeoPoint pos3; + pos3.latitude(39.4699F); + pos3.longitude(-0.3763F); + loc3.position(pos3); + locations.push_back(loc3); + + return locations; +} + +} // namespace + +ServerNode::ParticipantListener::ParticipantListener( + ServerNode& owner) + : owner_(owner) +{ +} + +void ServerNode::ParticipantListener::on_subscription_matched( + eprosima::fastdds::dds::DataReader* reader, + const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match( + reader->get_topicdescription()->get_name().c_str(), info.total_count); +} + +void ServerNode::ParticipantListener::on_publication_matched( + eprosima::fastdds::dds::DataWriter* writer, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match( + writer->get_topic()->get_name().c_str(), info.total_count); + + if (writer == owner_.charger_locations_datawriter_ && info.total_count_change > 0) + { + owner_.request_pilot_server_data( + safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION); + } +} + +ServerNode::ServerQueryListener::ServerQueryListener( + ServerNode& owner) + : owner_(owner) +{ +} + +void ServerNode::ServerQueryListener::on_data_available( + eprosima::fastdds::dds::DataReader* reader) noexcept +{ + safe_edge::pilot_server::ServerQuery sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + while (reader->take_next_sample(&sample, &info) == eprosima::fastdds::dds::RETCODE_OK) + { + if (info.valid_data) + { + owner_.on_server_query_received(sample); + } + } +} + +ServerNode::ServerNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , pilot_client_(runtime_config.pilot_server_base_url, "/etc/safe-edge/server.ini") + , participant_listener_(*this) + , server_query_listener_(*this) + , next_heartbeat_fire_(std::chrono::steady_clock::now() + std::chrono::seconds(5)) + , next_refresh_fire_(std::chrono::steady_clock::now() + std::chrono::seconds(30)) + , next_uptime_fire_(std::chrono::steady_clock::now() + std::chrono::seconds(300)) + , start_time_(std::chrono::steady_clock::now()) + , charger_location_type_support_(new safe_edge::pilot_server::ChargerLocationPubSubType()) + , server_query_type_support_(new safe_edge::pilot_server::ServerQueryPubSubType()) + , service_heartbeat_type_support_(new safe_edge::common::ServiceHeartbeatPubSubType()) +{ +} + +int ServerNode::run() +{ + if (!initialize()) + { + return 1; + } + + std::cout << "[server] [START] Running with participant port " + << runtime_config_.participant_port << std::endl; + std::cout << "[server] PilotServer base_url=" << runtime_config_.pilot_server_base_url + << " api_key=***" << std::endl; + + while (true) + { + const auto next = std::min({next_heartbeat_fire_, next_refresh_fire_, next_uptime_fire_}); + std::this_thread::sleep_until(next); + + const auto now = std::chrono::steady_clock::now(); + + if (now >= next_heartbeat_fire_) + { + publish_heartbeat(); + next_heartbeat_fire_ += std::chrono::seconds(5); + } + if (now >= next_refresh_fire_) + { + periodic_refresh_all_resources(); + next_refresh_fire_ += std::chrono::seconds(30); + } + if (now >= next_uptime_fire_) + { + log_uptime(); + next_uptime_fire_ += std::chrono::seconds(300); + } + } + + return 0; +} + +bool ServerNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities(); +} + +bool ServerNode::create_participant() +{ + eprosima::fastdds::dds::DomainParticipantQos participant_qos{}; + participant_qos.name(runtime_config_.participant_name); + + eprosima::fastdds::rtps::Locator_t announced_locator; + eprosima::fastdds::rtps::IPLocator::setIPv4(announced_locator, "127.0.0.1"); + announced_locator.port = runtime_config_.participant_port; + participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(announced_locator); + + static constexpr uint16_t peer_ports[] = { 8011U, 8030U }; + for (uint16_t port : peer_ports) + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, "127.0.0.1"); + peer.port = port; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } + + eprosima::fastdds::dds::StatusMask participant_mask = + eprosima::fastdds::dds::StatusMask::publication_matched(); + participant_mask |= eprosima::fastdds::dds::StatusMask::subscription_matched(); + + participant_ = eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->create_participant(runtime_config_.domain_id, participant_qos, + &participant_listener_, participant_mask); + + if (nullptr == participant_) + { + std::cerr << "[server] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool ServerNode::register_types() +{ + if (eprosima::fastdds::dds::RETCODE_OK != charger_location_type_support_.register_type(participant_)) + { + std::cerr << "[server] Failed to register type: ChargerLocation" << std::endl; + return false; + } + if (eprosima::fastdds::dds::RETCODE_OK != server_query_type_support_.register_type(participant_)) + { + std::cerr << "[server] Failed to register type: ServerQuery" << std::endl; + return false; + } + if (eprosima::fastdds::dds::RETCODE_OK != service_heartbeat_type_support_.register_type(participant_)) + { + std::cerr << "[server] Failed to register type: ServiceHeartbeat" << std::endl; + return false; + } + return true; +} + +bool ServerNode::create_topics() +{ + charger_locations_topic_name_ = common::topic_names::charger_locations(); + charger_locations_topic_ = participant_->create_topic( + charger_locations_topic_name_, + charger_location_type_support_.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + if (nullptr == charger_locations_topic_) + { + std::cerr << "[server] Failed to create topic: charger_locations" << std::endl; + return false; + } + + server_query_topic_name_ = common::topic_names::server_query(); + server_query_topic_ = participant_->create_topic( + server_query_topic_name_, + server_query_type_support_.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + if (nullptr == server_query_topic_) + { + std::cerr << "[server] Failed to create topic: server_query" << std::endl; + return false; + } + + service_heartbeat_topic_name_ = common::topic_names::service_heartbeat(); + service_heartbeat_topic_ = participant_->create_topic( + service_heartbeat_topic_name_, + service_heartbeat_type_support_.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + if (nullptr == service_heartbeat_topic_) + { + std::cerr << "[server] Failed to create topic: service_heartbeat" << std::endl; + return false; + } + + return true; +} + +bool ServerNode::create_endpoints() +{ + publisher_ = participant_->create_publisher( + eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + subscriber_ = participant_->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT, + nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[server] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::fastdds::dds::DataWriterQos writer_qos = eprosima::fastdds::dds::DATAWRITER_QOS_DEFAULT; + writer_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + charger_locations_datawriter_ = publisher_->create_datawriter( + charger_locations_topic_, writer_qos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + service_heartbeat_datawriter_ = publisher_->create_datawriter( + service_heartbeat_topic_, writer_qos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + + eprosima::fastdds::dds::DataReaderQos reader_qos = eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + reader_qos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + server_query_datareader_ = subscriber_->create_datareader( + server_query_topic_, reader_qos, + &server_query_listener_, + eprosima::fastdds::dds::StatusMask::data_available()); + + if (nullptr == charger_locations_datawriter_ || + nullptr == service_heartbeat_datawriter_ || + nullptr == server_query_datareader_) + { + std::cerr << "[server] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool ServerNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == publisher_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == charger_locations_datawriter_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == subscriber_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == server_query_datareader_->enable()); + enabled = enabled && (eprosima::fastdds::dds::RETCODE_OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[server] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +void ServerNode::on_server_query_received( + const safe_edge::pilot_server::ServerQuery& query) +{ + std::cout << "[server] DDS query from=" << query.requested_by() + << " type=" << static_cast(query.requested_data_type()) << std::endl; + request_pilot_server_data(query.requested_data_type()); +} + +void ServerNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[server] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void ServerNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[server] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +const char* ServerNode::resolve_endpoint( + safe_edge::pilot_server::RequestedDataType resource) const noexcept +{ + switch (resource) + { + case safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION: + return runtime_config_.charger_locations_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE: + return runtime_config_.charger_types_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION: + return runtime_config_.charging_sessions_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH: + return runtime_config_.transit_health_endpoint.c_str(); + case safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS: + return runtime_config_.transit_metrics_endpoint.c_str(); + default: + return nullptr; + } +} + +void ServerNode::request_pilot_server_data( + safe_edge::pilot_server::RequestedDataType resource) noexcept +{ + const char* endpoint = resolve_endpoint(resource); + if (nullptr == endpoint) + { + std::cerr << "[server] Unsupported resource=" << static_cast(resource) << std::endl; + return; + } + + std::cout << "[server] Request resource=" << static_cast(resource) + << " endpoint=" << endpoint << std::endl; + + const std::string body = pilot_client_.fetch(endpoint); + if (body.empty()) + { + std::cerr << "[server] HTTP fetch failed endpoint=" << endpoint << std::endl; + } + else if (resource == safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION + && nullptr != charger_locations_datawriter_) + { + const auto parsed = + safe_edge::server::common::PilotServerPayloadParser::parse_charger_locations(body); + safe_edge::server::common::PilotServerPublishHelper::publish_charger_locations( + *charger_locations_datawriter_, parsed); + std::cout << "[server] Published charger_locations count=" << parsed.size() + << " (from HTTP)" << std::endl; + } + else if (!body.empty()) + { + // TODO: add writers and parsers for other resource types + std::cout << "[server] HTTP response received resource=" + << static_cast(resource) + << " bytes=" << body.size() + << " (parse not yet implemented)" << std::endl; + } + + // Stub: preserve charger_locations publication until HTTP response is confirmed correct + if (resource == safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION + && nullptr != charger_locations_datawriter_) + { + const auto locations = build_charger_locations(); + for (const auto& loc : locations) + { + charger_locations_datawriter_->write( + const_cast(&loc), + eprosima::fastdds::dds::HANDLE_NIL); + } + std::cout << "[server] Published charger_locations count=" << locations.size() + << " (stub)" << std::endl; + } +} + +void ServerNode::periodic_refresh_all_resources() noexcept +{ + static constexpr safe_edge::pilot_server::RequestedDataType all_resources[] = { + safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION, + safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE, + safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION, + safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH, + safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS, + }; + + for (const auto resource : all_resources) + { + std::cout << "[server] Periodic refresh resource=" << static_cast(resource) << std::endl; + request_pilot_server_data(resource); + } +} + +void ServerNode::log_uptime() const noexcept +{ + const auto elapsed = std::chrono::steady_clock::now() - start_time_; + const auto seconds = std::chrono::duration_cast(elapsed).count(); + std::cout << "[server] Uptime=" << seconds << "s" << std::endl; +} + +void ServerNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat{}; + safe_edge::common::Header hdr; + hdr.source("server"); + heartbeat.header_st(hdr); + heartbeat.service_name("server"); + heartbeat.status(safe_edge::common::HealthStatus::HEALTH_OK); + heartbeat.detail("running"); + + if (eprosima::fastdds::dds::RETCODE_OK != + service_heartbeat_datawriter_->write(&heartbeat, eprosima::fastdds::dds::HANDLE_NIL)) + { + std::cerr << "[server] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[server] Published ServiceHeartbeat" << std::endl; +} + +} // namespace nodes +} // namespace server +} // namespace safe_edge diff --git a/fast_dds/server/test/test_server_integration.cpp b/fast_dds/server/test/test_server_integration.cpp new file mode 100644 index 0000000..efd54d8 --- /dev/null +++ b/fast_dds/server/test/test_server_integration.cpp @@ -0,0 +1,315 @@ +// test_server_integration.cpp +// +// Single binary integration test for safe_edge_server (Fast DDS variant). +// Starts the server as a subprocess, runs all test suites, stops the server. +// +// Usage: +// ./test_server_integration +// ./test_server_integration --gtest_output=xml:results.xml (CI / JUnit) +// +// Environment variables: +// SAFE_EDGE_FAST_SERVER_BIN path to safe_edge_server binary (default: safe_edge_server) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Helper: create a participant peered with the server at 8020 +// --------------------------------------------------------------------------- + +static eprosima::fastdds::dds::DomainParticipant* make_participant( + const char* name, + uint16_t port) +{ + eprosima::fastdds::dds::DomainParticipantQos qos{}; + qos.name(name); + + eprosima::fastdds::rtps::Locator_t announced; + eprosima::fastdds::rtps::IPLocator::setIPv4(announced, "127.0.0.1"); + announced.port = port; + qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(announced); + + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, "127.0.0.1"); + peer.port = 8020U; + qos.wire_protocol().builtin.initialPeersList.push_back(peer); + + return eprosima::fastdds::dds::DomainParticipantFactory::get_instance() + ->create_participant(0U, qos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); +} + +// --------------------------------------------------------------------------- +// Helper: poll DataReader until a valid ChargerLocation sample arrives +// --------------------------------------------------------------------------- + +static bool wait_for_charger_location( + eprosima::fastdds::dds::DataReader* reader, + int timeout_s) +{ + const auto deadline = + std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); + + while (std::chrono::steady_clock::now() < deadline) + { + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + if (reader->take_next_sample(&sample, &info) == + eprosima::fastdds::dds::RETCODE_OK && info.valid_data) + { + std::cout << " [dds] Received ChargerLocation id=" << sample.id() + << " name=" << sample.name() << "\n"; + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return false; +} + +// --------------------------------------------------------------------------- +// Global environment: lifecycle of safe_edge_server subprocess +// --------------------------------------------------------------------------- + +class ServerEnvironment : public ::testing::Environment +{ +public: + void SetUp() override + { + const char* bin = std::getenv("SAFE_EDGE_FAST_SERVER_BIN"); + const std::string cmd = + std::string(bin != nullptr ? bin : "safe_edge_server") + " &"; + ASSERT_EQ(0, std::system(cmd.c_str())) + << "Failed to launch safe_edge_server. " + "Set SAFE_EDGE_FAST_SERVER_BIN to the full path if needed."; + + std::cout << "[env] safe_edge_server started — waiting 5 s for init...\n"; + std::this_thread::sleep_for(std::chrono::seconds(5)); + std::cout << "[env] Server ready.\n"; + } + + void TearDown() override + { + std::cout << "[env] Stopping safe_edge_server...\n"; + std::system("pkill -f safe_edge_server 2>/dev/null || true"); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +}; + +// --------------------------------------------------------------------------- +// Suite 1: QueryDispatch +// Verifies that for each RequestedDataType the server dispatches correctly. +// --------------------------------------------------------------------------- + +class QueryDispatchTest : public ::testing::Test +{ +protected: + void SetUp() override + { + participant_ = make_participant("TestQueryDispatch", 8040U); + ASSERT_NE(nullptr, participant_); + + eprosima::fastdds::dds::TypeSupport charger_ts( + new safe_edge::pilot_server::ChargerLocationPubSubType()); + eprosima::fastdds::dds::TypeSupport query_ts( + new safe_edge::pilot_server::ServerQueryPubSubType()); + + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, + charger_ts.register_type(participant_)); + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, + query_ts.register_type(participant_)); + + charger_topic_ = participant_->create_topic( + safe_edge::server::common::topic_names::charger_locations(), + charger_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + query_topic_ = participant_->create_topic( + safe_edge::server::common::topic_names::server_query(), + query_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + ASSERT_NE(nullptr, charger_topic_); + ASSERT_NE(nullptr, query_topic_); + + publisher_ = participant_->create_publisher( + eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + subscriber_ = participant_->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, publisher_); + ASSERT_NE(nullptr, subscriber_); + + eprosima::fastdds::dds::DataWriterQos wqos = eprosima::fastdds::dds::DATAWRITER_QOS_DEFAULT; + wqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + eprosima::fastdds::dds::DataReaderQos rqos = eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + query_writer_ = publisher_->create_datawriter( + query_topic_, wqos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + charger_reader_ = subscriber_->create_datareader( + charger_topic_, rqos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, query_writer_); + ASSERT_NE(nullptr, charger_reader_); + + std::this_thread::sleep_for(std::chrono::seconds(3)); // discovery + } + + void send_query(safe_edge::pilot_server::RequestedDataType type) + { + safe_edge::pilot_server::ServerQuery q{}; + q.requested_by("test_query_dispatch"); + q.requested_data_type(type); + EXPECT_EQ(eprosima::fastdds::dds::RETCODE_OK, + query_writer_->write(&q, eprosima::fastdds::dds::HANDLE_NIL)); + } + + eprosima::fastdds::dds::DomainParticipant* participant_ = nullptr; + eprosima::fastdds::dds::Publisher* publisher_ = nullptr; + eprosima::fastdds::dds::Subscriber* subscriber_ = nullptr; + eprosima::fastdds::dds::Topic* charger_topic_ = nullptr; + eprosima::fastdds::dds::Topic* query_topic_ = nullptr; + eprosima::fastdds::dds::DataWriter* query_writer_ = nullptr; + eprosima::fastdds::dds::DataReader* charger_reader_= nullptr; +}; + +TEST_F(QueryDispatchTest, ChargerLocationReturnsData) +{ + send_query(safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION); + EXPECT_TRUE(wait_for_charger_location(charger_reader_, 5)) + << "No ChargerLocation received within 5 s"; +} + +TEST_F(QueryDispatchTest, ChargerTypeHandledWithoutCrash) +{ + send_query(safe_edge::pilot_server::RequestedDataType::CHARGER_TYPE); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchTest, ChargingSessionHandledWithoutCrash) +{ + send_query(safe_edge::pilot_server::RequestedDataType::CHARGING_SESSION); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchTest, TransitHealthHandledWithoutCrash) +{ + send_query(safe_edge::pilot_server::RequestedDataType::TRANSIT_HEALTH); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +TEST_F(QueryDispatchTest, TransitMetricsHandledWithoutCrash) +{ + send_query(safe_edge::pilot_server::RequestedDataType::TRANSIT_METRICS); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); +} + +// --------------------------------------------------------------------------- +// Suite 2: PeriodicRefresh +// Verifies the 30-second timer fires without any explicit query. +// --------------------------------------------------------------------------- + +TEST(PeriodicRefresh, TwoBurstsIn35Seconds) +{ + auto* participant = make_participant("TestPeriodicRefresh", 8041U); + ASSERT_NE(nullptr, participant); + + eprosima::fastdds::dds::TypeSupport charger_ts( + new safe_edge::pilot_server::ChargerLocationPubSubType()); + ASSERT_EQ(eprosima::fastdds::dds::RETCODE_OK, + charger_ts.register_type(participant)); + + auto* topic = participant->create_topic( + safe_edge::server::common::topic_names::charger_locations(), + charger_ts.get_type_name(), + eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); + ASSERT_NE(nullptr, topic); + + auto* sub = participant->create_subscriber( + eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, sub); + + eprosima::fastdds::dds::DataReaderQos rqos = eprosima::fastdds::dds::DATAREADER_QOS_DEFAULT; + rqos.reliability().kind = eprosima::fastdds::dds::RELIABLE_RELIABILITY_QOS; + + auto* reader = sub->create_datareader( + topic, rqos, nullptr, + eprosima::fastdds::dds::StatusMask::none()); + ASSERT_NE(nullptr, reader); + + std::this_thread::sleep_for(std::chrono::seconds(3)); // discovery + + int burst_count = 0; + const auto t0 = std::chrono::steady_clock::now(); + const auto end = t0 + std::chrono::seconds(35); + auto last_sample = t0 - std::chrono::seconds(10); + + while (std::chrono::steady_clock::now() < end) + { + safe_edge::pilot_server::ChargerLocation sample{}; + eprosima::fastdds::dds::SampleInfo info{}; + if (reader->take_next_sample(&sample, &info) == + eprosima::fastdds::dds::RETCODE_OK && info.valid_data) + { + const auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast( + now - last_sample).count() > 2000) + { + burst_count++; + std::cout << " [dds] Burst " << burst_count << " at t=" + << std::chrono::duration_cast( + now - t0).count() << " s\n"; + } + last_sample = now; + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + EXPECT_GE(burst_count, 2) + << "Expected >=2 bursts (publication_match trigger + 30 s periodic refresh), " + << "got " << burst_count; +} + +// --------------------------------------------------------------------------- +// main +// --------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + ::testing::AddGlobalTestEnvironment(new ServerEnvironment()); + return RUN_ALL_TESTS(); +} diff --git a/scripts/build_qnx.sh b/scripts/build_qnx.sh index 696ae3c..c976085 100755 --- a/scripts/build_qnx.sh +++ b/scripts/build_qnx.sh @@ -31,7 +31,7 @@ EXTRA_BUILD_ARGS=() usage() { cat <] +Usage: bash build_qnx.sh [-i|--idl] [-- ] Options: -i, --idl regenerate safe_dds/idl from the shared idl/*.idl sources @@ -203,7 +203,7 @@ fi if [[ ! -d "${SAFEDDS_DIR}" ]]; then echo "Safe DDS QNX install not found at '${SAFEDDS_DIR}'" >&2 - echo "Build it first with: bash scripts/build_safedds_qnx.sh -- -j2" >&2 + echo "Build it first with: bash build_safedds_qnx.sh -- -j2" >&2 exit 1 fi diff --git a/scripts/build_ubuntu.sh b/scripts/build_ubuntu.sh new file mode 100755 index 0000000..9f7b36a --- /dev/null +++ b/scripts/build_ubuntu.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +: "${FASTDDS_BASE_IMAGE:=eprosima/vulcanexus:kilted-base}" +: "${CMAKE_BUILD_TYPE:=Release}" + +BUILD_TESTS=0 + +usage() { + cat <&2 + usage >&2 + exit 1 + ;; + esac +done + +cd "${WORKSPACE_ROOT}" + +echo "Building safe-edge-server:fast ..." +docker build \ + --build-arg FASTDDS_BASE_IMAGE="${FASTDDS_BASE_IMAGE}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -f fast_dds/docker/server.Dockerfile \ + -t safe-edge-server:fast \ + . + +echo "Building safe-edge-edge:fast ..." +docker build \ + --build-arg FASTDDS_BASE_IMAGE="${FASTDDS_BASE_IMAGE}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -f fast_dds/docker/edge.Dockerfile \ + -t safe-edge-edge:fast \ + . + +if (( BUILD_TESTS )); then + echo "Building safe-edge-server:fast-test ..." + docker build \ + --build-arg FASTDDS_BASE_IMAGE="${FASTDDS_BASE_IMAGE}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -f fast_dds/docker/server.test.Dockerfile \ + -t safe-edge-server:fast-test \ + . + + echo "Building safe-edge-edge:fast-test ..." + docker build \ + --build-arg FASTDDS_BASE_IMAGE="${FASTDDS_BASE_IMAGE}" \ + --build-arg CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -f fast_dds/docker/edge.test.Dockerfile \ + -t safe-edge-edge:fast-test \ + . +fi + +echo "Done. Images built:" +echo " safe-edge-server:fast" +echo " safe-edge-edge:fast" +if (( BUILD_TESTS )); then + echo " safe-edge-server:fast-test" + echo " safe-edge-edge:fast-test" +fi diff --git a/scripts/check_setup.sh b/scripts/check_setup.sh index 5896ca1..4140b63 100755 --- a/scripts/check_setup.sh +++ b/scripts/check_setup.sh @@ -14,7 +14,7 @@ LINUX_ONLY=0 usage() { cat </dev/null 2>&1; then echo "Missing command: ${cmd}" >&2 - echo "For Ubuntu/Debian host packages, run: bash scripts/install_host_deps.sh" >&2 + echo "For Ubuntu/Debian host packages, run: bash install_host_deps.sh" >&2 return 1 fi } @@ -119,7 +119,7 @@ require_path "${SAFE_DDS_PATH}" if [[ ! -d "${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}/safedds" ]]; then echo "Safe DDS QNX install not found yet." - echo "Build it with: bash scripts/build_safedds_qnx.sh -- -j2" + echo "Build it with: bash build_safedds_qnx.sh -- -j2" fi echo "Setup check completed." diff --git a/scripts/launch_fast_edge_test.sh b/scripts/launch_fast_edge_test.sh new file mode 100755 index 0000000..ba5be0e --- /dev/null +++ b/scripts/launch_fast_edge_test.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +IMAGE="safe-edge-edge:fast-test" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_fast_edge_test.log" + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +mkdir -p "${LOG_DIR}" + +if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then + echo "[launch_fast_edge_test] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests +fi + +# Remove stale FastDDS shared-memory artifacts that can cause false failures. +sudo rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true + +echo "[launch_fast_edge_test] Running edge integration test..." +echo "[launch_fast_edge_test] Log: ${LOG_FILE}" + +set +e +docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" +TEST_EXIT=${PIPESTATUS[0]} +set -e + +if [[ ${TEST_EXIT} -eq 0 ]]; then + echo "[launch_fast_edge_test] PASSED" +else + echo "[launch_fast_edge_test] FAILED (exit ${TEST_EXIT})" >&2 +fi + +exit "${TEST_EXIT}" diff --git a/scripts/launch_fast_server_test.sh b/scripts/launch_fast_server_test.sh new file mode 100755 index 0000000..4aa7be5 --- /dev/null +++ b/scripts/launch_fast_server_test.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +IMAGE="safe-edge-server:fast-test" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_fast_server_test.log" + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +mkdir -p "${LOG_DIR}" + +if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then + echo "[launch_fast_server_test] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests +fi + +# Remove stale FastDDS shared-memory artifacts that can cause false failures. +sudo rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true + +echo "[launch_fast_server_test] Running server integration test..." +echo "[launch_fast_server_test] Log: ${LOG_FILE}" + +set +e +docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" +TEST_EXIT=${PIPESTATUS[0]} +set -e + +if [[ ${TEST_EXIT} -eq 0 ]]; then + echo "[launch_fast_server_test] PASSED" +else + echo "[launch_fast_server_test] FAILED (exit ${TEST_EXIT})" >&2 +fi + +exit "${TEST_EXIT}" From b76ebe5c874c0a0dd562ac4349331f09f46c97fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Tue, 2 Jun 2026 08:03:47 +0200 Subject: [PATCH 05/17] Add exceptions to .gitignore file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 6 ++++ .../server/common/PilotServerClient.hpp | 35 +++++++++++++++++++ .../common/PilotServerPayloadParser.hpp | 32 +++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 common_server/include/safe_edge/server/common/PilotServerClient.hpp create mode 100644 common_server/include/safe_edge/server/common/PilotServerPayloadParser.hpp diff --git a/.gitignore b/.gitignore index 504c345..071b535 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,9 @@ qnx/targets/qemu-qnx800-*/local/snippets/system_files.custom !fast_dds/** fast_dds/build/ fast_dds/install/ + +# SafeDDS component headers are source files, not generated build artifacts. +!safe_dds/*/include/ +!safe_dds/*/include/** +!common_server/include/ +!common_server/include/** diff --git a/common_server/include/safe_edge/server/common/PilotServerClient.hpp b/common_server/include/safe_edge/server/common/PilotServerClient.hpp new file mode 100644 index 0000000..4f817c6 --- /dev/null +++ b/common_server/include/safe_edge/server/common/PilotServerClient.hpp @@ -0,0 +1,35 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_PILOTSERVERCLIENT_HPP +#define SAFE_EDGE_SERVER_COMMON_PILOTSERVERCLIENT_HPP + +#include + +namespace safe_edge { +namespace server { +namespace common { + +class PilotServerClient +{ +public: + PilotServerClient( + const std::string& base_url, + const std::string& ini_path); + ~PilotServerClient(); + + // Executes HTTP GET base_url + endpoint with Authorization: Bearer . + // Returns the response body on success, empty string on any error. + // The api_key never appears in any log output. + std::string fetch(const std::string& endpoint) noexcept; + +private: + bool load_api_key(const std::string& ini_path); + + std::string base_url_; + std::string api_key_; + bool ready_ = false; +}; + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_PILOTSERVERCLIENT_HPP diff --git a/common_server/include/safe_edge/server/common/PilotServerPayloadParser.hpp b/common_server/include/safe_edge/server/common/PilotServerPayloadParser.hpp new file mode 100644 index 0000000..003b626 --- /dev/null +++ b/common_server/include/safe_edge/server/common/PilotServerPayloadParser.hpp @@ -0,0 +1,32 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_PILOTSERVERPAYLOADPARSER_HPP +#define SAFE_EDGE_SERVER_COMMON_PILOTSERVERPAYLOADPARSER_HPP + +#include +#include + +namespace safe_edge { +namespace server { +namespace common { + +// Neutral parsed representation — no DDS-stack types. +struct ParsedChargerLocation +{ + int id = 0; + std::string name; + float latitude = 0.0F; + float longitude = 0.0F; +}; + +class PilotServerPayloadParser +{ +public: + + static std::vector parse_charger_locations( + const std::string& body); +}; + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_PILOTSERVERPAYLOADPARSER_HPP From 008a894cb0beccc6d7791c7a9a192f96aa563546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Tue, 2 Jun 2026 08:21:43 +0200 Subject: [PATCH 06/17] Apply exceptions to Server and Edge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .../edge_module/common/HeaderFactory.hpp | 33 +++ .../edge_module/common/RuntimeConfig.hpp | 27 +++ .../edge_module/common/TopicNames.hpp | 20 ++ .../edge_module/logic/EdgeAdvisor.hpp | 25 +++ .../edge_module/nodes/EdgeGatewayNode.hpp | 189 ++++++++++++++++++ .../common/PilotServerPublishHelper.hpp | 28 +++ .../safe_edge/server/common/RuntimeConfig.hpp | 32 +++ .../safe_edge/server/common/TopicNames.hpp | 18 ++ .../safe_edge/server/nodes/ServerNode.hpp | 149 ++++++++++++++ 9 files changed, 521 insertions(+) create mode 100644 safe_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp create mode 100644 safe_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp create mode 100644 safe_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp create mode 100644 safe_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp create mode 100644 safe_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp create mode 100644 safe_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp create mode 100644 safe_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp create mode 100644 safe_dds/server/include/safe_edge/server/common/TopicNames.hpp create mode 100644 safe_dds/server/include/safe_edge/server/nodes/ServerNode.hpp diff --git a/safe_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp b/safe_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp new file mode 100644 index 0000000..fa53aa9 --- /dev/null +++ b/safe_dds/edge/include/safe_edge/edge_module/common/HeaderFactory.hpp @@ -0,0 +1,33 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP + +#include + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +class HeaderFactory +{ +public: + + explicit HeaderFactory(std::string source_name); + + safe_edge::common::Header make_header(const char* trace_suffix = nullptr); + + static uint64_t now_ms() noexcept; + +private: + + std::string source_name_; + uint64_t counter_ = 0U; +}; + +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_HEADERFACTORY_HPP diff --git a/safe_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp b/safe_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp new file mode 100644 index 0000000..88a357c --- /dev/null +++ b/safe_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp @@ -0,0 +1,27 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + std::string service_name; + std::string source_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + uint32_t status_interval_sec = 5U; +}; + +RuntimeConfig make_edge_gateway_runtime_config(); + +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_RUNTIMECONFIG_HPP diff --git a/safe_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp b/safe_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp new file mode 100644 index 0000000..eef164e --- /dev/null +++ b/safe_dds/edge/include/safe_edge/edge_module/common/TopicNames.hpp @@ -0,0 +1,20 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace edge_module { +namespace common { +namespace topic_names { + +const char* vehicle_edge_summary() noexcept; +const char* energy_advisory() noexcept; +const char* edge_gateway_status() noexcept; +const char* charger_locations() noexcept; +const char* service_heartbeat() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_COMMON_TOPICNAMES_HPP diff --git a/safe_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp b/safe_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp new file mode 100644 index 0000000..c6d7c7d --- /dev/null +++ b/safe_dds/edge/include/safe_edge/edge_module/logic/EdgeAdvisor.hpp @@ -0,0 +1,25 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP +#define SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP + +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace logic { + +class EdgeAdvisor +{ +public: + + static safe_edge::edge::EnergyAdvisory evaluate( + const safe_edge::edge::VehicleEdgeSummary& summary, + const safe_edge::pilot_server::ChargerLocation* chargers, + int32_t charger_count); +}; + +} // namespace logic +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_LOGIC_EDGEADVISOR_HPP diff --git a/safe_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp b/safe_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp new file mode 100644 index 0000000..4965527 --- /dev/null +++ b/safe_dds/edge/include/safe_edge/edge_module/nodes/EdgeGatewayNode.hpp @@ -0,0 +1,189 @@ +#ifndef SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP +#define SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace safe_edge { +namespace edge_module { +namespace nodes { + +class EdgeGatewayNode +{ +public: + + explicit EdgeGatewayNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(EdgeGatewayNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class VehicleEdgeSummaryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit VehicleEdgeSummaryListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class ChargerLocationListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargerLocationListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(EdgeGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + EdgeGatewayNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void on_vehicle_edge_summary_received(const safe_edge::edge::VehicleEdgeSummary& summary); + void on_charger_location_received(const safe_edge::pilot_server::ChargerLocation& location); + void on_server_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void publish_energy_advisory(const safe_edge::edge::EnergyAdvisory& advisory); + void publish_edge_gateway_status(); + + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + VehicleEdgeSummaryListener vehicle_edge_summary_listener_; + ChargerLocationListener charger_location_listener_; + HeartbeatListener heartbeat_listener_; + + safe_edge::edge::VehicleEdgeSummaryTypeSupport vehicle_edge_summary_type_support_; + safe_edge::edge::EnergyAdvisoryTypeSupport energy_advisory_type_support_; + safe_edge::edge::EdgeGatewayStatusTypeSupport edge_gateway_status_type_support_; + safe_edge::pilot_server::ChargerLocationTypeSupport charger_location_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* vehicle_edge_summary_topic_ = nullptr; + eprosima::safedds::dds::Topic* energy_advisory_topic_ = nullptr; + eprosima::safedds::dds::Topic* edge_gateway_status_topic_ = nullptr; + eprosima::safedds::dds::Topic* charger_location_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 vehicle_edge_summary_topic_name_; + eprosima::safedds::memory::container::StaticString256 energy_advisory_topic_name_; + eprosima::safedds::memory::container::StaticString256 edge_gateway_status_topic_name_; + eprosima::safedds::memory::container::StaticString256 charger_location_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + + eprosima::safedds::dds::DataWriter* energy_advisory_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* edge_gateway_status_datawriter_ = nullptr; + + eprosima::safedds::dds::TypedDataWriter* energy_advisory_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* edge_gateway_status_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* vehicle_edge_summary_datareader_ = nullptr; + eprosima::safedds::dds::TypedDataReader* vehicle_edge_summary_reader_ = + nullptr; + + eprosima::safedds::dds::DataReader* charger_location_datareader_ = nullptr; + eprosima::safedds::dds::TypedDataReader* charger_location_reader_ = + nullptr; + + eprosima::safedds::dds::DataReader* service_heartbeat_datareader_ = nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + + safe_edge::pilot_server::ChargerLocation cached_chargers_[3]; + int32_t cached_charger_count_ = 0; + uint64_t last_server_sync_ms_ = 0U; + uint64_t last_server_hb_ms_ = 0U; + bool server_available_ = false; + + eprosima::safedds::execution::Timer status_timer_; +}; + +} // namespace nodes +} // namespace edge_module +} // namespace safe_edge + +#endif // SAFE_EDGE_EDGE_MODULE_NODES_EDGEGATEWAYNODE_HPP diff --git a/safe_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp b/safe_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp new file mode 100644 index 0000000..c5c288d --- /dev/null +++ b/safe_dds/server/include/safe_edge/server/common/PilotServerPublishHelper.hpp @@ -0,0 +1,28 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP +#define SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP + +#include + +#include + +#include + +#include + +namespace safe_edge { +namespace server { +namespace common { + +struct PilotServerPublishHelper +{ + static void publish_charger_locations( + eprosima::safedds::dds::TypedDataWriter< + safe_edge::pilot_server::ChargerLocationTypeSupport>& writer, + const std::vector& parsed); +}; + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_PILOTSERVERPUBLISHHELPER_HPP diff --git a/safe_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp b/safe_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp new file mode 100644 index 0000000..c7577c4 --- /dev/null +++ b/safe_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp @@ -0,0 +1,32 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP + +#include +#include + +namespace safe_edge { +namespace server { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + + std::string pilot_server_base_url; + std::string pilot_server_api_key; + std::string charger_locations_endpoint; + std::string charger_types_endpoint; + std::string charging_sessions_endpoint; + std::string transit_health_endpoint; + std::string transit_metrics_endpoint; +}; + +RuntimeConfig make_server_runtime_config(); + +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_RUNTIMECONFIG_HPP diff --git a/safe_dds/server/include/safe_edge/server/common/TopicNames.hpp b/safe_dds/server/include/safe_edge/server/common/TopicNames.hpp new file mode 100644 index 0000000..6838cf5 --- /dev/null +++ b/safe_dds/server/include/safe_edge/server/common/TopicNames.hpp @@ -0,0 +1,18 @@ +#ifndef SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace server { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept; +const char* server_query() noexcept; +const char* service_heartbeat() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_COMMON_TOPICNAMES_HPP diff --git a/safe_dds/server/include/safe_edge/server/nodes/ServerNode.hpp b/safe_dds/server/include/safe_edge/server/nodes/ServerNode.hpp new file mode 100644 index 0000000..4ef534d --- /dev/null +++ b/safe_dds/server/include/safe_edge/server/nodes/ServerNode.hpp @@ -0,0 +1,149 @@ +#ifndef SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP +#define SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace safe_edge { +namespace server { +namespace nodes { + +class ServerNode +{ +public: + + explicit ServerNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(ServerNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + ServerNode& owner_; + }; + + class ServerQueryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ServerQueryListener(ServerNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + ServerNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + + void on_server_query_received(const safe_edge::pilot_server::ServerQuery& query); + void publish_heartbeat(); + + void request_pilot_server_data(safe_edge::pilot_server::RequestedDataType resource) noexcept; + void periodic_refresh_all_resources() noexcept; + const char* resolve_endpoint(safe_edge::pilot_server::RequestedDataType resource) const noexcept; + void log_uptime() const noexcept; + + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::PilotServerClient pilot_client_; + + ParticipantListener participant_listener_; + ServerQueryListener server_query_listener_; + + eprosima::safedds::execution::Timer heartbeat_timer_; + eprosima::safedds::execution::Timer refresh_timer_; + eprosima::safedds::execution::Timer uptime_timer_; + std::chrono::steady_clock::time_point start_time_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* charger_locations_topic_ = nullptr; + eprosima::safedds::dds::Topic* server_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 charger_locations_topic_name_; + eprosima::safedds::memory::container::StaticString256 server_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + + eprosima::safedds::dds::DataWriter* charger_locations_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + + eprosima::safedds::dds::DataReader* server_query_datareader_ = nullptr; + + void configure_participant_qos( + eprosima::safedds::dds::DomainParticipantQos& qos) noexcept; + +protected: + + safe_edge::pilot_server::ServerQueryTypeSupport server_query_type_support_; + + eprosima::safedds::dds::TypedDataReader* server_query_reader_ = nullptr; + + safe_edge::pilot_server::ChargerLocationTypeSupport charger_locations_type_support_; + + eprosima::safedds::dds::TypedDataWriter* charger_locations_writer_ = nullptr; + + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = nullptr; +}; + +} // namespace nodes +} // namespace server +} // namespace safe_edge + +#endif // SAFE_EDGE_SERVER_NODES_SERVERNODE_HPP From ab292f7710e65bd5db1e1b52cda626f3a0b81740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Mon, 8 Jun 2026 11:46:51 +0200 Subject: [PATCH 07/17] TPI 2.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- README.md | 51 +- idl/internal.idl | 78 + safe_dds/idl/internal.hpp | 3571 +++++++++++++++++ safe_dds/non_safety/CMakeLists.txt | 119 + .../common/HeaderFactory.hpp | 34 + .../common/RuntimeConfig.hpp | 31 + .../non_safety_domain/common/TopicNames.hpp | 28 + .../nodes/CloudGatewayNode.hpp | 318 ++ .../nodes/InfotainmentNode.hpp | 236 ++ .../nodes/OtaServiceNode.hpp | 196 + .../src/apps/cloud_gateway_main.cpp | 11 + .../non_safety/src/apps/infotainment_main.cpp | 11 + .../non_safety/src/apps/ota_service_main.cpp | 11 + .../non_safety/src/common/HeaderFactory.cpp | 54 + .../non_safety/src/common/RuntimeConfig.cpp | 54 + safe_dds/non_safety/src/common/TopicNames.cpp | 76 + .../non_safety/src/nodes/CloudGatewayNode.cpp | 823 ++++ .../non_safety/src/nodes/InfotainmentNode.cpp | 622 +++ .../non_safety/src/nodes/OtaServiceNode.cpp | 498 +++ safe_dds/safety/CMakeLists.txt | 131 + .../safety_domain/common/HeaderFactory.hpp | 34 + .../safety_domain/common/RuntimeConfig.hpp | 31 + .../safety_domain/common/TopicNames.hpp | 40 + .../safety_domain/nodes/PolicyEngineNode.hpp | 313 ++ .../nodes/SafetyIoAdaptersNode.hpp | 233 ++ .../safety_domain/nodes/VehicleMockNode.hpp | 89 + .../safety_domain/policy/IdlAdapters.hpp | 46 + .../safety_domain/policy/PolicyEngine.hpp | 22 + .../safety_domain/policy/PolicyTypes.hpp | 58 + .../safety/src/apps/policy_engine_main.cpp | 10 + .../src/apps/safety_io_adapters_main.cpp | 10 + .../safety/src/apps/vehicle_mock_main.cpp | 10 + safe_dds/safety/src/common/HeaderFactory.cpp | 54 + safe_dds/safety/src/common/RuntimeConfig.cpp | 44 + safe_dds/safety/src/common/TopicNames.cpp | 76 + .../safety/src/nodes/PolicyEngineNode.cpp | 846 ++++ .../safety/src/nodes/SafetyIoAdaptersNode.cpp | 659 +++ safe_dds/safety/src/nodes/VehicleMockNode.cpp | 349 ++ safe_dds/safety/src/policy/IdlAdapters.cpp | 156 + safe_dds/safety/src/policy/PolicyEngine.cpp | 51 + scripts/build_qnx.sh | 35 +- scripts/check_setup.sh | 6 +- scripts/launch_tpi_2_5_test.sh | 592 +++ 43 files changed, 10710 insertions(+), 7 deletions(-) create mode 100644 idl/internal.idl create mode 100644 safe_dds/idl/internal.hpp create mode 100644 safe_dds/non_safety/CMakeLists.txt create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/common/HeaderFactory.hpp create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/common/TopicNames.hpp create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/CloudGatewayNode.hpp create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/InfotainmentNode.hpp create mode 100644 safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/OtaServiceNode.hpp create mode 100644 safe_dds/non_safety/src/apps/cloud_gateway_main.cpp create mode 100644 safe_dds/non_safety/src/apps/infotainment_main.cpp create mode 100644 safe_dds/non_safety/src/apps/ota_service_main.cpp create mode 100644 safe_dds/non_safety/src/common/HeaderFactory.cpp create mode 100644 safe_dds/non_safety/src/common/RuntimeConfig.cpp create mode 100644 safe_dds/non_safety/src/common/TopicNames.cpp create mode 100644 safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp create mode 100644 safe_dds/non_safety/src/nodes/InfotainmentNode.cpp create mode 100644 safe_dds/non_safety/src/nodes/OtaServiceNode.cpp create mode 100644 safe_dds/safety/CMakeLists.txt create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/common/HeaderFactory.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/common/TopicNames.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/nodes/PolicyEngineNode.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/nodes/SafetyIoAdaptersNode.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/policy/IdlAdapters.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyEngine.hpp create mode 100644 safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyTypes.hpp create mode 100644 safe_dds/safety/src/apps/policy_engine_main.cpp create mode 100644 safe_dds/safety/src/apps/safety_io_adapters_main.cpp create mode 100644 safe_dds/safety/src/apps/vehicle_mock_main.cpp create mode 100644 safe_dds/safety/src/common/HeaderFactory.cpp create mode 100644 safe_dds/safety/src/common/RuntimeConfig.cpp create mode 100644 safe_dds/safety/src/common/TopicNames.cpp create mode 100644 safe_dds/safety/src/nodes/PolicyEngineNode.cpp create mode 100644 safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp create mode 100644 safe_dds/safety/src/nodes/VehicleMockNode.cpp create mode 100644 safe_dds/safety/src/policy/IdlAdapters.cpp create mode 100644 safe_dds/safety/src/policy/PolicyEngine.cpp create mode 100644 scripts/launch_tpi_2_5_test.sh diff --git a/README.md b/README.md index 52c69b3..3ef5a44 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,13 @@ bash check_setup.sh Build and test (QNX): ```bash +cd scripts bash build_safedds_qnx.sh -- -j2 -bash build_qnx.sh -- -j2 +bash build_qnx.sh --idl -- -j2 bash launch_tpi_2_3_test.sh bash launch_tpi_2_1_test.sh bash launch_tpi_2_2_test.sh +bash launch_tpi_2_5_test.sh ``` Build and test (FastDDS / Docker): @@ -76,6 +78,8 @@ bash launch_tpi_2_3_test.sh - Shared server code: `common_server/` - Safe DDS server: `safe_dds/server/` - Safe DDS edge: `safe_dds/edge/` +- Safe DDS safety domain: `safe_dds/safety/` +- Safe DDS non-safety domain: `safe_dds/non_safety/` - FastDDS server: `fast_dds/server/` - FastDDS edge: `fast_dds/edge/` - FastDDS generated headers: `fast_dds/idl/` @@ -167,6 +171,20 @@ export QNX_ARCH="x86_64" export CMAKE_BUILD_TYPE="Release" ``` +## Pilot Server API Key + +Requests to the Pilot Server read the API key from `/etc/safe-edge/server.ini`. +The key is not stored in this repository and should not be committed. + +Example file: + +```ini +[pilot_server] +api_key = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +``` + +If the file does not exist, the real Pilot Server checks in the Linux test are skipped. + ## Setup Check Install Ubuntu/Debian host packages: @@ -219,17 +237,27 @@ Build all QNX targets from this repository: bash build_qnx.sh -- -j2 ``` +If files under `idl/` changed, regenerate Safe DDS generated headers during the build: + +```bash +bash build_qnx.sh --idl -- -j2 +``` + This configures and installs: - `common_server` - `safe_dds/server` - `safe_dds/edge` +- `safe_dds/safety` +- `safe_dds/non_safety` Installed binaries end up in: - `common_server/install/server-common-qnx8-x86_64-Release/bin` - `safe_dds/install/server-qnx8-x86_64-Release/bin` - `safe_dds/install/edge-qnx8-x86_64-Release/bin` +- `safe_dds/install/safety-qnx8-x86_64-Release/bin` +- `safe_dds/install/non-safety-qnx8-x86_64-Release/bin` ### FastDDS Docker images @@ -284,6 +312,27 @@ bash launch_tpi_2_2_test.sh Log: `scripts/logs/launch_tpi_2_2.log` +### TPI 2.5: QNX vehicle node smoke test + +```bash +cd scripts +bash launch_tpi_2_5_test.sh +``` + +This starts the SafeDDS safety and non-safety nodes on the QNX VM, verifies that all six processes stay alive during the smoke-test window, prints their logs, and stops the VM. + +The underlying launcher can also leave the nodes running for manual inspection: + +```bash +cd scripts +bash aux_vehicle_nodes.sh +``` + +Logs: + +- `scripts/logs/launch_tpi_2_5.log` +- `scripts/logs/aux_vehicle_nodes.log` + ### FastDDS server integration test ```bash diff --git a/idl/internal.idl b/idl/internal.idl new file mode 100644 index 0000000..3a606ac --- /dev/null +++ b/idl/internal.idl @@ -0,0 +1,78 @@ +#include "common.idl" + +module safe_edge { +module internal { + +struct BatteryState +{ + safe_edge::common::Header header; + float soc_pct; + float available_charge_kw; + float available_discharge_kw; + boolean v2g_ready; +}; + +struct VehicleSafetyState +{ + safe_edge::common::Header header; + float speed_mps; + boolean braking_available; + boolean steering_available; + boolean emergency_stop; + boolean adas_fault; +}; + +struct SafetyInputFrame +{ + safe_edge::common::Header header; + BatteryState battery; + VehicleSafetyState safety; +}; + +struct PolicyDecision +{ + safe_edge::common::Header header; + safe_edge::common::PolicyMode mode; + string reason; + boolean allow_non_safety; + boolean allow_ota; +}; + +struct ChargingQuery +{ + safe_edge::common::Header header; + float soc_pct; +}; + +struct ChargingResponse +{ + safe_edge::common::Header header; + long preferred_charger_id; + string preferred_charger_name; + boolean has_charger; +}; + +struct RouteContextQuery +{ + safe_edge::common::Header header; + safe_edge::common::HealthStatus vehicle_health; +}; + +struct RouteContextResponse +{ + safe_edge::common::Header header; + string<32> transit_status; + long max_route_updates; + long vehicles_seen; + boolean has_data; +}; + +struct ServerAvailabilityStatus +{ + safe_edge::common::Header header; + boolean server_available; + string detail; +}; + +}; +}; diff --git a/safe_dds/idl/internal.hpp b/safe_dds/idl/internal.hpp new file mode 100644 index 0000000..876ea7e --- /dev/null +++ b/safe_dds/idl/internal.hpp @@ -0,0 +1,3571 @@ +// This file was generated by Safe DDS code generator, which is +// Copyright (C) 2023, Proyectos y Sistemas de Mantenimiento SL (eProsima) +// +// This program is commercial software licensed under the terms of the +// eProsima Software License Agreement Rev 03 (the "License") +// +// You may obtain a copy of the License at +// https://www.eprosima.com/licenses/LICENSE-REV03 + +/*! + * @file internal.hpp + * This header file contains the declaration of the types described in the IDL file. + * + * This file was generated by Safe-DDS-gen. + */ + +#ifndef SAFEDDS_GENERATED__SAFE_EDGE_INTERNAL_INTERNAL_HPP +#define SAFEDDS_GENERATED__SAFE_EDGE_INTERNAL_INTERNAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#include "common.hpp" + +namespace safe_edge { + namespace internal { + + // Forward declaration + struct BatteryStateSerialization; + + /*! + * @brief This struct represents the structure BatteryState defined by the user in the IDL file. + * @ingroup internal + */ + struct BatteryState + { + using SerializationStruct = BatteryStateSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member soc_pct + float soc_pct = 0.0; + /// Member available_charge_kw + float available_charge_kw = 0.0; + /// Member available_discharge_kw + float available_discharge_kw = 0.0; + /// Member v2g_ready + bool v2g_ready = false; + + // Ensure bounds + }; + + struct BatteryStateSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<553ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const BatteryState& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.soc_pct) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.available_charge_kw) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.available_discharge_kw) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.v2g_ready) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + BatteryState& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.soc_pct); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.available_charge_kw); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.available_discharge_kw); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.v2g_ready); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const BatteryState& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.soc_pct) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.available_charge_kw) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.available_discharge_kw) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.v2g_ready) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const BatteryState& first, + const BatteryState& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.soc_pct, second.soc_pct); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.available_charge_kw, second.available_charge_kw); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.available_discharge_kw, second.available_discharge_kw); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.v2g_ready, second.v2g_ready); + + return ret; + } + }; + + + struct BatteryStateTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = BatteryState; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::BatteryState"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return BatteryStateSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const BatteryState& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const BatteryState& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = BatteryStateSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + BatteryState& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = BatteryStateSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const BatteryState& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const BatteryState& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = BatteryStateSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct VehicleSafetyStateSerialization; + + /*! + * @brief This struct represents the structure VehicleSafetyState defined by the user in the IDL file. + * @ingroup internal + */ + struct VehicleSafetyState + { + using SerializationStruct = VehicleSafetyStateSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member speed_mps + float speed_mps = 0.0; + /// Member braking_available + bool braking_available = false; + /// Member steering_available + bool steering_available = false; + /// Member emergency_stop + bool emergency_stop = false; + /// Member adas_fault + bool adas_fault = false; + + // Ensure bounds + }; + + struct VehicleSafetyStateSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<548ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const VehicleSafetyState& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.speed_mps) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.braking_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.steering_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.emergency_stop) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(5, sample.adas_fault) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + VehicleSafetyState& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.speed_mps); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.braking_available); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.steering_available); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.emergency_stop); + break; + + case 5: + ret_value = inner_deser.deserialize_member(sample.adas_fault); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const VehicleSafetyState& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.speed_mps) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.braking_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.steering_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.emergency_stop) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(5, sample.adas_fault) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const VehicleSafetyState& first, + const VehicleSafetyState& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.speed_mps, second.speed_mps); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.braking_available, second.braking_available); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.steering_available, second.steering_available); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.emergency_stop, second.emergency_stop); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.adas_fault, second.adas_fault); + + return ret; + } + }; + + + struct VehicleSafetyStateTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = VehicleSafetyState; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::VehicleSafetyState"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return VehicleSafetyStateSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const VehicleSafetyState& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const VehicleSafetyState& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = VehicleSafetyStateSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + VehicleSafetyState& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = VehicleSafetyStateSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const VehicleSafetyState& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const VehicleSafetyState& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = VehicleSafetyStateSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct SafetyInputFrameSerialization; + + /*! + * @brief This struct represents the structure SafetyInputFrame defined by the user in the IDL file. + * @ingroup internal + */ + struct SafetyInputFrame + { + using SerializationStruct = SafetyInputFrameSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member battery + safe_edge::internal::BatteryState battery ; + /// Member safety + safe_edge::internal::VehicleSafetyState safety ; + + // Ensure bounds + }; + + struct SafetyInputFrameSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<1636ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const SafetyInputFrame& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.battery) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.safety) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + SafetyInputFrame& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.battery); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.safety); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const SafetyInputFrame& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.battery) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.safety) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const SafetyInputFrame& first, + const SafetyInputFrame& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.battery, second.battery); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.safety, second.safety); + + return ret; + } + }; + + + struct SafetyInputFrameTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = SafetyInputFrame; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::SafetyInputFrame"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return SafetyInputFrameSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const SafetyInputFrame& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const SafetyInputFrame& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = SafetyInputFrameSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + SafetyInputFrame& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = SafetyInputFrameSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const SafetyInputFrame& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const SafetyInputFrame& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = SafetyInputFrameSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct PolicyDecisionSerialization; + + /*! + * @brief This struct represents the structure PolicyDecision defined by the user in the IDL file. + * @ingroup internal + */ + struct PolicyDecision + { + using SerializationStruct = PolicyDecisionSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member mode + safe_edge::common::PolicyMode mode = safe_edge::common::PolicyMode::POLICY_UNKNOWN; + /// Member reason + std::string reason ; + /// Member allow_non_safety + bool allow_non_safety = false; + /// Member allow_ota + bool allow_ota = false; + + // Ensure bounds + static_assert(255 != 0, "reason shall be bounded or --default_unbounded_max_size shall be used"); + + + }; + + struct PolicyDecisionSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<806ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const PolicyDecision& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.reason.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.reason) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.allow_non_safety) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.allow_ota) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + PolicyDecision& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.mode); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.reason); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.allow_non_safety); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.allow_ota); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const PolicyDecision& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.reason.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.mode) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.reason) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.allow_non_safety) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.allow_ota) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const PolicyDecision& first, + const PolicyDecision& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.mode, second.mode); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.reason, second.reason); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.allow_non_safety, second.allow_non_safety); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.allow_ota, second.allow_ota); + + return ret; + } + }; + + + struct PolicyDecisionTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = PolicyDecision; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::PolicyDecision"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return PolicyDecisionSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const PolicyDecision& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const PolicyDecision& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = PolicyDecisionSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + PolicyDecision& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = PolicyDecisionSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const PolicyDecision& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const PolicyDecision& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = PolicyDecisionSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct ChargingQuerySerialization; + + /*! + * @brief This struct represents the structure ChargingQuery defined by the user in the IDL file. + * @ingroup internal + */ + struct ChargingQuery + { + using SerializationStruct = ChargingQuerySerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member soc_pct + float soc_pct = 0.0; + + // Ensure bounds + }; + + struct ChargingQuerySerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<544ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingQuery& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.soc_pct) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ChargingQuery& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.soc_pct); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingQuery& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.soc_pct) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ChargingQuery& first, + const ChargingQuery& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.soc_pct, second.soc_pct); + + return ret; + } + }; + + + struct ChargingQueryTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ChargingQuery; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::ChargingQuery"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ChargingQuerySerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ChargingQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ChargingQuery& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ChargingQuerySerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ChargingQuery& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ChargingQuerySerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ChargingQuery& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ChargingQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ChargingQuerySerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct ChargingResponseSerialization; + + /*! + * @brief This struct represents the structure ChargingResponse defined by the user in the IDL file. + * @ingroup internal + */ + struct ChargingResponse + { + using SerializationStruct = ChargingResponseSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member preferred_charger_id + int32_t preferred_charger_id = 0; + /// Member preferred_charger_name + std::string preferred_charger_name ; + /// Member has_charger + bool has_charger = false; + + // Ensure bounds + static_assert(255 != 0, "preferred_charger_name shall be bounded or --default_unbounded_max_size shall be used"); + + }; + + struct ChargingResponseSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<805ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingResponse& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.preferred_charger_name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.preferred_charger_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.preferred_charger_name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.has_charger) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ChargingResponse& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.preferred_charger_id); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.preferred_charger_name); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.has_charger); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ChargingResponse& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.preferred_charger_name.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.preferred_charger_id) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.preferred_charger_name) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.has_charger) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ChargingResponse& first, + const ChargingResponse& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.preferred_charger_id, second.preferred_charger_id); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.preferred_charger_name, second.preferred_charger_name); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.has_charger, second.has_charger); + + return ret; + } + }; + + + struct ChargingResponseTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ChargingResponse; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::ChargingResponse"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ChargingResponseSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ChargingResponse& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ChargingResponse& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ChargingResponseSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ChargingResponse& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ChargingResponseSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ChargingResponse& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ChargingResponse& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ChargingResponseSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct RouteContextQuerySerialization; + + /*! + * @brief This struct represents the structure RouteContextQuery defined by the user in the IDL file. + * @ingroup internal + */ + struct RouteContextQuery + { + using SerializationStruct = RouteContextQuerySerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member vehicle_health + safe_edge::common::HealthStatus vehicle_health = safe_edge::common::HealthStatus::HEALTH_UNKNOWN; + + // Ensure bounds + }; + + struct RouteContextQuerySerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<544ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteContextQuery& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.vehicle_health) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + RouteContextQuery& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.vehicle_health); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteContextQuery& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.vehicle_health) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const RouteContextQuery& first, + const RouteContextQuery& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.vehicle_health, second.vehicle_health); + + return ret; + } + }; + + + struct RouteContextQueryTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = RouteContextQuery; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::RouteContextQuery"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return RouteContextQuerySerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const RouteContextQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const RouteContextQuery& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = RouteContextQuerySerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + RouteContextQuery& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = RouteContextQuerySerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const RouteContextQuery& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const RouteContextQuery& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = RouteContextQuerySerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct RouteContextResponseSerialization; + + /*! + * @brief This struct represents the structure RouteContextResponse defined by the user in the IDL file. + * @ingroup internal + */ + struct RouteContextResponse + { + using SerializationStruct = RouteContextResponseSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member transit_status + std::string transit_status ; + /// Member max_route_updates + int32_t max_route_updates = 0; + /// Member vehicles_seen + int32_t vehicles_seen = 0; + /// Member has_data + bool has_data = false; + + // Ensure bounds + static_assert(32 != 0, "transit_status shall be bounded or --default_unbounded_max_size shall be used"); + + + + }; + + struct RouteContextResponseSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<589ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteContextResponse& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.transit_status.size() <= 32)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.transit_status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.max_route_updates) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(3, sample.vehicles_seen) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(4, sample.has_data) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + RouteContextResponse& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.transit_status); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.max_route_updates); + break; + + case 3: + ret_value = inner_deser.deserialize_member(sample.vehicles_seen); + break; + + case 4: + ret_value = inner_deser.deserialize_member(sample.has_data); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const RouteContextResponse& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.transit_status.size() <= 32)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.transit_status) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.max_route_updates) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(3, sample.vehicles_seen) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(4, sample.has_data) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const RouteContextResponse& first, + const RouteContextResponse& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.transit_status, second.transit_status); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.max_route_updates, second.max_route_updates); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.vehicles_seen, second.vehicles_seen); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.has_data, second.has_data); + + return ret; + } + }; + + + struct RouteContextResponseTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = RouteContextResponse; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::RouteContextResponse"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return RouteContextResponseSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const RouteContextResponse& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const RouteContextResponse& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = RouteContextResponseSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + RouteContextResponse& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = RouteContextResponseSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const RouteContextResponse& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const RouteContextResponse& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = RouteContextResponseSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + + // Forward declaration + struct ServerAvailabilityStatusSerialization; + + /*! + * @brief This struct represents the structure ServerAvailabilityStatus defined by the user in the IDL file. + * @ingroup internal + */ + struct ServerAvailabilityStatus + { + using SerializationStruct = ServerAvailabilityStatusSerialization; + + static constexpr eprosima::safedds::gen::IDLExtensibilityKind IDL_EXTENSIBILITY = eprosima::safedds::gen::IDLExtensibilityKind::APPENDABLE; + + /// Member header + safe_edge::common::Header header ; + /// Member server_available + bool server_available = false; + /// Member detail + std::string detail ; + + // Ensure bounds + static_assert(255 != 0, "detail shall be bounded or --default_unbounded_max_size shall be used"); + }; + + struct ServerAvailabilityStatusSerialization + { + static constexpr uint32_t max_serialized_size() + { + // Return the precalculated max_serialized_size plus required: + // - Representation Header size (+4 B) since it is serialized in the typesupport serialization method + // - Alignment to 4 B since the serialization method adds padding + return eprosima::safedds::memory::align_to<804ULL + 4ULL, 4U>(); + } + + static constexpr uint32_t max_key_serialized_size() + { + return 0ULL; + } + + static eprosima::safedds::ReturnCode serialize( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServerAvailabilityStatus& sample) + { + using namespace safe_edge::internal; + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm( + ser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(1, sample.server_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type(2, sample.detail) : ret; + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + } + + static eprosima::safedds::ReturnCode deserialize( + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& deser, + ServerAvailabilityStatus& sample) + { + using namespace safe_edge::internal; + + // Store the values of the external members in auxiliary variables + + sample = {}; + + // Restore the values of the external members + + const eprosima::safedds::gen::EncodingAlgorithm encoding = + deser.get_xcdr_version() == eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 ? + eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR : + eprosima::safedds::gen::EncodingAlgorithm::DELIMITED_CDR; + + return deser.deserialize_type(encoding, + [&sample](eprosima::safedds::gen::SafeDDSTypeSupportDeserializer& inner_deser, const uint32_t id) -> eprosima::safedds::ReturnCode + { + eprosima::safedds::ReturnCode ret_value = eprosima::safedds::ReturnCode::OK; + switch (id) + { + case 0: + ret_value = inner_deser.deserialize_member(sample.header); + break; + + case 1: + ret_value = inner_deser.deserialize_member(sample.server_available); + break; + + case 2: + ret_value = inner_deser.deserialize_member(sample.detail); + break; + + default: + ret_value = eprosima::safedds::ReturnCode::UNIMPLEMENTED; + break; + } + return ret_value; + }); + } + + static eprosima::safedds::ReturnCode serialize_key( + eprosima::safedds::gen::SafeDDSTypeSupportSerializer& ser, + const ServerAvailabilityStatus& sample) + { + using namespace safe_edge::internal; + + + eprosima::safedds::gen::EncodingAlgorithm prev_encoding = ser.get_encoding_algorithm(); + eprosima::safedds::ReturnCode ret = ser.set_encoding_algorithm(eprosima::safedds::gen::EncodingAlgorithm::PLAIN_CDR2); + + ret = ((eprosima::safedds::ReturnCode::OK == ret) && (sample.detail.size() <= 255)) ? ret : eprosima::safedds::ReturnCode::ERROR; + + + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.begin_serialize_type() : ret; + + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(0, sample.header) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(1, sample.server_available) : ret; + ret = (eprosima::safedds::ReturnCode::OK == ret) ? ser.serialize_type_key(2, sample.detail) : ret; + + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.end_serialize_type(); + } + + if(eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser.set_encoding_algorithm(prev_encoding); + } + + return ret; + + } + + static bool compare( + const ServerAvailabilityStatus& first, + const ServerAvailabilityStatus& second) + { + using namespace safe_edge::internal; + + bool ret = true; + + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.header, second.header); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.server_available, second.server_available); + ret = ret && eprosima::safedds::gen::SafeDDSTypeSupportComparator::equal(first.detail, second.detail); + + return ret; + } + }; + + + struct ServerAvailabilityStatusTypeSupport : + public eprosima::safedds::dds::TypedTypeSupport + { + using DataType = ServerAvailabilityStatus; + + eprosima::safedds::dds::ReturnCode register_type( + eprosima::safedds::dds::DomainParticipant& participant, + const eprosima::safedds::memory::IStringView& type_name) noexcept override + { + return participant.register_type(*this, type_name); + } + + const eprosima::safedds::memory::IStringView& get_type_name() const noexcept override + { + static constexpr char const* default_name = "safe_edge::internal::ServerAvailabilityStatus"; + static const eprosima::safedds::memory::container::StaticString256 typesupport_name(default_name); + return typesupport_name; + } + + uint32_t max_serialized_size() const noexcept override + { + return ServerAvailabilityStatusSerialization::max_serialized_size(); + } + + uint32_t serialized_size( + const ServerAvailabilityStatus& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == sample_serialized_size(sample, xcdr_version, sample_size)) + { + sample_size = eprosima::safedds::memory::align_to( + eprosima::safedds::portable::safe_sizeof() + + sample_size, 4); + } + + return sample_size; + } + + eprosima::safedds::ReturnCode serialize( + const ServerAvailabilityStatus& sample, + eprosima::safedds::memory::IByteArrayView& buffer, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version) const noexcept override + { + eprosima::safedds::ReturnCode ret = + (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version) || (eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2 == xcdr_version) ? + eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + if(eprosima::safedds::portable::MACHINE_ENDIANNESS == eprosima::safedds::platform::Endianness::SAFEDDS_BIG_ENDIAN) + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_BE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_BE; + } + else + { + representation_header = + eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 == xcdr_version ? + eprosima::safedds::serialization::REPRESENTATION_HEADER_CDR_LE : + eprosima::safedds::serialization::REPRESENTATION_HEADER_DELIMITED_CDR_LE; + } + } + + uint32_t sample_size = 0; + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = sample_serialized_size(sample, xcdr_version, sample_size); + } + + uint32_t aligned_size = eprosima::safedds::memory::align_to(sample_size, 4); + uint32_t payload_size = eprosima::safedds::portable::safe_sizeof() + aligned_size; + + if (eprosima::safedds::ReturnCode::OK == ret && buffer.size() >= payload_size) + { + // Indicate the number of padding bytes in the encapsulation options + uint32_t required_padding = aligned_size - sample_size; + representation_header.encapsulation_options[1] = static_cast(required_padding); + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + ret = ser_rh.serialize_array( + representation_header.encapsulation_kind.data(), + eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_KIND_SIZE); + + if (eprosima::safedds::ReturnCode::OK == ret) + { + ret = ser_rh.serialize_array( + representation_header.encapsulation_options.data(), eprosima::safedds::serialization::RepresentationHeader::ENCAPSULATION_OPTIONS_SIZE); + } + + if (eprosima::safedds::ReturnCode::OK == ret && sample_size > 0) + { + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = { + &buffer[ser_rh.serializer_used_size()], + ser_rh.serializer_remaining_size() + }; + + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(payload_view, eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + + ret = ServerAvailabilityStatusSerialization::serialize(ser, sample); + + // Add padding + for (uint32_t i = 0; (eprosima::safedds::ReturnCode::OK == ret) && (i < required_padding); ++i) + { + ret = ser.serialize('\0'); + } + } + } + + return ret; + } + + eprosima::safedds::ReturnCode deserialize( + const eprosima::safedds::memory::IConstByteArrayView& buffer, + ServerAvailabilityStatus& sample) const noexcept override + { + eprosima::safedds::serialization::RepresentationHeader representation_header{}; + + eprosima::safedds::serialization::cdr::Deserializer deserializer_rh(buffer, eprosima::safedds::portable::MACHINE_ENDIANNESS, eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1); + eprosima::safedds::ReturnCode ret = eprosima::safedds::protocol::rtps::RTPSTypeSupport::deserialize(deserializer_rh, representation_header); + + // Check expected RepresentationHeader type to be valid and supported + const bool is_cdr_v1 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] <= 0x03U); + const bool is_cdr_v2 = representation_header.encapsulation_kind[0U] == 0U && (representation_header.encapsulation_kind[1U] >= 0x06U && representation_header.encapsulation_kind[1U] <= 0x0BU); + ret = (eprosima::safedds::ReturnCode::OK == ret && (is_cdr_v1 || is_cdr_v2)) ? eprosima::safedds::ReturnCode::OK : eprosima::safedds::ReturnCode::ERROR; + + // Skip payload + eprosima::safedds::memory::byte_array::ByteArrayView payload_view = {nullptr, 0U}; + uint32_t remaining_size = deserializer_rh.deserializer_remaining_size(); + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + uint32_t padding_size = static_cast(representation_header.encapsulation_options[1] & 0x03); + if (remaining_size >= padding_size) + { + remaining_size -= padding_size; + ret = deserializer_rh.deserializer_skip(remaining_size, payload_view); + } + else + { + ret = eprosima::safedds::ReturnCode::SERIALIZATION_INVALID_REPRESENTATION; + } + } + + if (eprosima::safedds::ReturnCode::OK == ret && remaining_size > 0) + { + // Get received endianness and deserialize payload + eprosima::safedds::gen::SafeDDSTypeSupportDeserializer deserializer(payload_view, + representation_header.get_endianness(), + is_cdr_v1 ? eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV1 : eprosima::safedds::serialization::cdr::XCDRVersion::XCDRV2); + + ret = ServerAvailabilityStatusSerialization::deserialize(deserializer, sample); + } + + return ret; + } + + eprosima::safedds::dds::ReturnCode get_key( + const ServerAvailabilityStatus& data, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(data); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + eprosima::safedds::dds::ReturnCode get_key_from_payload( + const eprosima::safedds::memory::IConstByteArrayView& view, + const eprosima::safedds::protocol::PayloadKind& kind, + eprosima::safedds::dds::KeyHash& key_hash) const noexcept override + { + key_hash = {}; + + static_cast(view); + static_cast(kind); + + return eprosima::safedds::dds::ReturnCode::ILLEGAL_OPERATION; + } + + bool has_keys() const noexcept override + { + return false; + } + + private: + + eprosima::safedds::ReturnCode sample_serialized_size( + const ServerAvailabilityStatus& sample, + eprosima::safedds::serialization::cdr::XCDRVersion xcdr_version, + uint32_t & serialized_size) const noexcept + { + serialized_size = 0; + + // Create a fake serializer to calculate the serialized size + eprosima::safedds::gen::SafeDDSTypeSupportSerializer ser(eprosima::safedds::portable::MACHINE_ENDIANNESS, xcdr_version); + eprosima::safedds::ReturnCode ret = ServerAvailabilityStatusSerialization::serialize(ser, sample); + + if(eprosima::safedds::ReturnCode::OK == ret) + { + serialized_size = ser.serializer_used_size(); + } + + return ret; + } + + }; + + + } // namespace internal +} // namespace safe_edge + +#endif // SAFEDDS_GENERATED__SAFE_EDGE_INTERNAL_INTERNAL_HPP \ No newline at end of file diff --git a/safe_dds/non_safety/CMakeLists.txt b/safe_dds/non_safety/CMakeLists.txt new file mode 100644 index 0000000..3c8186f --- /dev/null +++ b/safe_dds/non_safety/CMakeLists.txt @@ -0,0 +1,119 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_non_safety_domain LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(safedds REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +endif() + +set(SAFE_EDGE_NON_SAFETY_DOMAIN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_NON_SAFETY_DOMAIN_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -fno-exceptions + -fno-rtti + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_non_safety_domain_common + STATIC + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp + src/common/HeaderFactory.cpp +) +safe_edge_apply_common_build_settings(safe_edge_non_safety_domain_common) +target_include_directories( + safe_edge_non_safety_domain_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_non_safety_domain_common + PUBLIC + safedds +) + +add_executable( + safe_edge_cloud_gateway + src/apps/cloud_gateway_main.cpp + src/nodes/CloudGatewayNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_cloud_gateway) +target_include_directories( + safe_edge_cloud_gateway + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_cloud_gateway + PRIVATE + safe_edge_non_safety_domain_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +add_executable( + safe_edge_ota_service + src/apps/ota_service_main.cpp + src/nodes/OtaServiceNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_ota_service) +target_include_directories( + safe_edge_ota_service + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_ota_service + PRIVATE + safe_edge_non_safety_domain_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +add_executable( + safe_edge_infotainment + src/apps/infotainment_main.cpp + src/nodes/InfotainmentNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_infotainment) +target_include_directories( + safe_edge_infotainment + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_infotainment + PRIVATE + safe_edge_non_safety_domain_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment + RUNTIME DESTINATION bin +) diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/HeaderFactory.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/HeaderFactory.hpp new file mode 100644 index 0000000..22dcffa --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/HeaderFactory.hpp @@ -0,0 +1,34 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP + +#include + +#include +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace common { + +class HeaderFactory +{ +public: + + explicit HeaderFactory(std::string source_name); + + safe_edge::common::Header make_header( + const char* trace_suffix = nullptr); + + static uint64_t now_ms() noexcept; + +private: + + std::string source_name_; + uint64_t counter_ = 0U; +}; + +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp new file mode 100644 index 0000000..e34493d --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp @@ -0,0 +1,31 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP + +#include +#include +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + std::string service_name; + std::string source_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + uint16_t initial_peer_ports[4] = {}; + std::size_t initial_peer_count = 0U; +}; + +RuntimeConfig make_cloud_gateway_runtime_config(); +RuntimeConfig make_ota_service_runtime_config(); +RuntimeConfig make_infotainment_runtime_config(); + +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/TopicNames.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/TopicNames.hpp new file mode 100644 index 0000000..bea3066 --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/TopicNames.hpp @@ -0,0 +1,28 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace non_safety_domain { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept; +const char* charger_types() noexcept; +const char* charging_sessions() noexcept; +const char* transit_health() noexcept; +const char* route_metrics() noexcept; +const char* transit_metrics() noexcept; +const char* service_heartbeat() noexcept; +const char* charging_query() noexcept; +const char* charging_response() noexcept; +const char* route_context_query() noexcept; +const char* route_context_response() noexcept; +const char* server_query() noexcept; +const char* server_availability_status() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/CloudGatewayNode.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/CloudGatewayNode.hpp new file mode 100644 index 0000000..0459916 --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/CloudGatewayNode.hpp @@ -0,0 +1,318 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_CLOUDGATEWAYNODE_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_CLOUDGATEWAYNODE_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +class CloudGatewayNode +{ +public: + + explicit CloudGatewayNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(CloudGatewayNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class ChargerLocationsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargerLocationsListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class ChargerTypesListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargerTypesListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class ChargingSessionsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargingSessionsListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class TransitHealthListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit TransitHealthListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class RouteMetricsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit RouteMetricsListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class TransitMetricsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit TransitMetricsListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + class ChargingQueryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargingQueryListener(CloudGatewayNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + CloudGatewayNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void on_charger_locations_received(const safe_edge::pilot_server::ChargerLocationSeq& locations); + void on_charger_types_received(const safe_edge::pilot_server::ChargerTypeSeq& types); + void on_charging_sessions_received(const safe_edge::pilot_server::ChargingSessionSeq& sessions); + void on_transit_health_received(const safe_edge::pilot_server::TransitHealth& health); + void on_route_metrics_received(const safe_edge::pilot_server::RouteMetricSeq& metrics); + void on_transit_metrics_received(const safe_edge::pilot_server::TransitMetrics& metrics); + void publish_heartbeat(); + void on_peer_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void on_charging_query_received(const safe_edge::internal::ChargingQuery& query); + void publish_charging_response(); + void publish_server_availability_status(bool server_available, const char* detail); + + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + ChargerLocationsListener charger_locations_listener_; + ChargerTypesListener charger_types_listener_; + ChargingSessionsListener charging_sessions_listener_; + TransitHealthListener transit_health_listener_; + RouteMetricsListener route_metrics_listener_; + TransitMetricsListener transit_metrics_listener_; + HeartbeatListener heartbeat_listener_; + ChargingQueryListener charging_query_listener_; + + safe_edge::pilot_server::ChargerLocationTypeSupport charger_locations_type_support_; + safe_edge::pilot_server::ChargerTypeTypeSupport charger_types_type_support_; + safe_edge::pilot_server::ChargingSessionTypeSupport charging_sessions_type_support_; + safe_edge::pilot_server::TransitHealthTypeSupport transit_health_type_support_; + safe_edge::pilot_server::RouteMetricTypeSupport route_metrics_type_support_; + safe_edge::pilot_server::TransitMetricsTypeSupport transit_metrics_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + safe_edge::internal::ChargingQueryTypeSupport charging_query_type_support_; + safe_edge::internal::ChargingResponseTypeSupport charging_response_type_support_; + safe_edge::pilot_server::ServerQueryTypeSupport server_query_type_support_; + safe_edge::internal::ServerAvailabilityStatusTypeSupport server_availability_status_type_support_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::memory::container::StaticList initial_peers_; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* charger_locations_topic_ = nullptr; + eprosima::safedds::dds::Topic* charger_types_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_sessions_topic_ = nullptr; + eprosima::safedds::dds::Topic* transit_health_topic_ = nullptr; + eprosima::safedds::dds::Topic* route_metrics_topic_ = nullptr; + eprosima::safedds::dds::Topic* transit_metrics_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_response_topic_ = nullptr; + eprosima::safedds::dds::Topic* server_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* server_availability_status_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 charger_locations_topic_name_; + eprosima::safedds::memory::container::StaticString256 charger_types_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_sessions_topic_name_; + eprosima::safedds::memory::container::StaticString256 transit_health_topic_name_; + eprosima::safedds::memory::container::StaticString256 route_metrics_topic_name_; + eprosima::safedds::memory::container::StaticString256 transit_metrics_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_response_topic_name_; + eprosima::safedds::memory::container::StaticString256 server_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 server_availability_status_topic_name_; + + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* charging_response_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* server_query_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* server_availability_status_datawriter_ = nullptr; + + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* charging_response_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* server_query_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* server_availability_status_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* charger_locations_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charger_types_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charging_sessions_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* transit_health_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* route_metrics_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* transit_metrics_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* heartbeat_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charging_query_datareader_ = nullptr; + + eprosima::safedds::dds::TypedDataReader* charger_locations_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charger_types_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charging_sessions_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* transit_health_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* route_metrics_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* transit_metrics_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charging_query_reader_ = + nullptr; + + eprosima::safedds::execution::Timer heartbeat_timer_; + + safe_edge::pilot_server::ChargerLocation cached_chargers_[3]; + int32_t cached_charger_count_ = 0; + bool charger_query_pending_ = false; + + uint64_t last_server_hb_ms_ = 0U; + bool server_available_ = false; +}; + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_CLOUDGATEWAYNODE_HPP diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/InfotainmentNode.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/InfotainmentNode.hpp new file mode 100644 index 0000000..1f59414 --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/InfotainmentNode.hpp @@ -0,0 +1,236 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_INFOTAINMENTNODE_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_INFOTAINMENTNODE_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +class InfotainmentNode +{ +public: + + explicit InfotainmentNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(InfotainmentNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + class TransitHealthListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit TransitHealthListener(InfotainmentNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + class RouteMetricsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit RouteMetricsListener(InfotainmentNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + class TransitMetricsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit TransitMetricsListener(InfotainmentNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(InfotainmentNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + class RouteContextQueryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit RouteContextQueryListener(InfotainmentNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + InfotainmentNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void on_transit_health_received(const safe_edge::pilot_server::TransitHealth& health); + void on_route_metrics_received(const safe_edge::pilot_server::RouteMetricSeq& metrics); + void on_transit_metrics_received(const safe_edge::pilot_server::TransitMetrics& metrics); + void publish_heartbeat(); + void on_peer_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void on_route_context_query_received(const safe_edge::internal::RouteContextQuery& query); + void publish_route_context_response(const safe_edge::internal::RouteContextQuery& query); + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + TransitHealthListener transit_health_listener_; + RouteMetricsListener route_metrics_listener_; + TransitMetricsListener transit_metrics_listener_; + HeartbeatListener heartbeat_listener_; + RouteContextQueryListener route_context_query_listener_; + + safe_edge::pilot_server::TransitHealthTypeSupport transit_health_type_support_; + safe_edge::pilot_server::RouteMetricTypeSupport route_metrics_type_support_; + safe_edge::pilot_server::TransitMetricsTypeSupport transit_metrics_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + safe_edge::internal::RouteContextQueryTypeSupport route_context_query_type_support_; + safe_edge::internal::RouteContextResponseTypeSupport route_context_response_type_support_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::memory::container::StaticList initial_peers_; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* transit_health_topic_ = nullptr; + eprosima::safedds::dds::Topic* route_metrics_topic_ = nullptr; + eprosima::safedds::dds::Topic* transit_metrics_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + eprosima::safedds::dds::Topic* route_context_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* route_context_response_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 transit_health_topic_name_; + eprosima::safedds::memory::container::StaticString256 route_metrics_topic_name_; + eprosima::safedds::memory::container::StaticString256 transit_metrics_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + eprosima::safedds::memory::container::StaticString256 route_context_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 route_context_response_topic_name_; + + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = + nullptr; + + eprosima::safedds::dds::DataWriter* route_context_response_datawriter_ = nullptr; + eprosima::safedds::dds::TypedDataWriter* route_context_response_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* transit_health_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* route_metrics_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* transit_metrics_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* heartbeat_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* route_context_query_datareader_ = nullptr; + + eprosima::safedds::dds::TypedDataReader* transit_health_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* route_metrics_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* transit_metrics_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* route_context_query_reader_ = + nullptr; + + eprosima::safedds::execution::Timer heartbeat_timer_; + + safe_edge::pilot_server::TransitHealth cached_transit_health_{}; + bool have_transit_health_ = false; + + safe_edge::pilot_server::RouteMetric cached_route_metrics_[512]; + int32_t cached_route_metric_count_ = 0; + + int32_t cached_vehicles_seen_ = 0; + bool have_transit_metrics_ = false; +}; + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_INFOTAINMENTNODE_HPP diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/OtaServiceNode.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/OtaServiceNode.hpp new file mode 100644 index 0000000..a2ad048 --- /dev/null +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/nodes/OtaServiceNode.hpp @@ -0,0 +1,196 @@ +#ifndef SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_OTASERVICENODE_HPP +#define SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_OTASERVICENODE_HPP + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +class OtaServiceNode +{ +public: + + explicit OtaServiceNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(OtaServiceNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + OtaServiceNode& owner_; + }; + + class ChargerLocationsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargerLocationsListener(OtaServiceNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + OtaServiceNode& owner_; + }; + + class ChargerTypesListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargerTypesListener(OtaServiceNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + OtaServiceNode& owner_; + }; + + class ChargingSessionsListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargingSessionsListener(OtaServiceNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + OtaServiceNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(OtaServiceNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + OtaServiceNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void on_charger_locations_received(const safe_edge::pilot_server::ChargerLocationSeq& locations); + void on_charger_types_received(const safe_edge::pilot_server::ChargerTypeSeq& types); + void on_charging_sessions_received(const safe_edge::pilot_server::ChargingSessionSeq& sessions); + void publish_heartbeat(); + void on_peer_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void log_subscription_match(const char* topic_name, int32_t total_count) const; + void log_publication_match(const char* topic_name, int32_t total_count) const; + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + ChargerLocationsListener charger_locations_listener_; + ChargerTypesListener charger_types_listener_; + ChargingSessionsListener charging_sessions_listener_; + HeartbeatListener heartbeat_listener_; + + safe_edge::pilot_server::ChargerLocationTypeSupport charger_locations_type_support_; + safe_edge::pilot_server::ChargerTypeTypeSupport charger_types_type_support_; + safe_edge::pilot_server::ChargingSessionTypeSupport charging_sessions_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::memory::container::StaticList initial_peers_; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* charger_locations_topic_ = nullptr; + eprosima::safedds::dds::Topic* charger_types_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_sessions_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 charger_locations_topic_name_; + eprosima::safedds::memory::container::StaticString256 charger_types_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_sessions_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* charger_locations_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charger_types_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charging_sessions_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* heartbeat_datareader_ = nullptr; + + eprosima::safedds::dds::TypedDataReader* charger_locations_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charger_types_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charging_sessions_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + + eprosima::safedds::execution::Timer heartbeat_timer_; +}; + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_NON_SAFETY_DOMAIN_NODES_OTASERVICENODE_HPP diff --git a/safe_dds/non_safety/src/apps/cloud_gateway_main.cpp b/safe_dds/non_safety/src/apps/cloud_gateway_main.cpp new file mode 100644 index 0000000..c9acb44 --- /dev/null +++ b/safe_dds/non_safety/src/apps/cloud_gateway_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::non_safety_domain::common::RuntimeConfig config = + safe_edge::non_safety_domain::common::make_cloud_gateway_runtime_config(); + + safe_edge::non_safety_domain::nodes::CloudGatewayNode node(config); + return node.run(); +} diff --git a/safe_dds/non_safety/src/apps/infotainment_main.cpp b/safe_dds/non_safety/src/apps/infotainment_main.cpp new file mode 100644 index 0000000..e635c53 --- /dev/null +++ b/safe_dds/non_safety/src/apps/infotainment_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::non_safety_domain::common::RuntimeConfig config = + safe_edge::non_safety_domain::common::make_infotainment_runtime_config(); + + safe_edge::non_safety_domain::nodes::InfotainmentNode node(config); + return node.run(); +} diff --git a/safe_dds/non_safety/src/apps/ota_service_main.cpp b/safe_dds/non_safety/src/apps/ota_service_main.cpp new file mode 100644 index 0000000..f2eae54 --- /dev/null +++ b/safe_dds/non_safety/src/apps/ota_service_main.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() +{ + const safe_edge::non_safety_domain::common::RuntimeConfig config = + safe_edge::non_safety_domain::common::make_ota_service_runtime_config(); + + safe_edge::non_safety_domain::nodes::OtaServiceNode node(config); + return node.run(); +} diff --git a/safe_dds/non_safety/src/common/HeaderFactory.cpp b/safe_dds/non_safety/src/common/HeaderFactory.cpp new file mode 100644 index 0000000..6ce6876 --- /dev/null +++ b/safe_dds/non_safety/src/common/HeaderFactory.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include +#include +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace common { + +HeaderFactory::HeaderFactory( + std::string source_name) + : source_name_(std::move(source_name)) +{ +} + +safe_edge::common::Header HeaderFactory::make_header( + const char* trace_suffix) +{ + safe_edge::common::Header header; + header.source = source_name_; + header.timestamp_ms = now_ms(); + header.trace_id = source_name_; + header.trace_id += "-"; + + std::array counter_buffer{}; + std::snprintf( + counter_buffer.data(), + counter_buffer.size(), + "%llu", + static_cast(counter_++)); + header.trace_id += counter_buffer.data(); + + if (nullptr != trace_suffix && trace_suffix[0] != '\0') + { + header.trace_id += "-"; + header.trace_id += trace_suffix; + } + + return header; +} + +uint64_t HeaderFactory::now_ms() noexcept +{ + const auto now = std::chrono::system_clock::now(); + const auto since_epoch = now.time_since_epoch(); + return static_cast( + std::chrono::duration_cast(since_epoch).count()); +} + +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/non_safety/src/common/RuntimeConfig.cpp b/safe_dds/non_safety/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..92f6f72 --- /dev/null +++ b/safe_dds/non_safety/src/common/RuntimeConfig.cpp @@ -0,0 +1,54 @@ +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace common { + +RuntimeConfig make_cloud_gateway_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeCloudGatewayParticipant"; + config.service_name = "cloud_gateway"; + config.source_name = "cloud_gateway"; + config.domain_id = 0U; + config.participant_port = 8011U; + config.initial_peer_ports[0] = 8012U; + config.initial_peer_ports[1] = 8013U; + config.initial_peer_ports[2] = 8002U; + config.initial_peer_count = 3U; + return config; +} + +RuntimeConfig make_ota_service_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeOtaServiceParticipant"; + config.service_name = "ota_service"; + config.source_name = "ota_service"; + config.domain_id = 0U; + config.participant_port = 8012U; + config.initial_peer_ports[0] = 8011U; + config.initial_peer_ports[1] = 8013U; + config.initial_peer_count = 2U; + return config; +} + +RuntimeConfig make_infotainment_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeInfotainmentParticipant"; + config.service_name = "infotainment"; + config.source_name = "infotainment"; + config.domain_id = 0U; + config.participant_port = 8013U; + config.initial_peer_ports[0] = 8011U; + config.initial_peer_ports[1] = 8012U; + config.initial_peer_ports[2] = 8001U; + config.initial_peer_ports[3] = 8002U; + config.initial_peer_count = 4U; + return config; +} + +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/non_safety/src/common/TopicNames.cpp b/safe_dds/non_safety/src/common/TopicNames.cpp new file mode 100644 index 0000000..70928a8 --- /dev/null +++ b/safe_dds/non_safety/src/common/TopicNames.cpp @@ -0,0 +1,76 @@ +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace common { +namespace topic_names { + +const char* charger_locations() noexcept +{ + return "safe_edge.pilot_server.charger_locations"; +} + +const char* charger_types() noexcept +{ + return "safe_edge.pilot_server.charger_types"; +} + +const char* charging_sessions() noexcept +{ + return "safe_edge.pilot_server.charging_sessions"; +} + +const char* transit_health() noexcept +{ + return "safe_edge.pilot_server.transit_health"; +} + +const char* route_metrics() noexcept +{ + return "safe_edge.pilot_server.route_metrics"; +} + +const char* transit_metrics() noexcept +{ + return "safe_edge.pilot_server.transit_metrics"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +const char* charging_query() noexcept +{ + return "safe_edge.internal.charging_query"; +} + +const char* charging_response() noexcept +{ + return "safe_edge.internal.charging_response"; +} + +const char* route_context_query() noexcept +{ + return "safe_edge.internal.route_context_query"; +} + +const char* route_context_response() noexcept +{ + return "safe_edge.internal.route_context_response"; +} + +const char* server_query() noexcept +{ + return "safe_edge.pilot_server.server_query"; +} + +const char* server_availability_status() noexcept +{ + return "safe_edge.internal.server_availability_status"; +} + +} // namespace topic_names +} // namespace common +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp new file mode 100644 index 0000000..f14e553 --- /dev/null +++ b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp @@ -0,0 +1,823 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {5, 0}; + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[cloud_gateway] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& topic_name, + TypeSupportT& type_support) +{ + return participant.create_topic( + topic_name, + type_support.get_type_name(), + eprosima::safedds::dds::TopicQos{}, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +} // namespace + +CloudGatewayNode::ParticipantListener::ParticipantListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void CloudGatewayNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +CloudGatewayNode::ChargerLocationsListener::ChargerLocationsListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::ChargerLocationsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast charger_locations reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargerLocation sample{}; + safe_edge::pilot_server::ChargerLocationSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charger_locations_received(batch); + } +} + +CloudGatewayNode::ChargerTypesListener::ChargerTypesListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::ChargerTypesListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast charger_types reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargerType sample{}; + safe_edge::pilot_server::ChargerTypeSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charger_types_received(batch); + } +} + +CloudGatewayNode::ChargingSessionsListener::ChargingSessionsListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::ChargingSessionsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast charging_sessions reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargingSession sample{}; + safe_edge::pilot_server::ChargingSessionSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charging_sessions_received(batch); + } +} + +CloudGatewayNode::TransitHealthListener::TransitHealthListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::TransitHealthListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast transit_health reader" << std::endl; + return; + } + + safe_edge::pilot_server::TransitHealth sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_transit_health_received(sample); + } + } +} + +CloudGatewayNode::RouteMetricsListener::RouteMetricsListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::RouteMetricsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast route_metrics reader" << std::endl; + return; + } + + safe_edge::pilot_server::RouteMetric sample{}; + safe_edge::pilot_server::RouteMetricSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_route_metrics_received(batch); + } +} + +CloudGatewayNode::TransitMetricsListener::TransitMetricsListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::TransitMetricsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast transit_metrics reader" << std::endl; + return; + } + + safe_edge::pilot_server::TransitMetrics sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_transit_metrics_received(sample); + } + } +} + +CloudGatewayNode::HeartbeatListener::HeartbeatListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_peer_heartbeat_received(sample); + } + } +} + +CloudGatewayNode::ChargingQueryListener::ChargingQueryListener( + CloudGatewayNode& owner) + : owner_(owner) +{ +} + +void CloudGatewayNode::ChargingQueryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[cloud_gateway] Failed to downcast charging_query reader" << std::endl; + return; + } + + safe_edge::internal::ChargingQuery sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_charging_query_received(sample); + } + } +} + +CloudGatewayNode::CloudGatewayNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , charger_locations_listener_(*this) + , charger_types_listener_(*this) + , charging_sessions_listener_(*this) + , transit_health_listener_(*this) + , route_metrics_listener_(*this) + , transit_metrics_listener_(*this) + , heartbeat_listener_(*this) + , charging_query_listener_(*this) + , heartbeat_timer_(TIMEOUT) +{ +} + +int CloudGatewayNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[cloud_gateway] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + + constexpr uint64_t SERVER_HB_TIMEOUT_MS = 10000U; + if (last_server_hb_ms_ > 0U && + (common::HeaderFactory::now_ms() - last_server_hb_ms_) > SERVER_HB_TIMEOUT_MS) + { + if (server_available_) + { + server_available_ = false; + publish_server_availability_status(false, "server_lost"); + } + } + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool CloudGatewayNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool CloudGatewayNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + } + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[cloud_gateway] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool CloudGatewayNode::register_types() +{ + return register_type(*participant_, charger_locations_type_support_, "ChargerLocationSeq") && + register_type(*participant_, charger_types_type_support_, "ChargerTypeSeq") && + register_type(*participant_, charging_sessions_type_support_, "ChargingSessionSeq") && + register_type(*participant_, transit_health_type_support_, "TransitHealth") && + register_type(*participant_, route_metrics_type_support_, "RouteMetricSeq") && + register_type(*participant_, transit_metrics_type_support_, "TransitMetrics") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat") && + register_type(*participant_, charging_query_type_support_, "ChargingQuery") && + register_type(*participant_, charging_response_type_support_, "ChargingResponse") && + register_type(*participant_, server_query_type_support_, "ServerQuery") && + register_type(*participant_, server_availability_status_type_support_, "ServerAvailabilityStatus"); +} + +bool CloudGatewayNode::create_topics() +{ + charger_locations_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charger_locations()); + charger_locations_topic_ = create_topic(*participant_, charger_locations_topic_name_, charger_locations_type_support_); + if (nullptr == charger_locations_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: charger_locations" << std::endl; return false; } + + charger_types_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charger_types()); + charger_types_topic_ = create_topic(*participant_, charger_types_topic_name_, charger_types_type_support_); + if (nullptr == charger_types_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: charger_types" << std::endl; return false; } + + charging_sessions_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_sessions()); + charging_sessions_topic_ = create_topic(*participant_, charging_sessions_topic_name_, charging_sessions_type_support_); + if (nullptr == charging_sessions_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: charging_sessions" << std::endl; return false; } + + transit_health_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::transit_health()); + transit_health_topic_ = create_topic(*participant_, transit_health_topic_name_, transit_health_type_support_); + if (nullptr == transit_health_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: transit_health" << std::endl; return false; } + + route_metrics_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::route_metrics()); + route_metrics_topic_ = create_topic(*participant_, route_metrics_topic_name_, route_metrics_type_support_); + if (nullptr == route_metrics_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: route_metrics" << std::endl; return false; } + + transit_metrics_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::transit_metrics()); + transit_metrics_topic_ = create_topic(*participant_, transit_metrics_topic_name_, transit_metrics_type_support_); + if (nullptr == transit_metrics_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: transit_metrics" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: service_heartbeat" << std::endl; return false; } + + charging_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_query()); + charging_query_topic_ = create_topic(*participant_, charging_query_topic_name_, charging_query_type_support_); + if (nullptr == charging_query_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: charging_query" << std::endl; return false; } + + charging_response_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_response()); + charging_response_topic_ = create_topic(*participant_, charging_response_topic_name_, charging_response_type_support_); + if (nullptr == charging_response_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: charging_response" << std::endl; return false; } + + server_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::server_query()); + server_query_topic_ = create_topic(*participant_, server_query_topic_name_, server_query_type_support_); + if (nullptr == server_query_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: server_query" << std::endl; return false; } + + server_availability_status_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::server_availability_status()); + server_availability_status_topic_ = create_topic(*participant_, server_availability_status_topic_name_, server_availability_status_type_support_); + if (nullptr == server_availability_status_topic_) { std::cerr << "[cloud_gateway] Failed to create topic: server_availability_status" << std::endl; return false; } + + return true; +} + +bool CloudGatewayNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[cloud_gateway] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + service_heartbeat_datawriter_ = publisher_->create_datawriter( + *service_heartbeat_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + + charging_response_datawriter_ = publisher_->create_datawriter( + *charging_response_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + charging_response_writer_ = downcast_writer(charging_response_datawriter_); + + server_query_datawriter_ = publisher_->create_datawriter( + *server_query_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + server_query_writer_ = downcast_writer(server_query_datawriter_); + + server_availability_status_datawriter_ = publisher_->create_datawriter( + *server_availability_status_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + server_availability_status_writer_ = downcast_writer(server_availability_status_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + charger_locations_datareader_ = subscriber_->create_datareader(*charger_locations_topic_, reader_qos, &charger_locations_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charger_types_datareader_ = subscriber_->create_datareader(*charger_types_topic_, reader_qos, &charger_types_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charging_sessions_datareader_ = subscriber_->create_datareader(*charging_sessions_topic_, reader_qos, &charging_sessions_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + transit_health_datareader_ = subscriber_->create_datareader(*transit_health_topic_, reader_qos, &transit_health_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + route_metrics_datareader_ = subscriber_->create_datareader(*route_metrics_topic_, reader_qos, &route_metrics_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + transit_metrics_datareader_ = subscriber_->create_datareader(*transit_metrics_topic_, reader_qos, &transit_metrics_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_datareader_ = subscriber_->create_datareader(*service_heartbeat_topic_, reader_qos, &heartbeat_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charging_query_datareader_ = subscriber_->create_datareader(*charging_query_topic_, reader_qos, &charging_query_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + charger_locations_reader_ = downcast_reader(charger_locations_datareader_); + charger_types_reader_ = downcast_reader(charger_types_datareader_); + charging_sessions_reader_ = downcast_reader(charging_sessions_datareader_); + transit_health_reader_ = downcast_reader(transit_health_datareader_); + route_metrics_reader_ = downcast_reader(route_metrics_datareader_); + transit_metrics_reader_ = downcast_reader(transit_metrics_datareader_); + heartbeat_reader_ = downcast_reader(heartbeat_datareader_); + charging_query_reader_ = downcast_reader(charging_query_datareader_); + + const bool success = nullptr != service_heartbeat_writer_ && + nullptr != charging_response_writer_ && + nullptr != server_query_writer_ && + nullptr != server_availability_status_writer_ && + nullptr != charger_locations_reader_ && + nullptr != charger_types_reader_ && + nullptr != charging_sessions_reader_ && + nullptr != transit_health_reader_ && + nullptr != route_metrics_reader_ && + nullptr != transit_metrics_reader_ && + nullptr != heartbeat_reader_ && + nullptr != charging_query_reader_; + + if (!success) + { + std::cerr << "[cloud_gateway] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool CloudGatewayNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_response_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == server_query_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == server_availability_status_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_locations_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_types_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_sessions_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == transit_health_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == route_metrics_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == transit_metrics_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_query_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[cloud_gateway] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool CloudGatewayNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[cloud_gateway] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void CloudGatewayNode::start_timers() noexcept +{ + heartbeat_timer_.start(); +} + +void CloudGatewayNode::on_charger_locations_received( + const safe_edge::pilot_server::ChargerLocationSeq& locations) +{ + cached_charger_count_ = 0; + for (uint32_t i = 0; i < locations.size() && cached_charger_count_ < 3; ++i) + { + cached_chargers_[cached_charger_count_++] = locations[i]; + } + std::cout << "[cloud_gateway] Received ChargerLocationSeq count=" << locations.size() << std::endl; + + if (charger_query_pending_) + { + charger_query_pending_ = false; + publish_charging_response(); + } +} + +void CloudGatewayNode::on_charger_types_received( + const safe_edge::pilot_server::ChargerTypeSeq& types) +{ + std::cout << "[cloud_gateway] Received ChargerTypeSeq count=" << types.size() << std::endl; +} + +void CloudGatewayNode::on_charging_sessions_received( + const safe_edge::pilot_server::ChargingSessionSeq& sessions) +{ + std::cout << "[cloud_gateway] Received ChargingSessionSeq count=" << sessions.size() << std::endl; +} + +void CloudGatewayNode::on_transit_health_received( + const safe_edge::pilot_server::TransitHealth& health) +{ + std::cout << "[cloud_gateway] Received TransitHealth status=" << health.status + << " last_fetch_ts=" << health.last_fetch_ts << std::endl; +} + +void CloudGatewayNode::on_route_metrics_received( + const safe_edge::pilot_server::RouteMetricSeq& metrics) +{ + std::cout << "[cloud_gateway] Received RouteMetricSeq count=" << metrics.size() << std::endl; +} + +void CloudGatewayNode::on_transit_metrics_received( + const safe_edge::pilot_server::TransitMetrics& metrics) +{ + std::cout << "[cloud_gateway] Received TransitMetrics routes=" << metrics.by_route.size() + << " vehicles_seen=" << metrics.vehicles_seen << std::endl; +} + +void CloudGatewayNode::on_charging_query_received( + const safe_edge::internal::ChargingQuery& query) +{ + std::cout << "[cloud_gateway] Received ChargingQuery soc_pct=" << query.soc_pct << std::endl; + + charger_query_pending_ = true; + + safe_edge::pilot_server::ServerQuery server_request{}; + server_request.requested_by = runtime_config_.source_name; + server_request.requested_data_type = safe_edge::pilot_server::RequestedDataType::CHARGER_LOCATION; + + std::cout << "[cloud_gateway] Forwarding query to server" << std::endl; + + if (eprosima::safedds::dds::ReturnCode::OK != + server_query_writer_->write(server_request, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[cloud_gateway] Failed to publish ServerQuery" << std::endl; + charger_query_pending_ = false; + } +} + +void CloudGatewayNode::publish_charging_response() +{ + safe_edge::internal::ChargingResponse response{}; + response.header = header_factory_.make_header("charging_response"); + + if (cached_charger_count_ > 0) + { + response.preferred_charger_id = cached_chargers_[0].id; + response.preferred_charger_name = cached_chargers_[0].name; + response.has_charger = true; + std::cout << "[cloud_gateway] Responding with charger id=" << response.preferred_charger_id + << " name=" << response.preferred_charger_name << std::endl; + } + else + { + response.preferred_charger_id = 0; + response.has_charger = false; + std::cout << "[cloud_gateway] No cached chargers, responding with has_charger=false" << std::endl; + } + + if (eprosima::safedds::dds::ReturnCode::OK != charging_response_writer_->write(response, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[cloud_gateway] Failed to publish ChargingResponse" << std::endl; + return; + } + + std::cout << "[cloud_gateway] Published ChargingResponse" << std::endl; +} + +void CloudGatewayNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat; + heartbeat.header_st = header_factory_.make_header("service_heartbeat"); + heartbeat.service_name = runtime_config_.service_name; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[cloud_gateway] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[cloud_gateway] Published ServiceHeartbeat" << std::endl; +} + +void CloudGatewayNode::on_peer_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + if (heartbeat.service_name == runtime_config_.service_name) + { + return; + } + + if (heartbeat.service_name == "server") + { + const bool was_available = server_available_; + last_server_hb_ms_ = common::HeaderFactory::now_ms(); + server_available_ = true; + if (!was_available) + { + publish_server_availability_status(true, "server_up"); + } + return; + } + + static_cast(heartbeat); +} + +void CloudGatewayNode::publish_server_availability_status( + bool server_available, + const char* detail) +{ + safe_edge::internal::ServerAvailabilityStatus msg{}; + msg.header = header_factory_.make_header("server_availability_status"); + msg.server_available = server_available; + msg.detail = detail; + + if (eprosima::safedds::dds::ReturnCode::OK != + server_availability_status_writer_->write(msg, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[cloud_gateway] Failed to publish ServerAvailabilityStatus" << std::endl; + return; + } + + std::cout << "[cloud_gateway] Published ServerAvailabilityStatus server_available=" + << (server_available ? "true" : "false") + << " detail=" << detail << std::endl; +} + +void CloudGatewayNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[cloud_gateway] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void CloudGatewayNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[cloud_gateway] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint CloudGatewayNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp new file mode 100644 index 0000000..db4cbcc --- /dev/null +++ b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp @@ -0,0 +1,622 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {5, 0}; + +const char* health_status_to_text( + safe_edge::common::HealthStatus status) noexcept +{ + switch (status) + { + case safe_edge::common::HealthStatus::HEALTH_OK: + return "HEALTH_OK"; + case safe_edge::common::HealthStatus::HEALTH_DEGRADED: + return "HEALTH_DEGRADED"; + case safe_edge::common::HealthStatus::HEALTH_ERROR: + return "HEALTH_ERROR"; + case safe_edge::common::HealthStatus::HEALTH_UNKNOWN: + return "HEALTH_UNKNOWN"; + default: + return "UNKNOWN"; + } +} + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[infotainment] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& topic_name, + TypeSupportT& type_support) +{ + return participant.create_topic( + topic_name, + type_support.get_type_name(), + eprosima::safedds::dds::TopicQos{}, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +} // namespace + +InfotainmentNode::ParticipantListener::ParticipantListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void InfotainmentNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +InfotainmentNode::TransitHealthListener::TransitHealthListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::TransitHealthListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[infotainment] Failed to downcast transit_health reader" << std::endl; + return; + } + + safe_edge::pilot_server::TransitHealth sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_transit_health_received(sample); + } + } +} + +InfotainmentNode::RouteMetricsListener::RouteMetricsListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::RouteMetricsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[infotainment] Failed to downcast route_metrics reader" << std::endl; + return; + } + + safe_edge::pilot_server::RouteMetric sample{}; + safe_edge::pilot_server::RouteMetricSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_route_metrics_received(batch); + } +} + +InfotainmentNode::TransitMetricsListener::TransitMetricsListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::TransitMetricsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[infotainment] Failed to downcast transit_metrics reader" << std::endl; + return; + } + + safe_edge::pilot_server::TransitMetrics sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_transit_metrics_received(sample); + } + } +} + +InfotainmentNode::HeartbeatListener::HeartbeatListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[infotainment] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_peer_heartbeat_received(sample); + } + } +} + +InfotainmentNode::RouteContextQueryListener::RouteContextQueryListener( + InfotainmentNode& owner) + : owner_(owner) +{ +} + +void InfotainmentNode::RouteContextQueryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[infotainment] Failed to downcast route_context_query reader" << std::endl; + return; + } + + safe_edge::internal::RouteContextQuery sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_route_context_query_received(sample); + } + } +} + +InfotainmentNode::InfotainmentNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , transit_health_listener_(*this) + , route_metrics_listener_(*this) + , transit_metrics_listener_(*this) + , heartbeat_listener_(*this) + , route_context_query_listener_(*this) + , heartbeat_timer_(TIMEOUT) +{ +} + +int InfotainmentNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[infotainment] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool InfotainmentNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool InfotainmentNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + } + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[infotainment] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool InfotainmentNode::register_types() +{ + return register_type(*participant_, transit_health_type_support_, "TransitHealth") && + register_type(*participant_, route_metrics_type_support_, "RouteMetricSeq") && + register_type(*participant_, transit_metrics_type_support_, "TransitMetrics") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat") && + register_type(*participant_, route_context_query_type_support_, "RouteContextQuery") && + register_type(*participant_, route_context_response_type_support_, "RouteContextResponse"); +} + +bool InfotainmentNode::create_topics() +{ + transit_health_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::transit_health()); + transit_health_topic_ = create_topic(*participant_, transit_health_topic_name_, transit_health_type_support_); + if (nullptr == transit_health_topic_) { std::cerr << "[infotainment] Failed to create topic: transit_health" << std::endl; return false; } + + route_metrics_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::route_metrics()); + route_metrics_topic_ = create_topic(*participant_, route_metrics_topic_name_, route_metrics_type_support_); + if (nullptr == route_metrics_topic_) { std::cerr << "[infotainment] Failed to create topic: route_metrics" << std::endl; return false; } + + transit_metrics_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::transit_metrics()); + transit_metrics_topic_ = create_topic(*participant_, transit_metrics_topic_name_, transit_metrics_type_support_); + if (nullptr == transit_metrics_topic_) { std::cerr << "[infotainment] Failed to create topic: transit_metrics" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[infotainment] Failed to create topic: service_heartbeat" << std::endl; return false; } + + route_context_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::route_context_query()); + route_context_query_topic_ = create_topic(*participant_, route_context_query_topic_name_, route_context_query_type_support_); + if (nullptr == route_context_query_topic_) { std::cerr << "[infotainment] Failed to create topic: route_context_query" << std::endl; return false; } + + route_context_response_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::route_context_response()); + route_context_response_topic_ = create_topic(*participant_, route_context_response_topic_name_, route_context_response_type_support_); + if (nullptr == route_context_response_topic_) { std::cerr << "[infotainment] Failed to create topic: route_context_response" << std::endl; return false; } + + return true; +} + +bool InfotainmentNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + if (nullptr == publisher_ ) + { + std::cerr << "[infotainment] Failed to create publisher" << std::endl; + return false; + } + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == subscriber_) + { + std::cerr << "[infotainment] Failed to create subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + service_heartbeat_datawriter_ = publisher_->create_datawriter( + *service_heartbeat_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + + route_context_response_datawriter_ = publisher_->create_datawriter( + *route_context_response_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + route_context_response_writer_ = downcast_writer(route_context_response_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + transit_health_datareader_ = subscriber_->create_datareader(*transit_health_topic_, reader_qos, &transit_health_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + route_metrics_datareader_ = subscriber_->create_datareader(*route_metrics_topic_, reader_qos, &route_metrics_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + transit_metrics_datareader_ = subscriber_->create_datareader(*transit_metrics_topic_, reader_qos, &transit_metrics_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_datareader_ = subscriber_->create_datareader(*service_heartbeat_topic_, reader_qos, &heartbeat_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + route_context_query_datareader_ = subscriber_->create_datareader(*route_context_query_topic_, reader_qos, &route_context_query_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + transit_health_reader_ = downcast_reader(transit_health_datareader_); + route_metrics_reader_ = downcast_reader(route_metrics_datareader_); + transit_metrics_reader_ = downcast_reader(transit_metrics_datareader_); + heartbeat_reader_ = downcast_reader(heartbeat_datareader_); + route_context_query_reader_ = downcast_reader(route_context_query_datareader_); + + const bool success = nullptr != service_heartbeat_writer_ && + nullptr != route_context_response_writer_ && + nullptr != transit_health_reader_ && + nullptr != route_metrics_reader_ && + nullptr != transit_metrics_reader_ && + nullptr != heartbeat_reader_ && + nullptr != route_context_query_reader_; + + if (!success) + { + std::cerr << "[infotainment] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool InfotainmentNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == transit_health_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == route_metrics_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == transit_metrics_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == route_context_response_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == route_context_query_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[infotainment] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool InfotainmentNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[infotainment] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void InfotainmentNode::start_timers() noexcept +{ + heartbeat_timer_.start(); +} + +void InfotainmentNode::on_transit_health_received( + const safe_edge::pilot_server::TransitHealth& health) +{ + cached_transit_health_ = health; + have_transit_health_ = true; + std::cout << "[infotainment] Received TransitHealth status=" << health.status + << " last_fetch_ts=" << health.last_fetch_ts << std::endl; +} + +void InfotainmentNode::on_route_metrics_received( + const safe_edge::pilot_server::RouteMetricSeq& metrics) +{ + cached_route_metric_count_ = 0; + for (uint32_t i = 0; i < metrics.size() && cached_route_metric_count_ < 512; ++i) + { + cached_route_metrics_[cached_route_metric_count_++] = metrics[i]; + } + std::cout << "[infotainment] Received RouteMetricSeq count=" << metrics.size() << std::endl; +} + +void InfotainmentNode::on_transit_metrics_received( + const safe_edge::pilot_server::TransitMetrics& metrics) +{ + cached_vehicles_seen_ = metrics.vehicles_seen; + have_transit_metrics_ = true; + std::cout << "[infotainment] Received TransitMetrics routes=" << metrics.by_route.size() + << " vehicles_seen=" << metrics.vehicles_seen << std::endl; +} + +void InfotainmentNode::on_route_context_query_received( + const safe_edge::internal::RouteContextQuery& query) +{ + std::cout << "[infotainment] Received RouteContextQuery vehicle_health=" + << static_cast(query.vehicle_health) << std::endl; + publish_route_context_response(query); +} + +void InfotainmentNode::publish_route_context_response( + const safe_edge::internal::RouteContextQuery& query) +{ + safe_edge::internal::RouteContextResponse response{}; + response.header = header_factory_.make_header("route_context_response"); + response.has_data = have_transit_health_ || have_transit_metrics_; + response.transit_status = have_transit_health_ ? cached_transit_health_.status : ""; + response.vehicles_seen = cached_vehicles_seen_; + + response.max_route_updates = 0; + for (int32_t i = 0; i < cached_route_metric_count_; ++i) + { + if (cached_route_metrics_[i].updates_count > response.max_route_updates) + { + response.max_route_updates = cached_route_metrics_[i].updates_count; + } + } + + (void)query; + + if (eprosima::safedds::dds::ReturnCode::OK != + route_context_response_writer_->write(response, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[infotainment] Failed to publish RouteContextResponse" << std::endl; + return; + } + + std::cout << "[infotainment] Published RouteContextResponse" + << " transit_status=" << response.transit_status + << " max_route_updates=" << response.max_route_updates + << " vehicles_seen=" << response.vehicles_seen << std::endl; +} + +void InfotainmentNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat; + heartbeat.header_st = header_factory_.make_header("service_heartbeat"); + heartbeat.service_name = runtime_config_.service_name; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[infotainment] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[infotainment] Published ServiceHeartbeat" << std::endl; +} + +void InfotainmentNode::on_peer_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + if (heartbeat.service_name == runtime_config_.service_name) + { + return; + } + + std::cout << "[infotainment] Received ServiceHeartbeat service=" + << heartbeat.service_name + << " status=" << health_status_to_text(heartbeat.status) + << " detail=" << heartbeat.detail << std::endl; +} + +void InfotainmentNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[infotainment] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void InfotainmentNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[infotainment] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint InfotainmentNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp new file mode 100644 index 0000000..7fc535a --- /dev/null +++ b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp @@ -0,0 +1,498 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace non_safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {5, 0}; + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[ota_service] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& topic_name, + TypeSupportT& type_support) +{ + return participant.create_topic( + topic_name, + type_support.get_type_name(), + eprosima::safedds::dds::TopicQos{}, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +} // namespace + +OtaServiceNode::ParticipantListener::ParticipantListener( + OtaServiceNode& owner) + : owner_(owner) +{ +} + +void OtaServiceNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void OtaServiceNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +OtaServiceNode::ChargerLocationsListener::ChargerLocationsListener( + OtaServiceNode& owner) + : owner_(owner) +{ +} + +void OtaServiceNode::ChargerLocationsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[ota_service] Failed to downcast charger_locations reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargerLocation sample{}; + safe_edge::pilot_server::ChargerLocationSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charger_locations_received(batch); + } +} + +OtaServiceNode::ChargerTypesListener::ChargerTypesListener( + OtaServiceNode& owner) + : owner_(owner) +{ +} + +void OtaServiceNode::ChargerTypesListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[ota_service] Failed to downcast charger_types reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargerType sample{}; + safe_edge::pilot_server::ChargerTypeSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charger_types_received(batch); + } +} + +OtaServiceNode::ChargingSessionsListener::ChargingSessionsListener( + OtaServiceNode& owner) + : owner_(owner) +{ +} + +void OtaServiceNode::ChargingSessionsListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[ota_service] Failed to downcast charging_sessions reader" << std::endl; + return; + } + + safe_edge::pilot_server::ChargingSession sample{}; + safe_edge::pilot_server::ChargingSessionSeq batch{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + batch.push_back(sample); + } + } + if (!batch.empty()) + { + owner_.on_charging_sessions_received(batch); + } +} + +OtaServiceNode::HeartbeatListener::HeartbeatListener( + OtaServiceNode& owner) + : owner_(owner) +{ +} + +void OtaServiceNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast(reader); + if (nullptr == typed_reader) + { + std::cerr << "[ota_service] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_peer_heartbeat_received(sample); + } + } +} + +OtaServiceNode::OtaServiceNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , charger_locations_listener_(*this) + , charger_types_listener_(*this) + , charging_sessions_listener_(*this) + , heartbeat_listener_(*this) + , heartbeat_timer_(TIMEOUT) +{ +} + +int OtaServiceNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[ota_service] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool OtaServiceNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool OtaServiceNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + } + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[ota_service] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool OtaServiceNode::register_types() +{ + return register_type(*participant_, charger_locations_type_support_, "ChargerLocationSeq") && + register_type(*participant_, charger_types_type_support_, "ChargerTypeSeq") && + register_type(*participant_, charging_sessions_type_support_, "ChargingSessionSeq") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat"); +} + +bool OtaServiceNode::create_topics() +{ + charger_locations_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charger_locations()); + charger_locations_topic_ = create_topic(*participant_, charger_locations_topic_name_, charger_locations_type_support_); + if (nullptr == charger_locations_topic_) { std::cerr << "[ota_service] Failed to create topic: charger_locations" << std::endl; return false; } + + charger_types_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charger_types()); + charger_types_topic_ = create_topic(*participant_, charger_types_topic_name_, charger_types_type_support_); + if (nullptr == charger_types_topic_) { std::cerr << "[ota_service] Failed to create topic: charger_types" << std::endl; return false; } + + charging_sessions_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_sessions()); + charging_sessions_topic_ = create_topic(*participant_, charging_sessions_topic_name_, charging_sessions_type_support_); + if (nullptr == charging_sessions_topic_) { std::cerr << "[ota_service] Failed to create topic: charging_sessions" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[ota_service] Failed to create topic: service_heartbeat" << std::endl; return false; } + + return true; +} + +bool OtaServiceNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[ota_service] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + service_heartbeat_datawriter_ = publisher_->create_datawriter( + *service_heartbeat_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + charger_locations_datareader_ = subscriber_->create_datareader(*charger_locations_topic_, reader_qos, &charger_locations_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charger_types_datareader_ = subscriber_->create_datareader(*charger_types_topic_, reader_qos, &charger_types_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + charging_sessions_datareader_ = subscriber_->create_datareader(*charging_sessions_topic_, reader_qos, &charging_sessions_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_datareader_ = subscriber_->create_datareader(*service_heartbeat_topic_, reader_qos, &heartbeat_listener_, eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + charger_locations_reader_ = downcast_reader(charger_locations_datareader_); + charger_types_reader_ = downcast_reader(charger_types_datareader_); + charging_sessions_reader_ = downcast_reader(charging_sessions_datareader_); + heartbeat_reader_ = downcast_reader(heartbeat_datareader_); + + const bool success = nullptr != service_heartbeat_writer_ && + nullptr != charger_locations_reader_ && + nullptr != charger_types_reader_ && + nullptr != charging_sessions_reader_ && + nullptr != heartbeat_reader_; + + if (!success) + { + std::cerr << "[ota_service] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool OtaServiceNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_locations_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charger_types_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_sessions_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[ota_service] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool OtaServiceNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[ota_service] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void OtaServiceNode::start_timers() noexcept +{ + heartbeat_timer_.start(); +} + +void OtaServiceNode::on_charger_locations_received( + const safe_edge::pilot_server::ChargerLocationSeq& locations) +{ + std::cout << "[ota_service] Received ChargerLocationSeq count=" << locations.size() << std::endl; +} + +void OtaServiceNode::on_charger_types_received( + const safe_edge::pilot_server::ChargerTypeSeq& types) +{ + std::cout << "[ota_service] Received ChargerTypeSeq count=" << types.size() << std::endl; +} + +void OtaServiceNode::on_charging_sessions_received( + const safe_edge::pilot_server::ChargingSessionSeq& sessions) +{ + std::cout << "[ota_service] Received ChargingSessionSeq count=" << sessions.size() << std::endl; +} + +void OtaServiceNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat; + heartbeat.header_st = header_factory_.make_header("service_heartbeat"); + heartbeat.service_name = runtime_config_.service_name; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[ota_service] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[ota_service] Published ServiceHeartbeat" << std::endl; +} + +void OtaServiceNode::on_peer_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + static_cast(heartbeat); +} + +void OtaServiceNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[ota_service] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void OtaServiceNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[ota_service] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint OtaServiceNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace non_safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/CMakeLists.txt b/safe_dds/safety/CMakeLists.txt new file mode 100644 index 0000000..d3c75d7 --- /dev/null +++ b/safe_dds/safety/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.16) + +project(safe_edge_safety_domain LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +find_package(safedds REQUIRED) + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + set(SAFE_EDGE_PLATFORM_SOCKET_LIB socket) +else() + set(SAFE_EDGE_PLATFORM_SOCKET_LIB "") +endif() + +set(SAFE_EDGE_SAFETY_DOMAIN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(SAFE_EDGE_IDL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../idl") + +function(safe_edge_apply_common_build_settings target_name) + target_include_directories( + "${target_name}" + PUBLIC + "${SAFE_EDGE_SAFETY_DOMAIN_INCLUDE_DIR}" + ) + + target_compile_options( + "${target_name}" + PRIVATE + -fno-exceptions + -fno-rtti + -Wall + -Werror + -Wextra + -Wpedantic + ) +endfunction() + +add_library( + safe_edge_safety_domain_common + STATIC + src/common/RuntimeConfig.cpp + src/common/TopicNames.cpp + src/common/HeaderFactory.cpp + src/policy/IdlAdapters.cpp +) +safe_edge_apply_common_build_settings(safe_edge_safety_domain_common) +target_include_directories( + safe_edge_safety_domain_common + PUBLIC + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_safety_domain_common + PUBLIC + safedds +) + +add_library( + safe_edge_policy_core + STATIC + src/policy/PolicyEngine.cpp +) +safe_edge_apply_common_build_settings(safe_edge_policy_core) + +add_executable( + safe_edge_safety_io_adapters + src/apps/safety_io_adapters_main.cpp + src/nodes/SafetyIoAdaptersNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_safety_io_adapters) +target_include_directories( + safe_edge_safety_io_adapters + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_safety_io_adapters + PRIVATE + safe_edge_safety_domain_common + safe_edge_policy_core + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +add_executable( + safe_edge_policy_engine + src/apps/policy_engine_main.cpp + src/nodes/PolicyEngineNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_policy_engine) +target_include_directories( + safe_edge_policy_engine + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_policy_engine + PRIVATE + safe_edge_safety_domain_common + safe_edge_policy_core + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +add_executable( + safe_edge_vehicle_mock + src/apps/vehicle_mock_main.cpp + src/nodes/VehicleMockNode.cpp +) +safe_edge_apply_common_build_settings(safe_edge_vehicle_mock) +target_include_directories( + safe_edge_vehicle_mock + PRIVATE + "${SAFE_EDGE_IDL_INCLUDE_DIR}" +) +target_link_libraries( + safe_edge_vehicle_mock + PRIVATE + safe_edge_safety_domain_common + safedds + ${SAFE_EDGE_PLATFORM_SOCKET_LIB} +) + +install( + TARGETS + safe_edge_safety_io_adapters + safe_edge_policy_engine + safe_edge_vehicle_mock + RUNTIME DESTINATION bin +) diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/HeaderFactory.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/HeaderFactory.hpp new file mode 100644 index 0000000..3896c27 --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/HeaderFactory.hpp @@ -0,0 +1,34 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP + +#include + +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace common { + +class HeaderFactory +{ +public: + + explicit HeaderFactory(std::string source_name); + + safe_edge::common::Header make_header( + const char* trace_suffix = nullptr); + + static uint64_t now_ms() noexcept; + +private: + + std::string source_name_; + uint64_t counter_ = 0U; +}; + +} // namespace common +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_COMMON_HEADERFACTORY_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp new file mode 100644 index 0000000..3a9e004 --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp @@ -0,0 +1,31 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP + +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace common { + +struct RuntimeConfig +{ + std::string participant_name; + std::string service_name; + std::string source_name; + uint32_t domain_id = 0U; + uint16_t participant_port = 0U; + uint16_t initial_peer_port = 0U; +}; + +RuntimeConfig make_safety_io_adapters_runtime_config(); + +RuntimeConfig make_policy_engine_runtime_config(); + +RuntimeConfig make_vehicle_mock_runtime_config(); + +} // namespace common +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/TopicNames.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/TopicNames.hpp new file mode 100644 index 0000000..61ae5ab --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/TopicNames.hpp @@ -0,0 +1,40 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP + +namespace safe_edge { +namespace safety_domain { +namespace common { +namespace topic_names { + +const char* safety_input_frame() noexcept; + +const char* policy_decision() noexcept; + +const char* service_heartbeat() noexcept; + +const char* energy_advisory() noexcept; + +const char* edge_gateway_status() noexcept; + +const char* vehicle_edge_summary() noexcept; + +const char* charging_query() noexcept; + +const char* charging_response() noexcept; + +const char* route_context_query() noexcept; + +const char* route_context_response() noexcept; + +const char* edge_charger_query() noexcept; + +const char* edge_charger_response() noexcept; + +const char* server_availability_status() noexcept; + +} // namespace topic_names +} // namespace common +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_COMMON_TOPICNAMES_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/nodes/PolicyEngineNode.hpp b/safe_dds/safety/include/safe_edge/safety_domain/nodes/PolicyEngineNode.hpp new file mode 100644 index 0000000..390ae2f --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/nodes/PolicyEngineNode.hpp @@ -0,0 +1,313 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_NODES_POLICYENGINENODE_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_NODES_POLICYENGINENODE_HPP + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +class PolicyEngineNode +{ +public: + + explicit PolicyEngineNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(PolicyEngineNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class SafetyInputFrameListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit SafetyInputFrameListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class EnergyAdvisoryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit EnergyAdvisoryListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class EdgeGatewayStatusListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit EdgeGatewayStatusListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class ChargingResponseListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ChargingResponseListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class ServerAvailabilityStatusListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit ServerAvailabilityStatusListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + class EdgeChargerResponseListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit EdgeChargerResponseListener(PolicyEngineNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + PolicyEngineNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void evaluate_and_publish(); + void publish_policy_decision(); + void publish_heartbeat(); + void publish_charging_query(float soc_pct); + + void publish_edge_charger_query(float soc_pct); + void on_server_availability_status_received(const safe_edge::internal::ServerAvailabilityStatus& status); + void on_edge_charger_response_received(const safe_edge::internal::ChargingResponse& response); + + void on_safety_input_frame_received(const safe_edge::internal::SafetyInputFrame& frame); + void on_energy_advisory_received(const safe_edge::edge::EnergyAdvisory& advisory); + void on_edge_gateway_status_received(const safe_edge::edge::EdgeGatewayStatus& status); + void on_peer_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + void on_charging_response_received(const safe_edge::internal::ChargingResponse& response); + + void log_subscription_match( + const char* topic_name, + int32_t total_count) const; + + void log_publication_match( + const char* topic_name, + int32_t total_count) const; + + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + policy::PolicyEngine policy_engine_; + + ParticipantListener participant_listener_; + SafetyInputFrameListener safety_input_frame_listener_; + EnergyAdvisoryListener energy_advisory_listener_; + EdgeGatewayStatusListener edge_gateway_status_listener_; + HeartbeatListener heartbeat_listener_; + ChargingResponseListener charging_response_listener_; + ServerAvailabilityStatusListener server_availability_status_listener_; + EdgeChargerResponseListener edge_charger_response_listener_; + + safe_edge::internal::SafetyInputFrameTypeSupport safety_input_frame_type_support_; + safe_edge::edge::EnergyAdvisoryTypeSupport energy_advisory_type_support_; + safe_edge::edge::EdgeGatewayStatusTypeSupport edge_gateway_status_type_support_; + safe_edge::internal::PolicyDecisionTypeSupport policy_decision_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + safe_edge::internal::ChargingQueryTypeSupport charging_query_type_support_; + safe_edge::internal::ChargingResponseTypeSupport charging_response_type_support_; + safe_edge::internal::ServerAvailabilityStatusTypeSupport server_availability_status_type_support_; + + eprosima::safedds::memory::container::StaticList initial_peers_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* safety_input_frame_topic_ = nullptr; + eprosima::safedds::dds::Topic* energy_advisory_topic_ = nullptr; + eprosima::safedds::dds::Topic* edge_gateway_status_topic_ = nullptr; + eprosima::safedds::dds::Topic* policy_decision_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* charging_response_topic_ = nullptr; + eprosima::safedds::dds::Topic* server_availability_status_topic_ = nullptr; + eprosima::safedds::dds::Topic* edge_charger_query_topic_ = nullptr; + eprosima::safedds::dds::Topic* edge_charger_response_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 safety_input_frame_topic_name_; + eprosima::safedds::memory::container::StaticString256 energy_advisory_topic_name_; + eprosima::safedds::memory::container::StaticString256 edge_gateway_status_topic_name_; + eprosima::safedds::memory::container::StaticString256 policy_decision_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 charging_response_topic_name_; + eprosima::safedds::memory::container::StaticString256 server_availability_status_topic_name_; + eprosima::safedds::memory::container::StaticString256 edge_charger_query_topic_name_; + eprosima::safedds::memory::container::StaticString256 edge_charger_response_topic_name_; + + eprosima::safedds::dds::DataWriter* policy_decision_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* charging_query_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* edge_charger_query_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* energy_advisory_out_datawriter_ = nullptr; + + eprosima::safedds::dds::TypedDataWriter* policy_decision_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* charging_query_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* edge_charger_query_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* energy_advisory_out_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* safety_input_frame_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* energy_advisory_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* edge_gateway_status_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* heartbeat_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* charging_response_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* server_availability_status_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* edge_charger_response_datareader_ = nullptr; + + eprosima::safedds::dds::TypedDataReader* safety_input_frame_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* energy_advisory_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* edge_gateway_status_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* charging_response_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* server_availability_status_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* edge_charger_response_reader_ = + nullptr; + + eprosima::safedds::execution::Timer heartbeat_timer_; + + safe_edge::internal::SafetyInputFrame latest_safety_input_frame_{}; + safe_edge::edge::EnergyAdvisory latest_energy_advisory_{}; + safe_edge::edge::EdgeGatewayStatus latest_edge_gateway_status_{}; + + bool have_safety_input_frame_ = false; + bool have_energy_advisory_ = false; + bool have_edge_gateway_status_ = false; + bool pending_policy_publish_ = false; + bool charging_query_pending_ = false; + bool edge_charger_query_pending_ = false; + bool server_available_ = false; + uint64_t edge_status_last_ms_ = 0U; +}; + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_NODES_POLICYENGINENODE_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/nodes/SafetyIoAdaptersNode.hpp b/safe_dds/safety/include/safe_edge/safety_domain/nodes/SafetyIoAdaptersNode.hpp new file mode 100644 index 0000000..921999c --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/nodes/SafetyIoAdaptersNode.hpp @@ -0,0 +1,233 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_NODES_SAFETYIOADAPTERSNODE_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_NODES_SAFETYIOADAPTERSNODE_HPP + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +class SafetyIoAdaptersNode +{ +public: + + explicit SafetyIoAdaptersNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(SafetyIoAdaptersNode& owner); + + void on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept override; + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + SafetyIoAdaptersNode& owner_; + }; + + class PolicyDecisionListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit PolicyDecisionListener(SafetyIoAdaptersNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + SafetyIoAdaptersNode& owner_; + }; + + class EnergyAdvisoryListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit EnergyAdvisoryListener(SafetyIoAdaptersNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + SafetyIoAdaptersNode& owner_; + }; + + class HeartbeatListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit HeartbeatListener(SafetyIoAdaptersNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + SafetyIoAdaptersNode& owner_; + }; + + class SafetyInputFrameListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit SafetyInputFrameListener(SafetyIoAdaptersNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + SafetyIoAdaptersNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + void start_timers() noexcept; + + void publish_edge_gateway_status(); + void publish_heartbeat(); + void publish_vehicle_edge_summary(); + + void on_safety_input_frame_received(const safe_edge::internal::SafetyInputFrame& frame); + void on_policy_decision_received(const safe_edge::internal::PolicyDecision& decision); + void on_energy_advisory_received(const safe_edge::edge::EnergyAdvisory& advisory); + void on_peer_heartbeat_received(const safe_edge::common::ServiceHeartbeat& heartbeat); + + void log_subscription_match( + const char* topic_name, + int32_t total_count) const; + + void log_publication_match( + const char* topic_name, + int32_t total_count) const; + + eprosima::safedds::execution::TimePoint next_wakeup_time() const noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + PolicyDecisionListener policy_decision_listener_; + EnergyAdvisoryListener energy_advisory_listener_; + HeartbeatListener heartbeat_listener_; + SafetyInputFrameListener safety_input_frame_listener_; + + safe_edge::internal::SafetyInputFrameTypeSupport safety_input_frame_type_support_; + safe_edge::edge::EnergyAdvisoryTypeSupport energy_advisory_type_support_; + safe_edge::edge::EdgeGatewayStatusTypeSupport edge_gateway_status_type_support_; + safe_edge::edge::VehicleEdgeSummaryTypeSupport vehicle_edge_summary_type_support_; + safe_edge::internal::PolicyDecisionTypeSupport policy_decision_type_support_; + safe_edge::common::ServiceHeartbeatTypeSupport service_heartbeat_type_support_; + + eprosima::safedds::memory::container::StaticList initial_peers_; + + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* safety_input_frame_topic_ = nullptr; + eprosima::safedds::dds::Topic* energy_advisory_topic_ = nullptr; + eprosima::safedds::dds::Topic* edge_gateway_status_topic_ = nullptr; + eprosima::safedds::dds::Topic* vehicle_edge_summary_topic_ = nullptr; + eprosima::safedds::dds::Topic* policy_decision_topic_ = nullptr; + eprosima::safedds::dds::Topic* service_heartbeat_topic_ = nullptr; + + eprosima::safedds::memory::container::StaticString256 safety_input_frame_topic_name_; + eprosima::safedds::memory::container::StaticString256 energy_advisory_topic_name_; + eprosima::safedds::memory::container::StaticString256 edge_gateway_status_topic_name_; + eprosima::safedds::memory::container::StaticString256 vehicle_edge_summary_topic_name_; + eprosima::safedds::memory::container::StaticString256 policy_decision_topic_name_; + eprosima::safedds::memory::container::StaticString256 service_heartbeat_topic_name_; + + eprosima::safedds::dds::DataWriter* edge_gateway_status_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* vehicle_edge_summary_datawriter_ = nullptr; + eprosima::safedds::dds::DataWriter* service_heartbeat_datawriter_ = nullptr; + + eprosima::safedds::dds::TypedDataWriter* edge_gateway_status_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* vehicle_edge_summary_writer_ = + nullptr; + eprosima::safedds::dds::TypedDataWriter* service_heartbeat_writer_ = + nullptr; + + eprosima::safedds::dds::DataReader* safety_input_frame_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* policy_decision_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* energy_advisory_datareader_ = nullptr; + eprosima::safedds::dds::DataReader* heartbeat_datareader_ = nullptr; + + eprosima::safedds::dds::TypedDataReader* safety_input_frame_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* policy_decision_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* energy_advisory_reader_ = + nullptr; + eprosima::safedds::dds::TypedDataReader* heartbeat_reader_ = + nullptr; + + eprosima::safedds::execution::Timer edge_gateway_status_timer_; + eprosima::safedds::execution::Timer heartbeat_timer_; + + safe_edge::internal::SafetyInputFrame latest_safety_input_frame_{}; + safe_edge::internal::PolicyDecision latest_policy_decision_{}; + bool have_safety_input_frame_ = false; + bool have_policy_decision_ = false; + bool pending_vehicle_edge_summary_publish_ = false; + + safe_edge::edge::VehicleEdgeSummary last_published_vehicle_edge_summary_{}; + bool has_published_vehicle_edge_summary_ = false; + + uint64_t last_advisory_received_ms_ = 0U; +}; + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_NODES_SAFETYIOADAPTERSNODE_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp new file mode 100644 index 0000000..b60e88b --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp @@ -0,0 +1,89 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_NODES_VEHICLEMOCKNODE_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_NODES_VEHICLEMOCKNODE_HPP + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +class VehicleMockNode +{ +public: + + explicit VehicleMockNode(const common::RuntimeConfig& runtime_config); + + int run(); + +private: + + class ParticipantListener : + public eprosima::safedds::dds::DomainParticipantListener + { + public: + + explicit ParticipantListener(VehicleMockNode& owner); + + void on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept override; + + private: + + VehicleMockNode& owner_; + }; + + bool initialize(); + bool create_participant(); + bool register_types(); + bool create_topics(); + bool create_endpoints(); + bool enable_entities(); + bool create_executor(); + + void publish_frame(); + void republish_last_frame() noexcept; + + eprosima::safedds::dds::DomainParticipantFactory factory_; + common::RuntimeConfig runtime_config_; + common::HeaderFactory header_factory_; + + ParticipantListener participant_listener_; + + safe_edge::internal::SafetyInputFrameTypeSupport safety_input_frame_type_support_; + eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; + eprosima::safedds::dds::Publisher* publisher_ = nullptr; + eprosima::safedds::execution::ISpinnable* executor_ = nullptr; + + eprosima::safedds::dds::Topic* safety_input_frame_topic_ = nullptr; + eprosima::safedds::memory::container::StaticString256 safety_input_frame_topic_name_; + eprosima::safedds::dds::DataWriter* safety_input_frame_datawriter_ = nullptr; + eprosima::safedds::dds::TypedDataWriter* + safety_input_frame_writer_ = nullptr; + + eprosima::safedds::execution::Timer publish_timer_; + + safe_edge::internal::SafetyInputFrame last_frame_{}; + bool have_last_frame_ = false; +}; + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_NODES_VEHICLEMOCKNODE_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/policy/IdlAdapters.hpp b/safe_dds/safety/include/safe_edge/safety_domain/policy/IdlAdapters.hpp new file mode 100644 index 0000000..3ce0245 --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/policy/IdlAdapters.hpp @@ -0,0 +1,46 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_POLICY_IDLADAPTERS_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_POLICY_IDLADAPTERS_HPP + +#include + +#include +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace policy { + +PolicyModeValue from_idl_policy_mode( + safe_edge::common::PolicyMode mode) noexcept; + +safe_edge::common::PolicyMode to_idl_policy_mode( + PolicyModeValue mode) noexcept; + +GatewayHealth from_idl_health_status( + safe_edge::common::HealthStatus status) noexcept; + +safe_edge::common::HealthStatus to_idl_health_status( + GatewayHealth health) noexcept; + +PolicyInputs to_policy_inputs( + const safe_edge::internal::SafetyInputFrame& frame, + const safe_edge::edge::EnergyAdvisory* advisory, + const safe_edge::edge::EdgeGatewayStatus* gateway_status, + bool server_available, + bool edge_available) noexcept; + +safe_edge::internal::PolicyDecision to_policy_decision( + const PolicyOutputs& outputs, + const safe_edge::common::Header& header); + +safe_edge::edge::VehicleEdgeSummary to_vehicle_edge_summary( + const safe_edge::internal::SafetyInputFrame& frame, + const safe_edge::internal::PolicyDecision& decision, + const safe_edge::common::Header& header) noexcept; + +} // namespace policy +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_POLICY_IDLADAPTERS_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyEngine.hpp b/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyEngine.hpp new file mode 100644 index 0000000..2106126 --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyEngine.hpp @@ -0,0 +1,22 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYENGINE_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYENGINE_HPP + +#include + +namespace safe_edge { +namespace safety_domain { +namespace policy { + +class PolicyEngine +{ +public: + + PolicyOutputs evaluate( + const PolicyInputs& inputs) const noexcept; +}; + +} // namespace policy +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYENGINE_HPP diff --git a/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyTypes.hpp b/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyTypes.hpp new file mode 100644 index 0000000..4d16469 --- /dev/null +++ b/safe_dds/safety/include/safe_edge/safety_domain/policy/PolicyTypes.hpp @@ -0,0 +1,58 @@ +#ifndef SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYTYPES_HPP +#define SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYTYPES_HPP + +#include + +namespace safe_edge { +namespace safety_domain { +namespace policy { + +enum class PolicyModeValue +{ + unknown, + nominal, + low_soc, + edge_autonomous, + degraded_server_down, + degraded_complete +}; + +enum class GatewayHealth +{ + unknown, + ok, + degraded, + error +}; + +struct AdvisoryInput +{ + bool available = false; + PolicyModeValue suggested_mode = PolicyModeValue::unknown; + std::string reason; +}; + +struct PolicyInputs +{ + float soc_pct = 0.0F; + bool emergency_stop = false; + bool adas_fault = false; + GatewayHealth gateway_health = GatewayHealth::unknown; + AdvisoryInput advisory; + bool server_available = true; + bool edge_available = true; +}; + +struct PolicyOutputs +{ + PolicyModeValue mode = PolicyModeValue::unknown; + bool allow_non_safety = false; + bool allow_ota = false; + std::string reason; +}; + +} // namespace policy +} // namespace safety_domain +} // namespace safe_edge + +#endif // SAFE_EDGE_SAFETY_DOMAIN_POLICY_POLICYTYPES_HPP diff --git a/safe_dds/safety/src/apps/policy_engine_main.cpp b/safe_dds/safety/src/apps/policy_engine_main.cpp new file mode 100644 index 0000000..718d131 --- /dev/null +++ b/safe_dds/safety/src/apps/policy_engine_main.cpp @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + const safe_edge::safety_domain::common::RuntimeConfig runtime_config = + safe_edge::safety_domain::common::make_policy_engine_runtime_config(); + safe_edge::safety_domain::nodes::PolicyEngineNode node(runtime_config); + return node.run(); +} diff --git a/safe_dds/safety/src/apps/safety_io_adapters_main.cpp b/safe_dds/safety/src/apps/safety_io_adapters_main.cpp new file mode 100644 index 0000000..a69d4be --- /dev/null +++ b/safe_dds/safety/src/apps/safety_io_adapters_main.cpp @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + const safe_edge::safety_domain::common::RuntimeConfig runtime_config = + safe_edge::safety_domain::common::make_safety_io_adapters_runtime_config(); + safe_edge::safety_domain::nodes::SafetyIoAdaptersNode node(runtime_config); + return node.run(); +} diff --git a/safe_dds/safety/src/apps/vehicle_mock_main.cpp b/safe_dds/safety/src/apps/vehicle_mock_main.cpp new file mode 100644 index 0000000..454852f --- /dev/null +++ b/safe_dds/safety/src/apps/vehicle_mock_main.cpp @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + const safe_edge::safety_domain::common::RuntimeConfig runtime_config = + safe_edge::safety_domain::common::make_vehicle_mock_runtime_config(); + safe_edge::safety_domain::nodes::VehicleMockNode node(runtime_config); + return node.run(); +} diff --git a/safe_dds/safety/src/common/HeaderFactory.cpp b/safe_dds/safety/src/common/HeaderFactory.cpp new file mode 100644 index 0000000..1a7210f --- /dev/null +++ b/safe_dds/safety/src/common/HeaderFactory.cpp @@ -0,0 +1,54 @@ +#include + +#include +#include +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace common { + +HeaderFactory::HeaderFactory( + std::string source_name) + : source_name_(std::move(source_name)) +{ +} + +safe_edge::common::Header HeaderFactory::make_header( + const char* trace_suffix) +{ + safe_edge::common::Header header; + header.source = source_name_; + header.timestamp_ms = now_ms(); + header.trace_id = source_name_; + header.trace_id += "-"; + + std::array counter_buffer{}; + std::snprintf( + counter_buffer.data(), + counter_buffer.size(), + "%llu", + static_cast(counter_++)); + header.trace_id += counter_buffer.data(); + + if (nullptr != trace_suffix && trace_suffix[0] != '\0') + { + header.trace_id += "-"; + header.trace_id += trace_suffix; + } + + return header; +} + +uint64_t HeaderFactory::now_ms() noexcept +{ + const auto now = std::chrono::system_clock::now(); + const auto since_epoch = now.time_since_epoch(); + return static_cast( + std::chrono::duration_cast(since_epoch).count()); +} + +} // namespace common +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/common/RuntimeConfig.cpp b/safe_dds/safety/src/common/RuntimeConfig.cpp new file mode 100644 index 0000000..f3e8f4a --- /dev/null +++ b/safe_dds/safety/src/common/RuntimeConfig.cpp @@ -0,0 +1,44 @@ +#include + +namespace safe_edge { +namespace safety_domain { +namespace common { + +RuntimeConfig make_safety_io_adapters_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeSafetyIoAdaptersParticipant"; + config.service_name = "safety_io_adapters"; + config.source_name = "safety_io_adapters"; + config.domain_id = 0U; + config.participant_port = 8001U; + config.initial_peer_port = 8002U; + return config; +} + +RuntimeConfig make_policy_engine_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgePolicyEngineParticipant"; + config.service_name = "policy_engine"; + config.source_name = "policy_engine"; + config.domain_id = 0U; + config.participant_port = 8002U; + config.initial_peer_port = 8001U; + return config; +} + +RuntimeConfig make_vehicle_mock_runtime_config() +{ + RuntimeConfig config; + config.participant_name = "SafeEdgeVehicleMockParticipant"; + config.service_name = "vehicle_mock"; + config.source_name = "vehicle_mock"; + config.domain_id = 0U; + config.participant_port = 8003U; + return config; +} + +} // namespace common +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/common/TopicNames.cpp b/safe_dds/safety/src/common/TopicNames.cpp new file mode 100644 index 0000000..1e62421 --- /dev/null +++ b/safe_dds/safety/src/common/TopicNames.cpp @@ -0,0 +1,76 @@ +#include + +namespace safe_edge { +namespace safety_domain { +namespace common { +namespace topic_names { + +const char* safety_input_frame() noexcept +{ + return "safe_edge.internal.safety_input_frame"; +} + +const char* policy_decision() noexcept +{ + return "safe_edge.internal.policy_decision"; +} + +const char* service_heartbeat() noexcept +{ + return "safe_edge.common.service_heartbeat"; +} + +const char* energy_advisory() noexcept +{ + return "safe_edge.edge.energy_advisory"; +} + +const char* edge_gateway_status() noexcept +{ + return "safe_edge.edge.edge_gateway_status"; +} + +const char* vehicle_edge_summary() noexcept +{ + return "safe_edge.edge.vehicle_edge_summary"; +} + +const char* charging_query() noexcept +{ + return "safe_edge.internal.charging_query"; +} + +const char* charging_response() noexcept +{ + return "safe_edge.internal.charging_response"; +} + +const char* route_context_query() noexcept +{ + return "safe_edge.internal.route_context_query"; +} + +const char* route_context_response() noexcept +{ + return "safe_edge.internal.route_context_response"; +} + +const char* edge_charger_query() noexcept +{ + return "safe_edge.internal.edge_charger_query"; +} + +const char* edge_charger_response() noexcept +{ + return "safe_edge.internal.edge_charger_response"; +} + +const char* server_availability_status() noexcept +{ + return "safe_edge.internal.server_availability_status"; +} + +} // namespace topic_names +} // namespace common +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp new file mode 100644 index 0000000..fddebd4 --- /dev/null +++ b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp @@ -0,0 +1,846 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {5, 0}; +constexpr uint64_t EDGE_STATUS_TIMEOUT_MS = 10000U; + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[policy_engine] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& safe_topic_name, + TypeSupportT& type_support) +{ + eprosima::safedds::dds::TopicQos topic_qos{}; + return participant.create_topic( + safe_topic_name, + type_support.get_type_name(), + topic_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +} // namespace + +PolicyEngineNode::ParticipantListener::ParticipantListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void PolicyEngineNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +PolicyEngineNode::SafetyInputFrameListener::SafetyInputFrameListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::SafetyInputFrameListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast safety input reader" << std::endl; + return; + } + + safe_edge::internal::SafetyInputFrame sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_safety_input_frame_received(sample); + } + } +} + +PolicyEngineNode::EnergyAdvisoryListener::EnergyAdvisoryListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::EnergyAdvisoryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast energy advisory reader" << std::endl; + return; + } + + safe_edge::edge::EnergyAdvisory sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_energy_advisory_received(sample); + } + } +} + +PolicyEngineNode::EdgeGatewayStatusListener::EdgeGatewayStatusListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::EdgeGatewayStatusListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast gateway status reader" << std::endl; + return; + } + + safe_edge::edge::EdgeGatewayStatus sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_edge_gateway_status_received(sample); + } + } +} + +PolicyEngineNode::HeartbeatListener::HeartbeatListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_peer_heartbeat_received(sample); + } + } +} + +PolicyEngineNode::ChargingResponseListener::ChargingResponseListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::ChargingResponseListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast charging response reader" << std::endl; + return; + } + + safe_edge::internal::ChargingResponse sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_charging_response_received(sample); + } + } +} + +PolicyEngineNode::ServerAvailabilityStatusListener::ServerAvailabilityStatusListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::ServerAvailabilityStatusListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast server_availability_status reader" << std::endl; + return; + } + + safe_edge::internal::ServerAvailabilityStatus sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_server_availability_status_received(sample); + } + } +} + +PolicyEngineNode::EdgeChargerResponseListener::EdgeChargerResponseListener( + PolicyEngineNode& owner) + : owner_(owner) +{ +} + +void PolicyEngineNode::EdgeChargerResponseListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[policy_engine] Failed to downcast edge_charger_response reader" << std::endl; + return; + } + + safe_edge::internal::ChargingResponse sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_edge_charger_response_received(sample); + } + } +} + +PolicyEngineNode::PolicyEngineNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , safety_input_frame_listener_(*this) + , energy_advisory_listener_(*this) + , edge_gateway_status_listener_(*this) + , heartbeat_listener_(*this) + , charging_response_listener_(*this) + , server_availability_status_listener_(*this) + , edge_charger_response_listener_(*this) + , heartbeat_timer_(TIMEOUT) +{ +} + +int PolicyEngineNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[policy_engine] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (pending_policy_publish_) + { + publish_policy_decision(); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + evaluate_and_publish(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool PolicyEngineNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool PolicyEngineNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + participant_qos.wire_protocol_qos().use_multicast_discovery = false; + + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[policy_engine] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool PolicyEngineNode::register_types() +{ + return register_type(*participant_, safety_input_frame_type_support_, "SafetyInputFrame") && + register_type(*participant_, energy_advisory_type_support_, "EnergyAdvisory") && + register_type(*participant_, edge_gateway_status_type_support_, "EdgeGatewayStatus") && + register_type(*participant_, policy_decision_type_support_, "PolicyDecision") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat") && + register_type(*participant_, charging_query_type_support_, "ChargingQuery") && + register_type(*participant_, charging_response_type_support_, "ChargingResponse") && + register_type(*participant_, server_availability_status_type_support_, "ServerAvailabilityStatus"); +} + +bool PolicyEngineNode::create_topics() +{ + safety_input_frame_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::safety_input_frame()); + safety_input_frame_topic_ = create_topic(*participant_, safety_input_frame_topic_name_, safety_input_frame_type_support_); + if (nullptr == safety_input_frame_topic_) { std::cerr << "[policy_engine] Failed to create topic: safety_input_frame" << std::endl; return false; } + + energy_advisory_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::energy_advisory()); + energy_advisory_topic_ = create_topic(*participant_, energy_advisory_topic_name_, energy_advisory_type_support_); + if (nullptr == energy_advisory_topic_) { std::cerr << "[policy_engine] Failed to create topic: energy_advisory" << std::endl; return false; } + + edge_gateway_status_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::edge_gateway_status()); + edge_gateway_status_topic_ = create_topic(*participant_, edge_gateway_status_topic_name_, edge_gateway_status_type_support_); + if (nullptr == edge_gateway_status_topic_) { std::cerr << "[policy_engine] Failed to create topic: edge_gateway_status" << std::endl; return false; } + + policy_decision_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::policy_decision()); + policy_decision_topic_ = create_topic(*participant_, policy_decision_topic_name_, policy_decision_type_support_); + if (nullptr == policy_decision_topic_) { std::cerr << "[policy_engine] Failed to create topic: policy_decision" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[policy_engine] Failed to create topic: service_heartbeat" << std::endl; return false; } + + charging_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_query()); + charging_query_topic_ = create_topic(*participant_, charging_query_topic_name_, charging_query_type_support_); + if (nullptr == charging_query_topic_) { std::cerr << "[policy_engine] Failed to create topic: charging_query" << std::endl; return false; } + + charging_response_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::charging_response()); + charging_response_topic_ = create_topic(*participant_, charging_response_topic_name_, charging_response_type_support_); + if (nullptr == charging_response_topic_) { std::cerr << "[policy_engine] Failed to create topic: charging_response" << std::endl; return false; } + + server_availability_status_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::server_availability_status()); + server_availability_status_topic_ = create_topic(*participant_, server_availability_status_topic_name_, server_availability_status_type_support_); + if (nullptr == server_availability_status_topic_) { std::cerr << "[policy_engine] Failed to create topic: server_availability_status" << std::endl; return false; } + + edge_charger_query_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::edge_charger_query()); + edge_charger_query_topic_ = create_topic(*participant_, edge_charger_query_topic_name_, charging_query_type_support_); + if (nullptr == edge_charger_query_topic_) { std::cerr << "[policy_engine] Failed to create topic: edge_charger_query" << std::endl; return false; } + + edge_charger_response_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::edge_charger_response()); + edge_charger_response_topic_ = create_topic(*participant_, edge_charger_response_topic_name_, charging_response_type_support_); + if (nullptr == edge_charger_response_topic_) { std::cerr << "[policy_engine] Failed to create topic: edge_charger_response" << std::endl; return false; } + + return true; +} + +bool PolicyEngineNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[policy_engine] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + policy_decision_datawriter_ = publisher_->create_datawriter( + *policy_decision_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_datawriter_ = publisher_->create_datawriter( + *service_heartbeat_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + charging_query_datawriter_ = publisher_->create_datawriter( + *charging_query_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + edge_charger_query_datawriter_ = publisher_->create_datawriter( + *edge_charger_query_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + energy_advisory_out_datawriter_ = publisher_->create_datawriter( + *energy_advisory_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + policy_decision_writer_ = downcast_writer(policy_decision_datawriter_); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + charging_query_writer_ = downcast_writer(charging_query_datawriter_); + edge_charger_query_writer_ = downcast_writer(edge_charger_query_datawriter_); + energy_advisory_out_writer_ = downcast_writer(energy_advisory_out_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + safety_input_frame_datareader_ = subscriber_->create_datareader( + *safety_input_frame_topic_, + reader_qos, + &safety_input_frame_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + energy_advisory_datareader_ = subscriber_->create_datareader( + *energy_advisory_topic_, + reader_qos, + &energy_advisory_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + edge_gateway_status_datareader_ = subscriber_->create_datareader( + *edge_gateway_status_topic_, + reader_qos, + &edge_gateway_status_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_datareader_ = subscriber_->create_datareader( + *service_heartbeat_topic_, + reader_qos, + &heartbeat_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + safety_input_frame_reader_ = downcast_reader(safety_input_frame_datareader_); + energy_advisory_reader_ = downcast_reader(energy_advisory_datareader_); + edge_gateway_status_reader_ = downcast_reader(edge_gateway_status_datareader_); + charging_response_datareader_ = subscriber_->create_datareader( + *charging_response_topic_, + reader_qos, + &charging_response_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + server_availability_status_datareader_ = subscriber_->create_datareader( + *server_availability_status_topic_, + reader_qos, + &server_availability_status_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + server_availability_status_reader_ = downcast_reader(server_availability_status_datareader_); + + edge_charger_response_datareader_ = subscriber_->create_datareader( + *edge_charger_response_topic_, + reader_qos, + &edge_charger_response_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + edge_charger_response_reader_ = downcast_reader(edge_charger_response_datareader_); + + heartbeat_reader_ = downcast_reader(heartbeat_datareader_); + charging_response_reader_ = downcast_reader(charging_response_datareader_); + + const bool success = nullptr != policy_decision_writer_ && + nullptr != service_heartbeat_writer_ && + nullptr != charging_query_writer_ && + nullptr != edge_charger_query_writer_ && + nullptr != energy_advisory_out_writer_ && + nullptr != safety_input_frame_reader_ && + nullptr != energy_advisory_reader_ && + nullptr != edge_gateway_status_reader_ && + nullptr != heartbeat_reader_ && + nullptr != charging_response_reader_ && + nullptr != server_availability_status_reader_ && + nullptr != edge_charger_response_reader_; + + if (!success) + { + std::cerr << "[policy_engine] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool PolicyEngineNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == policy_decision_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_query_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == energy_advisory_out_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == safety_input_frame_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == energy_advisory_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == edge_gateway_status_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == charging_response_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == edge_charger_query_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == server_availability_status_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == edge_charger_response_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[policy_engine] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool PolicyEngineNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[policy_engine] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void PolicyEngineNode::start_timers() noexcept +{ + heartbeat_timer_.start(); +} + +void PolicyEngineNode::evaluate_and_publish() +{ + if (!have_safety_input_frame_) + { + return; + } + + pending_policy_publish_ = true; +} + +void PolicyEngineNode::publish_policy_decision() +{ + pending_policy_publish_ = false; + + const safe_edge::edge::EnergyAdvisory* advisory = have_energy_advisory_ ? &latest_energy_advisory_ : nullptr; + const safe_edge::edge::EdgeGatewayStatus* gateway_status = have_edge_gateway_status_ ? &latest_edge_gateway_status_ : nullptr; + + const bool edge_available = have_edge_gateway_status_ && + (common::HeaderFactory::now_ms() - edge_status_last_ms_) < EDGE_STATUS_TIMEOUT_MS; + + const policy::PolicyInputs inputs = policy::to_policy_inputs( + latest_safety_input_frame_, + advisory, + gateway_status, + server_available_, + edge_available); + const policy::PolicyOutputs outputs = policy_engine_.evaluate(inputs); + const safe_edge::internal::PolicyDecision decision = policy::to_policy_decision( + outputs, + header_factory_.make_header("policy_decision")); + + if (eprosima::safedds::dds::ReturnCode::OK != policy_decision_writer_->write(decision, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[policy_engine] Failed to publish PolicyDecision" << std::endl; + return; + } + + std::cout << "[policy_engine] Published PolicyDecision mode=" << static_cast(decision.mode) + << " allow_non_safety=" << decision.allow_non_safety + << " allow_ota=" << decision.allow_ota + << " reason=" << decision.reason << std::endl; +} + +void PolicyEngineNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat; + heartbeat.header_st = header_factory_.make_header("service_heartbeat"); + heartbeat.service_name = runtime_config_.service_name; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[policy_engine] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[policy_engine] Published ServiceHeartbeat" << std::endl; +} + +void PolicyEngineNode::on_safety_input_frame_received( + const safe_edge::internal::SafetyInputFrame& frame) +{ + latest_safety_input_frame_ = frame; + have_safety_input_frame_ = true; + std::cout << "[policy_engine] Received SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; + + if (frame.battery.soc_pct < 20.0F) + { + const bool edge_available = have_edge_gateway_status_ && + (common::HeaderFactory::now_ms() - edge_status_last_ms_) < EDGE_STATUS_TIMEOUT_MS; + + if (server_available_ && !charging_query_pending_) + { + publish_charging_query(frame.battery.soc_pct); + } + else if (!server_available_ && edge_available && !edge_charger_query_pending_) + { + publish_edge_charger_query(frame.battery.soc_pct); + } + // else: both down → no query (degraded_complete) + } + + evaluate_and_publish(); +} + +void PolicyEngineNode::publish_charging_query(float soc_pct) +{ + charging_query_pending_ = true; + + safe_edge::internal::ChargingQuery query{}; + query.header = header_factory_.make_header("charging_query"); + query.soc_pct = soc_pct; + + if (eprosima::safedds::dds::ReturnCode::OK != + charging_query_writer_->write(query, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[policy_engine] Failed to publish ChargingQuery" << std::endl; + charging_query_pending_ = false; + return; + } + + std::cout << "[policy_engine] Published ChargingQuery soc=" << soc_pct << std::endl; +} + +void PolicyEngineNode::on_charging_response_received( + const safe_edge::internal::ChargingResponse& response) +{ + charging_query_pending_ = false; + std::cout << "[policy_engine] Received ChargingResponse has_charger=" << response.has_charger + << " charger_id=" << response.preferred_charger_id << std::endl; +} + +void PolicyEngineNode::publish_edge_charger_query(float soc_pct) +{ + edge_charger_query_pending_ = true; + + safe_edge::internal::ChargingQuery query{}; + query.header = header_factory_.make_header("edge_charger_query"); + query.soc_pct = soc_pct; + + if (eprosima::safedds::dds::ReturnCode::OK != + edge_charger_query_writer_->write(query, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[policy_engine] Failed to publish edge_charger_query" << std::endl; + edge_charger_query_pending_ = false; + return; + } + + std::cout << "[policy_engine] Published edge_charger_query soc=" << soc_pct << std::endl; +} + +void PolicyEngineNode::on_server_availability_status_received( + const safe_edge::internal::ServerAvailabilityStatus& status) +{ + server_available_ = status.server_available; + std::cout << "[policy_engine] Received ServerAvailabilityStatus server_available=" + << (status.server_available ? "true" : "false") + << " detail=" << status.detail << std::endl; + evaluate_and_publish(); +} + +void PolicyEngineNode::on_edge_charger_response_received( + const safe_edge::internal::ChargingResponse& response) +{ + edge_charger_query_pending_ = false; + std::cout << "[policy_engine] Received edge_charger_response has_charger=" << response.has_charger + << " charger_id=" << response.preferred_charger_id << std::endl; +} + +void PolicyEngineNode::on_energy_advisory_received( + const safe_edge::edge::EnergyAdvisory& advisory) +{ + latest_energy_advisory_ = advisory; + have_energy_advisory_ = true; + std::cout << "[policy_engine] Received EnergyAdvisory mode=" << static_cast(advisory.suggested_mode) + << " reason=" << advisory.advisory_reason << std::endl; + evaluate_and_publish(); +} + +void PolicyEngineNode::on_edge_gateway_status_received( + const safe_edge::edge::EdgeGatewayStatus& status) +{ + latest_edge_gateway_status_ = status; + have_edge_gateway_status_ = true; + edge_status_last_ms_ = common::HeaderFactory::now_ms(); + std::cout << "[policy_engine] Received EdgeGatewayStatus status=" << static_cast(status.status) + << " detail=" << status.detail << std::endl; + evaluate_and_publish(); +} + +void PolicyEngineNode::on_peer_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + if (heartbeat.service_name == runtime_config_.service_name) + { + return; + } + + if (heartbeat.service_name != "cloud_gateway") + { + return; + } + + static_cast(heartbeat); +} + +void PolicyEngineNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[policy_engine] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void PolicyEngineNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[policy_engine] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint PolicyEngineNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp new file mode 100644 index 0000000..f5f9fe3 --- /dev/null +++ b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp @@ -0,0 +1,659 @@ +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {5, 0}; + +// Gateway health thresholds (ms since last advisory) +constexpr uint64_t GATEWAY_OK_THRESHOLD_MS = 5000U; +constexpr uint64_t GATEWAY_DEGRADED_THRESHOLD_MS = 15000U; + +template +bool register_type( + eprosima::safedds::dds::DomainParticipant& participant, + TypeSupportT& type_support, + const char* label) +{ + if (eprosima::safedds::dds::ReturnCode::OK != type_support.register_type(participant, type_support.get_type_name())) + { + std::cerr << "[safety_io_adapters] Failed to register type: " << label << std::endl; + return false; + } + + return true; +} + +template +eprosima::safedds::dds::Topic* create_topic( + eprosima::safedds::dds::DomainParticipant& participant, + eprosima::safedds::memory::container::StaticString256& safe_topic_name, + TypeSupportT& type_support) +{ + eprosima::safedds::dds::TopicQos topic_qos{}; + return participant.create_topic( + safe_topic_name, + type_support.get_type_name(), + topic_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); +} + +template +eprosima::safedds::dds::TypedDataWriter* downcast_writer( + eprosima::safedds::dds::DataWriter* writer) +{ + if (nullptr == writer) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataWriter::downcast(*writer); +} + +template +eprosima::safedds::dds::TypedDataReader* downcast_reader( + eprosima::safedds::dds::DataReader* reader) +{ + if (nullptr == reader) + { + return nullptr; + } + + return eprosima::safedds::dds::TypedDataReader::downcast(*reader); +} + +} // namespace + +SafetyIoAdaptersNode::ParticipantListener::ParticipantListener( + SafetyIoAdaptersNode& owner) + : owner_(owner) +{ +} + +void SafetyIoAdaptersNode::ParticipantListener::on_subscription_matched( + eprosima::safedds::dds::DataReader& reader, + const eprosima::safedds::dds::SubscriptionMatchedStatus& info) noexcept +{ + owner_.log_subscription_match(reader.get_topicdescription().get_name().const_string_data(), info.total_count); +} + +void SafetyIoAdaptersNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + owner_.log_publication_match(writer.get_topic().get_name().const_string_data(), info.total_count); +} + +SafetyIoAdaptersNode::PolicyDecisionListener::PolicyDecisionListener( + SafetyIoAdaptersNode& owner) + : owner_(owner) +{ +} + +void SafetyIoAdaptersNode::PolicyDecisionListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[safety_io_adapters] Failed to downcast policy decision reader" << std::endl; + return; + } + + safe_edge::internal::PolicyDecision sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_policy_decision_received(sample); + } + } +} + +SafetyIoAdaptersNode::EnergyAdvisoryListener::EnergyAdvisoryListener( + SafetyIoAdaptersNode& owner) + : owner_(owner) +{ +} + +void SafetyIoAdaptersNode::EnergyAdvisoryListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[safety_io_adapters] Failed to downcast energy advisory reader" << std::endl; + return; + } + + safe_edge::edge::EnergyAdvisory sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_energy_advisory_received(sample); + } + } +} + +SafetyIoAdaptersNode::HeartbeatListener::HeartbeatListener( + SafetyIoAdaptersNode& owner) + : owner_(owner) +{ +} + +void SafetyIoAdaptersNode::HeartbeatListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[safety_io_adapters] Failed to downcast heartbeat reader" << std::endl; + return; + } + + safe_edge::common::ServiceHeartbeat sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_peer_heartbeat_received(sample); + } + } +} + +SafetyIoAdaptersNode::SafetyInputFrameListener::SafetyInputFrameListener( + SafetyIoAdaptersNode& owner) + : owner_(owner) +{ +} + +void SafetyIoAdaptersNode::SafetyInputFrameListener::on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept +{ + auto* typed_reader = eprosima::safedds::dds::TypedDataReader::downcast( + reader); + + if (nullptr == typed_reader) + { + std::cerr << "[safety_io_adapters] Failed to downcast safety_input_frame reader" << std::endl; + return; + } + + safe_edge::internal::SafetyInputFrame sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + while (typed_reader->take_next_sample(sample, info) == eprosima::safedds::dds::ReturnCode::OK) + { + if (info.valid_data) + { + owner_.on_safety_input_frame_received(sample); + } + } +} + +SafetyIoAdaptersNode::SafetyIoAdaptersNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , policy_decision_listener_(*this) + , energy_advisory_listener_(*this) + , heartbeat_listener_(*this) + , safety_input_frame_listener_(*this) + , edge_gateway_status_timer_(TIMEOUT) + , heartbeat_timer_(TIMEOUT) +{ +} + +int SafetyIoAdaptersNode::run() +{ + if (!initialize()) + { + return 1; + } + + start_timers(); + std::cout << "[safety_io_adapters] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (edge_gateway_status_timer_.is_triggered_and_reset()) + { + publish_edge_gateway_status(); + } + + if (heartbeat_timer_.is_triggered_and_reset()) + { + publish_heartbeat(); + } + + if (pending_vehicle_edge_summary_publish_) + { + publish_vehicle_edge_summary(); + } + + executor_->spin(next_wakeup_time()); + } + + return 0; +} + +bool SafetyIoAdaptersNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool SafetyIoAdaptersNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + participant_qos.wire_protocol_qos().use_multicast_discovery = false; + + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS | + eprosima::safedds::dds::SUBSCRIPTION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[safety_io_adapters] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool SafetyIoAdaptersNode::register_types() +{ + return register_type(*participant_, safety_input_frame_type_support_, "SafetyInputFrame") && + register_type(*participant_, energy_advisory_type_support_, "EnergyAdvisory") && + register_type(*participant_, edge_gateway_status_type_support_, "EdgeGatewayStatus") && + register_type(*participant_, vehicle_edge_summary_type_support_, "VehicleEdgeSummary") && + register_type(*participant_, policy_decision_type_support_, "PolicyDecision") && + register_type(*participant_, service_heartbeat_type_support_, "ServiceHeartbeat"); +} + +bool SafetyIoAdaptersNode::create_topics() +{ + safety_input_frame_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::safety_input_frame()); + safety_input_frame_topic_ = create_topic(*participant_, safety_input_frame_topic_name_, safety_input_frame_type_support_); + if (nullptr == safety_input_frame_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: safety_input_frame" << std::endl; return false; } + + energy_advisory_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::energy_advisory()); + energy_advisory_topic_ = create_topic(*participant_, energy_advisory_topic_name_, energy_advisory_type_support_); + if (nullptr == energy_advisory_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: energy_advisory" << std::endl; return false; } + + edge_gateway_status_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::edge_gateway_status()); + edge_gateway_status_topic_ = create_topic(*participant_, edge_gateway_status_topic_name_, edge_gateway_status_type_support_); + if (nullptr == edge_gateway_status_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: edge_gateway_status" << std::endl; return false; } + + vehicle_edge_summary_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::vehicle_edge_summary()); + vehicle_edge_summary_topic_ = create_topic(*participant_, vehicle_edge_summary_topic_name_, vehicle_edge_summary_type_support_); + if (nullptr == vehicle_edge_summary_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: vehicle_edge_summary" << std::endl; return false; } + + policy_decision_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::policy_decision()); + policy_decision_topic_ = create_topic(*participant_, policy_decision_topic_name_, policy_decision_type_support_); + if (nullptr == policy_decision_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: policy_decision" << std::endl; return false; } + + service_heartbeat_topic_name_ = eprosima::safedds::memory::container::StaticString256(common::topic_names::service_heartbeat()); + service_heartbeat_topic_ = create_topic(*participant_, service_heartbeat_topic_name_, service_heartbeat_type_support_); + if (nullptr == service_heartbeat_topic_) { std::cerr << "[safety_io_adapters] Failed to create topic: service_heartbeat" << std::endl; return false; } + + return true; +} + +bool SafetyIoAdaptersNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + eprosima::safedds::dds::SubscriberQos subscriber_qos{}; + subscriber_ = participant_->create_subscriber( + subscriber_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_ || nullptr == subscriber_) + { + std::cerr << "[safety_io_adapters] Failed to create publisher or subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + edge_gateway_status_datawriter_ = publisher_->create_datawriter( + *edge_gateway_status_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + vehicle_edge_summary_datawriter_ = publisher_->create_datawriter( + *vehicle_edge_summary_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + service_heartbeat_datawriter_ = publisher_->create_datawriter( + *service_heartbeat_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + edge_gateway_status_writer_ = downcast_writer(edge_gateway_status_datawriter_); + vehicle_edge_summary_writer_ = downcast_writer(vehicle_edge_summary_datawriter_); + service_heartbeat_writer_ = downcast_writer(service_heartbeat_datawriter_); + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + safety_input_frame_datareader_ = subscriber_->create_datareader( + *safety_input_frame_topic_, + reader_qos, + &safety_input_frame_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + policy_decision_datareader_ = subscriber_->create_datareader( + *policy_decision_topic_, + reader_qos, + &policy_decision_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + energy_advisory_datareader_ = subscriber_->create_datareader( + *energy_advisory_topic_, + reader_qos, + &energy_advisory_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + heartbeat_datareader_ = subscriber_->create_datareader( + *service_heartbeat_topic_, + reader_qos, + &heartbeat_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + safety_input_frame_reader_ = downcast_reader(safety_input_frame_datareader_); + policy_decision_reader_ = downcast_reader(policy_decision_datareader_); + energy_advisory_reader_ = downcast_reader(energy_advisory_datareader_); + heartbeat_reader_ = downcast_reader(heartbeat_datareader_); + + const bool success = nullptr != edge_gateway_status_writer_ && + nullptr != vehicle_edge_summary_writer_ && + nullptr != service_heartbeat_writer_ && + nullptr != safety_input_frame_reader_ && + nullptr != policy_decision_reader_ && + nullptr != energy_advisory_reader_ && + nullptr != heartbeat_reader_; + + if (!success) + { + std::cerr << "[safety_io_adapters] Failed to create endpoints" << std::endl; + return false; + } + + return true; +} + +bool SafetyIoAdaptersNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == edge_gateway_status_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == vehicle_edge_summary_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == service_heartbeat_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == safety_input_frame_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == policy_decision_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == energy_advisory_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == heartbeat_datareader_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[safety_io_adapters] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool SafetyIoAdaptersNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[safety_io_adapters] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void SafetyIoAdaptersNode::start_timers() noexcept +{ + edge_gateway_status_timer_.start(); + heartbeat_timer_.start(); +} + +void SafetyIoAdaptersNode::on_safety_input_frame_received( + const safe_edge::internal::SafetyInputFrame& frame) +{ + latest_safety_input_frame_ = frame; + have_safety_input_frame_ = true; + pending_vehicle_edge_summary_publish_ = true; +} + +void SafetyIoAdaptersNode::publish_edge_gateway_status() +{ + const uint64_t now_ms = common::HeaderFactory::now_ms(); + const safe_edge::common::Header header = header_factory_.make_header("edge_gateway_status"); + + safe_edge::edge::EdgeGatewayStatus status; + status.header = header; + status.last_server_sync_ms = last_advisory_received_ms_; + + if (last_advisory_received_ms_ == 0U) + { + status.status = safe_edge::common::HealthStatus::HEALTH_ERROR; + status.detail = "cloud_advisory_age_ms=never"; + } + else + { + const uint64_t age_ms = now_ms - last_advisory_received_ms_; + if (age_ms < GATEWAY_OK_THRESHOLD_MS) + { + status.status = safe_edge::common::HealthStatus::HEALTH_OK; + } + else if (age_ms < GATEWAY_DEGRADED_THRESHOLD_MS) + { + status.status = safe_edge::common::HealthStatus::HEALTH_DEGRADED; + } + else + { + status.status = safe_edge::common::HealthStatus::HEALTH_ERROR; + } + + char detail_buf[64]; + static_cast(snprintf(detail_buf, sizeof(detail_buf), "cloud_advisory_age_ms=%llu", + static_cast(age_ms))); + status.detail = detail_buf; + } + + if (safe_edge::common::HealthStatus::HEALTH_ERROR == status.status) + { + return; + } + + if (eprosima::safedds::dds::ReturnCode::OK != edge_gateway_status_writer_->write(status, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[safety_io_adapters] Failed to publish EdgeGatewayStatus" << std::endl; + return; + } + + std::cout << "[safety_io_adapters] Published EdgeGatewayStatus status=" + << static_cast(status.status) + << " " << status.detail << std::endl; +} + +void SafetyIoAdaptersNode::publish_heartbeat() +{ + safe_edge::common::ServiceHeartbeat heartbeat; + heartbeat.header_st = header_factory_.make_header("service_heartbeat"); + heartbeat.service_name = runtime_config_.service_name; + heartbeat.status = safe_edge::common::HealthStatus::HEALTH_OK; + heartbeat.detail = "running"; + + if (eprosima::safedds::dds::ReturnCode::OK != service_heartbeat_writer_->write(heartbeat, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[safety_io_adapters] Failed to publish ServiceHeartbeat" << std::endl; + return; + } + + std::cout << "[safety_io_adapters] Published ServiceHeartbeat" << std::endl; +} + +void SafetyIoAdaptersNode::publish_vehicle_edge_summary() +{ + pending_vehicle_edge_summary_publish_ = false; + + if (!have_safety_input_frame_ || !have_policy_decision_) + { + return; + } + + const safe_edge::common::Header header = header_factory_.make_header("vehicle_edge_summary"); + safe_edge::edge::VehicleEdgeSummary summary = policy::to_vehicle_edge_summary( + latest_safety_input_frame_, + latest_policy_decision_, + header); + + if (has_published_vehicle_edge_summary_ && + summary.current_mode == last_published_vehicle_edge_summary_.current_mode && + summary.soc_pct == last_published_vehicle_edge_summary_.soc_pct && + summary.vehicle_health == last_published_vehicle_edge_summary_.vehicle_health && + summary.v2g_ready == last_published_vehicle_edge_summary_.v2g_ready) + { + return; + } + + if (eprosima::safedds::dds::ReturnCode::OK != vehicle_edge_summary_writer_->write(summary, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[safety_io_adapters] Failed to publish VehicleEdgeSummary" << std::endl; + return; + } + + last_published_vehicle_edge_summary_ = summary; + has_published_vehicle_edge_summary_ = true; + + std::cout << "[safety_io_adapters] Published VehicleEdgeSummary mode=" + << static_cast(summary.current_mode) << std::endl; +} + +void SafetyIoAdaptersNode::on_policy_decision_received( + const safe_edge::internal::PolicyDecision& decision) +{ + latest_policy_decision_ = decision; + have_policy_decision_ = true; + pending_vehicle_edge_summary_publish_ = true; + + std::cout << "[safety_io_adapters] Received PolicyDecision mode=" << static_cast(decision.mode) + << " allow_non_safety=" << decision.allow_non_safety + << " allow_ota=" << decision.allow_ota + << " reason=" << decision.reason << std::endl; +} + +void SafetyIoAdaptersNode::on_energy_advisory_received( + const safe_edge::edge::EnergyAdvisory& advisory) +{ + last_advisory_received_ms_ = common::HeaderFactory::now_ms(); + + std::cout << "[safety_io_adapters] Received EnergyAdvisory mode=" + << static_cast(advisory.suggested_mode) + << " reason=" << advisory.advisory_reason << std::endl; +} + +void SafetyIoAdaptersNode::on_peer_heartbeat_received( + const safe_edge::common::ServiceHeartbeat& heartbeat) +{ + static_cast(heartbeat); +} + +void SafetyIoAdaptersNode::log_subscription_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[safety_io_adapters] Subscription matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +void SafetyIoAdaptersNode::log_publication_match( + const char* topic_name, + int32_t total_count) const +{ + std::cout << "[safety_io_adapters] Publication matched on " << topic_name + << " total_count=" << total_count << std::endl; +} + +eprosima::safedds::execution::TimePoint SafetyIoAdaptersNode::next_wakeup_time() const noexcept +{ + eprosima::safedds::execution::TimePoint next = executor_->get_next_work_timepoint(); + next = eprosima::safedds::execution::TimePoint::min(next, edge_gateway_status_timer_.next_trigger()); + next = eprosima::safedds::execution::TimePoint::min(next, heartbeat_timer_.next_trigger()); + return next; +} + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/nodes/VehicleMockNode.cpp b/safe_dds/safety/src/nodes/VehicleMockNode.cpp new file mode 100644 index 0000000..ab5f99f --- /dev/null +++ b/safe_dds/safety/src/nodes/VehicleMockNode.cpp @@ -0,0 +1,349 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace safe_edge { +namespace safety_domain { +namespace nodes { + +namespace { + +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {1, 0}; + +static constexpr const char* INPUT_FILE_PATH = "/data/safe-edge-stage2/input.txt"; + +struct InputConfig +{ + float soc; + bool emergency_stop; + bool adas_fault; + float available_charge_kw; + float available_discharge_kw; + bool v2g_ready; + float speed_mps; + bool braking_available; + bool steering_available; +}; + +static InputConfig read_input_file() +{ + InputConfig cfg; + cfg.soc = 50.0F; + cfg.emergency_stop = false; + cfg.adas_fault = false; + cfg.available_charge_kw = 50.0F; + cfg.available_discharge_kw = 50.0F; + cfg.v2g_ready = true; + cfg.speed_mps = 0.0F; + cfg.braking_available = true; + cfg.steering_available = true; + + std::FILE* f = std::fopen(INPUT_FILE_PATH, "r"); + if (nullptr != f) + { + char line[64]; + int ival; + + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "soc=%f", &cfg.soc); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "emergency_stop=%d", &ival); + cfg.emergency_stop = (ival != 0); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "adas_fault=%d", &ival); + cfg.adas_fault = (ival != 0); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "available_charge_kw=%f", &cfg.available_charge_kw); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "available_discharge_kw=%f", &cfg.available_discharge_kw); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "v2g_ready=%d", &ival); + cfg.v2g_ready = (ival != 0); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "speed_mps=%f", &cfg.speed_mps); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "braking_available=%d", &ival); + cfg.braking_available = (ival != 0); + } + if (nullptr != std::fgets(line, static_cast(sizeof(line)), f)) + { + std::sscanf(line, "steering_available=%d", &ival); + cfg.steering_available = (ival != 0); + } + + std::fclose(f); + } + return cfg; +} + +} // namespace + +VehicleMockNode::ParticipantListener::ParticipantListener( + VehicleMockNode& owner) + : owner_(owner) +{ +} + +void VehicleMockNode::ParticipantListener::on_publication_matched( + eprosima::safedds::dds::DataWriter& writer, + const eprosima::safedds::dds::PublicationMatchedStatus& info) noexcept +{ + if (&writer == owner_.safety_input_frame_datawriter_ && + info.total_count_change > 0 && + owner_.have_last_frame_) + { + owner_.republish_last_frame(); + } +} + +VehicleMockNode::VehicleMockNode( + const common::RuntimeConfig& runtime_config) + : runtime_config_(runtime_config) + , header_factory_(runtime_config.source_name) + , participant_listener_(*this) + , publish_timer_(TIMEOUT) +{ +} + +int VehicleMockNode::run() +{ + if (!initialize()) + { + return 1; + } + + publish_timer_.start(); + std::cout << "[vehicle_mock] [START] Running with participant port " << runtime_config_.participant_port << std::endl; + + while (true) + { + while (executor_->has_pending_work()) + { + executor_->spin(eprosima::safedds::execution::TIME_ZERO); + } + + if (publish_timer_.is_triggered_and_reset()) + { + publish_frame(); + } + + executor_->spin(publish_timer_.next_trigger()); + } + + return 0; +} + +bool VehicleMockNode::initialize() +{ + return create_participant() && + register_types() && + create_topics() && + create_endpoints() && + enable_entities() && + create_executor(); +} + +bool VehicleMockNode::create_participant() +{ + eprosima::safedds::dds::DomainParticipantQos participant_qos{}; + eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); + participant_qos.participant_name() = participant_name; + participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( + {127, 0, 0, 1}, + runtime_config_.participant_port); + + participant_ = factory_.create_participant( + runtime_config_.domain_id, + participant_qos, + &participant_listener_, + eprosima::safedds::dds::PUBLICATION_MATCHED_STATUS); + + if (nullptr == participant_) + { + std::cerr << "[vehicle_mock] Failed to create participant" << std::endl; + return false; + } + + return true; +} + +bool VehicleMockNode::register_types() +{ + if (eprosima::safedds::dds::ReturnCode::OK != + safety_input_frame_type_support_.register_type( + *participant_, safety_input_frame_type_support_.get_type_name())) + { + std::cerr << "[vehicle_mock] Failed to register type: SafetyInputFrame" << std::endl; + return false; + } + + return true; +} + +bool VehicleMockNode::create_topics() +{ + eprosima::safedds::dds::TopicQos topic_qos{}; + safety_input_frame_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::safety_input_frame()); + + safety_input_frame_topic_ = participant_->create_topic( + safety_input_frame_topic_name_, + safety_input_frame_type_support_.get_type_name(), + topic_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == safety_input_frame_topic_) + { + std::cerr << "[vehicle_mock] Failed to create topic: safety_input_frame" << std::endl; + return false; + } + + return true; +} + +bool VehicleMockNode::create_endpoints() +{ + eprosima::safedds::dds::PublisherQos publisher_qos{}; + publisher_ = participant_->create_publisher( + publisher_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == publisher_) + { + std::cerr << "[vehicle_mock] Failed to create publisher" << std::endl; + return false; + } + + eprosima::safedds::dds::DataWriterQos writer_qos{}; + writer_qos.reliability().kind = eprosima::safedds::dds::ReliabilityQosPolicyKind::RELIABLE_RELIABILITY_QOS; + + safety_input_frame_datawriter_ = publisher_->create_datawriter( + *safety_input_frame_topic_, + writer_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == safety_input_frame_datawriter_) + { + std::cerr << "[vehicle_mock] Failed to create safety_input_frame datawriter" << std::endl; + return false; + } + + safety_input_frame_writer_ = eprosima::safedds::dds::TypedDataWriter< + safe_edge::internal::SafetyInputFrameTypeSupport>::downcast(*safety_input_frame_datawriter_); + + if (nullptr == safety_input_frame_writer_) + { + std::cerr << "[vehicle_mock] Failed to downcast safety_input_frame writer" << std::endl; + return false; + } + + return true; +} + +bool VehicleMockNode::enable_entities() +{ + bool enabled = true; + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == safety_input_frame_datawriter_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); + + if (!enabled) + { + std::cerr << "[vehicle_mock] Failed to enable DDS entities" << std::endl; + } + + return enabled; +} + +bool VehicleMockNode::create_executor() +{ + executor_ = factory_.create_default_executor(); + + if (nullptr == executor_) + { + std::cerr << "[vehicle_mock] Failed to create executor" << std::endl; + return false; + } + + return true; +} + +void VehicleMockNode::publish_frame() +{ + const InputConfig cfg = read_input_file(); + + const safe_edge::common::Header frame_header = header_factory_.make_header("safety_input_frame"); + + safe_edge::internal::SafetyInputFrame frame{}; + frame.header = frame_header; + frame.battery.header = frame_header; + frame.battery.soc_pct = cfg.soc; + frame.battery.available_charge_kw = cfg.available_charge_kw; + frame.battery.available_discharge_kw = cfg.available_discharge_kw; + frame.battery.v2g_ready = cfg.v2g_ready; + + frame.safety.header = frame_header; + frame.safety.speed_mps = cfg.speed_mps; + frame.safety.braking_available = cfg.braking_available; + frame.safety.steering_available = cfg.steering_available; + frame.safety.emergency_stop = cfg.emergency_stop; + frame.safety.adas_fault = cfg.adas_fault; + + last_frame_ = frame; + have_last_frame_ = true; + + if (eprosima::safedds::dds::ReturnCode::OK != + safety_input_frame_writer_->write(frame, eprosima::safedds::dds::HANDLE_NIL)) + { + std::cerr << "[vehicle_mock] Failed to publish SafetyInputFrame" << std::endl; + } + else + { + std::cout << "[vehicle_mock] Published SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; + } +} + +void VehicleMockNode::republish_last_frame() noexcept +{ + if (nullptr == safety_input_frame_writer_ || !have_last_frame_) + { + return; + } + + safety_input_frame_writer_->write(last_frame_, eprosima::safedds::dds::HANDLE_NIL); + std::cout << "[vehicle_mock] Republished SafetyInputFrame on match soc=" << last_frame_.battery.soc_pct << std::endl; +} + +} // namespace nodes +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/policy/IdlAdapters.cpp b/safe_dds/safety/src/policy/IdlAdapters.cpp new file mode 100644 index 0000000..4b255ef --- /dev/null +++ b/safe_dds/safety/src/policy/IdlAdapters.cpp @@ -0,0 +1,156 @@ +#include + +namespace safe_edge { +namespace safety_domain { +namespace policy { + +namespace { + +safe_edge::common::HealthStatus infer_vehicle_health( + const safe_edge::internal::SafetyInputFrame& frame) noexcept +{ + if (frame.safety.emergency_stop || frame.safety.adas_fault) + { + return safe_edge::common::HealthStatus::HEALTH_ERROR; + } + + return safe_edge::common::HealthStatus::HEALTH_OK; +} + +} // namespace + +PolicyModeValue from_idl_policy_mode( + safe_edge::common::PolicyMode mode) noexcept +{ + switch (mode) + { + case safe_edge::common::PolicyMode::POLICY_NOMINAL: + return PolicyModeValue::nominal; + case safe_edge::common::PolicyMode::POLICY_LOW_SOC: + return PolicyModeValue::low_soc; + case safe_edge::common::PolicyMode::POLICY_EDGE_AUTONOMOUS: + return PolicyModeValue::edge_autonomous; + case safe_edge::common::PolicyMode::POLICY_DEGRADED_SERVER_DOWN: + return PolicyModeValue::degraded_server_down; + case safe_edge::common::PolicyMode::POLICY_DEGRADED_COMPLETE: + return PolicyModeValue::degraded_complete; + case safe_edge::common::PolicyMode::POLICY_UNKNOWN: + default: + return PolicyModeValue::unknown; + } +} + +safe_edge::common::PolicyMode to_idl_policy_mode( + PolicyModeValue mode) noexcept +{ + switch (mode) + { + case PolicyModeValue::nominal: + return safe_edge::common::PolicyMode::POLICY_NOMINAL; + case PolicyModeValue::low_soc: + return safe_edge::common::PolicyMode::POLICY_LOW_SOC; + case PolicyModeValue::edge_autonomous: + return safe_edge::common::PolicyMode::POLICY_EDGE_AUTONOMOUS; + case PolicyModeValue::degraded_server_down: + return safe_edge::common::PolicyMode::POLICY_DEGRADED_SERVER_DOWN; + case PolicyModeValue::degraded_complete: + return safe_edge::common::PolicyMode::POLICY_DEGRADED_COMPLETE; + case PolicyModeValue::unknown: + default: + return safe_edge::common::PolicyMode::POLICY_UNKNOWN; + } +} + +GatewayHealth from_idl_health_status( + safe_edge::common::HealthStatus status) noexcept +{ + switch (status) + { + case safe_edge::common::HealthStatus::HEALTH_OK: + return GatewayHealth::ok; + case safe_edge::common::HealthStatus::HEALTH_DEGRADED: + return GatewayHealth::degraded; + case safe_edge::common::HealthStatus::HEALTH_ERROR: + return GatewayHealth::error; + case safe_edge::common::HealthStatus::HEALTH_UNKNOWN: + default: + return GatewayHealth::unknown; + } +} + +safe_edge::common::HealthStatus to_idl_health_status( + GatewayHealth health) noexcept +{ + switch (health) + { + case GatewayHealth::ok: + return safe_edge::common::HealthStatus::HEALTH_OK; + case GatewayHealth::degraded: + return safe_edge::common::HealthStatus::HEALTH_DEGRADED; + case GatewayHealth::error: + return safe_edge::common::HealthStatus::HEALTH_ERROR; + case GatewayHealth::unknown: + default: + return safe_edge::common::HealthStatus::HEALTH_UNKNOWN; + } +} + +PolicyInputs to_policy_inputs( + const safe_edge::internal::SafetyInputFrame& frame, + const safe_edge::edge::EnergyAdvisory* advisory, + const safe_edge::edge::EdgeGatewayStatus* gateway_status, + bool server_available, + bool edge_available) noexcept +{ + PolicyInputs inputs; + inputs.soc_pct = frame.battery.soc_pct; + inputs.emergency_stop = frame.safety.emergency_stop; + inputs.adas_fault = frame.safety.adas_fault; + inputs.server_available = server_available; + inputs.edge_available = edge_available; + + if (nullptr != advisory) + { + inputs.advisory.available = true; + inputs.advisory.suggested_mode = from_idl_policy_mode(advisory->suggested_mode); + inputs.advisory.reason = advisory->advisory_reason; + } + + if (nullptr != gateway_status) + { + inputs.gateway_health = from_idl_health_status(gateway_status->status); + } + + return inputs; +} + +safe_edge::internal::PolicyDecision to_policy_decision( + const PolicyOutputs& outputs, + const safe_edge::common::Header& header) +{ + safe_edge::internal::PolicyDecision decision; + decision.header = header; + decision.mode = to_idl_policy_mode(outputs.mode); + decision.reason = outputs.reason; + decision.allow_non_safety = outputs.allow_non_safety; + decision.allow_ota = outputs.allow_ota; + return decision; +} + +safe_edge::edge::VehicleEdgeSummary to_vehicle_edge_summary( + const safe_edge::internal::SafetyInputFrame& frame, + const safe_edge::internal::PolicyDecision& decision, + const safe_edge::common::Header& header) noexcept +{ + safe_edge::edge::VehicleEdgeSummary summary; + summary.header = header; + summary.soc_pct = frame.battery.soc_pct; + summary.current_mode = decision.mode; + summary.vehicle_health = infer_vehicle_health(frame); + summary.v2g_ready = frame.battery.v2g_ready; + return summary; +} + +} // namespace policy +} // namespace safety_domain +} // namespace safe_edge diff --git a/safe_dds/safety/src/policy/PolicyEngine.cpp b/safe_dds/safety/src/policy/PolicyEngine.cpp new file mode 100644 index 0000000..6f3e583 --- /dev/null +++ b/safe_dds/safety/src/policy/PolicyEngine.cpp @@ -0,0 +1,51 @@ +#include + +namespace safe_edge { +namespace safety_domain { +namespace policy { + +PolicyOutputs PolicyEngine::evaluate( + const PolicyInputs& inputs) const noexcept +{ + PolicyOutputs outputs; + + if (inputs.emergency_stop || inputs.adas_fault) + { + outputs.mode = PolicyModeValue::edge_autonomous; + outputs.reason = "emergency_stop_or_adas_fault"; + } + else if (!inputs.server_available && !inputs.edge_available) + { + outputs.mode = PolicyModeValue::degraded_complete; + outputs.reason = "server_down_edge_unavailable"; + } + else if (!inputs.server_available && inputs.edge_available) + { + outputs.mode = PolicyModeValue::degraded_server_down; + outputs.reason = "server_down_edge_available"; + } + else if (inputs.soc_pct < 20.0F) + { + outputs.mode = PolicyModeValue::low_soc; + outputs.reason = "battery_soc_below_threshold"; + } + else if (inputs.advisory.available && inputs.advisory.suggested_mode != PolicyModeValue::unknown) + { + outputs.mode = inputs.advisory.suggested_mode; + outputs.reason = inputs.advisory.reason.empty() ? "edge_advisory" : inputs.advisory.reason; + } + else + { + outputs.mode = PolicyModeValue::nominal; + outputs.reason = "nominal_vehicle_state"; + } + + outputs.allow_non_safety = outputs.mode == PolicyModeValue::nominal; + outputs.allow_ota = outputs.mode == PolicyModeValue::nominal && inputs.gateway_health == GatewayHealth::ok; + + return outputs; +} + +} // namespace policy +} // namespace safety_domain +} // namespace safe_edge diff --git a/scripts/build_qnx.sh b/scripts/build_qnx.sh index c976085..6a8eadd 100755 --- a/scripts/build_qnx.sh +++ b/scripts/build_qnx.sh @@ -19,6 +19,10 @@ QNX_USER="${USER:-$(id -un)}" : "${SERVER_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/server-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" : "${EDGE_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" : "${EDGE_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${SAFETY_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${SAFETY_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${NON_SAFETY_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" +: "${NON_SAFETY_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}}" : "${SAFE_DDS_IDL_GENERATOR:-}" : "${SAFE_DDS_IDL_GENERATOR_ARGS:-}" : "${SAFEDDS_DIR:=${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}/safedds}" @@ -49,6 +53,8 @@ The script always configures and builds: - common_server - safe_dds/server - safe_dds/edge + - safe_dds/safety + - safe_dds/non_safety If there are no source changes, CMake will skip recompilation internally. EOF @@ -80,12 +86,20 @@ done find_idl_generator() { if [[ -n "${SAFE_DDS_IDL_GENERATOR:-}" ]]; then local generator_bin="${SAFE_DDS_IDL_GENERATOR%% *}" - if command -v "${generator_bin}" >/dev/null 2>&1; then + if [[ -x "${generator_bin}" ]] || command -v "${generator_bin}" >/dev/null 2>&1; then echo "${SAFE_DDS_IDL_GENERATOR}" return 0 fi fi + if [[ -n "${SAFE_DDS_PATH:-}" ]]; then + local safedds_source_generator="${SAFE_DDS_PATH}/code-gen/scripts/safeddsgen" + if [[ -x "${safedds_source_generator}" ]]; then + echo "${safedds_source_generator}" + return 0 + fi + fi + local candidates=(safeddsgen safedds-idl-gen safedds-gen eprosima_safeddsgen) for prog in "${candidates[@]}"; do if command -v "${prog}" >/dev/null 2>&1; then @@ -127,10 +141,10 @@ regenerate_idl() { echo "Regenerating Safe DDS IDL artifacts" echo " source : ${COMMON_IDL_SOURCE_DIR}" echo " generated: ${SAFE_DDS_IDL_DIR}" - echo " generator: ${generator} ${SAFE_DDS_IDL_GENERATOR_ARGS:-}" + echo " generator: ${generator} -D . ${SAFE_DDS_IDL_GENERATOR_ARGS:-} *.idl" pushd "${SAFE_DDS_IDL_DIR}" >/dev/null - if ! bash -lc "${generator} ${SAFE_DDS_IDL_GENERATOR_ARGS:-}"; then + if ! bash -lc "${generator} -D . ${SAFE_DDS_IDL_GENERATOR_ARGS:-} *.idl"; then popd >/dev/null rm -f "${cleanup_links[@]}" exit 1 @@ -203,7 +217,7 @@ fi if [[ ! -d "${SAFEDDS_DIR}" ]]; then echo "Safe DDS QNX install not found at '${SAFEDDS_DIR}'" >&2 - echo "Build it first with: bash build_safedds_qnx.sh -- -j2" >&2 + echo "Build it first with: bash scripts/build_safedds_qnx.sh -- -j2" >&2 exit 1 fi @@ -225,12 +239,23 @@ if (( REGEN_IDL )); then regenerate_idl fi +if [[ -f "${COMMON_IDL_SOURCE_DIR}/internal.idl" && ! -f "${SAFE_DDS_IDL_DIR}/internal.hpp" ]]; then + echo "Generated Safe DDS IDL header missing: ${SAFE_DDS_IDL_DIR}/internal.hpp" >&2 + echo "Regenerate IDL with: bash scripts/build_qnx.sh --idl" >&2 + echo "If the generator is not in PATH, set SAFE_DDS_IDL_GENERATOR first." >&2 + exit 1 +fi + mkdir -p "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" \ "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" \ - "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" + "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" \ + "${SAFETY_BUILD_FOLDER}" "${SAFETY_INSTALL_FOLDER}" \ + "${NON_SAFETY_BUILD_FOLDER}" "${NON_SAFETY_INSTALL_FOLDER}" configure_and_build "${WORKSPACE_ROOT}/common_server" "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" "common_server" configure_and_build "${WORKSPACE_ROOT}/safe_dds/server" "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" "safe_dds/server" configure_and_build "${WORKSPACE_ROOT}/safe_dds/edge" "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" "safe_dds/edge" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/safety" "${SAFETY_BUILD_FOLDER}" "${SAFETY_INSTALL_FOLDER}" "safe_dds/safety" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/non_safety" "${NON_SAFETY_BUILD_FOLDER}" "${NON_SAFETY_INSTALL_FOLDER}" "safe_dds/non_safety" echo "All SafeEDGE QNX targets built successfully." diff --git a/scripts/check_setup.sh b/scripts/check_setup.sh index 4140b63..7e15115 100755 --- a/scripts/check_setup.sh +++ b/scripts/check_setup.sh @@ -63,11 +63,15 @@ require_path "${WORKSPACE_ROOT}/README.md" require_path "${WORKSPACE_ROOT}/common_server/CMakeLists.txt" require_path "${WORKSPACE_ROOT}/safe_dds/server/CMakeLists.txt" require_path "${WORKSPACE_ROOT}/safe_dds/edge/CMakeLists.txt" +require_path "${WORKSPACE_ROOT}/safe_dds/safety/CMakeLists.txt" +require_path "${WORKSPACE_ROOT}/safe_dds/non_safety/CMakeLists.txt" require_path "${WORKSPACE_ROOT}/scripts/build_qnx.sh" require_path "${WORKSPACE_ROOT}/scripts/build_safedds_qnx.sh" require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_1_test.sh" require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_2_test.sh" require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_3_test.sh" +require_path "${WORKSPACE_ROOT}/scripts/launch_tpi_2_5_test.sh" +require_path "${WORKSPACE_ROOT}/scripts/aux_vehicle_nodes.sh" if (( LINUX_ONLY )); then echo "Linux-only setup check completed." @@ -119,7 +123,7 @@ require_path "${SAFE_DDS_PATH}" if [[ ! -d "${WORKSPACE_ROOT}/qnx/install/safedds-qnx8-${QNX_ARCH}/safedds" ]]; then echo "Safe DDS QNX install not found yet." - echo "Build it with: bash build_safedds_qnx.sh -- -j2" + echo "Build it with: bash scripts/build_safedds_qnx.sh -- -j2" fi echo "Setup check completed." diff --git a/scripts/launch_tpi_2_5_test.sh b/scripts/launch_tpi_2_5_test.sh new file mode 100644 index 0000000..ffab568 --- /dev/null +++ b/scripts/launch_tpi_2_5_test.sh @@ -0,0 +1,592 @@ +#!/usr/bin/env bash +# Run TPI 2.5 smoke test for SafeDDS safety and non-safety QNX nodes. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +LOG_DIR="${SCRIPT_DIR}/logs" +LOG_FILE="${LOG_DIR}/launch_tpi_2_5.log" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" +: "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${SMOKE_TEST_SECONDS:=10}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" + +mkdir -p "${LOG_DIR}" +exec > >(tee "${LOG_FILE}") 2>&1 + +OPT_NO_REBUILD=0 +OPT_STOP=0 + +VEHICLE_NODE_BINS=( + safe_edge_safety_io_adapters + safe_edge_policy_engine + safe_edge_vehicle_mock + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment +) + +usage() { + cat <&2 + exit 1 + fi + SMOKE_TEST_SECONDS="$2" + shift 2 + ;; + --no-rebuild) + OPT_NO_REBUILD=1 + shift + ;; + --stop) + OPT_STOP=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: ${1}" >&2 + usage >&2 + exit 1 + ;; + esac +done + +if [[ ! "${SMOKE_TEST_SECONDS}" =~ ^[0-9]+$ || "${SMOKE_TEST_SECONDS}" -eq 0 ]]; then + echo "Invalid smoke-test duration: ${SMOKE_TEST_SECONDS}" >&2 + exit 1 +fi + +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 + exit 1 +fi + +if [[ ! -d "${TARGET_DIR}" ]]; then + echo "QNX target directory not found: ${TARGET_DIR}" >&2 + exit 1 +fi + +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2 + exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2 + exit 1 +fi + +if ! command -v brctl >/dev/null 2>&1; then + echo "brctl not found. Install with: sudo apt install bridge-utils" >&2 + exit 1 +fi + +_get_ip_address() { + local max_tries=20 + local ip + local i + + for ((i = 0; i < max_tries; i++)); do + set +e + ip="$(mkqnximage --getip 2>/dev/null)" + set -e + if [[ -n "${ip}" ]]; then + echo "${ip}" + return 0 + fi + sleep 2 + done + + echo "Timed out waiting for VM IP address." >&2 + return 1 +} + +_ssh_run() { + local ip="$1" + local cmd="$2" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_wait_for_ssh() { + local ip="$1" + local max_tries=45 + local i + + for ((i = 1; i <= max_tries; i++)); do + if _ssh_run "${ip}" "true" >/dev/null 2>&1; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " waiting for SSH (${i}/${max_tries})..." + fi + sleep 2 + done + + echo "Timed out waiting for SSH on ${ip}." >&2 + return 1 +} + +_print_section() { + local title="$1" + echo + echo "===== ${title} =====" +} + +_find_conflicting_qemu_targets() { + local proc_dir + local pid + local cwd + local cmdline + + for proc_dir in /proc/[0-9]*; do + pid="${proc_dir##*/}" + [[ -r "${proc_dir}/cmdline" ]] || continue + + cmdline="$(tr '\0' ' ' < "${proc_dir}/cmdline" 2>/dev/null || true)" + [[ "${cmdline}" == qemu-system-x86_64* ]] || continue + + cwd="$(readlink "${proc_dir}/cwd" 2>/dev/null || true)" + [[ -n "${cwd}" ]] || continue + + case "${cwd}" in + "${WORKSPACE_ROOT}"/qnx/targets/*) + if [[ "${cwd}" != "${TARGET_DIR}" ]]; then + printf '%s\t%s\n' "${pid}" "${cwd}" + fi + ;; + esac + done +} + +_validate_qnx_binary() { + local bin="$1" + local description + + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_host_bin_path() { + local name="$1" + case "${name}" in + safe_edge_safety_io_adapters|safe_edge_policy_engine|safe_edge_vehicle_mock) + echo "${SAFETY_BIN_DIR}/${name}" + ;; + safe_edge_cloud_gateway|safe_edge_ota_service|safe_edge_infotainment) + echo "${NON_SAFETY_BIN_DIR}/${name}" + ;; + *) + echo "Unknown vehicle node binary: ${name}" >&2 + return 1 + ;; + esac +} + +_validate_vehicle_binaries() { + local name + local bin + + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2 + exit 1 + fi + _validate_qnx_binary "${bin}" + done +} + +_refresh_vehicle_system_files_snippet() { + local snippet="${TARGET_DIR}/local/snippets/system_files.custom" + local name + local bin + + mkdir -p "$(dirname "${snippet}")" + { + echo "# local/snippets/system_files.custom" + echo "# Generated by scripts/launch_tpi_2_5_test.sh" + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + echo "[perms=555] bin/${name}=${bin}" + done + } > "${snippet}" +} + +_refresh_vehicle_ifs_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/ifs_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_tpi_2_5_test.sh +EOF +} + +_refresh_vehicle_post_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/post_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_tpi_2_5_test.sh +route add -net 224.0.0.0/4 vtnet0 +EOF +} + +_reset_generated_target_output() { + rm -rf "${TARGET_DIR}/output" +} + +_prepare_local_target_dirs() { + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" +} + +_remote_log_path_for_bin() { + local bin="$1" + echo "/tmp/safe_edge_vehicle_nodes/${bin}.log" +} + +_escape_single_quotes() { + local value="$1" + printf "%s" "${value//\'/\'\\\'\'}" +} + +_remote_pid_is_running() { + local ip="$1" + local bin="$2" + local pid_file="/tmp/safe_edge_vehicle_nodes/${bin}.pid" + local escaped_pid_file + + escaped_pid_file="$(_escape_single_quotes "${pid_file}")" + _ssh_run "${ip}" "test -f '${escaped_pid_file}' && pid=\$(cat '${escaped_pid_file}') && kill -0 \"\$pid\" 2>/dev/null" >/dev/null 2>&1 +} + +_remote_file_nonempty() { + local ip="$1" + local file="$2" + local escaped_file + + escaped_file="$(_escape_single_quotes "${file}")" + _ssh_run "${ip}" "test -s '${escaped_file}'" >/dev/null 2>&1 +} + +_remote_file_contains() { + local ip="$1" + local file="$2" + local pattern="$3" + local escaped_file escaped_pattern + + escaped_file="$(_escape_single_quotes "${file}")" + escaped_pattern="$(_escape_single_quotes "${pattern}")" + _ssh_run "${ip}" "grep -Fq -- '${escaped_pattern}' '${escaped_file}'" >/dev/null 2>&1 +} + +_wait_for_remote_file_contains() { + local ip="$1" + local file="$2" + local pattern="$3" + local max_tries="${4:-8}" + local sleep_seconds="${5:-1}" + local i + + for ((i = 1; i <= max_tries; i++)); do + if _remote_file_contains "${ip}" "${file}" "${pattern}"; then + return 0 + fi + sleep "${sleep_seconds}" + done + + return 1 +} + +_print_remote_log_tail() { + local ip="$1" + local file="$2" + local escaped_file + + escaped_file="$(_escape_single_quotes "${file}")" + _ssh_run "${ip}" "if [ -f '${escaped_file}' ]; then echo '--- ${file} ---'; tail -40 '${escaped_file}'; fi" || true +} + +_start_vehicle_nodes() { + local ip="$1" + local cmd + local name + + cmd='set -e; mkdir -p /tmp/safe_edge_vehicle_nodes /data/safe-edge-stage2;' + cmd+=' printf "soc=50.0\nemergency_stop=0\nadas_fault=0\navailable_charge_kw=50.0\navailable_discharge_kw=50.0\nv2g_ready=1\nspeed_mps=0.0\nbraking_available=1\nsteering_available=1\n" > /data/safe-edge-stage2/input.txt;' + cmd+=' rm -f /tmp/safe_edge_vehicle_nodes/*.pid /tmp/safe_edge_vehicle_nodes/*.log;' + cmd+=' /system/bin/safe_edge_infotainment >/tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.log 2>&1 & echo $! >/tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.pid;' + cmd+=' sleep 2;' + for name in "${VEHICLE_NODE_BINS[@]}"; do + [[ "${name}" == "safe_edge_infotainment" ]] && continue + cmd+=" /system/bin/${name} >/tmp/safe_edge_vehicle_nodes/${name}.log 2>&1 & echo \$! >/tmp/safe_edge_vehicle_nodes/${name}.pid;" + done + + _ssh_run "${ip}" "${cmd}" +} + +_stop_vehicle_nodes() { + local ip="$1" + _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; pid=\$(cat \"\$f\"); kill \"\$pid\" 2>/dev/null || true; done" \ + >/dev/null 2>&1 || true +} + +_print_vehicle_node_logs() { + local ip="$1" + _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.log; do [ -f \"\$f\" ] || continue; echo \"--- \$f ---\"; tail -40 \"\$f\"; done" \ + || true +} + +_service_name_for_bin() { + local bin="$1" + case "${bin}" in + safe_edge_safety_io_adapters) echo "safety_io_adapters" ;; + safe_edge_policy_engine) echo "policy_engine" ;; + safe_edge_vehicle_mock) echo "vehicle_mock" ;; + safe_edge_cloud_gateway) echo "cloud_gateway" ;; + safe_edge_ota_service) echo "ota_service" ;; + safe_edge_infotainment) echo "infotainment" ;; + *) + echo "Unknown service name for binary: ${bin}" >&2 + return 1 + ;; + esac +} + +_test_name_for_bin() { + local bin="$1" + case "${bin}" in + safe_edge_infotainment) echo "InfotainmentLiveliness" ;; + safe_edge_safety_io_adapters) echo "SafetyIoAdaptersLiveliness" ;; + safe_edge_policy_engine) echo "PolicyEngineLiveliness" ;; + safe_edge_vehicle_mock) echo "VehicleMockLiveliness" ;; + safe_edge_cloud_gateway) echo "CloudGatewayLiveliness" ;; + safe_edge_ota_service) echo "OtaServiceLiveliness" ;; + *) + echo "Unknown test name for binary: ${bin}" >&2 + return 1 + ;; + esac +} + +_run_liveliness_test_case() { + local ip="$1" + local name="$2" + local test_name="$3" + local infotainment_log="/tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.log" + local node_log service_name hb_line failed=0 + + node_log="$(_remote_log_path_for_bin "${name}")" + echo "[ RUN ] ${test_name}" + + if _remote_pid_is_running "${ip}" "${name}"; then + echo " [qemu] pid exists and process is alive" + else + echo " [qemu] FAIL pid missing or process not alive" + failed=1 + fi + + if [[ "${name}" == "safe_edge_infotainment" ]]; then + if _wait_for_remote_file_contains "${ip}" "${infotainment_log}" "Published ServiceHeartbeat" 8 1; then + echo " [dds] Published ServiceHeartbeat found" + else + echo " [dds] FAIL Published ServiceHeartbeat not found" + failed=1 + fi + elif [[ "${name}" == "safe_edge_vehicle_mock" ]]; then + if _remote_file_nonempty "${ip}" "${node_log}"; then + echo " [dds] node log is not empty" + else + echo " [dds] FAIL node log is empty" + failed=1 + fi + else + service_name="$(_service_name_for_bin "${name}")" + hb_line="Received ServiceHeartbeat service=${service_name} status=HEALTH_OK detail=running" + + if _wait_for_remote_file_contains "${ip}" "${node_log}" "Published ServiceHeartbeat" 8 1; then + echo " [dds] Published ServiceHeartbeat found" + else + echo " [dds] FAIL Published ServiceHeartbeat not found" + failed=1 + fi + + if _wait_for_remote_file_contains "${ip}" "${infotainment_log}" "${hb_line}" 8 1; then + echo " [dds] infotainment received heartbeat from ${service_name}" + else + echo " [dds] FAIL infotainment did not receive heartbeat from ${service_name}" + failed=1 + fi + fi + + if [[ "${failed}" -eq 0 ]]; then + echo "[ OK ] ${test_name}" + return 0 + fi + + echo "[ FAILED ] ${test_name}" + return 1 +} + +_test_nodes_launch_and_stay_alive() { + local ip="$1" + local name failed=0 + local total_tests=6 + local passed_tests=0 + local test_name + + _print_section "TEST: node launch and liveness" + echo "infotainment starts first so it can register the heartbeats from the rest of nodes." + echo "[==========] Running ${total_tests} liveliness tests." + echo "[----------] ${total_tests} tests" + + for name in safe_edge_infotainment safe_edge_safety_io_adapters safe_edge_policy_engine safe_edge_vehicle_mock safe_edge_cloud_gateway safe_edge_ota_service; do + test_name="$(_test_name_for_bin "${name}")" + if _run_liveliness_test_case "${ip}" "${name}" "${test_name}"; then + passed_tests=$((passed_tests + 1)) + else + failed=1 + fi + done + + echo "[----------] ${total_tests} tests" + echo "[ PASSED ] ${passed_tests} tests." + if [[ "${passed_tests}" -lt "${total_tests}" ]]; then + echo "[ FAILED ] $((total_tests - passed_tests)) tests." + fi + + return "${failed}" +} + +_test_smoke_logs() { + local ip="$1" + local name failed=0 + + _print_section "LOGS: startup smoke test" + echo "Waiting ${SMOKE_TEST_SECONDS}s before collecting node logs..." + sleep "${SMOKE_TEST_SECONDS}" + + for name in "${VEHICLE_NODE_BINS[@]}"; do + if ! _remote_file_nonempty "${ip}" "$(_remote_log_path_for_bin "${name}")"; then + echo "Log file is missing or empty for node: ${name}" + failed=1 + fi + done + + _print_vehicle_node_logs "${ip}" + return "${failed}" +} + +_prepare_local_target_dirs + +CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" +if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then + echo "A QNX VM from another repo target is still running." >&2 + echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 + echo >&2 + echo "Conflicts detected:" >&2 + while IFS=$'\t' read -r pid cwd; do + [[ -n "${pid}" ]] || continue + echo " PID ${pid}: ${cwd}" >&2 + done <<< "${CONFLICTING_QEMU_TARGETS}" + exit 1 +fi + +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_vehicle_binaries + _refresh_vehicle_ifs_start_snippet + _refresh_vehicle_post_start_snippet + _refresh_vehicle_system_files_snippet + _reset_generated_target_output +fi + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 +else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for VM IP..." +VM_IP="$(_get_ip_address)" +echo "VM is up: ${VM_IP}" +echo "Waiting for SSH..." +_wait_for_ssh "${VM_IP}" +echo "VM is reachable." + +echo "Starting vehicle nodes..." +_start_vehicle_nodes "${VM_IP}" +echo "Vehicle nodes launched." + +TEST_RC=0 +_test_nodes_launch_and_stay_alive "${VM_IP}" || TEST_RC=1 +_test_smoke_logs "${VM_IP}" || TEST_RC=1 + +_stop_vehicle_nodes "${VM_IP}" +echo -e "\nStopping QNX VM..." +mkqnximage --stop 2>/dev/null || true + +exit "${TEST_RC}" From ceb31122708cef1cb6155500ce0bcfeeb3eb05e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Mon, 8 Jun 2026 11:47:37 +0200 Subject: [PATCH 08/17] TPI 2.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .../safety_domain/common/RuntimeConfig.hpp | 4 + .../safety_domain/nodes/VehicleMockNode.hpp | 63 ++ safe_dds/safety/src/common/RuntimeConfig.cpp | 5 + .../safety/src/nodes/PolicyEngineNode.cpp | 23 + safe_dds/safety/src/nodes/VehicleMockNode.cpp | 159 +++++ scripts/launch_tpi_2_6_test.sh | 671 ++++++++++++++++++ 6 files changed, 925 insertions(+) create mode 100644 scripts/launch_tpi_2_6_test.sh diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp index 3a9e004..1b03230 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp @@ -16,6 +16,10 @@ struct RuntimeConfig uint32_t domain_id = 0U; uint16_t participant_port = 0U; uint16_t initial_peer_port = 0U; +<<<<<<< Updated upstream +======= + uint16_t initial_peer_port_2 = 0U; +>>>>>>> Stashed changes }; RuntimeConfig make_safety_io_adapters_runtime_config(); diff --git a/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp index b60e88b..1854a3d 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp @@ -6,15 +6,33 @@ #include +<<<<<<< Updated upstream +======= +#include +#include +>>>>>>> Stashed changes #include #include #include #include #include +<<<<<<< Updated upstream #include #include #include #include +======= +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +>>>>>>> Stashed changes #include @@ -48,22 +66,56 @@ class VehicleMockNode VehicleMockNode& owner_; }; +<<<<<<< Updated upstream +======= + class PolicyDecisionListener : + public eprosima::safedds::dds::DataReaderListener + { + public: + + explicit PolicyDecisionListener(VehicleMockNode& owner); + + void on_data_available( + eprosima::safedds::dds::DataReader& reader) noexcept override; + + private: + + VehicleMockNode& owner_; + }; + +>>>>>>> Stashed changes bool initialize(); bool create_participant(); bool register_types(); bool create_topics(); bool create_endpoints(); +<<<<<<< Updated upstream +======= + bool create_subscriber(); +>>>>>>> Stashed changes bool enable_entities(); bool create_executor(); void publish_frame(); void republish_last_frame() noexcept; +<<<<<<< Updated upstream +======= + void on_policy_decision_received( + const safe_edge::internal::PolicyDecision& decision, + const eprosima::safedds::execution::TimePoint& t_rx) noexcept; +>>>>>>> Stashed changes eprosima::safedds::dds::DomainParticipantFactory factory_; common::RuntimeConfig runtime_config_; common::HeaderFactory header_factory_; ParticipantListener participant_listener_; +<<<<<<< Updated upstream +======= + PolicyDecisionListener policy_decision_listener_; + + eprosima::safedds::memory::container::StaticList initial_peers_; +>>>>>>> Stashed changes safe_edge::internal::SafetyInputFrameTypeSupport safety_input_frame_type_support_; eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; @@ -80,6 +132,17 @@ class VehicleMockNode safe_edge::internal::SafetyInputFrame last_frame_{}; bool have_last_frame_ = false; +<<<<<<< Updated upstream +======= + + safe_edge::internal::PolicyDecisionTypeSupport policy_decision_type_support_; + eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; + eprosima::safedds::dds::Topic* policy_decision_topic_ = nullptr; + eprosima::safedds::memory::container::StaticString256 policy_decision_topic_name_; + eprosima::safedds::dds::DataReader* policy_decision_reader_ = nullptr; + eprosima::safedds::dds::TypedDataReader< + safe_edge::internal::PolicyDecisionTypeSupport>* policy_decision_typed_ = nullptr; +>>>>>>> Stashed changes }; } // namespace nodes diff --git a/safe_dds/safety/src/common/RuntimeConfig.cpp b/safe_dds/safety/src/common/RuntimeConfig.cpp index f3e8f4a..767d9ac 100644 --- a/safe_dds/safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/safety/src/common/RuntimeConfig.cpp @@ -36,6 +36,11 @@ RuntimeConfig make_vehicle_mock_runtime_config() config.source_name = "vehicle_mock"; config.domain_id = 0U; config.participant_port = 8003U; +<<<<<<< Updated upstream +======= + config.initial_peer_port = 8001U; + config.initial_peer_port_2 = 8002U; +>>>>>>> Stashed changes return config; } diff --git a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp index fddebd4..0820048 100644 --- a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp +++ b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp @@ -14,6 +14,10 @@ #include #include #include +<<<<<<< Updated upstream +======= +#include +>>>>>>> Stashed changes #include #include @@ -668,7 +672,15 @@ void PolicyEngineNode::publish_policy_decision() return; } +<<<<<<< Updated upstream std::cout << "[policy_engine] Published PolicyDecision mode=" << static_cast(decision.mode) +======= + const eprosima::safedds::execution::TimePoint t_dec = + eprosima::safedds::get_platform().get_current_timepoint(); + std::cout << "[policy_engine] Published PolicyDecision" + << " t_dec=" << t_dec.seconds << "." << t_dec.nanoseconds + << " mode=" << static_cast(decision.mode) +>>>>>>> Stashed changes << " allow_non_safety=" << decision.allow_non_safety << " allow_ota=" << decision.allow_ota << " reason=" << decision.reason << std::endl; @@ -694,9 +706,20 @@ void PolicyEngineNode::publish_heartbeat() void PolicyEngineNode::on_safety_input_frame_received( const safe_edge::internal::SafetyInputFrame& frame) { +<<<<<<< Updated upstream latest_safety_input_frame_ = frame; have_safety_input_frame_ = true; std::cout << "[policy_engine] Received SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; +======= + const eprosima::safedds::execution::TimePoint t_rx = + eprosima::safedds::get_platform().get_current_timepoint(); + latest_safety_input_frame_ = frame; + have_safety_input_frame_ = true; + std::cout << "[policy_engine] Received SafetyInputFrame" + << " t_rx=" << t_rx.seconds << "." << t_rx.nanoseconds + << " soc=" << frame.battery.soc_pct + << " emergency_stop=" << frame.safety.emergency_stop << std::endl; +>>>>>>> Stashed changes if (frame.battery.soc_pct < 20.0F) { diff --git a/safe_dds/safety/src/nodes/VehicleMockNode.cpp b/safe_dds/safety/src/nodes/VehicleMockNode.cpp index ab5f99f..8a30353 100644 --- a/safe_dds/safety/src/nodes/VehicleMockNode.cpp +++ b/safe_dds/safety/src/nodes/VehicleMockNode.cpp @@ -4,11 +4,23 @@ #include #include +<<<<<<< Updated upstream #include #include #include #include #include +======= +#include +#include +#include +#include +#include +#include +#include +#include +#include +>>>>>>> Stashed changes #include #include @@ -20,7 +32,11 @@ namespace nodes { namespace { +<<<<<<< Updated upstream constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {1, 0}; +======= +constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {0, 250'000'000}; +>>>>>>> Stashed changes static constexpr const char* INPUT_FILE_PATH = "/data/safe-edge-stage2/input.txt"; @@ -123,11 +139,40 @@ void VehicleMockNode::ParticipantListener::on_publication_matched( } } +<<<<<<< Updated upstream +======= +VehicleMockNode::PolicyDecisionListener::PolicyDecisionListener( + VehicleMockNode& owner) + : owner_(owner) +{ +} + +void VehicleMockNode::PolicyDecisionListener::on_data_available( + eprosima::safedds::dds::DataReader&) noexcept +{ + const eprosima::safedds::execution::TimePoint t_rx = + eprosima::safedds::get_platform().get_current_timepoint(); + + safe_edge::internal::PolicyDecision sample{}; + eprosima::safedds::dds::SampleInfo info{}; + + if (owner_.policy_decision_typed_->take_next_sample(sample, info) + == eprosima::safedds::dds::ReturnCode::OK && info.valid_data) + { + owner_.on_policy_decision_received(sample, t_rx); + } +} + +>>>>>>> Stashed changes VehicleMockNode::VehicleMockNode( const common::RuntimeConfig& runtime_config) : runtime_config_(runtime_config) , header_factory_(runtime_config.source_name) , participant_listener_(*this) +<<<<<<< Updated upstream +======= + , policy_decision_listener_(*this) +>>>>>>> Stashed changes , publish_timer_(TIMEOUT) { } @@ -166,6 +211,10 @@ bool VehicleMockNode::initialize() register_types() && create_topics() && create_endpoints() && +<<<<<<< Updated upstream +======= + create_subscriber() && +>>>>>>> Stashed changes enable_entities() && create_executor(); } @@ -179,6 +228,13 @@ bool VehicleMockNode::create_participant() {127, 0, 0, 1}, runtime_config_.participant_port); +<<<<<<< Updated upstream +======= + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port_2)); + participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; + +>>>>>>> Stashed changes participant_ = factory_.create_participant( runtime_config_.domain_id, participant_qos, @@ -204,6 +260,17 @@ bool VehicleMockNode::register_types() return false; } +<<<<<<< Updated upstream +======= + if (eprosima::safedds::dds::ReturnCode::OK != + policy_decision_type_support_.register_type( + *participant_, policy_decision_type_support_.get_type_name())) + { + std::cerr << "[vehicle_mock] Failed to register type: PolicyDecision" << std::endl; + return false; + } + +>>>>>>> Stashed changes return true; } @@ -226,6 +293,25 @@ bool VehicleMockNode::create_topics() return false; } +<<<<<<< Updated upstream +======= + policy_decision_topic_name_ = eprosima::safedds::memory::container::StaticString256( + common::topic_names::policy_decision()); + + policy_decision_topic_ = participant_->create_topic( + policy_decision_topic_name_, + policy_decision_type_support_.get_type_name(), + topic_qos, + nullptr, + eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == policy_decision_topic_) + { + std::cerr << "[vehicle_mock] Failed to create topic: policy_decision" << std::endl; + return false; + } + +>>>>>>> Stashed changes return true; } @@ -270,11 +356,59 @@ bool VehicleMockNode::create_endpoints() return true; } +<<<<<<< Updated upstream +======= +bool VehicleMockNode::create_subscriber() +{ + eprosima::safedds::dds::SubscriberQos sub_qos{}; + subscriber_ = participant_->create_subscriber( + sub_qos, nullptr, eprosima::safedds::dds::NONE_STATUS_MASK); + + if (nullptr == subscriber_) + { + std::cerr << "[vehicle_mock] Failed to create subscriber" << std::endl; + return false; + } + + eprosima::safedds::dds::DataReaderQos reader_qos{}; + reader_qos.reliability().kind = + eprosima::safedds::dds::ReliabilityQosPolicyKind::BEST_EFFORT_RELIABILITY_QOS; + + policy_decision_reader_ = subscriber_->create_datareader( + *policy_decision_topic_, + reader_qos, + &policy_decision_listener_, + eprosima::safedds::dds::DATA_AVAILABLE_STATUS); + + if (nullptr == policy_decision_reader_) + { + std::cerr << "[vehicle_mock] Failed to create policy_decision datareader" << std::endl; + return false; + } + + policy_decision_typed_ = eprosima::safedds::dds::TypedDataReader< + safe_edge::internal::PolicyDecisionTypeSupport>::downcast(*policy_decision_reader_); + + if (nullptr == policy_decision_typed_) + { + std::cerr << "[vehicle_mock] Failed to downcast policy_decision reader" << std::endl; + return false; + } + + return true; +} + +>>>>>>> Stashed changes bool VehicleMockNode::enable_entities() { bool enabled = true; enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == safety_input_frame_datawriter_->enable()); +<<<<<<< Updated upstream +======= + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); + enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == policy_decision_reader_->enable()); +>>>>>>> Stashed changes enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); if (!enabled) @@ -322,6 +456,12 @@ void VehicleMockNode::publish_frame() last_frame_ = frame; have_last_frame_ = true; +<<<<<<< Updated upstream +======= + const eprosima::safedds::execution::TimePoint t_pub = + eprosima::safedds::get_platform().get_current_timepoint(); + +>>>>>>> Stashed changes if (eprosima::safedds::dds::ReturnCode::OK != safety_input_frame_writer_->write(frame, eprosima::safedds::dds::HANDLE_NIL)) { @@ -329,10 +469,29 @@ void VehicleMockNode::publish_frame() } else { +<<<<<<< Updated upstream std::cout << "[vehicle_mock] Published SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; } } +======= + std::cout << "[vehicle_mock] Published SafetyInputFrame" + << " t_pub=" << t_pub.seconds << "." << t_pub.nanoseconds + << " soc=" << frame.battery.soc_pct + << " emergency_stop=" << frame.safety.emergency_stop << std::endl; + } +} + +void VehicleMockNode::on_policy_decision_received( + const safe_edge::internal::PolicyDecision& decision, + const eprosima::safedds::execution::TimePoint& t_rx) noexcept +{ + std::cout << "[vehicle_mock] Received PolicyDecision" + << " t_rx_dec=" << t_rx.seconds << "." << t_rx.nanoseconds + << " mode=" << static_cast(decision.mode) << std::endl; +} + +>>>>>>> Stashed changes void VehicleMockNode::republish_last_frame() noexcept { if (nullptr == safety_input_frame_writer_ || !have_last_frame_) diff --git a/scripts/launch_tpi_2_6_test.sh b/scripts/launch_tpi_2_6_test.sh new file mode 100644 index 0000000..67fbadf --- /dev/null +++ b/scripts/launch_tpi_2_6_test.sh @@ -0,0 +1,671 @@ +#!/usr/bin/env bash +# Run TPI 2.6 safety communication path latency benchmark for SafeDDS QNX nodes. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +LOG_DIR="${SCRIPT_DIR}/logs" +RAW_LOG="${LOG_DIR}/tpi_2_6_raw.log" +REPORT="${LOG_DIR}/tpi_2_6_report.txt" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" +: "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" + +OPT_NO_REBUILD=0 +OPT_STOP=0 +OPT_SAMPLES=100 +OPT_LOAD=0 +OPT_PRIO=0 + +VEHICLE_NODE_BINS=( + safe_edge_safety_io_adapters + safe_edge_policy_engine + safe_edge_vehicle_mock + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment +) + +INPUT_FILE_CONTENT='soc=50.0 +emergency_stop=0 +adas_fault=0 +available_charge_kw=50.0 +available_discharge_kw=50.0 +v2g_ready=1 +speed_mps=0.0 +braking_available=1 +steering_available=1' + +E2E_THRESHOLD_MS=20 +REACTION_THRESHOLD_MS=100 + +usage() { + cat <&2; exit 1 + fi + OPT_SAMPLES="$2" + shift 2 + ;; + --load) + if [[ $# -lt 2 ]]; then + echo "--load requires a value" >&2; exit 1 + fi + OPT_LOAD="$2" + shift 2 + ;; + --stop) + OPT_STOP=1 + shift + ;; + --prio) + OPT_PRIO=1 + shift + ;; + -h|--help) + usage; exit 0 + ;; + *) + echo "Unknown option: ${1}" >&2; usage >&2; exit 1 + ;; + esac +done + +if [[ ! "${OPT_SAMPLES}" =~ ^[0-9]+$ || "${OPT_SAMPLES}" -lt 1 ]]; then + echo "Invalid --samples value: ${OPT_SAMPLES}" >&2; exit 1 +fi +if [[ ! "${OPT_LOAD}" =~ ^[012]$ ]]; then + echo "Invalid --load value: ${OPT_LOAD} (must be 0, 1, or 2)" >&2; exit 1 +fi + +mkdir -p "${LOG_DIR}" + +_ssh_run() { + local ip="$1" + local cmd="$2" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_scp_get() { + local ip="$1" + local remote="$2" + local local_dst="$3" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" scp ${_SSH_OPTS} "${_SSH_USER}@${ip}:${remote}" "${local_dst}" +} + +_wait_for_ssh() { + local ip="$1" + local max_tries=45 + local i + for ((i = 1; i <= max_tries; i++)); do + if _ssh_run "${ip}" "true" >/dev/null 2>&1; then return 0; fi + if (( i == 1 || i % 5 == 0 )); then echo " waiting for SSH (${i}/${max_tries})..."; fi + sleep 2 + done + echo "Timed out waiting for SSH on ${ip}." >&2; return 1 +} + +_get_ip_address() { + local max_tries=20 + local ip i + for ((i = 0; i < max_tries; i++)); do + set +e; ip="$(mkqnximage --getip 2>/dev/null)"; set -e + if [[ -n "${ip}" ]]; then echo "${ip}"; return 0; fi + sleep 2 + done + echo "Timed out waiting for VM IP address." >&2; return 1 +} + +_find_conflicting_qemu_targets() { + local proc_dir pid cwd cmdline + for proc_dir in /proc/[0-9]*; do + pid="${proc_dir##*/}" + [[ -r "${proc_dir}/cmdline" ]] || continue + cmdline="$(tr '\0' ' ' < "${proc_dir}/cmdline" 2>/dev/null || true)" + [[ "${cmdline}" == qemu-system-x86_64* ]] || continue + cwd="$(readlink "${proc_dir}/cwd" 2>/dev/null || true)" + [[ -n "${cwd}" ]] || continue + case "${cwd}" in + "${WORKSPACE_ROOT}"/qnx/targets/*) + if [[ "${cwd}" != "${TARGET_DIR}" ]]; then + printf '%s\t%s\n' "${pid}" "${cwd}" + fi ;; + esac + done +} + +_validate_qnx_binary() { + local bin="$1" + local description + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2; return 1 + fi + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_host_bin_path() { + local name="$1" + case "${name}" in + safe_edge_safety_io_adapters|safe_edge_policy_engine|safe_edge_vehicle_mock) + echo "${SAFETY_BIN_DIR}/${name}" ;; + safe_edge_cloud_gateway|safe_edge_ota_service|safe_edge_infotainment) + echo "${NON_SAFETY_BIN_DIR}/${name}" ;; + *) + echo "Unknown vehicle node binary: ${name}" >&2; return 1 ;; + esac +} + +_validate_vehicle_binaries() { + local name bin + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2; exit 1 + fi + _validate_qnx_binary "${bin}" + done +} + +_refresh_vehicle_system_files_snippet() { + local snippet="${TARGET_DIR}/local/snippets/system_files.custom" + local name bin + mkdir -p "$(dirname "${snippet}")" + { + echo "# local/snippets/system_files.custom" + echo "# Generated by scripts/launch_tpi_2_6_test.sh" + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + echo "[perms=555] bin/${name}=${bin}" + done + } > "${snippet}" +} + +_refresh_vehicle_ifs_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/ifs_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_tpi_2_6_test.sh +EOF +} + +_refresh_vehicle_post_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/post_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_tpi_2_6_test.sh +route add -net 224.0.0.0/4 vtnet0 +EOF +} + +_reset_generated_target_output() { + rm -rf "${TARGET_DIR}/output" +} + +_prepare_local_target_dirs() { + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" +} + +_escape_single_quotes() { + local value="$1" + printf "%s" "${value//\'/\'\\\'\'}" +} + +_remote_file_contains() { + local ip="$1" file="$2" pattern="$3" + local ef ep + ef="$(_escape_single_quotes "${file}")" + ep="$(_escape_single_quotes "${pattern}")" + _ssh_run "${ip}" "grep -Fq -- '${ep}' '${ef}'" >/dev/null 2>&1 +} + +_wait_for_remote_file_contains() { + local ip="$1" file="$2" pattern="$3" + local max_tries="${4:-8}" sleep_sec="${5:-1}" i + for ((i = 1; i <= max_tries; i++)); do + if _remote_file_contains "${ip}" "${file}" "${pattern}"; then return 0; fi + sleep "${sleep_sec}" + done + return 1 +} + +_start_vehicle_nodes() { + local ip="$1" + local cmd name + local input_escaped + input_escaped="$(_escape_single_quotes "${INPUT_FILE_CONTENT}")" + + cmd="set -e; mkdir -p /tmp/safe_edge_vehicle_nodes /data/safe-edge-stage2;" + cmd+=" printf '${input_escaped}' > /data/safe-edge-stage2/input.txt;" + cmd+=" rm -f /tmp/safe_edge_vehicle_nodes/*.pid /tmp/safe_edge_vehicle_nodes/*.log;" + cmd+=" /system/bin/safe_edge_infotainment > /tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.log 2>&1 & echo \$! > /tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.pid;" + cmd+=" sleep 2;" + for name in "${VEHICLE_NODE_BINS[@]}"; do + [[ "${name}" == "safe_edge_infotainment" ]] && continue + cmd+=" /system/bin/${name} > /tmp/safe_edge_vehicle_nodes/${name}.log 2>&1 & echo \$! > /tmp/safe_edge_vehicle_nodes/${name}.pid;" + done + + _ssh_run "${ip}" "${cmd}" +} + +_start_load() { + local ip="$1" level="$2" + + # vCPU warmer: always launched at priority 1:r (minimum user priority). + # Preempted instantly by any SafeDDS thread (10:r) so it never competes. + # Keeps the QEMU vCPU thread scheduled on a host core at all levels, + # eliminating the idle-vCPU artifact from baseline measurements. + # Without this, load 0 shows artificially high latency because the host + # Linux scheduler deskedules the QEMU vCPU thread between DDS events. + local _warmer="on -d -p 1:r awk 'BEGIN{x=1.0;while(1){x=sin(x)+cos(x)}}'" + + # Level 1 — 1 duty-cycled burner at 10:r, ~80% duty cycle: + # burns 2M FP iterations then sleeps 10ms. High enough duty cycle to + # compete with SafeDDS threads most of the time. + # Expected impact: latency clearly above baseline. + # Level 1 — 1 duty-cycled burner at 10:r, ~90% duty cycle (sleep 5ms). + local _duty1="on -d -p 10:r sh -c 'while true; do awk \"BEGIN{for(i=0;i<2000000;i++){x=sin(i)+cos(i)}}\"; sleep 0.005; done'" + + # Level 2 — level 1 + a gentle second process: few iterations, long sleep. + # Just enough to occasionally steal a CPU slice from SafeDDS on top of + # the level 1 burner. ~20% duty cycle, minimal additional pressure. + local _duty2_light="on -d -p 10:r sh -c 'while true; do awk \"BEGIN{for(i=0;i<50000;i++){x=sin(i)+cos(i)}}\"; sleep 0.3; done'" + + local cmd="${_warmer} /dev/null 2>&1 &" + if [[ "${level}" -ge 1 ]]; then + cmd+=" ${_duty1} /dev/null 2>&1 &" + fi + if [[ "${level}" -ge 2 ]]; then + cmd+=" ${_duty2_light} /dev/null 2>&1 &" + fi + + _ssh_run "${ip}" "${cmd}" +} + +_stop_vehicle_nodes() { + local ip="$1" + # Kill tracked node pids + _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; pid=\$(cat \"\$f\"); kill \"\$pid\" 2>/dev/null || true; done" \ + >/dev/null 2>&1 || true + # Kill any load stressor processes (busy loops, dd) + _ssh_run "${ip}" "killall dd 2>/dev/null || true" >/dev/null 2>&1 || true + # Kill orphaned shell busy-loops spawned for load (best effort) + _ssh_run "${ip}" "kill \$(ps -e | grep 'sh' | awk '{print \$1}' | grep -v \$\$) 2>/dev/null || true" >/dev/null 2>&1 || true +} + +_trigger_sample_cycle() { + local ip="$1" + local esc1 esc0 + esc1="$(_escape_single_quotes "${INPUT_FILE_CONTENT/emergency_stop=0/emergency_stop=1}")" + esc0="$(_escape_single_quotes "${INPUT_FILE_CONTENT}")" + # Single SSH call: write trigger, hold for one vehicle_mock tick, reset — avoids 3×handshake overhead + _ssh_run "${ip}" "printf '${esc1}' > /data/safe-edge-stage2/input.txt; sleep 0.25; printf '${esc0}' > /data/safe-edge-stage2/input.txt" +} + +_run_measurement_loop() { + local ip="$1" + local pe_log="/tmp/safe_edge_vehicle_nodes/safe_edge_policy_engine.log" + local i + + echo "Waiting for nodes to initialize..." + _wait_for_remote_file_contains "${ip}" "${pe_log}" "Published ServiceHeartbeat" 60 1 || { + echo "policy_engine did not publish ServiceHeartbeat within 60 s — nodes may have crashed." >&2 + _dump_node_diagnostics "${ip}" + return 1 + } + echo "Nodes ready. Starting ${OPT_SAMPLES} measurement samples..." + + for ((i = 1; i <= OPT_SAMPLES; i++)); do + _trigger_sample_cycle "${ip}" + if (( i % 10 == 0 )); then echo " ${i}/${OPT_SAMPLES} samples collected"; fi + done + echo "Measurement loop complete." +} + +_collect_logs() { + local ip="$1" + local name tmp + + echo "Collecting logs..." + : > "${RAW_LOG}" + for name in "${VEHICLE_NODE_BINS[@]}"; do + tmp="$(mktemp)" + if _scp_get "${ip}" "/tmp/safe_edge_vehicle_nodes/${name}.log" "${tmp}" 2>/dev/null; then + cat "${tmp}" >> "${RAW_LOG}" + else + echo "[${name}] (log not found on VM)" >> "${RAW_LOG}" + fi + rm -f "${tmp}" + done + echo "Raw log: ${RAW_LOG}" +} + +_dump_node_diagnostics() { + local ip="$1" + local name + echo "--- node diagnostics ---" >&2 + { _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; n=\${f##*/}; n=\${n%.pid}; pid=\$(cat \"\$f\"); alive=dead; kill -0 \"\$pid\" 2>/dev/null && alive=running; echo \"\$n: pid=\$pid [\$alive]\"; done" \ + 2>/dev/null || true; } >&2 + for name in "${VEHICLE_NODE_BINS[@]}"; do + echo "--- ${name}.log (tail 10) ---" >&2 + { _ssh_run "${ip}" "tail -10 /tmp/safe_edge_vehicle_nodes/${name}.log 2>/dev/null || echo '(empty)'" || true; } >&2 + done +} + +_generate_report() { + python3 - "${RAW_LOG}" "${REPORT}" "${OPT_SAMPLES}" "${E2E_THRESHOLD_MS}" "${REACTION_THRESHOLD_MS}" <<'PYEOF' +import sys +import re + +raw_log = sys.argv[1] +report_path = sys.argv[2] +n_samples = int(sys.argv[3]) +e2e_limit = float(sys.argv[4]) +rxn_limit = float(sys.argv[5]) + +def parse_ts(s, ns): + return int(s) * 1_000_000_000 + int(ns) + +def ns_to_ms(ns): + return ns / 1_000_000.0 + +# vehicle_mock: stimulus publish (t_pub, same clock as t_rx_dec) +vm_pub_re = re.compile( + r'\[vehicle_mock\] Published SafetyInputFrame' + r'.*t_pub=(\d+)\.(\d+).*emergency_stop=1') + +# vehicle_mock: PolicyDecision received (t_rx_dec, same clock as t_pub) +vm_dec_re = re.compile( + r'\[vehicle_mock\] Received PolicyDecision t_rx_dec=(\d+)\.(\d+)') + +# policy_engine: SafetyInputFrame received (t_rx, policy_engine clock) +pe_rx_re = re.compile( + r'\[policy_engine\] Received SafetyInputFrame' + r'.*t_rx=(\d+)\.(\d+).*emergency_stop=1') + +# policy_engine: PolicyDecision published (t_dec, policy_engine clock) +pe_dec_re = re.compile( + r'\[policy_engine\] Published PolicyDecision.*t_dec=(\d+)\.(\d+)') + +with open(raw_log) as f: + lines = f.readlines() + +e2e_samples = [] +rxn_samples = [] + +i = 0 +while i < len(lines): + pub_m = vm_pub_re.search(lines[i]) + if pub_m: + t_pub = parse_ts(pub_m.group(1), pub_m.group(2)) + # Find next vehicle_mock PolicyDecision receive → E2E + j = i + 1 + dec_m = None + while j < len(lines): + dec_m = vm_dec_re.search(lines[j]) + if dec_m: + break + j += 1 + if dec_m is not None: + t_rx_dec = parse_ts(dec_m.group(1), dec_m.group(2)) + e2e_samples.append(ns_to_ms(t_rx_dec - t_pub)) + i += 1 + else: + # Reaction time: policy_engine receive → policy_engine publish + rx_m = pe_rx_re.search(lines[i]) + if rx_m: + t_rx = parse_ts(rx_m.group(1), rx_m.group(2)) + k = i + 1 + pdec_m = None + while k < len(lines): + pdec_m = pe_dec_re.search(lines[k]) + if pdec_m: + break + k += 1 + if pdec_m is not None: + t_dec = parse_ts(pdec_m.group(1), pdec_m.group(2)) + rxn_samples.append(ns_to_ms(t_dec - t_rx)) + i = k + 1 + else: + i += 1 + +def percentile(data, p): + if not data: + return float('nan') + data_s = sorted(data) + idx = (len(data_s) - 1) * p / 100.0 + lo = int(idx) + hi = lo + 1 + if hi >= len(data_s): + return data_s[lo] + return data_s[lo] + (data_s[hi] - data_s[lo]) * (idx - lo) + +def stats(data): + if not data: + return {k: float('nan') for k in ('min','p50','p90','p95','p99','max')} + return { + 'min': min(data), + 'p50': percentile(data, 50), + 'p90': percentile(data, 90), + 'p95': percentile(data, 95), + 'p99': percentile(data, 99), + 'max': max(data), + } + +e2e_st = stats(e2e_samples) +rxn_st = stats(rxn_samples) + +e2e_pass = (not (e2e_st['p99'] != e2e_st['p99'])) and e2e_st['p99'] < e2e_limit +rxn_pass = (not (rxn_st['p99'] != rxn_st['p99'])) and rxn_st['p99'] < rxn_limit +overall = e2e_pass and rxn_pass + +report = [] +report.append("SafeEDGE TPI 2.6 — Safety Path Latency Benchmark") +report.append("Environment: QNX 8.0 / QEMU x86_64") +report.append(f"Raw log: {raw_log}") +report.append(f"Samples collected: {len(e2e_samples)} E2E, {len(rxn_samples)} reaction (requested: {n_samples})") +report.append("") +report.append("=== E2E Latency (vehicle_mock publish -> vehicle_mock receive PolicyDecision) ===") +report.append(f"Proposal limit: < {e2e_limit:.0f} ms Derived KPI (P99): < {e2e_limit:.0f} ms") +report.append("") +report.append(f" {'min':>8} {'P50':>8} {'P90':>8} {'P95':>8} {'P99':>8} {'max':>8}") +report.append(f" {e2e_st['min']:>7.2f}ms {e2e_st['p50']:>7.2f}ms {e2e_st['p90']:>7.2f}ms {e2e_st['p95']:>7.2f}ms {e2e_st['p99']:>7.2f}ms {e2e_st['max']:>7.2f}ms") +report.append("") +report.append(f"Result: {'PASS' if e2e_pass else 'FAIL'}") +report.append("") +report.append("=== Policy-Reaction Time (policy_engine receive -> publish PolicyDecision) ===") +report.append(f"Proposal limit: < {rxn_limit:.0f} ms Derived KPI (P99): < {rxn_limit:.0f} ms") +report.append("") +report.append(f" {'min':>8} {'P50':>8} {'P90':>8} {'P95':>8} {'P99':>8} {'max':>8}") +report.append(f" {rxn_st['min']:>7.2f}ms {rxn_st['p50']:>7.2f}ms {rxn_st['p90']:>7.2f}ms {rxn_st['p95']:>7.2f}ms {rxn_st['p99']:>7.2f}ms {rxn_st['max']:>7.2f}ms") +report.append("") +report.append(f"Result: {'PASS' if rxn_pass else 'FAIL'}") +report.append("") +report.append(f"Overall: {'PASS' if overall else 'FAIL'}") + +text = "\n".join(report) +print(text) +with open(report_path, 'w') as f: + f.write(text + "\n") + +sys.exit(0 if overall else 1) +PYEOF +} + +# ─── Main ─────────────────────────────────────────────────────────────────── + +_prepare_local_target_dirs + +CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" +if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then + echo "A QNX VM from another repo target is still running." >&2 + echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 + echo >&2 + echo "Conflicts detected:" >&2 + while IFS=$'\t' read -r pid cwd; do + [[ -n "${pid}" ]] || continue + echo " PID ${pid}: ${cwd}" >&2 + done <<< "${CONFLICTING_QEMU_TARGETS}" + exit 1 +fi + +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2; exit 1 +fi +if [[ ! -d "${TARGET_DIR}" ]]; then + echo "QNX target directory not found: ${TARGET_DIR}" >&2; exit 1 +fi +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2; exit 1 +fi +if ! command -v python3 >/dev/null 2>&1; then + echo "python3 not found on host — required for post-processing" >&2; exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2; exit 1 +fi +if ! command -v brctl >/dev/null 2>&1; then + echo "brctl not found. Install with: sudo apt install bridge-utils" >&2; exit 1 +fi + +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_vehicle_binaries + _refresh_vehicle_ifs_start_snippet + _refresh_vehicle_post_start_snippet + _refresh_vehicle_system_files_snippet + _reset_generated_target_output + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 +else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for VM IP..." +VM_IP="$(_get_ip_address)" +echo "VM is up: ${VM_IP}" +echo "Waiting for SSH..." +_wait_for_ssh "${VM_IP}" +echo "VM is reachable." + +echo "Starting vehicle nodes..." +_start_vehicle_nodes "${VM_IP}" +echo "Vehicle nodes launched." + +if [[ "${OPT_PRIO}" -eq 1 ]]; then + if [[ "${OPT_LOAD}" -gt 0 ]]; then + echo "Starting load level ${OPT_LOAD}..." + _start_load "${VM_IP}" "${OPT_LOAD}" + echo "Load stressors launched." + fi + echo "Waiting for policy_engine heartbeat (up to 60s)..." + NODE_LOG_DIR="/tmp/safe_edge_vehicle_nodes" + if ! _wait_for_remote_file_contains "${VM_IP}" \ + "${NODE_LOG_DIR}/safe_edge_policy_engine.log" \ + "Published ServiceHeartbeat" \ + 60 1; then + echo "WARNING: heartbeat not seen after 60s — querying anyway" >&2 + else + echo "Nodes up. Querying priorities..." + fi + echo "" + echo "=== pidin header (raw) ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | head -3" 2>/dev/null || true + echo "" + echo "=== all processes (pidin, first 40 lines) ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | head -40" 2>/dev/null || true + echo "" + echo "=== safe_edge grep ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | grep -i safe" 2>/dev/null || true + echo "" + echo "=== stressor grep ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | grep -iE 'awk|nc '" 2>/dev/null || true + echo "" + echo "Stopping VM..." + mkqnximage --stop 2>/dev/null || true + exit 0 +fi + +if [[ "${OPT_LOAD}" -gt 0 ]]; then + echo "Starting load level ${OPT_LOAD}..." + _start_load "${VM_IP}" "${OPT_LOAD}" + echo "Load stressors active." +fi + +TEST_RC=0 +_run_measurement_loop "${VM_IP}" || TEST_RC=1 + +_collect_logs "${VM_IP}" + +_stop_vehicle_nodes "${VM_IP}" +echo "Stopping QNX VM..." +mkqnximage --stop 2>/dev/null || true + +echo "Generating latency report..." +_generate_report || TEST_RC=1 +echo "Report: ${REPORT}" + +exit "${TEST_RC}" From 0a438e26214e3b035f30e18443c227960b9c1bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Mon, 8 Jun 2026 12:28:16 +0200 Subject: [PATCH 09/17] Minor updates and remove junk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- safe_dds/edge/test/test_edge_integration.cpp | 6 +- .../non_safety/src/nodes/CloudGatewayNode.cpp | 1 + .../non_safety/src/nodes/InfotainmentNode.cpp | 1 + .../non_safety/src/nodes/OtaServiceNode.cpp | 1 + .../safety_domain/common/RuntimeConfig.hpp | 3 - .../safety_domain/nodes/VehicleMockNode.hpp | 23 +- safe_dds/safety/src/common/RuntimeConfig.cpp | 3 - .../safety/src/nodes/PolicyEngineNode.cpp | 11 +- safe_dds/safety/src/nodes/VehicleMockNode.cpp | 49 +--- .../server/test/test_server_integration.cpp | 6 +- scripts/build_native.sh | 227 ++++++++++++++++++ scripts/build_qnx.sh | 0 12 files changed, 245 insertions(+), 86 deletions(-) create mode 100644 scripts/build_native.sh mode change 100755 => 100644 scripts/build_qnx.sh diff --git a/safe_dds/edge/test/test_edge_integration.cpp b/safe_dds/edge/test/test_edge_integration.cpp index 3e942e7..4b7021d 100644 --- a/safe_dds/edge/test/test_edge_integration.cpp +++ b/safe_dds/edge/test/test_edge_integration.cpp @@ -123,8 +123,9 @@ class EdgeEnvironment : public ::testing::Environment void TearDown() override { std::cout << "[env] safe_edge_edge_gateway log:\n"; - std::system((std::string("sh -c 'if [ -f ") + EDGE_LOG_FILE + + const int dump_rc = std::system((std::string("sh -c 'if [ -f ") + EDGE_LOG_FILE + " ]; then cat " + EDGE_LOG_FILE + "; else echo missing log; fi'").c_str()); + (void)dump_rc; std::cout << "[env] Stopping safe_edge_edge_gateway...\n"; const std::string stop_cmd = "sh -c '" @@ -137,7 +138,8 @@ class EdgeEnvironment : public ::testing::Environment "fi; " "pkill -f safe_edge_edge_gateway 2>/dev/null || true" "'"; - std::system(stop_cmd.c_str()); + const int stop_rc = std::system(stop_cmd.c_str()); + (void)stop_rc; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }; diff --git a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp index f14e553..13cd259 100644 --- a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp +++ b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp @@ -410,6 +410,7 @@ bool CloudGatewayNode::create_participant() participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( {127, 0, 0, 1}, runtime_config_.participant_port); + participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); diff --git a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp index db4cbcc..2e9719b 100644 --- a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp +++ b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp @@ -318,6 +318,7 @@ bool InfotainmentNode::create_participant() participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( {127, 0, 0, 1}, runtime_config_.participant_port); + participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); diff --git a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp index 7fc535a..a4a2348 100644 --- a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp +++ b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp @@ -282,6 +282,7 @@ bool OtaServiceNode::create_participant() participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( {127, 0, 0, 1}, runtime_config_.participant_port); + participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp index 1b03230..3ef6199 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp @@ -16,10 +16,7 @@ struct RuntimeConfig uint32_t domain_id = 0U; uint16_t participant_port = 0U; uint16_t initial_peer_port = 0U; -<<<<<<< Updated upstream -======= uint16_t initial_peer_port_2 = 0U; ->>>>>>> Stashed changes }; RuntimeConfig make_safety_io_adapters_runtime_config(); diff --git a/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp index 1854a3d..692c62b 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/nodes/VehicleMockNode.hpp @@ -6,22 +6,19 @@ #include -<<<<<<< Updated upstream -======= #include #include ->>>>>>> Stashed changes +#include +#include #include #include #include #include #include -<<<<<<< Updated upstream #include #include #include #include -======= #include #include #include @@ -32,7 +29,6 @@ #include #include #include ->>>>>>> Stashed changes #include @@ -66,8 +62,6 @@ class VehicleMockNode VehicleMockNode& owner_; }; -<<<<<<< Updated upstream -======= class PolicyDecisionListener : public eprosima::safedds::dds::DataReaderListener { @@ -83,39 +77,29 @@ class VehicleMockNode VehicleMockNode& owner_; }; ->>>>>>> Stashed changes bool initialize(); bool create_participant(); bool register_types(); bool create_topics(); bool create_endpoints(); -<<<<<<< Updated upstream -======= bool create_subscriber(); ->>>>>>> Stashed changes bool enable_entities(); bool create_executor(); void publish_frame(); void republish_last_frame() noexcept; -<<<<<<< Updated upstream -======= void on_policy_decision_received( const safe_edge::internal::PolicyDecision& decision, const eprosima::safedds::execution::TimePoint& t_rx) noexcept; ->>>>>>> Stashed changes eprosima::safedds::dds::DomainParticipantFactory factory_; common::RuntimeConfig runtime_config_; common::HeaderFactory header_factory_; ParticipantListener participant_listener_; -<<<<<<< Updated upstream -======= PolicyDecisionListener policy_decision_listener_; eprosima::safedds::memory::container::StaticList initial_peers_; ->>>>>>> Stashed changes safe_edge::internal::SafetyInputFrameTypeSupport safety_input_frame_type_support_; eprosima::safedds::dds::DomainParticipant* participant_ = nullptr; @@ -132,8 +116,6 @@ class VehicleMockNode safe_edge::internal::SafetyInputFrame last_frame_{}; bool have_last_frame_ = false; -<<<<<<< Updated upstream -======= safe_edge::internal::PolicyDecisionTypeSupport policy_decision_type_support_; eprosima::safedds::dds::Subscriber* subscriber_ = nullptr; @@ -142,7 +124,6 @@ class VehicleMockNode eprosima::safedds::dds::DataReader* policy_decision_reader_ = nullptr; eprosima::safedds::dds::TypedDataReader< safe_edge::internal::PolicyDecisionTypeSupport>* policy_decision_typed_ = nullptr; ->>>>>>> Stashed changes }; } // namespace nodes diff --git a/safe_dds/safety/src/common/RuntimeConfig.cpp b/safe_dds/safety/src/common/RuntimeConfig.cpp index 767d9ac..8a9ef90 100644 --- a/safe_dds/safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/safety/src/common/RuntimeConfig.cpp @@ -36,11 +36,8 @@ RuntimeConfig make_vehicle_mock_runtime_config() config.source_name = "vehicle_mock"; config.domain_id = 0U; config.participant_port = 8003U; -<<<<<<< Updated upstream -======= config.initial_peer_port = 8001U; config.initial_peer_port_2 = 8002U; ->>>>>>> Stashed changes return config; } diff --git a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp index 0820048..8b63813 100644 --- a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp +++ b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp @@ -14,10 +14,7 @@ #include #include #include -<<<<<<< Updated upstream -======= #include ->>>>>>> Stashed changes #include #include @@ -672,15 +669,12 @@ void PolicyEngineNode::publish_policy_decision() return; } -<<<<<<< Updated upstream - std::cout << "[policy_engine] Published PolicyDecision mode=" << static_cast(decision.mode) -======= + std::cout << "[policy_engine] Published PolicyDecision mode=" << static_cast(decision.mode); const eprosima::safedds::execution::TimePoint t_dec = eprosima::safedds::get_platform().get_current_timepoint(); std::cout << "[policy_engine] Published PolicyDecision" << " t_dec=" << t_dec.seconds << "." << t_dec.nanoseconds << " mode=" << static_cast(decision.mode) ->>>>>>> Stashed changes << " allow_non_safety=" << decision.allow_non_safety << " allow_ota=" << decision.allow_ota << " reason=" << decision.reason << std::endl; @@ -706,11 +700,9 @@ void PolicyEngineNode::publish_heartbeat() void PolicyEngineNode::on_safety_input_frame_received( const safe_edge::internal::SafetyInputFrame& frame) { -<<<<<<< Updated upstream latest_safety_input_frame_ = frame; have_safety_input_frame_ = true; std::cout << "[policy_engine] Received SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; -======= const eprosima::safedds::execution::TimePoint t_rx = eprosima::safedds::get_platform().get_current_timepoint(); latest_safety_input_frame_ = frame; @@ -719,7 +711,6 @@ void PolicyEngineNode::on_safety_input_frame_received( << " t_rx=" << t_rx.seconds << "." << t_rx.nanoseconds << " soc=" << frame.battery.soc_pct << " emergency_stop=" << frame.safety.emergency_stop << std::endl; ->>>>>>> Stashed changes if (frame.battery.soc_pct < 20.0F) { diff --git a/safe_dds/safety/src/nodes/VehicleMockNode.cpp b/safe_dds/safety/src/nodes/VehicleMockNode.cpp index 8a30353..b8365ed 100644 --- a/safe_dds/safety/src/nodes/VehicleMockNode.cpp +++ b/safe_dds/safety/src/nodes/VehicleMockNode.cpp @@ -4,13 +4,11 @@ #include #include -<<<<<<< Updated upstream #include #include #include #include #include -======= #include #include #include @@ -20,7 +18,6 @@ #include #include #include ->>>>>>> Stashed changes #include #include @@ -32,11 +29,7 @@ namespace nodes { namespace { -<<<<<<< Updated upstream -constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {1, 0}; -======= constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {0, 250'000'000}; ->>>>>>> Stashed changes static constexpr const char* INPUT_FILE_PATH = "/data/safe-edge-stage2/input.txt"; @@ -139,8 +132,6 @@ void VehicleMockNode::ParticipantListener::on_publication_matched( } } -<<<<<<< Updated upstream -======= VehicleMockNode::PolicyDecisionListener::PolicyDecisionListener( VehicleMockNode& owner) : owner_(owner) @@ -163,16 +154,12 @@ void VehicleMockNode::PolicyDecisionListener::on_data_available( } } ->>>>>>> Stashed changes VehicleMockNode::VehicleMockNode( const common::RuntimeConfig& runtime_config) : runtime_config_(runtime_config) , header_factory_(runtime_config.source_name) , participant_listener_(*this) -<<<<<<< Updated upstream -======= , policy_decision_listener_(*this) ->>>>>>> Stashed changes , publish_timer_(TIMEOUT) { } @@ -199,7 +186,9 @@ int VehicleMockNode::run() publish_frame(); } - executor_->spin(publish_timer_.next_trigger()); + eprosima::safedds::execution::TimePoint next_work_timepoint = eprosima::safedds::execution::TimePoint::min(executor_->get_next_work_timepoint(), publish_timer_.next_trigger()); + + executor_->spin(next_work_timepoint); } return 0; @@ -211,10 +200,7 @@ bool VehicleMockNode::initialize() register_types() && create_topics() && create_endpoints() && -<<<<<<< Updated upstream -======= create_subscriber() && ->>>>>>> Stashed changes enable_entities() && create_executor(); } @@ -227,14 +213,10 @@ bool VehicleMockNode::create_participant() participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( {127, 0, 0, 1}, runtime_config_.participant_port); - -<<<<<<< Updated upstream -======= + participant_qos.wire_protocol_qos().use_multicast_discovery = false; initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port_2)); participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; - ->>>>>>> Stashed changes participant_ = factory_.create_participant( runtime_config_.domain_id, participant_qos, @@ -260,8 +242,6 @@ bool VehicleMockNode::register_types() return false; } -<<<<<<< Updated upstream -======= if (eprosima::safedds::dds::ReturnCode::OK != policy_decision_type_support_.register_type( *participant_, policy_decision_type_support_.get_type_name())) @@ -269,8 +249,6 @@ bool VehicleMockNode::register_types() std::cerr << "[vehicle_mock] Failed to register type: PolicyDecision" << std::endl; return false; } - ->>>>>>> Stashed changes return true; } @@ -293,8 +271,6 @@ bool VehicleMockNode::create_topics() return false; } -<<<<<<< Updated upstream -======= policy_decision_topic_name_ = eprosima::safedds::memory::container::StaticString256( common::topic_names::policy_decision()); @@ -311,7 +287,6 @@ bool VehicleMockNode::create_topics() return false; } ->>>>>>> Stashed changes return true; } @@ -356,8 +331,6 @@ bool VehicleMockNode::create_endpoints() return true; } -<<<<<<< Updated upstream -======= bool VehicleMockNode::create_subscriber() { eprosima::safedds::dds::SubscriberQos sub_qos{}; @@ -398,17 +371,13 @@ bool VehicleMockNode::create_subscriber() return true; } ->>>>>>> Stashed changes bool VehicleMockNode::enable_entities() { bool enabled = true; enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == publisher_->enable()); enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == safety_input_frame_datawriter_->enable()); -<<<<<<< Updated upstream -======= enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == subscriber_->enable()); enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == policy_decision_reader_->enable()); ->>>>>>> Stashed changes enabled = enabled && (eprosima::safedds::dds::ReturnCode::OK == participant_->enable()); if (!enabled) @@ -456,12 +425,9 @@ void VehicleMockNode::publish_frame() last_frame_ = frame; have_last_frame_ = true; -<<<<<<< Updated upstream -======= const eprosima::safedds::execution::TimePoint t_pub = eprosima::safedds::get_platform().get_current_timepoint(); ->>>>>>> Stashed changes if (eprosima::safedds::dds::ReturnCode::OK != safety_input_frame_writer_->write(frame, eprosima::safedds::dds::HANDLE_NIL)) { @@ -469,12 +435,6 @@ void VehicleMockNode::publish_frame() } else { -<<<<<<< Updated upstream - std::cout << "[vehicle_mock] Published SafetyInputFrame soc=" << frame.battery.soc_pct << std::endl; - } -} - -======= std::cout << "[vehicle_mock] Published SafetyInputFrame" << " t_pub=" << t_pub.seconds << "." << t_pub.nanoseconds << " soc=" << frame.battery.soc_pct @@ -491,7 +451,6 @@ void VehicleMockNode::on_policy_decision_received( << " mode=" << static_cast(decision.mode) << std::endl; } ->>>>>>> Stashed changes void VehicleMockNode::republish_last_frame() noexcept { if (nullptr == safety_input_frame_writer_ || !have_last_frame_) diff --git a/safe_dds/server/test/test_server_integration.cpp b/safe_dds/server/test/test_server_integration.cpp index 61f681d..acd0881 100644 --- a/safe_dds/server/test/test_server_integration.cpp +++ b/safe_dds/server/test/test_server_integration.cpp @@ -119,8 +119,9 @@ class ServerEnvironment : public ::testing::Environment void TearDown() override { std::cout << "[env] safe_edge_server log:\n"; - std::system((std::string("sh -c 'if [ -f ") + SERVER_LOG_FILE + + const int dump_rc = std::system((std::string("sh -c 'if [ -f ") + SERVER_LOG_FILE + " ]; then cat " + SERVER_LOG_FILE + "; else echo missing log; fi'").c_str()); + (void)dump_rc; std::cout << "[env] Stopping safe_edge_server...\n"; const std::string stop_cmd = "sh -c '" @@ -133,7 +134,8 @@ class ServerEnvironment : public ::testing::Environment "fi; " "pkill -f safe_edge_server 2>/dev/null || true" "'"; - std::system(stop_cmd.c_str()); + const int stop_rc = std::system(stop_cmd.c_str()); + (void)stop_rc; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }; diff --git a/scripts/build_native.sh b/scripts/build_native.sh new file mode 100644 index 0000000..fee7623 --- /dev/null +++ b/scripts/build_native.sh @@ -0,0 +1,227 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +: "${CMAKE_BUILD_TYPE:=Release}" +: "${CMAKE_GENERATOR:=Unix Makefiles}" +: "${NATIVE_COMMON_CXXFLAGS:=}" +: "${SAFE_DDS_IDL_GENERATOR:-}" +: "${SAFE_DDS_IDL_GENERATOR_ARGS:-}" +: "${SAFEDDS_DIR:=}" +: "${COMMON_SERVER_BUILD_FOLDER:=${WORKSPACE_ROOT}/common_server/build/server-common-native-${CMAKE_BUILD_TYPE}}" +: "${COMMON_SERVER_INSTALL_FOLDER:=${WORKSPACE_ROOT}/common_server/install/server-common-native-${CMAKE_BUILD_TYPE}}" +: "${SERVER_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/server-native-${CMAKE_BUILD_TYPE}}" +: "${SERVER_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/server-native-${CMAKE_BUILD_TYPE}}" +: "${EDGE_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/edge-native-${CMAKE_BUILD_TYPE}}" +: "${EDGE_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/edge-native-${CMAKE_BUILD_TYPE}}" +: "${SAFETY_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/safety-native-${CMAKE_BUILD_TYPE}}" +: "${SAFETY_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/safety-native-${CMAKE_BUILD_TYPE}}" +: "${NON_SAFETY_BUILD_FOLDER:=${WORKSPACE_ROOT}/safe_dds/build/non-safety-native-${CMAKE_BUILD_TYPE}}" +: "${NON_SAFETY_INSTALL_FOLDER:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-native-${CMAKE_BUILD_TYPE}}" + +COMMON_IDL_SOURCE_DIR="${WORKSPACE_ROOT}/idl" +SAFE_DDS_IDL_DIR="${WORKSPACE_ROOT}/safe_dds/idl" + +REGEN_IDL=0 +EXTRA_BUILD_ARGS=() + +usage() { + cat <] + +Builds the same CMake targets as build_qnx.sh, but natively for Ubuntu/Linux. + +Options: + -i, --idl regenerate safe_dds/idl from the shared idl/*.idl sources + -h, --help show this help message + +Environment variables: + SAFEDDS_DIR path to the native Safe DDS CMake package + SAFE_DDS_IDL_GENERATOR command used to regenerate Safe DDS IDL artifacts + SAFE_DDS_IDL_GENERATOR_ARGS extra args appended to the IDL generator command + CMAKE_BUILD_TYPE CMake configuration type (default: Release) + +The script always configures and builds: + - common_server + - safe_dds/server + - safe_dds/edge + - safe_dds/safety + - safe_dds/non_safety +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -i|--idl) + REGEN_IDL=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + EXTRA_BUILD_ARGS=("$@") + break + ;; + *) + echo "Unknown option: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +find_idl_generator() { + if [[ -n "${SAFE_DDS_IDL_GENERATOR:-}" ]]; then + local generator_bin="${SAFE_DDS_IDL_GENERATOR%% *}" + if [[ -x "${generator_bin}" ]] || command -v "${generator_bin}" >/dev/null 2>&1; then + echo "${SAFE_DDS_IDL_GENERATOR}" + return 0 + fi + fi + + if [[ -n "${SAFE_DDS_PATH:-}" ]]; then + local safedds_source_generator="${SAFE_DDS_PATH}/code-gen/scripts/safeddsgen" + if [[ -x "${safedds_source_generator}" ]]; then + echo "${safedds_source_generator}" + return 0 + fi + fi + + local candidates=(safeddsgen safedds-idl-gen safedds-gen eprosima_safeddsgen) + for prog in "${candidates[@]}"; do + if command -v "${prog}" >/dev/null 2>&1; then + echo "${prog}" + return 0 + fi + done + + return 1 +} + +regenerate_idl() { + local generator + generator="$(find_idl_generator)" || { + echo "No IDL generator found. Set SAFE_DDS_IDL_GENERATOR to a valid command." >&2 + exit 1 + } + + if [[ ! -d "${COMMON_IDL_SOURCE_DIR}" ]]; then + echo "Shared IDL source directory not found at '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 + fi + + mkdir -p "${SAFE_DDS_IDL_DIR}" + + local cleanup_links=() + local idl_file + for idl_file in "${COMMON_IDL_SOURCE_DIR}"/*.idl; do + if [[ ! -e "${idl_file}" ]]; then + echo "No .idl files found in '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 + fi + + local link_target="${SAFE_DDS_IDL_DIR}/$(basename "${idl_file}")" + ln -sfn "${idl_file}" "${link_target}" + cleanup_links+=("${link_target}") + done + + echo "Regenerating Safe DDS IDL artifacts" + echo " source : ${COMMON_IDL_SOURCE_DIR}" + echo " generated: ${SAFE_DDS_IDL_DIR}" + echo " generator: ${generator} -D . ${SAFE_DDS_IDL_GENERATOR_ARGS:-} *.idl" + + pushd "${SAFE_DDS_IDL_DIR}" >/dev/null + if ! bash -lc "${generator} -D . ${SAFE_DDS_IDL_GENERATOR_ARGS:-} *.idl"; then + popd >/dev/null + rm -f "${cleanup_links[@]}" + exit 1 + fi + popd >/dev/null + + rm -f "${cleanup_links[@]}" +} + +configure_and_build() { + local src_dir="$1" + local build_dir="$2" + local install_dir="$3" + local name="$4" + shift 4 + local extra_cmake_args=("$@") + local cache_file="${build_dir}/CMakeCache.txt" + + if [[ -f "${cache_file}" ]]; then + local cached_source + cached_source="$(sed -n 's/^CMAKE_HOME_DIRECTORY:INTERNAL=//p' "${cache_file}")" + if [[ -n "${cached_source}" && "${cached_source}" != "${src_dir}" ]]; then + echo "Removing stale CMake cache for ${name}" + echo " cached source: ${cached_source}" + rm -rf "${build_dir}" + mkdir -p "${build_dir}" + fi + fi + + local cmake_args=( + -S "${src_dir}" + -B "${build_dir}" + -G "${CMAKE_GENERATOR}" + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + -DCMAKE_INSTALL_PREFIX="${install_dir}" + ) + + if [[ -n "${CMAKE_CXX_FLAGS:-${NATIVE_COMMON_CXXFLAGS}}" ]]; then + cmake_args+=("-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS:-${NATIVE_COMMON_CXXFLAGS}}") + fi + if [[ -n "${SAFEDDS_DIR:-}" ]]; then + cmake_args+=("-Dsafedds_DIR=${SAFEDDS_DIR}") + fi + cmake_args+=("${extra_cmake_args[@]}") + + echo "Configuring ${name}" + echo " source : ${src_dir}" + echo " build : ${build_dir}" + echo " install: ${install_dir}" + cmake "${cmake_args[@]}" + cmake --build "${build_dir}" --target install "${EXTRA_BUILD_ARGS[@]}" + echo -e "Finished building ${name}\n" +} + +if [[ ! -d "${COMMON_IDL_SOURCE_DIR}" ]]; then + echo "Shared IDL source directory not found at '${COMMON_IDL_SOURCE_DIR}'" >&2 + exit 1 +fi + +if [[ ! -d "${SAFE_DDS_IDL_DIR}" ]]; then + echo "Safe DDS generated IDL directory not found at '${SAFE_DDS_IDL_DIR}'" >&2 + exit 1 +fi + +if (( REGEN_IDL )); then + regenerate_idl +fi + +if [[ -f "${COMMON_IDL_SOURCE_DIR}/internal.idl" && ! -f "${SAFE_DDS_IDL_DIR}/internal.hpp" ]]; then + echo "Generated Safe DDS IDL header missing: ${SAFE_DDS_IDL_DIR}/internal.hpp" >&2 + echo "Regenerate IDL with: bash scripts/build_native.sh --idl" >&2 + echo "If the generator is not in PATH, set SAFE_DDS_IDL_GENERATOR first." >&2 + exit 1 +fi + +mkdir -p "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" \ + "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" \ + "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" \ + "${SAFETY_BUILD_FOLDER}" "${SAFETY_INSTALL_FOLDER}" \ + "${NON_SAFETY_BUILD_FOLDER}" "${NON_SAFETY_INSTALL_FOLDER}" + +configure_and_build "${WORKSPACE_ROOT}/common_server" "${COMMON_SERVER_BUILD_FOLDER}" "${COMMON_SERVER_INSTALL_FOLDER}" "common_server" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/server" "${SERVER_BUILD_FOLDER}" "${SERVER_INSTALL_FOLDER}" "safe_dds/server" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/edge" "${EDGE_BUILD_FOLDER}" "${EDGE_INSTALL_FOLDER}" "safe_dds/edge" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/safety" "${SAFETY_BUILD_FOLDER}" "${SAFETY_INSTALL_FOLDER}" "safe_dds/safety" +configure_and_build "${WORKSPACE_ROOT}/safe_dds/non_safety" "${NON_SAFETY_BUILD_FOLDER}" "${NON_SAFETY_INSTALL_FOLDER}" "safe_dds/non_safety" + +echo "All SafeEDGE native targets built successfully." diff --git a/scripts/build_qnx.sh b/scripts/build_qnx.sh old mode 100755 new mode 100644 From 1843e347518135a0ee61a92474b737b9dca91688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Tue, 9 Jun 2026 10:47:09 +0200 Subject: [PATCH 10/17] Hypervisor - Only 1 QNX guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 23 + README.md | 24 + .../local/options | 119 +++ .../local/snippets/data_files.opt_guest | 2 + .../local/snippets/ifs_files.custom | 2 + .../local/snippets/profile.custom | 2 + .../local/valgrind.files | 12 + .../mkqnximage-wrapper.sh | 9 + .../qvm-safe-edge-qnx800-x86_64/local/options | 119 +++ .../local/snippets/ifs_files.custom | 11 + .../local/snippets/profile.custom | 2 + .../local/valgrind.files | 12 + .../mkqnximage-wrapper.sh | 9 + scripts/launch_hypervisor_nodes.sh | 829 ++++++++++++++++++ 14 files changed, 1175 insertions(+) create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.opt_guest create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/ifs_files.custom create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/profile.custom create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/local/valgrind.files create mode 100644 qnx/targets/qemu-qnx800-x86_64-hypervisor/mkqnximage-wrapper.sh create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/options create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/valgrind.files create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/mkqnximage-wrapper.sh create mode 100644 scripts/launch_hypervisor_nodes.sh diff --git a/.gitignore b/.gitignore index 071b535..6e77ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,29 @@ qnx/targets/qemu-qnx800-*/local/snippets/ifs_start.custom qnx/targets/qemu-qnx800-*/local/snippets/post_start.custom qnx/targets/qemu-qnx800-*/local/snippets/system_files.custom +# Hypervisor host launcher-generated snippets — suppress even though qemu-qnx800-* +# wildcard patterns above would otherwise re-include some of them. +qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.custom + +# Hypervisor guest target — version only the minimal skeleton source. +!qnx/targets/qvm-safe-edge-qnx800-x86_64/ +!qnx/targets/qvm-safe-edge-qnx800-x86_64/mkqnximage-wrapper.sh +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/ +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/opt_defaults +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/opt_scripts +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/options +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/valgrind.files +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom +qnx/targets/qvm-safe-edge-qnx800-x86_64/output/ +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/misc_files/ +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/ssh-ident +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/data_files.custom +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_start.custom +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/post_start.custom +qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/system_files.custom + # fast_dds/ FastDDS components — source and generated IDL files are intentionally versioned. # The broad patterns above (*include*, *build*, *install*) would otherwise hide them. !fast_dds/ diff --git a/README.md b/README.md index 3ef5a44..fa791d8 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,30 @@ bash launch_tpi_2_2_test.sh bash launch_tpi_2_5_test.sh ``` +## Hypervisor Path + +Launch vehicle-side nodes inside a QNX Hypervisor guest (Linux host → QNX host VM → QNX guest VM): + +```bash +bash scripts/launch_hypervisor_nodes.sh +``` + +Stop the hypervisor host VM: + +```bash +bash scripts/launch_hypervisor_nodes.sh --stop +``` + +Prerequisites: same host-side tools as the plain QEMU path. The launcher builds +the guest image first and the host image second. `--no-rebuild` reuses existing +artifacts. + +On successful launch, SSH access hints for host and guest are printed. + +What is committed vs generated: +- Committed: `qnx/targets/qemu-qnx800-x86_64-hypervisor/` and `qnx/targets/qvm-safe-edge-qnx800-x86_64/` skeletons (`options`, wrappers, stable snippets) +- Generated at runtime: `data_files.custom`, `system_files.custom`, `ifs_start.custom`, `post_start.custom`, `output/` + Build and test (FastDDS / Docker): ```bash diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options new file mode 100644 index 0000000..f2c5789 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options @@ -0,0 +1,119 @@ +OPT_AARCH64_VERSION='8' +DEF_OPT_AARCH64_VERSION='8' +OPT_ABLELOCK='yes' +DEF_OPT_ABLELOCK='yes' +OPT_ARCH='x86_64' +DEF_OPT_ARCH='x86_64' +OPT_ASLR='yes' +DEF_OPT_ASLR='yes' +OPT_ASSUMED_IP='none' +DEF_OPT_ASSUMED_IP='none' +OPT_BOOT_SIZE='0' +DEF_OPT_BOOT_SIZE='0' +OPT_BUILD='all' +DEF_OPT_BUILD='all' +OPT_CERTICOM='no' +DEF_OPT_CERTICOM='no' +OPT_COPY='all' +DEF_OPT_COPY='all' +OPT_COPY_DEST='none' +DEF_OPT_COPY_DEST='none' +OPT_CPU='2' +DEF_OPT_CPU='2' +OPT_CRYPTODEV='no' +DEF_OPT_CRYPTODEV='no' +OPT_DATA_INODES='3000' +DEF_OPT_DATA_INODES='3000' +OPT_DATA_SIZE='60' +DEF_OPT_DATA_SIZE='60' +OPT_EXTRA_DIRS='none' +DEF_OPT_EXTRA_DIRS='none' +OPT_GRAPHICS='no' +DEF_OPT_GRAPHICS='no' +OPT_GUEST='none' +DEF_OPT_GUEST='none' +OPT_HOSTNAME='safe-edge-qvm-host' +DEF_OPT_HOSTNAME='noname' +OPT_IO_SOCK_DIAG='no' +DEF_OPT_IO_SOCK_DIAG='no' +OPT_IP='dhcp' +DEF_OPT_IP='dhcp' +OPT_MACADDR='52:54:00:38:3a:92' +DEF_OPT_MACADDR='generate' +OPT_NFS='no' +DEF_OPT_NFS='no' +OPT_PART_SIZES='full' +DEF_OPT_PART_SIZES='full' +OPT_PATHTRUST='no' +DEF_OPT_PATHTRUST='no' +OPT_PERL='no' +DEF_OPT_PERL='no' +OPT_PKCS11='no' +DEF_OPT_PKCS11='no' +OPT_POLICY='none' +DEF_OPT_POLICY='none' +OPT_PROC='no' +DEF_OPT_PROC='no' +OPT_PYTHON='no' +DEF_OPT_PYTHON='no' +OPT_QAUDIT='no' +DEF_OPT_QAUDIT='no' +OPT_QCFS='no' +DEF_OPT_QCFS='no' +OPT_QFIM='no' +DEF_OPT_QFIM='no' +OPT_QH_CONFIG='no' +DEF_OPT_QH_CONFIG='no' +OPT_QTD='no' +DEF_OPT_QTD='no' +OPT_QTSAFEFS='no' +DEF_OPT_QTSAFEFS='no' +OPT_QVM='yes' +DEF_OPT_QVM='no' +OPT_RAM='1536M' +DEF_OPT_RAM='1G' +OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +DEF_OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +OPT_ROOT='no' +DEF_OPT_ROOT='no' +OPT_SANITIZERS='no' +DEF_OPT_SANITIZERS='no' +OPT_SECPOL='no' +DEF_OPT_SECPOL='no' +OPT_SECURE_DATA='no' +DEF_OPT_SECURE_DATA='no' +OPT_SECURE_PROCFS='yes' +DEF_OPT_SECURE_PROCFS='yes' +OPT_SLM='no' +DEF_OPT_SLM='no' +OPT_SSHD_PREGEN='yes' +DEF_OPT_SSHD_PREGEN='yes' +OPT_SSH_IDENT='prompt' +DEF_OPT_SSH_IDENT='prompt' +OPT_SYS_INODES='1000' +DEF_OPT_SYS_INODES='1000' +OPT_SYS_SIZE='20' +DEF_OPT_SYS_SIZE='20' +OPT_TCG='no' +DEF_OPT_TCG='no' +OPT_TIME_SERVERS='pool.ntp.org' +DEF_OPT_TIME_SERVERS='pool.ntp.org' +OPT_TOMCRYPT='no' +DEF_OPT_TOMCRYPT='no' +OPT_TYPE='qemu' +DEF_OPT_TYPE='qemu' +OPT_TZ='UTC0' +DEF_OPT_TZ='UTC0' +OPT_UNION='yes' +DEF_OPT_UNION='yes' +OPT_UPDATE='all' +DEF_OPT_UPDATE='all' +OPT_USB='yes' +DEF_OPT_USB='yes' +OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +DEF_OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +OPT_VALGRIND='no' +DEF_OPT_VALGRIND='no' +OPT_ZONEINFO='no' +DEF_OPT_ZONEINFO='no' +VERSION=3 diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.opt_guest b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.opt_guest new file mode 100644 index 0000000..dae2ddf --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.opt_guest @@ -0,0 +1,2 @@ +# local/snippets/data_files.opt_guest +# Intentionally empty. Guest bundle is generated into data_files.custom by the launcher. diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/ifs_files.custom b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/ifs_files.custom new file mode 100644 index 0000000..f54033c --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/ifs_files.custom @@ -0,0 +1,2 @@ +# local/snippets/ifs_files.custom +# Placeholder for local list of files to add to ifs diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/profile.custom b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/profile.custom new file mode 100644 index 0000000..61b37bb --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/profile.custom @@ -0,0 +1,2 @@ +# local/snippets/profile.custom +# Contents are included at the end of /etc/profile diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/valgrind.files b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/valgrind.files new file mode 100644 index 0000000..65cf691 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/valgrind.files @@ -0,0 +1,12 @@ +# Add the names of the binaries you want to add a symbols to the target +# to debug through valgrind. +# +# e.g sbin/devb-ram +# +# Binaries that should be picked up from SDP/target_symbols should be +# suffixed with .sym else the stage or normal SDP binary will be picked up +# +# e.g. sbin/devb-ram.sym +# +# Note: libc and ldqnx are added automatically. +# diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/mkqnximage-wrapper.sh b/qnx/targets/qemu-qnx800-x86_64-hypervisor/mkqnximage-wrapper.sh new file mode 100644 index 0000000..087aa69 --- /dev/null +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/mkqnximage-wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" +exec mkqnximage "$@" diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/options b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/options new file mode 100644 index 0000000..e74433d --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/options @@ -0,0 +1,119 @@ +OPT_AARCH64_VERSION='8' +DEF_OPT_AARCH64_VERSION='8' +OPT_ABLELOCK='yes' +DEF_OPT_ABLELOCK='yes' +OPT_ARCH='x86_64' +DEF_OPT_ARCH='x86_64' +OPT_ASLR='yes' +DEF_OPT_ASLR='yes' +OPT_ASSUMED_IP='none' +DEF_OPT_ASSUMED_IP='none' +OPT_BOOT_SIZE='0' +DEF_OPT_BOOT_SIZE='0' +OPT_BUILD='all' +DEF_OPT_BUILD='all' +OPT_CERTICOM='no' +DEF_OPT_CERTICOM='no' +OPT_COPY='all' +DEF_OPT_COPY='all' +OPT_COPY_DEST='none' +DEF_OPT_COPY_DEST='none' +OPT_CPU='2' +DEF_OPT_CPU='2' +OPT_CRYPTODEV='no' +DEF_OPT_CRYPTODEV='no' +OPT_DATA_INODES='3000' +DEF_OPT_DATA_INODES='3000' +OPT_DATA_SIZE='60' +DEF_OPT_DATA_SIZE='60' +OPT_EXTRA_DIRS='none' +DEF_OPT_EXTRA_DIRS='none' +OPT_GRAPHICS='no' +DEF_OPT_GRAPHICS='no' +OPT_GUEST='none' +DEF_OPT_GUEST='none' +OPT_HOSTNAME='safe-edge-qvm-guest' +DEF_OPT_HOSTNAME='noname' +OPT_IO_SOCK_DIAG='no' +DEF_OPT_IO_SOCK_DIAG='no' +OPT_IP='192.168.10.2' +DEF_OPT_IP='dhcp' +OPT_MACADDR='52:54:00:38:3a:92' +DEF_OPT_MACADDR='generate' +OPT_NFS='no' +DEF_OPT_NFS='no' +OPT_PART_SIZES='full' +DEF_OPT_PART_SIZES='full' +OPT_PATHTRUST='no' +DEF_OPT_PATHTRUST='no' +OPT_PERL='no' +DEF_OPT_PERL='no' +OPT_PKCS11='no' +DEF_OPT_PKCS11='no' +OPT_POLICY='none' +DEF_OPT_POLICY='none' +OPT_PROC='no' +DEF_OPT_PROC='no' +OPT_PYTHON='no' +DEF_OPT_PYTHON='no' +OPT_QAUDIT='no' +DEF_OPT_QAUDIT='no' +OPT_QCFS='no' +DEF_OPT_QCFS='no' +OPT_QFIM='no' +DEF_OPT_QFIM='no' +OPT_QH_CONFIG='no' +DEF_OPT_QH_CONFIG='no' +OPT_QTD='no' +DEF_OPT_QTD='no' +OPT_QTSAFEFS='no' +DEF_OPT_QTSAFEFS='no' +OPT_QVM='no' +DEF_OPT_QVM='no' +OPT_RAM='1G' +DEF_OPT_RAM='1G' +OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +DEF_OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +OPT_ROOT='no' +DEF_OPT_ROOT='no' +OPT_SANITIZERS='no' +DEF_OPT_SANITIZERS='no' +OPT_SECPOL='no' +DEF_OPT_SECPOL='no' +OPT_SECURE_DATA='no' +DEF_OPT_SECURE_DATA='no' +OPT_SECURE_PROCFS='yes' +DEF_OPT_SECURE_PROCFS='yes' +OPT_SLM='no' +DEF_OPT_SLM='no' +OPT_SSHD_PREGEN='yes' +DEF_OPT_SSHD_PREGEN='yes' +OPT_SSH_IDENT='prompt' +DEF_OPT_SSH_IDENT='prompt' +OPT_SYS_INODES='1000' +DEF_OPT_SYS_INODES='1000' +OPT_SYS_SIZE='20' +DEF_OPT_SYS_SIZE='20' +OPT_TCG='no' +DEF_OPT_TCG='no' +OPT_TIME_SERVERS='pool.ntp.org' +DEF_OPT_TIME_SERVERS='pool.ntp.org' +OPT_TOMCRYPT='no' +DEF_OPT_TOMCRYPT='no' +OPT_TYPE='qvm' +DEF_OPT_TYPE='qemu' +OPT_TZ='UTC0' +DEF_OPT_TZ='UTC0' +OPT_UNION='yes' +DEF_OPT_UNION='yes' +OPT_UPDATE='all' +DEF_OPT_UPDATE='all' +OPT_USB='yes' +DEF_OPT_USB='yes' +OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +DEF_OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +OPT_VALGRIND='no' +DEF_OPT_VALGRIND='no' +OPT_ZONEINFO='no' +DEF_OPT_ZONEINFO='no' +VERSION=3 diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom new file mode 100644 index 0000000..3c5357e --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom @@ -0,0 +1,11 @@ +# local/snippets/ifs_files.custom +# Waits for /dev/hd0t177 (virtio-blk partition node) before mounting. +[-dupignore] +[perms=555] mount_fs.sh = { +#!/bin/sh +while ! test -e /dev/hd0t177; do waitfor /dev/hd0t177 10; done +while ! dd if=/dev/hd0t177 of=/dev/null bs=512 count=1 2>/dev/null; do sleep 2; done +exec /proc/boot/mount_fs_orig.sh "$@" +} +[+dupignore] +[perms=555] mount_fs_orig.sh=output/build/mount_fs.sh diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom new file mode 100644 index 0000000..61b37bb --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom @@ -0,0 +1,2 @@ +# local/snippets/profile.custom +# Contents are included at the end of /etc/profile diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/valgrind.files b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/valgrind.files new file mode 100644 index 0000000..65cf691 --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/valgrind.files @@ -0,0 +1,12 @@ +# Add the names of the binaries you want to add a symbols to the target +# to debug through valgrind. +# +# e.g sbin/devb-ram +# +# Binaries that should be picked up from SDP/target_symbols should be +# suffixed with .sym else the stage or normal SDP binary will be picked up +# +# e.g. sbin/devb-ram.sym +# +# Note: libc and ldqnx are added automatically. +# diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/mkqnximage-wrapper.sh b/qnx/targets/qvm-safe-edge-qnx800-x86_64/mkqnximage-wrapper.sh new file mode 100644 index 0000000..087aa69 --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/mkqnximage-wrapper.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" +exec mkqnximage "$@" diff --git a/scripts/launch_hypervisor_nodes.sh b/scripts/launch_hypervisor_nodes.sh new file mode 100644 index 0000000..9ced84d --- /dev/null +++ b/scripts/launch_hypervisor_nodes.sh @@ -0,0 +1,829 @@ +#!/usr/bin/env bash +# Launch vehicle-side SafeEDGE nodes inside a QNX Hypervisor guest. +# +# Topology: +# Linux host -> QNX hypervisor host VM -> QNX guest VM +# Guest IP: 192.168.10.2 (host guest-link: 192.168.10.1) +# +# Usage: +# bash scripts/launch_hypervisor_nodes.sh [options] +# +# Options: +# --no-rebuild skip image rebuild and reuse existing artifacts +# --no-run do not auto-start guest nodes; print manual launch commands +# --stop stop a running hypervisor host VM and exit +# -h, --help +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${HYP_GUEST_PRIVATE_KEY_FILE:=${HOME}/.ssh/id_ed25519}" +: "${HYP_GUEST_AUTHORIZED_KEY_FILE:=${HYP_GUEST_PRIVATE_KEY_FILE}.pub}" +: "${HYP_GUEST_STARTUP_PATCH_MODE:=forced}" +: "${HYP_GUEST_STARTUP_FREQ:=1000000000}" + +TARGET_DIR="${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-x86_64-hypervisor" +GUEST_TARGET_DIR="${WORKSPACE_ROOT}/qnx/targets/qvm-safe-edge-qnx800-x86_64" +GUEST_IFS="${GUEST_TARGET_DIR}/output/ifs.bin" +GUEST_DISK="${GUEST_TARGET_DIR}/output/disk-qvm" +: "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" +_GUEST_IP="192.168.10.2" +_HOST_GUEST_LOG="/data/var/log/hypervisor/start_guest.log" +_GUEST_CTL="/tmp/ssh-guest-ctl" # ControlPath socket for persistent guest SSH connection + +OPT_NO_REBUILD=0 +OPT_NO_RUN=0 +OPT_STOP=0 +OPT_SMOKE_TEST=0 +OPT_STABILITY_TEST=0 + +# All known vehicle node binaries (used for binary validation). +VEHICLE_NODE_BINS=( + safe_edge_safety_io_adapters + safe_edge_policy_engine + safe_edge_vehicle_mock + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment +) + +# Nodes to actually launch in the guest. Edit this list to add/remove nodes. +# Order matters: nodes are started in sequence with a 2s delay after vehicle_mock. +ACTIVE_NODES=( + safe_edge_vehicle_mock + safe_edge_safety_io_adapters + safe_edge_policy_engine + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment +) + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +_ssh_host_run() { + local ip="${1}" cmd="${2}" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_ssh_guest_open() { + local host_ip="${1}" + rm -f "${_GUEST_CTL}" + # No sshpass on outer ssh — guest uses pubkey; host password via ProxyCommand only. + # shellcheck disable=SC2086 + ssh ${_SSH_OPTS} \ + -o ProxyCommand="sshpass -p ${_SSH_PASS} ssh ${_SSH_OPTS} ${_SSH_USER}@${host_ip} -W %h:%p" \ + -i "${HYP_GUEST_PRIVATE_KEY_FILE}" \ + -o ControlMaster=yes -o ControlPath="${_GUEST_CTL}" -o ControlPersist=yes \ + -fN "${_SSH_USER}@${_GUEST_IP}" +} + +_ssh_guest_run() { + local host_ip="${1}" cmd="${2}" + # Reuse the persistent ControlMaster opened by _wait_for_guest_ssh (-fN). + # No new handshake — no entropy cost. + ssh -o ControlPath="${_GUEST_CTL}" "${_SSH_USER}@${_GUEST_IP}" "${cmd}" +} + +_ssh_guest_close() { + if [[ -S "${_GUEST_CTL}" ]]; then + ssh -o ControlPath="${_GUEST_CTL}" -O exit "${_SSH_USER}@${_GUEST_IP}" 2>/dev/null || true + rm -f "${_GUEST_CTL}" + fi +} + +_get_host_ip_address() { + local max_tries=20 ip i + for ((i = 0; i < max_tries; i++)); do + set +e; ip="$(mkqnximage --getip 2>/dev/null)"; set -e + if [[ -n "${ip}" ]]; then echo "${ip}"; return 0; fi + sleep 2 + done + echo "Timed out waiting for hypervisor host IP address." >&2 + return 1 +} + +_dump_guest_diagnostics() { + local host_ip="${1}" + echo "" + echo "=== host qvm processes ===" + _ssh_host_run "${host_ip}" "pidin ar | grep qvm" || echo "(no qvm process visible)" + echo "" + echo "=== host vp0 status ===" + _ssh_host_run "${host_ip}" "ifconfig vp0" || echo "(vp0 not available)" + echo "" + echo "=== host ping to guest (${_GUEST_IP}) ===" + _ssh_host_run "${host_ip}" "ping -c 1 ${_GUEST_IP}" || echo "(host could not ping guest)" + echo "" + echo "=== host start_guest log ===" + _ssh_host_run "${host_ip}" "cat ${_HOST_GUEST_LOG}" || echo "(guest log not available yet)" +} + +_guest_boot_log_has_fatal_error() { + local host_ip="${1}" + _ssh_host_run "${host_ip}" \ + "grep -q \ + -e 'init_qtime.c:.*ASSERT((r == 0)) failed!' \ + -e '\\*\\* ERROR: No system clock \\*\\*' \ + -e 'Failed to arm a resource manager:' \ + ${_HOST_GUEST_LOG}" >/dev/null 2>&1 +} + +_wait_for_guest_boot_log() { + local host_ip="${1}" + local max_tries=75 i + for ((i = 1; i <= max_tries; i++)); do + if _ssh_host_run "${host_ip}" "test -s ${_HOST_GUEST_LOG}" >/dev/null 2>&1; then + if _guest_boot_log_has_fatal_error "${host_ip}"; then + echo "Fatal error detected in guest boot log." >&2 + _dump_guest_diagnostics "${host_ip}" + return 1 + fi + if _ssh_host_run "${host_ip}" \ + "grep -q -e 'Starting SSH daemon' -e 'Starting sshd' ${_HOST_GUEST_LOG}" \ + >/dev/null 2>&1; then + echo "Guest boot log shows SSH startup." + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " guest boot log exists; waiting for SSH daemon startup..." + fi + else + if (( i == 1 || i % 5 == 0 )); then + echo " waiting for host-side guest log at ${_HOST_GUEST_LOG}..." + fi + fi + sleep 2 + done + echo "Timed out waiting for guest boot output on the host." >&2 + _dump_guest_diagnostics "${host_ip}" + return 1 +} + +_wait_for_guest_ping() { + local host_ip="${1}" + local max_tries=30 i + for ((i = 1; i <= max_tries; i++)); do + if _ssh_host_run "${host_ip}" "ping -c 1 ${_GUEST_IP}" >/dev/null 2>&1; then + echo "Host can ping guest ${_GUEST_IP}." + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " guest ${_GUEST_IP} not reachable from host yet..." + fi + sleep 1 + done + echo "Timed out waiting for host-side reachability to guest ${_GUEST_IP}." >&2 + _dump_guest_diagnostics "${host_ip}" + return 1 +} + +_wait_for_guest_ssh() { + local host_ip="${1}" + local max_tries=75 i + _wait_for_guest_boot_log "${host_ip}" + _wait_for_guest_ping "${host_ip}" + rm -f "${_GUEST_CTL}" + sleep 5 # Give post_start time to seed /dev/random before first SSH attempt + for ((i = 1; i <= max_tries; i++)); do + rm -f "${_GUEST_CTL}" + # Open a persistent background ControlMaster (-fN: fork after auth, no command). + # The guest uses pubkey — no sshpass needed on the outer ssh. + # sshpass is only used inside ProxyCommand (to authenticate against the host). + # Without sshpass wrapping the outer ssh, the daemon is not killed by PTY closure + # when sshpass exits (sshpass PTY teardown sends SIGHUP to its SSH child). + # shellcheck disable=SC2086 + if ssh ${_SSH_OPTS} \ + -o ProxyCommand="sshpass -p ${_SSH_PASS} ssh ${_SSH_OPTS} ${_SSH_USER}@${host_ip} -W %h:%p" \ + -i "${HYP_GUEST_PRIVATE_KEY_FILE}" \ + -o ControlMaster=yes -o ControlPath="${_GUEST_CTL}" -o ControlPersist=yes \ + -fN "${_SSH_USER}@${_GUEST_IP}" 2>/dev/null; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " guest booted and pingable; waiting for nested SSH..." + fi + sleep 2 + done + echo "Timed out waiting for guest SSH reachability via host ${host_ip}." >&2 + echo "=== SSH debug (last attempt) ===" >&2 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} -v \ + -o ProxyCommand="sshpass -p ${_SSH_PASS} ssh ${_SSH_OPTS} ${_SSH_USER}@${host_ip} -W %h:%p" \ + -i "${HYP_GUEST_PRIVATE_KEY_FILE}" \ + "${_SSH_USER}@${_GUEST_IP}" "echo guest-ready" 2>&1 | tail -30 >&2 || true + _dump_guest_diagnostics "${host_ip}" + return 1 +} + +_validate_qnx_binary() { + local bin="${1}" description + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2; return 1 + fi + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_host_bin_path() { + local name="${1}" + case "${name}" in + safe_edge_safety_io_adapters|safe_edge_policy_engine|safe_edge_vehicle_mock) + echo "${SAFETY_BIN_DIR}/${name}" ;; + safe_edge_cloud_gateway|safe_edge_ota_service|safe_edge_infotainment) + echo "${NON_SAFETY_BIN_DIR}/${name}" ;; + *) echo "Unknown vehicle node binary: ${name}" >&2; return 1 ;; + esac +} + +_validate_vehicle_binaries() { + local name bin + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2; exit 1 + fi + _validate_qnx_binary "${bin}" + done +} + +_patch_guest_startup_flags() { + local ifs_build="${GUEST_TARGET_DIR}/output/build/ifs.build" + + if [[ ! -f "${ifs_build}" ]]; then + echo "Guest ifs.build not found: ${ifs_build}" >&2 + exit 1 + fi + + case "${HYP_GUEST_STARTUP_PATCH_MODE}" in + forced) + # Replace mkqnximage-generated startup flags (-zz breaks clock init in QVM) + # with the flags that currently allow the guest to boot. + sed -i "s/startup-apic \\(.*\\) -zz[[:space:]]*/startup-apic \\1 -vv -f ,,${HYP_GUEST_STARTUP_FREQ} /" \ + "${ifs_build}" 2>/dev/null || true + ;; + unforced) + # Experimental mode for IDEA 4: + # keep verbose output but do not force a timer frequency. + sed -i 's/startup-apic \(.*\) -zz[[:space:]]*/startup-apic \1 -vv /' \ + "${ifs_build}" 2>/dev/null || true + ;; + *) + echo "Unsupported HYP_GUEST_STARTUP_PATCH_MODE='${HYP_GUEST_STARTUP_PATCH_MODE}'" >&2 + echo "Use 'forced' or 'unforced'." >&2 + exit 1 + ;; + esac +} + +# --------------------------------------------------------------------------- +# Snippet generators +# --------------------------------------------------------------------------- + +_refresh_guest_system_files_snippet() { + local snippet="${GUEST_TARGET_DIR}/local/snippets/system_files.custom" + local run_nodes="${GUEST_TARGET_DIR}/local/misc_files/run_nodes.sh" + local authorized_keys="${GUEST_TARGET_DIR}/local/misc_files/authorized_keys" + local name bin + mkdir -p "$(dirname "${snippet}")" "$(dirname "${run_nodes}")" + + # Generate run_nodes.sh dynamically from ACTIVE_NODES. + # Written as a real host file so the mkqnximage buildfile parser never sees + # the absolute guest paths in its own syntax. + { + cat << 'HEADER' +#!/bin/sh +LOG_DIR=/data/safe-edge-stage2/logs +DATA_DIR=/data/safe-edge-stage2 +mkdir -p "${LOG_DIR}" "${DATA_DIR}" +printf "soc=50.0\nemergency_stop=0\nadas_fault=0\navailable_charge_kw=50.0\navailable_discharge_kw=50.0\nv2g_ready=1\nspeed_mps=0.0\nbraking_available=1\nsteering_available=1\n" > "${DATA_DIR}/input.txt" +rm -f "${LOG_DIR}"/*.pid "${LOG_DIR}"/*.log +HEADER + local first=1 + for name in "${ACTIVE_NODES[@]}"; do + if [[ "${first}" -eq 1 ]]; then + first=0 + else + echo "sleep 1" + fi + echo "/system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + echo "echo \$! > \"\${LOG_DIR}/${name}.pid\"" + done + } > "${run_nodes}" + chmod +x "${run_nodes}" + cp "${HYP_GUEST_AUTHORIZED_KEY_FILE}" "${authorized_keys}" + chmod 600 "${authorized_keys}" + + { + echo "# local/snippets/system_files.custom" + echo "# Generated by scripts/launch_hypervisor_nodes.sh" + echo "[dperms=755 type=dir] root/.ssh" + for name in "${VEHICLE_NODE_BINS[@]}"; do + bin="$(_host_bin_path "${name}")" + echo "[perms=555] bin/${name}=${bin}" + done + echo "[perms=555] bin/run_nodes.sh=${run_nodes}" + echo "[perms=644] root/.ssh/authorized_keys=${authorized_keys}" + # Override sshd_config to keep sessions alive and allow enough auth attempts. + # [-dupignore] replaces the default inline sshd_config from the base system.build. + cat <<'SSHD' +[-dupignore] +[perms=444] etc/ssh/sshd_config = { +Protocol 2 +HostKey /data/var/ssh/ssh_host_rsa_key +HostKey /data/var/ssh/ssh_host_ed25519_key +Ciphers aes128-ctr,aes192-ctr,aes256-ctr +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256 +PubkeyAuthentication yes +AuthorizedKeysFile /data/ssh/authorized_keys +StrictModes no +KbdInteractiveAuthentication no +ChallengeResponseAuthentication no +UsePAM no +PasswordAuthentication no +PermitUserEnvironment yes +PermitRootLogin yes +PidFile none +Subsystem sftp /system/bin/sftp-server +SshdSessionPath /system/bin/sshd-session +ClientAliveInterval 30 +ClientAliveCountMax 10 +MaxAuthTries 20 +} +[+dupignore] +SSHD + } > "${snippet}" +} + +_refresh_guest_post_start_snippet() { + local snippet="${GUEST_TARGET_DIR}/local/snippets/post_start.custom" + local pubkey + pubkey="$(cat "${HYP_GUEST_AUTHORIZED_KEY_FILE}")" + mkdir -p "$(dirname "${snippet}")" + # Write authorized_keys to /data (writable virtio-blk) at runtime. + # Avoids IFS path-prefix issues with system_files.custom (/system/ prefix). + # sshd reads authorized_keys per-connection so writing it here (before any + # SSH attempt) is sufficient. + # Seed the guest PRNG by restarting random with a seed file on /data. + # The guest's default random startup fails (no virtio-entropy/rdrand), leaving + # sshd with no entropy. Seeding here (post_start = after /data is mounted) fixes it. + # Seed is base64-encoded (no null bytes) and decoded with base64 on the guest. + # This is a fixed test seed — not suitable for production. + local seed_b64="IedEPk92NsxQW/slRiEnDIw5/AFr6kd1MGponQbBjP1JGf1i8c62f9pJeM11xjaG9co3QEppfVIlHc45a7Nlivu+HfCOs6ywetbZ/rv4jgv7FhfFwO7FfsNy9foYm6tNR0shcF8iwO5ElEpk5W59WJ8Per3zrgs1fWqqOOgHEsA=" + # Write seed to /dev/random directly — random manager creates the device even when + # unseeded. Writing adds entropy without needing to kill/restart the daemon. + # Also persist seed to /data so random -s picks it up on subsequent boots. + local seed_line="waitfor /dev/random 10" + seed_line+=" && echo '${seed_b64}' | base64 -d > /dev/random 2>/dev/null" + seed_line+="; mkdir -p /data/var/random" + seed_line+=" && echo '${seed_b64}' | base64 -d > /data/var/random/rnd-seed 2>/dev/null" + seed_line+="; true" + # Nodes are NOT started from post_startup.sh — they are launched via SSH after + # the ControlMaster is established. This prevents DDS nodes from consuming + # /dev/random entropy before our SSH handshake completes. + cat > "${snippet}" < /data/ssh/authorized_keys +chmod 600 /data/ssh/authorized_keys +EOF +} + +_print_manual_guest_run_commands() { + local host_ip="${1}" + cat < "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_hypervisor_nodes.sh +EOF +} + +_refresh_host_system_files_snippet() { + local snippet="${TARGET_DIR}/local/snippets/system_files.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/system_files.custom +# Generated by scripts/launch_hypervisor_nodes.sh +EOF +} + +_refresh_host_post_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/post_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_hypervisor_nodes.sh +route add -net 224.0.0.0/4 vtnet0 +mkdir -p /data/var/log/hypervisor +/data/hypervisor/start_guest >/data/var/log/hypervisor/start_guest.log 2>&1 & +EOF +} + +_refresh_host_ifs_start_snippet() { + local snippet="${TARGET_DIR}/local/snippets/ifs_start.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_hypervisor_nodes.sh +EOF +} + +_refresh_host_guest_bundle_snippet() { + local snippet="${TARGET_DIR}/local/snippets/data_files.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" <- + intr myioapic:4 + name ser8250_0 + +vdev timer8254 + intr myioapic:0 + name timer8254_0 + +vdev mc146818 + name mc146818_0 + +vdev shmem + name shmem_0 + +vdev pckeyboard + name pckeyboard_0 + +vdev virtio-net + name network + peerfeats 0x0382 + peer /dev/vdevpeers/vp0 + +vdev virtio-blk + hostdev /dev/qvmdisk0 + name virtio-blk_qvmdisk0 + +vdev 8259 + loc 0x20 +vdev 8259 + loc 0xa0 + +vdev hpet + intr myioapic:2 + name hpet_0 + +} +EOF +} + +_reset_generated_target_output() { + rm -rf "${1}/output" +} + +# --------------------------------------------------------------------------- +# Preflight checks +# --------------------------------------------------------------------------- + +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2; exit 1 +fi +for dir in "${TARGET_DIR}" "${GUEST_TARGET_DIR}"; do + if [[ ! -d "${dir}" ]]; then + echo "QNX target directory not found: ${dir}" >&2; exit 1 + fi +done +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2; exit 1 +fi +if [[ ! -f "${HYP_GUEST_PRIVATE_KEY_FILE}" ]]; then + echo "Guest SSH private key not found: ${HYP_GUEST_PRIVATE_KEY_FILE}" >&2; exit 1 +fi +if [[ ! -f "${HYP_GUEST_AUTHORIZED_KEY_FILE}" ]]; then + echo "Guest SSH public key not found: ${HYP_GUEST_AUTHORIZED_KEY_FILE}" >&2; exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2; exit 1 +fi +if ! command -v brctl >/dev/null 2>&1; then + echo "brctl not found. Install with: sudo apt install bridge-utils" >&2; exit 1 +fi + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + _ssh_guest_close + echo "Stopping hypervisor host VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +# Inject tsc_freq into QEMU so the QNX hypervisor host sees a fixed TSC. +# qvm inherits this base for its virtual LAPIC → guest clock ratio ≈ 1x. +# Without this, QEMU uses the host TSC (~2.6 GHz turbo) while the QNX hint +# is 1 GHz → guest clock runs ~2.6x faster than real time. +_QEMU_WRAPPER="/tmp/qemu-tsc-wrapper-$$" +cat > "${_QEMU_WRAPPER}" <<'WRAPPER_EOF' +#!/bin/bash +args=() +for arg in "$@"; do + if [[ "${arg}" == "host" ]]; then + args+=("host,tsc_freq=_TSC_FREQ_,invtsc=on") + else + args+=("${arg}") + fi +done +exec /usr/bin/qemu-system-x86_64 "${args[@]}" +WRAPPER_EOF +sed -i "s/_TSC_FREQ_/${HYP_GUEST_STARTUP_FREQ}/" "${_QEMU_WRAPPER}" +chmod +x "${_QEMU_WRAPPER}" +ln -sf "${_QEMU_WRAPPER}" /tmp/qemu-system-x86_64 +export PATH="/tmp:${PATH}" + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_vehicle_binaries + + # Generate guest snippets + _refresh_guest_ifs_start_snippet + _refresh_guest_post_start_snippet + _refresh_guest_system_files_snippet + _reset_generated_target_output "${GUEST_TARGET_DIR}" + + echo "Building QNX guest image..." + echo " guest target: ${GUEST_TARGET_DIR}" + echo " startup mode: ${HYP_GUEST_STARTUP_PATCH_MODE}" + # Generate build files first, then patch startup flags before building. + (cd "${GUEST_TARGET_DIR}" && mkqnximage --noprompt --clean >/dev/null 2>&1) || true + _patch_guest_startup_flags + (cd "${GUEST_TARGET_DIR}" && mkqnximage --noprompt --build >/dev/null 2>&1) + + for artifact in "${GUEST_IFS}" "${GUEST_DISK}"; do + if [[ ! -f "${artifact}" ]]; then + echo "Expected guest artifact missing after build: ${artifact}" >&2; exit 1 + fi + done + + # Generate host snippets (after guest build — data_files.custom references output artifacts) + _refresh_host_ifs_start_snippet + _refresh_host_post_start_snippet + _refresh_host_system_files_snippet + _refresh_host_guest_bundle_snippet + _reset_generated_target_output "${TARGET_DIR}" + + echo "Building QNX hypervisor host image and starting QEMU..." + echo " host target : ${TARGET_DIR}" + mkqnximage --noprompt --guest=none --run=-h --clean >/dev/null 2>&1 +else + for artifact in "${GUEST_IFS}" "${GUEST_DISK}"; do + if [[ ! -f "${artifact}" ]]; then + echo "Guest artifact not found: ${artifact}" >&2 + echo "Rebuild first with: bash scripts/launch_hypervisor_nodes.sh" >&2; exit 1 + fi + done + echo "Starting QNX hypervisor host QEMU (skipping rebuild)..." + mkqnximage --noprompt --guest=none --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for hypervisor host IP address..." +HOST_IP="$(_get_host_ip_address)" +echo "Hypervisor host is up: ${HOST_IP}" + +echo "Waiting for guest SSH reachability at ${_GUEST_IP} via host..." +_wait_for_guest_ssh "${HOST_IP}" +echo "Guest is reachable over SSH." + +echo "" +echo "Host SSH hint : sshpass -p root ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@${HOST_IP}" +echo "Guest SSH hint: ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlPath=${_GUEST_CTL} root@${_GUEST_IP}" +echo "" +if [[ "${OPT_NO_RUN}" -eq 1 ]]; then + _print_manual_guest_run_commands "${HOST_IP}" + echo "When finished: bash scripts/launch_hypervisor_nodes.sh --stop" +elif [[ "${OPT_SMOKE_TEST}" -eq 1 || "${OPT_STABILITY_TEST}" -eq 1 ]]; then + # Shared guest-side monitoring function used by both modes. + # smoke-test : runs for 60s then stops the VM. + # stability-test: runs indefinitely (infinite loop) until the user kills the script. + echo "LAPIC hint: ${HYP_GUEST_STARTUP_FREQ} Hz" + if [[ "${OPT_SMOKE_TEST}" -eq 1 ]]; then + _MONITOR_CMD=' + /system/bin/run_nodes.sh + LOG_DIR=/data/safe-edge-stage2/logs + NODES="safe_edge_vehicle_mock safe_edge_safety_io_adapters safe_edge_policy_engine safe_edge_cloud_gateway safe_edge_ota_service safe_edge_infotainment" + _print_stats() { + _elapsed="$1" _total="$2" _show_df="$3" + _m=$((_elapsed/60)); _s=$((_elapsed%60)) + _tm=$((_total/60)); _ts=$((_total%60)) + printf "\n (%dm%02ds / %dm%02ds)\n" "${_m}" "${_s}" "${_tm}" "${_ts}" + printf " %-32s %5s %8s %8s %8s\n" "node" "alive" "start" "now" "delta" + printf " %-32s %5s %8s %8s %8s\n" "----" "-----" "-----" "---" "-----" + for _node in ${NODES}; do + _log="${LOG_DIR}/${_node}.log" + _alive=N; pidin 2>/dev/null | grep -q "${_node}" && _alive=Y + _now=0; [ -f "${_log}" ] && _now=$(wc -l < "${_log}" 2>/dev/null || echo 0) + _start=$(eval echo \${_prev_${_node}:-0}) + _delta=$((_now - _start)) + eval "_prev_${_node}=${_now}" + printf " %-32s %5s %8d %8d %8d\n" "${_node}" "${_alive}" "${_start}" "${_now}" "${_delta}" + done + [ "${_show_df}" = "1" ] && { echo ""; df 2>/dev/null; } + } + for _node in ${NODES}; do eval "_prev_${_node}=0"; done + echo "=== Smoke test (1 min) ===" + _e=0; while [ "${_e}" -lt 60 ]; do + sleep 10; _e=$((_e+10)) + _show_df=0; [ "$((_e%60))" -eq 0 ] && _show_df=1 + _print_stats "${_e}" 60 "${_show_df}" + done + echo "" + echo "=== pidin ===" + pidin 2>/dev/null | grep safe_edge || echo "(no safe_edge processes)"' + _ssh_guest_run "${HOST_IP}" "${_MONITOR_CMD}" + echo "" + echo "Smoke test complete. Stopping VM..." + _ssh_guest_close + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + else + _MONITOR_CMD=' + /system/bin/run_nodes.sh + LOG_DIR=/data/safe-edge-stage2/logs + NODES="safe_edge_vehicle_mock safe_edge_safety_io_adapters safe_edge_policy_engine safe_edge_cloud_gateway safe_edge_ota_service safe_edge_infotainment" + _print_stats() { + _elapsed="$1" _show_df="$2" + _h=$((_elapsed/3600)); _m=$(((_elapsed%3600)/60)); _s=$((_elapsed%60)) + printf "\n (%dh%02dm%02ds elapsed)\n" "${_h}" "${_m}" "${_s}" + printf " %-32s %5s %8s %8s %8s\n" "node" "alive" "start" "now" "delta" + printf " %-32s %5s %8s %8s %8s\n" "----" "-----" "-----" "---" "-----" + for _node in ${NODES}; do + _log="${LOG_DIR}/${_node}.log" + _alive=N; pidin 2>/dev/null | grep -q "${_node}" && _alive=Y + _now=0; [ -f "${_log}" ] && _now=$(wc -l < "${_log}" 2>/dev/null || echo 0) + _start=$(eval echo \${_prev_${_node}:-0}) + _delta=$((_now - _start)) + eval "_prev_${_node}=${_now}" + printf " %-32s %5s %8d %8d %8d\n" "${_node}" "${_alive}" "${_start}" "${_now}" "${_delta}" + done + [ "${_show_df}" = "1" ] && { echo ""; df 2>/dev/null; } + } + for _node in ${NODES}; do eval "_prev_${_node}=0"; done + echo "=== Stability test (Ctrl+C to stop) ===" + _e=0 + while true; do + sleep 10; _e=$((_e+10)) + _show_df=0; [ "$((_e%60))" -eq 0 ] && _show_df=1 + _print_stats "${_e}" "${_show_df}" + done' + echo "Stability test running. Press Ctrl+C or run '--stop' from another terminal to stop." + trap '_ssh_guest_close; echo "Stopped."; exit 0' INT TERM + _ssh_guest_run "${HOST_IP}" "${_MONITOR_CMD}" + fi +else + # Default: launch nodes and block until stopped. + # The guest has no entropy source — each new SSH handshake drains the pool. + # To keep the ControlMaster alive without a new handshake, we run a long + # sleep on the guest through the existing socket. While that sleep is active, + # the ControlMaster stays open and the user can connect reusing it. + echo "Launching nodes via SSH..." + _ssh_guest_run "${HOST_IP}" '/system/bin/run_nodes.sh' + + echo "" + echo "VM is running. Connect from another terminal with:" + echo " Host : sshpass -p root ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@${HOST_IP}" + echo " Guest: ssh -o StrictHostKeyChecking=no -o ControlPath=${_GUEST_CTL} root@${_GUEST_IP}" + echo "" + echo "Logs: /data/safe-edge-stage2/logs/" + echo "Press Ctrl+C or run 'bash scripts/launch_hypervisor_nodes.sh --stop' to stop." + + # Block until the ControlMaster (-fN background process) dies or user interrupts. + trap '_ssh_guest_close; echo "Stopped."; exit 0' INT TERM + while ssh -o ControlPath="${_GUEST_CTL}" "${_SSH_USER}@${_GUEST_IP}" "true" >/dev/null 2>&1; do + sleep 30 + done + echo "Guest SSH connection lost. VM may have stopped." >&2 +fi From fae151771a98c18b2ff6d55b785597be5d5fc606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Tue, 9 Jun 2026 16:34:12 +0200 Subject: [PATCH 11/17] Hypervisor - 2 QNX guests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .gitignore | 2 + .../local/options | 2 +- .../local/variants/non-safety/options | 119 +++ .../local/variants/safety/options | 119 +++ scripts/launch_hypervisor_split.sh | 705 ++++++++++++++++++ 5 files changed, 946 insertions(+), 1 deletion(-) create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/non-safety/options create mode 100644 qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/safety/options create mode 100644 scripts/launch_hypervisor_split.sh diff --git a/.gitignore b/.gitignore index 6e77ba1..96b2002 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,8 @@ qnx/targets/qemu-qnx800-x86_64-hypervisor/local/snippets/data_files.custom !qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ !qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/ifs_files.custom !qnx/targets/qvm-safe-edge-qnx800-x86_64/local/snippets/profile.custom +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/ +!qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/** qnx/targets/qvm-safe-edge-qnx800-x86_64/output/ qnx/targets/qvm-safe-edge-qnx800-x86_64/local/misc_files/ qnx/targets/qvm-safe-edge-qnx800-x86_64/local/ssh-ident diff --git a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options index f2c5789..43d52b2 100644 --- a/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options +++ b/qnx/targets/qemu-qnx800-x86_64-hypervisor/local/options @@ -24,7 +24,7 @@ OPT_CRYPTODEV='no' DEF_OPT_CRYPTODEV='no' OPT_DATA_INODES='3000' DEF_OPT_DATA_INODES='3000' -OPT_DATA_SIZE='60' +OPT_DATA_SIZE='512' DEF_OPT_DATA_SIZE='60' OPT_EXTRA_DIRS='none' DEF_OPT_EXTRA_DIRS='none' diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/non-safety/options b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/non-safety/options new file mode 100644 index 0000000..8dca8ca --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/non-safety/options @@ -0,0 +1,119 @@ +OPT_AARCH64_VERSION='8' +DEF_OPT_AARCH64_VERSION='8' +OPT_ABLELOCK='yes' +DEF_OPT_ABLELOCK='yes' +OPT_ARCH='x86_64' +DEF_OPT_ARCH='x86_64' +OPT_ASLR='yes' +DEF_OPT_ASLR='yes' +OPT_ASSUMED_IP='none' +DEF_OPT_ASSUMED_IP='none' +OPT_BOOT_SIZE='0' +DEF_OPT_BOOT_SIZE='0' +OPT_BUILD='all' +DEF_OPT_BUILD='all' +OPT_CERTICOM='no' +DEF_OPT_CERTICOM='no' +OPT_COPY='all' +DEF_OPT_COPY='all' +OPT_COPY_DEST='none' +DEF_OPT_COPY_DEST='none' +OPT_CPU='2' +DEF_OPT_CPU='2' +OPT_CRYPTODEV='no' +DEF_OPT_CRYPTODEV='no' +OPT_DATA_INODES='3000' +DEF_OPT_DATA_INODES='3000' +OPT_DATA_SIZE='60' +DEF_OPT_DATA_SIZE='60' +OPT_EXTRA_DIRS='none' +DEF_OPT_EXTRA_DIRS='none' +OPT_GRAPHICS='no' +DEF_OPT_GRAPHICS='no' +OPT_GUEST='none' +DEF_OPT_GUEST='none' +OPT_HOSTNAME='safe-edge-qvm-non-safety-guest' +DEF_OPT_HOSTNAME='noname' +OPT_IO_SOCK_DIAG='no' +DEF_OPT_IO_SOCK_DIAG='no' +OPT_IP='192.168.20.2' +DEF_OPT_IP='dhcp' +OPT_MACADDR='52:54:00:38:3a:93' +DEF_OPT_MACADDR='generate' +OPT_NFS='no' +DEF_OPT_NFS='no' +OPT_PART_SIZES='full' +DEF_OPT_PART_SIZES='full' +OPT_PATHTRUST='no' +DEF_OPT_PATHTRUST='no' +OPT_PERL='no' +DEF_OPT_PERL='no' +OPT_PKCS11='no' +DEF_OPT_PKCS11='no' +OPT_POLICY='none' +DEF_OPT_POLICY='none' +OPT_PROC='no' +DEF_OPT_PROC='no' +OPT_PYTHON='no' +DEF_OPT_PYTHON='no' +OPT_QAUDIT='no' +DEF_OPT_QAUDIT='no' +OPT_QCFS='no' +DEF_OPT_QCFS='no' +OPT_QFIM='no' +DEF_OPT_QFIM='no' +OPT_QH_CONFIG='no' +DEF_OPT_QH_CONFIG='no' +OPT_QTD='no' +DEF_OPT_QTD='no' +OPT_QTSAFEFS='no' +DEF_OPT_QTSAFEFS='no' +OPT_QVM='no' +DEF_OPT_QVM='no' +OPT_RAM='1G' +DEF_OPT_RAM='1G' +OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +DEF_OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +OPT_ROOT='no' +DEF_OPT_ROOT='no' +OPT_SANITIZERS='no' +DEF_OPT_SANITIZERS='no' +OPT_SECPOL='no' +DEF_OPT_SECPOL='no' +OPT_SECURE_DATA='no' +DEF_OPT_SECURE_DATA='no' +OPT_SECURE_PROCFS='yes' +DEF_OPT_SECURE_PROCFS='yes' +OPT_SLM='no' +DEF_OPT_SLM='no' +OPT_SSHD_PREGEN='yes' +DEF_OPT_SSHD_PREGEN='yes' +OPT_SSH_IDENT='prompt' +DEF_OPT_SSH_IDENT='prompt' +OPT_SYS_INODES='1000' +DEF_OPT_SYS_INODES='1000' +OPT_SYS_SIZE='20' +DEF_OPT_SYS_SIZE='20' +OPT_TCG='no' +DEF_OPT_TCG='no' +OPT_TIME_SERVERS='pool.ntp.org' +DEF_OPT_TIME_SERVERS='pool.ntp.org' +OPT_TOMCRYPT='no' +DEF_OPT_TOMCRYPT='no' +OPT_TYPE='qvm' +DEF_OPT_TYPE='qemu' +OPT_TZ='UTC0' +DEF_OPT_TZ='UTC0' +OPT_UNION='yes' +DEF_OPT_UNION='yes' +OPT_UPDATE='all' +DEF_OPT_UPDATE='all' +OPT_USB='yes' +DEF_OPT_USB='yes' +OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +DEF_OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +OPT_VALGRIND='no' +DEF_OPT_VALGRIND='no' +OPT_ZONEINFO='no' +DEF_OPT_ZONEINFO='no' +VERSION=3 diff --git a/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/safety/options b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/safety/options new file mode 100644 index 0000000..e74433d --- /dev/null +++ b/qnx/targets/qvm-safe-edge-qnx800-x86_64/local/variants/safety/options @@ -0,0 +1,119 @@ +OPT_AARCH64_VERSION='8' +DEF_OPT_AARCH64_VERSION='8' +OPT_ABLELOCK='yes' +DEF_OPT_ABLELOCK='yes' +OPT_ARCH='x86_64' +DEF_OPT_ARCH='x86_64' +OPT_ASLR='yes' +DEF_OPT_ASLR='yes' +OPT_ASSUMED_IP='none' +DEF_OPT_ASSUMED_IP='none' +OPT_BOOT_SIZE='0' +DEF_OPT_BOOT_SIZE='0' +OPT_BUILD='all' +DEF_OPT_BUILD='all' +OPT_CERTICOM='no' +DEF_OPT_CERTICOM='no' +OPT_COPY='all' +DEF_OPT_COPY='all' +OPT_COPY_DEST='none' +DEF_OPT_COPY_DEST='none' +OPT_CPU='2' +DEF_OPT_CPU='2' +OPT_CRYPTODEV='no' +DEF_OPT_CRYPTODEV='no' +OPT_DATA_INODES='3000' +DEF_OPT_DATA_INODES='3000' +OPT_DATA_SIZE='60' +DEF_OPT_DATA_SIZE='60' +OPT_EXTRA_DIRS='none' +DEF_OPT_EXTRA_DIRS='none' +OPT_GRAPHICS='no' +DEF_OPT_GRAPHICS='no' +OPT_GUEST='none' +DEF_OPT_GUEST='none' +OPT_HOSTNAME='safe-edge-qvm-guest' +DEF_OPT_HOSTNAME='noname' +OPT_IO_SOCK_DIAG='no' +DEF_OPT_IO_SOCK_DIAG='no' +OPT_IP='192.168.10.2' +DEF_OPT_IP='dhcp' +OPT_MACADDR='52:54:00:38:3a:92' +DEF_OPT_MACADDR='generate' +OPT_NFS='no' +DEF_OPT_NFS='no' +OPT_PART_SIZES='full' +DEF_OPT_PART_SIZES='full' +OPT_PATHTRUST='no' +DEF_OPT_PATHTRUST='no' +OPT_PERL='no' +DEF_OPT_PERL='no' +OPT_PKCS11='no' +DEF_OPT_PKCS11='no' +OPT_POLICY='none' +DEF_OPT_POLICY='none' +OPT_PROC='no' +DEF_OPT_PROC='no' +OPT_PYTHON='no' +DEF_OPT_PYTHON='no' +OPT_QAUDIT='no' +DEF_OPT_QAUDIT='no' +OPT_QCFS='no' +DEF_OPT_QCFS='no' +OPT_QFIM='no' +DEF_OPT_QFIM='no' +OPT_QH_CONFIG='no' +DEF_OPT_QH_CONFIG='no' +OPT_QTD='no' +DEF_OPT_QTD='no' +OPT_QTSAFEFS='no' +DEF_OPT_QTSAFEFS='no' +OPT_QVM='no' +DEF_OPT_QVM='no' +OPT_RAM='1G' +DEF_OPT_RAM='1G' +OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +DEF_OPT_REPOS='$QNX_STAGE_nto:$QNX_TARGET' +OPT_ROOT='no' +DEF_OPT_ROOT='no' +OPT_SANITIZERS='no' +DEF_OPT_SANITIZERS='no' +OPT_SECPOL='no' +DEF_OPT_SECPOL='no' +OPT_SECURE_DATA='no' +DEF_OPT_SECURE_DATA='no' +OPT_SECURE_PROCFS='yes' +DEF_OPT_SECURE_PROCFS='yes' +OPT_SLM='no' +DEF_OPT_SLM='no' +OPT_SSHD_PREGEN='yes' +DEF_OPT_SSHD_PREGEN='yes' +OPT_SSH_IDENT='prompt' +DEF_OPT_SSH_IDENT='prompt' +OPT_SYS_INODES='1000' +DEF_OPT_SYS_INODES='1000' +OPT_SYS_SIZE='20' +DEF_OPT_SYS_SIZE='20' +OPT_TCG='no' +DEF_OPT_TCG='no' +OPT_TIME_SERVERS='pool.ntp.org' +DEF_OPT_TIME_SERVERS='pool.ntp.org' +OPT_TOMCRYPT='no' +DEF_OPT_TOMCRYPT='no' +OPT_TYPE='qvm' +DEF_OPT_TYPE='qemu' +OPT_TZ='UTC0' +DEF_OPT_TZ='UTC0' +OPT_UNION='yes' +DEF_OPT_UNION='yes' +OPT_UPDATE='all' +DEF_OPT_UPDATE='all' +OPT_USB='yes' +DEF_OPT_USB='yes' +OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +DEF_OPT_USERS='root:qnxuser:user1:user2:user3:user4:user5:user6' +OPT_VALGRIND='no' +DEF_OPT_VALGRIND='no' +OPT_ZONEINFO='no' +DEF_OPT_ZONEINFO='no' +VERSION=3 diff --git a/scripts/launch_hypervisor_split.sh b/scripts/launch_hypervisor_split.sh new file mode 100644 index 0000000..318d14b --- /dev/null +++ b/scripts/launch_hypervisor_split.sh @@ -0,0 +1,705 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +QNX_USER="${USER:-$(id -un)}" +: "${QNX_SDP_ROOT:=/home/${QNX_USER}/qnx800}" +: "${QNX_ARCH:=x86_64}" +: "${CMAKE_BUILD_TYPE:=Release}" +: "${HYP_GUEST_PRIVATE_KEY_FILE:=${HOME}/.ssh/id_ed25519}" +: "${HYP_GUEST_AUTHORIZED_KEY_FILE:=${HYP_GUEST_PRIVATE_KEY_FILE}.pub}" +: "${HYP_GUEST_STARTUP_PATCH_MODE:=forced}" +: "${HYP_GUEST_STARTUP_FREQ:=1000000000}" + +TARGET_DIR="${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-x86_64-hypervisor" +BASE_TARGET_DIR="${WORKSPACE_ROOT}/qnx/targets/qvm-safe-edge-qnx800-x86_64" +SAFETY_VARIANT_DIR="${BASE_TARGET_DIR}/local/variants/safety" +NON_SAFETY_VARIANT_DIR="${BASE_TARGET_DIR}/local/variants/non-safety" + +STAGING_ROOT="/tmp/safe-edge-hyp-split" +SAFETY_STAGING="${STAGING_ROOT}/guest-safety" +NON_SAFETY_STAGING="${STAGING_ROOT}/guest-non-safety" + +SAFETY_GUEST_IFS="${SAFETY_STAGING}/output/ifs.bin" +SAFETY_GUEST_DISK="${SAFETY_STAGING}/output/disk-qvm" +NON_SAFETY_GUEST_IFS="${NON_SAFETY_STAGING}/output/ifs.bin" +NON_SAFETY_GUEST_DISK="${NON_SAFETY_STAGING}/output/disk-qvm" + +: "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" + +_SSH_PASS="root" +_SSH_USER="root" +_SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=30 -o LogLevel=ERROR" + +SAFETY_GUEST_IP="192.168.10.2" +SAFETY_HOST_LINK_IP="192.168.10.1/24" +SAFETY_HOST_LOG="/data/var/log/hypervisor/start_guest_safety.log" +SAFETY_GUEST_CTL="/tmp/ssh-guest-safety-ctl" + +NON_SAFETY_GUEST_IP="192.168.20.2" +NON_SAFETY_HOST_LINK_IP="192.168.20.1/24" +NON_SAFETY_HOST_LOG="/data/var/log/hypervisor/start_guest_non_safety.log" +NON_SAFETY_GUEST_CTL="/tmp/ssh-guest-non-safety-ctl" + +OPT_NO_REBUILD=0 +OPT_NO_RUN=0 +OPT_STOP=0 + +SAFETY_NODES=( + safe_edge_vehicle_mock + safe_edge_safety_io_adapters + safe_edge_policy_engine +) + +NON_SAFETY_NODES=( + safe_edge_cloud_gateway + safe_edge_ota_service + safe_edge_infotainment +) + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +_ssh_host_run() { + local ip="${1}" cmd="${2}" + # shellcheck disable=SC2086 + sshpass -p "${_SSH_PASS}" ssh ${_SSH_OPTS} "${_SSH_USER}@${ip}" "${cmd}" +} + +_ssh_guest_run() { + local ctl="${1}" guest_ip="${2}" cmd="${3}" + ssh -o ControlPath="${ctl}" "${_SSH_USER}@${guest_ip}" "${cmd}" +} + +_ssh_guest_close() { + local ctl="${1}" guest_ip="${2}" + if [[ -S "${ctl}" ]]; then + ssh -o ControlPath="${ctl}" -O exit "${_SSH_USER}@${guest_ip}" 2>/dev/null || true + rm -f "${ctl}" + fi +} + +_get_host_ip_address() { + local max_tries=20 ip i + for ((i = 0; i < max_tries; i++)); do + set +e; ip="$(mkqnximage --getip 2>/dev/null)"; set -e + if [[ -n "${ip}" ]]; then + echo "${ip}" + return 0 + fi + sleep 2 + done + echo "Timed out waiting for hypervisor host IP address." >&2 + return 1 +} + +_dump_guest_log() { + local host_ip="${1}" log_path="${2}" + echo "=== ${log_path} ===" + _ssh_host_run "${host_ip}" "cat ${log_path}" || echo "(guest log not available)" +} + +_wait_for_guest_boot_log() { + local host_ip="${1}" log_path="${2}" + local max_tries=75 i + for ((i = 1; i <= max_tries; i++)); do + if _ssh_host_run "${host_ip}" "test -s ${log_path}" >/dev/null 2>&1; then + if _ssh_host_run "${host_ip}" \ + "grep -q -e 'Starting SSH daemon' -e 'Starting sshd' ${log_path}" \ + >/dev/null 2>&1; then + return 0 + fi + fi + if (( i == 1 || i % 5 == 0 )); then + echo " waiting for guest boot log ${log_path} to reach sshd..." + fi + sleep 2 + done + echo "Timed out waiting for guest boot output in ${log_path}." >&2 + _dump_guest_log "${host_ip}" "${log_path}" + return 1 +} + +_wait_for_guest_ping() { + local host_ip="${1}" host_link_ip="${2}" guest_ip="${3}" + local max_tries=30 i + for ((i = 1; i <= max_tries; i++)); do + if _ssh_host_run "${host_ip}" \ + "ping -c 1 ${guest_ip}" >/dev/null 2>&1; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " guest ${guest_ip} not reachable from host yet..." + fi + sleep 1 + done + echo "Timed out waiting for guest ${guest_ip} reachability from host." >&2 + return 1 +} + +_wait_for_guest_ssh() { + local host_ip="${1}" guest_ip="${2}" ctl="${3}" log_path="${4}" host_link_ip="${5}" + local max_tries=75 i + _wait_for_guest_boot_log "${host_ip}" "${log_path}" + _wait_for_guest_ping "${host_ip}" "${host_link_ip}" "${guest_ip}" + rm -f "${ctl}" + sleep 5 + for ((i = 1; i <= max_tries; i++)); do + rm -f "${ctl}" + # shellcheck disable=SC2086 + if ssh ${_SSH_OPTS} \ + -o ProxyCommand="sshpass -p ${_SSH_PASS} ssh ${_SSH_OPTS} ${_SSH_USER}@${host_ip} -W %h:%p" \ + -i "${HYP_GUEST_PRIVATE_KEY_FILE}" \ + -o ControlMaster=yes -o ControlPath="${ctl}" -o ControlPersist=yes \ + -fN "${_SSH_USER}@${guest_ip}" 2>/dev/null; then + return 0 + fi + if (( i == 1 || i % 5 == 0 )); then + echo " guest ${guest_ip} booted and pingable; waiting for nested SSH..." + fi + sleep 2 + done + echo "Timed out waiting for guest SSH at ${guest_ip}." >&2 + _dump_guest_log "${host_ip}" "${log_path}" + return 1 +} + +_validate_qnx_binary() { + local bin="${1}" description + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + if grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary appears to be a Linux executable, not a QNX executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_qnx.sh" >&2 + return 1 + fi +} + +_host_bin_path() { + local name="${1}" + case "${name}" in + safe_edge_safety_io_adapters|safe_edge_policy_engine|safe_edge_vehicle_mock) + echo "${SAFETY_BIN_DIR}/${name}" ;; + safe_edge_cloud_gateway|safe_edge_ota_service|safe_edge_infotainment) + echo "${NON_SAFETY_BIN_DIR}/${name}" ;; + *) echo "Unknown vehicle node binary: ${name}" >&2; return 1 ;; + esac +} + +_validate_node_binaries() { + local name bin + for name in "$@"; do + bin="$(_host_bin_path "${name}")" + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_qnx.sh" >&2 + exit 1 + fi + _validate_qnx_binary "${bin}" + done +} + +_patch_guest_startup_flags() { + local guest_target_dir="${1}" + local ifs_build="${guest_target_dir}/output/build/ifs.build" + + if [[ ! -f "${ifs_build}" ]]; then + echo "Guest ifs.build not found: ${ifs_build}" >&2 + exit 1 + fi + + case "${HYP_GUEST_STARTUP_PATCH_MODE}" in + forced) + sed -i "s/startup-apic \\(.*\\) -zz[[:space:]]*/startup-apic \\1 -vv -f ,,${HYP_GUEST_STARTUP_FREQ} /" \ + "${ifs_build}" 2>/dev/null || true + ;; + unforced) + sed -i 's/startup-apic \(.*\) -zz[[:space:]]*/startup-apic \1 -vv /' \ + "${ifs_build}" 2>/dev/null || true + ;; + *) + echo "Unsupported HYP_GUEST_STARTUP_PATCH_MODE='${HYP_GUEST_STARTUP_PATCH_MODE}'" >&2 + exit 1 + ;; + esac +} + +_refresh_guest_files() { + local guest_target_dir="${1}" guest_ip="${2}" run_script_name="${3}" log_dir="${4}" data_dir="${5}" env_prefix="${6}" + shift 6 + local nodes=("$@") + local snippet="${guest_target_dir}/local/snippets/system_files.custom" + local post_start="${guest_target_dir}/local/snippets/post_start.custom" + local ifs_start="${guest_target_dir}/local/snippets/ifs_start.custom" + local run_script="${guest_target_dir}/local/misc_files/${run_script_name}" + local authorized_keys="${guest_target_dir}/local/misc_files/authorized_keys" + local name bin + + mkdir -p "$(dirname "${snippet}")" "$(dirname "${run_script}")" + + { + echo "#!/bin/sh" + echo "LOG_DIR=${log_dir}" + echo "DATA_DIR=${data_dir}" + echo 'mkdir -p "${LOG_DIR}" "${DATA_DIR}"' + echo 'rm -f "${LOG_DIR}"/*.pid "${LOG_DIR}"/*.log' + if [[ "${env_prefix}" == "safety" ]]; then + echo 'printf "soc=50.0\nemergency_stop=0\nadas_fault=0\navailable_charge_kw=50.0\navailable_discharge_kw=50.0\nv2g_ready=1\nspeed_mps=0.0\nbraking_available=1\nsteering_available=1\n" > "${DATA_DIR}/input.txt"' + fi + local first=1 + for name in "${nodes[@]}"; do + if [[ "${first}" -eq 0 ]]; then + echo "sleep 1" + fi + first=0 + if [[ "${env_prefix}" == "safety" ]]; then + echo "SAFE_EDGE_OWN_IP=${guest_ip} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + else + echo "/system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + fi + echo "echo \$! > \"\${LOG_DIR}/${name}.pid\"" + done + } > "${run_script}" + chmod +x "${run_script}" + cp "${HYP_GUEST_AUTHORIZED_KEY_FILE}" "${authorized_keys}" + chmod 600 "${authorized_keys}" + + { + echo "# local/snippets/system_files.custom" + echo "# Generated by scripts/launch_hypervisor_split.sh" + echo "[dperms=755 type=dir] root/.ssh" + for name in "${nodes[@]}"; do + bin="$(_host_bin_path "${name}")" + echo "[perms=555] bin/${name}=${bin}" + done + echo "[perms=555] bin/${run_script_name}=${run_script}" + echo "[perms=644] root/.ssh/authorized_keys=${authorized_keys}" + cat <<'SSHD' +[-dupignore] +[perms=444] etc/ssh/sshd_config = { +Protocol 2 +HostKey /data/var/ssh/ssh_host_rsa_key +HostKey /data/var/ssh/ssh_host_ed25519_key +Ciphers aes128-ctr,aes192-ctr,aes256-ctr +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256 +PubkeyAuthentication yes +AuthorizedKeysFile /data/ssh/authorized_keys +StrictModes no +KbdInteractiveAuthentication no +ChallengeResponseAuthentication no +UsePAM no +PasswordAuthentication no +PermitUserEnvironment yes +PermitRootLogin yes +PidFile none +Subsystem sftp /system/bin/sftp-server +SshdSessionPath /system/bin/sshd-session +ClientAliveInterval 30 +ClientAliveCountMax 10 +MaxAuthTries 20 +} +[+dupignore] +SSHD + } > "${snippet}" + + cat > "${ifs_start}" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_hypervisor_split.sh +EOF + + local pubkey seed_b64 seed_line + pubkey="$(cat "${HYP_GUEST_AUTHORIZED_KEY_FILE}")" + seed_b64="IedEPk92NsxQW/slRiEnDIw5/AFr6kd1MGponQbBjP1JGf1i8c62f9pJeM11xjaG9co3QEppfVIlHc45a7Nlivu+HfCOs6ywetbZ/rv4jgv7FhfFwO7FfsNy9foYm6tNR0shcF8iwO5ElEpk5W59WJ8Per3zrgs1fWqqOOgHEsA=" + seed_line="waitfor /dev/random 10" + seed_line+=" && echo '${seed_b64}' | base64 -d > /dev/random 2>/dev/null" + seed_line+="; mkdir -p /data/var/random" + seed_line+=" && echo '${seed_b64}' | base64 -d > /data/var/random/rnd-seed 2>/dev/null" + seed_line+="; true" + cat > "${post_start}" < /data/ssh/authorized_keys +chmod 600 /data/ssh/authorized_keys +EOF +} + +_setup_staging() { + rm -rf "${STAGING_ROOT}" + mkdir -p "${STAGING_ROOT}" + cp -a "${BASE_TARGET_DIR}" "${SAFETY_STAGING}" + cp -a "${BASE_TARGET_DIR}" "${NON_SAFETY_STAGING}" + rm -rf "${SAFETY_STAGING}/output" "${SAFETY_STAGING}/local/variants" + rm -rf "${NON_SAFETY_STAGING}/output" "${NON_SAFETY_STAGING}/local/variants" + cp "${SAFETY_VARIANT_DIR}/options" "${SAFETY_STAGING}/local/options" + cp "${NON_SAFETY_VARIANT_DIR}/options" "${NON_SAFETY_STAGING}/local/options" +} + +_refresh_host_common_snippets() { + mkdir -p "${TARGET_DIR}/local/snippets" + cat > "${TARGET_DIR}/local/snippets/system_files.custom" <<'EOF' +# local/snippets/system_files.custom +# Generated by scripts/launch_hypervisor_split.sh +EOF + cat > "${TARGET_DIR}/local/snippets/ifs_start.custom" <<'EOF' +# local/snippets/ifs_start.custom +# Generated by scripts/launch_hypervisor_split.sh +EOF + cat > "${TARGET_DIR}/local/snippets/post_start.custom" <<'EOF' +# local/snippets/post_start.custom +# Generated by scripts/launch_hypervisor_split.sh +route add -net 224.0.0.0/4 vtnet0 +mkdir -p /data/var/log/hypervisor +/data/hypervisor/start_safety_guest >/data/var/log/hypervisor/start_guest_safety.log 2>&1 & +EOF +} + +_refresh_host_guest_bundle_snippet() { + local snippet="${TARGET_DIR}/local/snippets/data_files.custom" + mkdir -p "$(dirname "${snippet}")" + cat > "${snippet}" </dev/null 2>&1; then + ifconfig vp0 create ${SAFETY_HOST_LINK_IP} up +fi +vpctl vp0 peer=/dev/qvm/mkqnximage-guest-safety/network bind=/dev/vdevpeers/vp0 +qvm @/data/hypervisor/guest-safety/guest.conf +} +[perms=555] /hypervisor/start_non_safety_guest = { +#!/bin/sh +if [ ! -e /dev/qvmdiskn0 ]; then + devb-loopback loopback blksz=512,prefix=qvmdiskn,fd=/data/hypervisor/guest-non-safety/disk-qvm + waitfor /dev/qvmdiskn0 +fi +if ! ifconfig vp1 >/dev/null 2>&1; then + ifconfig vp1 create ${NON_SAFETY_HOST_LINK_IP} up +fi +vpctl vp1 peer=/dev/qvm/mkqnximage-guest-non-safety/network bind=/dev/vdevpeers/vp1 +qvm @/data/hypervisor/guest-non-safety/guest.conf +} +hypervisor/guest-safety/guest.conf = { +system mkqnximage-guest-safety + +ram 0xa0000 +rom 0xc0000,0x40000 +ram 512M +cpu +load /data/hypervisor/guest-safety/ifs.bin + +vdev ioapic + loc 0xf8000000 + intr apic + name myioapic + +vdev ser8250 + hostdev >- + intr myioapic:4 + name ser8250_0 + +vdev timer8254 + intr myioapic:0 + name timer8254_0 + +vdev mc146818 + name mc146818_0 + +vdev shmem + name shmem_0 + +vdev pckeyboard + name pckeyboard_0 + +vdev virtio-net + name network + peerfeats 0x0382 + peer /dev/vdevpeers/vp0 + +vdev virtio-blk + hostdev /dev/qvmdisks0 + name virtio-blk_qvmdisk0 + +vdev 8259 + loc 0x20 +vdev 8259 + loc 0xa0 + +vdev hpet + intr myioapic:2 + name hpet_0 +} +hypervisor/guest-non-safety/guest.conf = { +system mkqnximage-guest-non-safety + +ram 0xa0000 +rom 0xc0000,0x40000 +ram 512M +cpu +load /data/hypervisor/guest-non-safety/ifs.bin + +vdev ioapic + loc 0xf8000000 + intr apic + name myioapic + +vdev ser8250 + hostdev >- + intr myioapic:4 + name ser8250_0 + +vdev timer8254 + intr myioapic:0 + name timer8254_0 + +vdev mc146818 + name mc146818_0 + +vdev shmem + name shmem_0 + +vdev pckeyboard + name pckeyboard_0 + +vdev virtio-net + name network + peerfeats 0x0382 + peer /dev/vdevpeers/vp1 + +vdev virtio-blk + hostdev /dev/qvmdiskn0 + name virtio-blk_qvmdisk0 + +vdev 8259 + loc 0x20 +vdev 8259 + loc 0xa0 + +vdev hpet + intr myioapic:2 + name hpet_0 +} +EOF +} + +_print_manual_commands() { + local host_ip="${1}" + cat <&2 + exit 1 + fi +done +if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then + echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 + exit 1 +fi +if ! command -v sshpass >/dev/null 2>&1; then + echo "sshpass not found. Install with: sudo apt install sshpass" >&2 + exit 1 +fi +if [[ ! -f "${HYP_GUEST_PRIVATE_KEY_FILE}" || ! -f "${HYP_GUEST_AUTHORIZED_KEY_FILE}" ]]; then + echo "Guest SSH keypair not found." >&2 + exit 1 +fi + +# shellcheck source=/dev/null +source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 + +if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then + echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2 + exit 1 +fi + +cd "${TARGET_DIR}" + +if [[ "${OPT_STOP}" -eq 1 ]]; then + _ssh_guest_close "${SAFETY_GUEST_CTL}" "${SAFETY_GUEST_IP}" + _ssh_guest_close "${NON_SAFETY_GUEST_CTL}" "${NON_SAFETY_GUEST_IP}" + echo "Stopping hypervisor host VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 +fi + +kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + +_QEMU_WRAPPER="/tmp/qemu-tsc-wrapper-split-$$" +cat > "${_QEMU_WRAPPER}" <<'WRAPPER_EOF' +#!/bin/bash +args=() +for arg in "$@"; do + if [[ "${arg}" == "host" ]]; then + args+=("host,tsc_freq=_TSC_FREQ_,invtsc=on") + else + args+=("${arg}") + fi +done +exec /usr/bin/qemu-system-x86_64 "${args[@]}" +WRAPPER_EOF +sed -i "s/_TSC_FREQ_/${HYP_GUEST_STARTUP_FREQ}/" "${_QEMU_WRAPPER}" +chmod +x "${_QEMU_WRAPPER}" +ln -sf "${_QEMU_WRAPPER}" /tmp/qemu-system-x86_64 +export PATH="/tmp:${PATH}" + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_node_binaries "${SAFETY_NODES[@]}" + _validate_node_binaries "${NON_SAFETY_NODES[@]}" + + _setup_staging + + _refresh_guest_files \ + "${SAFETY_STAGING}" "${SAFETY_GUEST_IP}" "run_safety_nodes.sh" \ + "/data/safe-edge-safety/logs" "/data/safe-edge-safety" "safety" \ + "${SAFETY_NODES[@]}" + echo "Building safety QNX guest image..." + (cd "${SAFETY_STAGING}" && mkqnximage --noprompt --clean >/dev/null 2>&1) || true + _patch_guest_startup_flags "${SAFETY_STAGING}" + (cd "${SAFETY_STAGING}" && mkqnximage --noprompt --build >/dev/null 2>&1) + + _refresh_guest_files \ + "${NON_SAFETY_STAGING}" "${NON_SAFETY_GUEST_IP}" "run_non_safety_nodes.sh" \ + "/data/safe-edge-non-safety/logs" "/data/safe-edge-non-safety" "non-safety" \ + "${NON_SAFETY_NODES[@]}" + echo "Building non-safety QNX guest image..." + (cd "${NON_SAFETY_STAGING}" && mkqnximage --noprompt --clean >/dev/null 2>&1) || true + _patch_guest_startup_flags "${NON_SAFETY_STAGING}" + (cd "${NON_SAFETY_STAGING}" && mkqnximage --noprompt --build >/dev/null 2>&1) + + for artifact in \ + "${SAFETY_GUEST_IFS}" "${SAFETY_GUEST_DISK}" \ + "${NON_SAFETY_GUEST_IFS}" "${NON_SAFETY_GUEST_DISK}"; do + if [[ ! -f "${artifact}" ]]; then + echo "Expected guest artifact missing after build: ${artifact}" >&2 + exit 1 + fi + done + + _refresh_host_common_snippets + _refresh_host_guest_bundle_snippet + rm -rf "${TARGET_DIR}/output" + + echo "Building QNX hypervisor host image and starting QEMU..." + mkqnximage --noprompt --guest=none --run=-h --clean >/dev/null 2>&1 +else + for artifact in \ + "${SAFETY_GUEST_IFS}" "${SAFETY_GUEST_DISK}" \ + "${NON_SAFETY_GUEST_IFS}" "${NON_SAFETY_GUEST_DISK}"; do + if [[ ! -f "${artifact}" ]]; then + echo "Guest artifact not found: ${artifact}" >&2 + echo "Rebuild first with: bash scripts/launch_hypervisor_split.sh" >&2 + + exit 1 + fi + done + echo "Starting QNX hypervisor host QEMU (skipping rebuild)..." + mkqnximage --noprompt --guest=none --run=-h >/dev/null 2>&1 +fi + +echo "Waiting for hypervisor host IP address..." +HOST_IP="$(_get_host_ip_address)" +echo "Hypervisor host is up: ${HOST_IP}" + +echo "Waiting for safety guest SSH reachability at ${SAFETY_GUEST_IP}..." +_wait_for_guest_ssh "${HOST_IP}" "${SAFETY_GUEST_IP}" "${SAFETY_GUEST_CTL}" "${SAFETY_HOST_LOG}" "${SAFETY_HOST_LINK_IP}" +echo "Safety guest is reachable over SSH." + +echo "Starting non-safety guest on host..." +_ssh_host_run "${HOST_IP}" \ + "nohup /data/hypervisor/start_non_safety_guest >/data/var/log/hypervisor/start_guest_non_safety.log 2>&1 /dev/null 2>&1 && \ + ssh -o ControlPath="${NON_SAFETY_GUEST_CTL}" "${_SSH_USER}@${NON_SAFETY_GUEST_IP}" "true" >/dev/null 2>&1; do + sleep 30 +done +echo "At least one guest SSH connection was lost. VM may have stopped." >&2 From fadcc3d1e8b46795154316439037183232c54252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 11 Jun 2026 08:17:30 +0200 Subject: [PATCH 12/17] Hypervisor - Communication between guests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .../common/RuntimeConfig.hpp | 6 +- .../non_safety/src/common/RuntimeConfig.cpp | 65 ++++++++++++++++- .../non_safety/src/nodes/CloudGatewayNode.cpp | 8 ++- .../non_safety/src/nodes/InfotainmentNode.cpp | 8 ++- .../non_safety/src/nodes/OtaServiceNode.cpp | 8 ++- .../safety_domain/common/RuntimeConfig.hpp | 9 ++- safe_dds/safety/src/common/RuntimeConfig.cpp | 69 +++++++++++++++++-- .../safety/src/nodes/PolicyEngineNode.cpp | 12 +++- .../safety/src/nodes/SafetyIoAdaptersNode.cpp | 12 +++- safe_dds/safety/src/nodes/VehicleMockNode.cpp | 12 +++- scripts/launch_hypervisor_split.sh | 15 +++- 11 files changed, 196 insertions(+), 28 deletions(-) diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp index e34493d..56e76e5 100644 --- a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp @@ -5,6 +5,8 @@ #include #include +#include + namespace safe_edge { namespace non_safety_domain { namespace common { @@ -16,7 +18,9 @@ struct RuntimeConfig std::string source_name; uint32_t domain_id = 0U; uint16_t participant_port = 0U; - uint16_t initial_peer_ports[4] = {}; + eprosima::safedds::transport::Locator::IPv4 own_ip = {127, 0, 0, 1}; + eprosima::safedds::transport::Locator::IPv4 cross_domain_peer_ip = {127, 0, 0, 1}; + uint16_t initial_peer_ports[5] = {}; std::size_t initial_peer_count = 0U; }; diff --git a/safe_dds/non_safety/src/common/RuntimeConfig.cpp b/safe_dds/non_safety/src/common/RuntimeConfig.cpp index 92f6f72..cbd73a2 100644 --- a/safe_dds/non_safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/non_safety/src/common/RuntimeConfig.cpp @@ -1,9 +1,63 @@ #include +#include +#include + namespace safe_edge { namespace non_safety_domain { namespace common { +namespace { + +void resolve_ip_from_env( + const char* env_name, + eprosima::safedds::transport::Locator::IPv4& out, + const eprosima::safedds::transport::Locator::IPv4& fallback) noexcept +{ + out[0] = fallback[0]; + out[1] = fallback[1]; + out[2] = fallback[2]; + out[3] = fallback[3]; + + const char* value = std::getenv(env_name); + if (nullptr == value || '\0' == value[0]) + { + return; + } + + std::stringstream ss(value); + int octet[4] = {}; + char dot1 = '\0'; + char dot2 = '\0'; + char dot3 = '\0'; + if (!(ss >> octet[0] >> dot1 >> octet[1] >> dot2 >> octet[2] >> dot3 >> octet[3])) + { + return; + } + if (dot1 != '.' || dot2 != '.' || dot3 != '.') + { + return; + } + for (int i = 0; i < 4; ++i) + { + if (octet[i] < 0 || octet[i] > 255) + { + return; + } + out[i] = static_cast(octet[i]); + } +} + +void populate_common_network_config( + RuntimeConfig& config) noexcept +{ + const eprosima::safedds::transport::Locator::IPv4 localhost = {127, 0, 0, 1}; + resolve_ip_from_env("SAFE_EDGE_OWN_IP", config.own_ip, localhost); + resolve_ip_from_env("SAFE_EDGE_CROSS_DOMAIN_IP", config.cross_domain_peer_ip, localhost); +} + +} // namespace + RuntimeConfig make_cloud_gateway_runtime_config() { RuntimeConfig config; @@ -12,10 +66,12 @@ RuntimeConfig make_cloud_gateway_runtime_config() config.source_name = "cloud_gateway"; config.domain_id = 0U; config.participant_port = 8011U; + populate_common_network_config(config); config.initial_peer_ports[0] = 8012U; config.initial_peer_ports[1] = 8013U; - config.initial_peer_ports[2] = 8002U; - config.initial_peer_count = 3U; + config.initial_peer_ports[2] = 8001U; + config.initial_peer_ports[3] = 8002U; + config.initial_peer_count = 4U; return config; } @@ -27,6 +83,7 @@ RuntimeConfig make_ota_service_runtime_config() config.source_name = "ota_service"; config.domain_id = 0U; config.participant_port = 8012U; + populate_common_network_config(config); config.initial_peer_ports[0] = 8011U; config.initial_peer_ports[1] = 8013U; config.initial_peer_count = 2U; @@ -41,11 +98,13 @@ RuntimeConfig make_infotainment_runtime_config() config.source_name = "infotainment"; config.domain_id = 0U; config.participant_port = 8013U; + populate_common_network_config(config); config.initial_peer_ports[0] = 8011U; config.initial_peer_ports[1] = 8012U; config.initial_peer_ports[2] = 8001U; config.initial_peer_ports[3] = 8002U; - config.initial_peer_count = 4U; + config.initial_peer_ports[4] = 8003U; + config.initial_peer_count = 5U; return config; } diff --git a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp index 13cd259..b5d665b 100644 --- a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp +++ b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp @@ -408,12 +408,16 @@ bool CloudGatewayNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp index 2e9719b..13cd7de 100644 --- a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp +++ b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp @@ -316,12 +316,16 @@ bool InfotainmentNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp index a4a2348..4aa2ec1 100644 --- a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp +++ b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp @@ -280,12 +280,16 @@ bool OtaServiceNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) { - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_ports[i])); + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp index 3ef6199..a545426 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp @@ -1,9 +1,12 @@ #ifndef SAFE_EDGE_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP #define SAFE_EDGE_SAFETY_DOMAIN_COMMON_RUNTIMECONFIG_HPP +#include #include #include +#include + namespace safe_edge { namespace safety_domain { namespace common { @@ -15,8 +18,10 @@ struct RuntimeConfig std::string source_name; uint32_t domain_id = 0U; uint16_t participant_port = 0U; - uint16_t initial_peer_port = 0U; - uint16_t initial_peer_port_2 = 0U; + eprosima::safedds::transport::Locator::IPv4 own_ip = {127, 0, 0, 1}; + eprosima::safedds::transport::Locator::IPv4 cross_domain_peer_ip = {127, 0, 0, 1}; + uint16_t initial_peer_ports[5] = {}; + std::size_t initial_peer_count = 0U; }; RuntimeConfig make_safety_io_adapters_runtime_config(); diff --git a/safe_dds/safety/src/common/RuntimeConfig.cpp b/safe_dds/safety/src/common/RuntimeConfig.cpp index 8a9ef90..0cf3e7c 100644 --- a/safe_dds/safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/safety/src/common/RuntimeConfig.cpp @@ -1,9 +1,63 @@ #include +#include +#include + namespace safe_edge { namespace safety_domain { namespace common { +namespace { + +void resolve_ip_from_env( + const char* env_name, + eprosima::safedds::transport::Locator::IPv4& out, + const eprosima::safedds::transport::Locator::IPv4& fallback) noexcept +{ + out[0] = fallback[0]; + out[1] = fallback[1]; + out[2] = fallback[2]; + out[3] = fallback[3]; + + const char* value = std::getenv(env_name); + if (nullptr == value || '\0' == value[0]) + { + return; + } + + std::stringstream ss(value); + int octet[4] = {}; + char dot1 = '\0'; + char dot2 = '\0'; + char dot3 = '\0'; + if (!(ss >> octet[0] >> dot1 >> octet[1] >> dot2 >> octet[2] >> dot3 >> octet[3])) + { + return; + } + if (dot1 != '.' || dot2 != '.' || dot3 != '.') + { + return; + } + for (int i = 0; i < 4; ++i) + { + if (octet[i] < 0 || octet[i] > 255) + { + return; + } + out[i] = static_cast(octet[i]); + } +} + +void populate_common_network_config( + RuntimeConfig& config) noexcept +{ + const eprosima::safedds::transport::Locator::IPv4 localhost = {127, 0, 0, 1}; + resolve_ip_from_env("SAFE_EDGE_OWN_IP", config.own_ip, localhost); + resolve_ip_from_env("SAFE_EDGE_CROSS_DOMAIN_IP", config.cross_domain_peer_ip, localhost); +} + +} // namespace + RuntimeConfig make_safety_io_adapters_runtime_config() { RuntimeConfig config; @@ -12,7 +66,9 @@ RuntimeConfig make_safety_io_adapters_runtime_config() config.source_name = "safety_io_adapters"; config.domain_id = 0U; config.participant_port = 8001U; - config.initial_peer_port = 8002U; + populate_common_network_config(config); + config.initial_peer_ports[0] = 8002U; + config.initial_peer_count = 1U; return config; } @@ -24,7 +80,10 @@ RuntimeConfig make_policy_engine_runtime_config() config.source_name = "policy_engine"; config.domain_id = 0U; config.participant_port = 8002U; - config.initial_peer_port = 8001U; + populate_common_network_config(config); + config.initial_peer_ports[0] = 8001U; + config.initial_peer_ports[1] = 8011U; + config.initial_peer_count = 2U; return config; } @@ -36,8 +95,10 @@ RuntimeConfig make_vehicle_mock_runtime_config() config.source_name = "vehicle_mock"; config.domain_id = 0U; config.participant_port = 8003U; - config.initial_peer_port = 8001U; - config.initial_peer_port_2 = 8002U; + populate_common_network_config(config); + config.initial_peer_ports[0] = 8001U; + config.initial_peer_ports[1] = 8002U; + config.initial_peer_count = 2U; return config; } diff --git a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp index 8b63813..8afea47 100644 --- a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp +++ b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp @@ -380,11 +380,17 @@ bool PolicyEngineNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8001U && runtime_config_.initial_peer_ports[i] <= 8003U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; participant_ = factory_.create_participant( diff --git a/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp index f5f9fe3..a30ff75 100644 --- a/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp +++ b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp @@ -295,11 +295,17 @@ bool SafetyIoAdaptersNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8001U && runtime_config_.initial_peer_ports[i] <= 8003U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; participant_ = factory_.create_participant( diff --git a/safe_dds/safety/src/nodes/VehicleMockNode.cpp b/safe_dds/safety/src/nodes/VehicleMockNode.cpp index b8365ed..3529f96 100644 --- a/safe_dds/safety/src/nodes/VehicleMockNode.cpp +++ b/safe_dds/safety/src/nodes/VehicleMockNode.cpp @@ -211,11 +211,17 @@ bool VehicleMockNode::create_participant() eprosima::safedds::memory::container::StaticString256 participant_name(runtime_config_.participant_name.c_str()); participant_qos.participant_name() = participant_name; participant_qos.wire_protocol_qos().announced_locator = eprosima::safedds::transport::Locator::from_ipv4( - {127, 0, 0, 1}, + runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port)); - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4({127, 0, 0, 1}, runtime_config_.initial_peer_port_2)); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const auto& peer_ip = + (runtime_config_.initial_peer_ports[i] >= 8001U && runtime_config_.initial_peer_ports[i] <= 8003U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; participant_ = factory_.create_participant( runtime_config_.domain_id, diff --git a/scripts/launch_hypervisor_split.sh b/scripts/launch_hypervisor_split.sh index 318d14b..ea3900c 100644 --- a/scripts/launch_hypervisor_split.sh +++ b/scripts/launch_hypervisor_split.sh @@ -258,7 +258,7 @@ _refresh_guest_files() { local ifs_start="${guest_target_dir}/local/snippets/ifs_start.custom" local run_script="${guest_target_dir}/local/misc_files/${run_script_name}" local authorized_keys="${guest_target_dir}/local/misc_files/authorized_keys" - local name bin + local name bin cross_subnet gateway_ip mkdir -p "$(dirname "${snippet}")" "$(dirname "${run_script}")" @@ -278,9 +278,9 @@ _refresh_guest_files() { fi first=0 if [[ "${env_prefix}" == "safety" ]]; then - echo "SAFE_EDGE_OWN_IP=${guest_ip} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${NON_SAFETY_GUEST_IP} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" else - echo "/system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${SAFETY_GUEST_IP} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" fi echo "echo \$! > \"\${LOG_DIR}/${name}.pid\"" done @@ -341,10 +341,18 @@ EOF seed_line+="; mkdir -p /data/var/random" seed_line+=" && echo '${seed_b64}' | base64 -d > /data/var/random/rnd-seed 2>/dev/null" seed_line+="; true" + if [[ "${env_prefix}" == "safety" ]]; then + cross_subnet="192.168.20.0/24" + gateway_ip="${SAFETY_HOST_LINK_IP%/*}" + else + cross_subnet="192.168.10.0/24" + gateway_ip="${NON_SAFETY_HOST_LINK_IP%/*}" + fi cat > "${post_start}" < /data/ssh/authorized_keys @@ -377,6 +385,7 @@ EOF # local/snippets/post_start.custom # Generated by scripts/launch_hypervisor_split.sh route add -net 224.0.0.0/4 vtnet0 +sysctl -w net.inet.ip.forwarding=1 mkdir -p /data/var/log/hypervisor /data/hypervisor/start_safety_guest >/data/var/log/hypervisor/start_guest_safety.log 2>&1 & EOF From 1058001dd03862eecc1497717f9de7fbd5a053ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 11 Jun 2026 12:18:00 +0200 Subject: [PATCH 13/17] Hypervisor - External communication & minor fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .../edge_module/common/RuntimeConfig.hpp | 5 + fast_dds/edge/src/common/RuntimeConfig.cpp | 20 ++ fast_dds/edge/src/nodes/EdgeGatewayNode.cpp | 26 +- .../safe_edge/server/common/RuntimeConfig.hpp | 5 + fast_dds/server/src/common/RuntimeConfig.cpp | 21 ++ fast_dds/server/src/nodes/ServerNode.cpp | 34 ++- .../common/RuntimeConfig.hpp | 2 + .../non_safety/src/common/RuntimeConfig.cpp | 46 ++++ .../non_safety/src/nodes/CloudGatewayNode.cpp | 23 +- .../non_safety/src/nodes/InfotainmentNode.cpp | 23 +- .../non_safety/src/nodes/OtaServiceNode.cpp | 23 +- .../safety_domain/common/RuntimeConfig.hpp | 5 +- safe_dds/safety/src/common/RuntimeConfig.cpp | 55 +++- .../safety/src/nodes/PolicyEngineNode.cpp | 23 +- .../safety/src/nodes/SafetyIoAdaptersNode.cpp | 23 +- scripts/launch_all.sh | 247 ++++++++++++++++++ scripts/launch_fast_edge_test.sh | 92 +++++-- scripts/launch_fast_server_test.sh | 89 +++++-- scripts/launch_hypervisor_split.sh | 24 +- 19 files changed, 690 insertions(+), 96 deletions(-) create mode 100755 scripts/launch_all.sh diff --git a/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp b/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp index 88a357c..2f390e4 100644 --- a/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp +++ b/fast_dds/edge/include/safe_edge/edge_module/common/RuntimeConfig.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include namespace safe_edge { namespace edge_module { @@ -16,6 +18,9 @@ struct RuntimeConfig uint32_t domain_id = 0U; uint16_t participant_port = 0U; uint32_t status_interval_sec = 5U; + std::string own_ip = "127.0.0.1"; + std::string safety_ip = "127.0.0.1"; + std::vector> initial_peers; }; RuntimeConfig make_edge_gateway_runtime_config(); diff --git a/fast_dds/edge/src/common/RuntimeConfig.cpp b/fast_dds/edge/src/common/RuntimeConfig.cpp index ee6eb27..76f76aa 100644 --- a/fast_dds/edge/src/common/RuntimeConfig.cpp +++ b/fast_dds/edge/src/common/RuntimeConfig.cpp @@ -1,5 +1,8 @@ #include +#include +#include + namespace safe_edge { namespace edge_module { namespace common { @@ -13,6 +16,23 @@ RuntimeConfig make_edge_gateway_runtime_config() config.domain_id = 0U; config.participant_port = 8030U; config.status_interval_sec = 5U; + if (const char* v = std::getenv("SAFE_EDGE_OWN_IP")) { config.own_ip = v; } + if (const char* v = std::getenv("SAFE_EDGE_SAFETY_IP")) { config.safety_ip = v; } + if (const char* v = std::getenv("SAFE_EDGE_INITIAL_PEERS")) + { + std::istringstream ss(v); + std::string token; + while (std::getline(ss, token, ',')) + { + const auto sep = token.rfind(':'); + if (sep != std::string::npos) + { + config.initial_peers.emplace_back( + token.substr(0, sep), + static_cast(std::stoul(token.substr(sep + 1)))); + } + } + } return config; } diff --git a/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp b/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp index b453cf6..1166f14 100644 --- a/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp +++ b/fast_dds/edge/src/nodes/EdgeGatewayNode.cpp @@ -195,9 +195,33 @@ bool EdgeGatewayNode::create_participant() participant_qos.name(runtime_config_.participant_name); eprosima::fastdds::rtps::Locator_t locator; - eprosima::fastdds::rtps::IPLocator::setIPv4(locator, "127.0.0.1"); + eprosima::fastdds::rtps::IPLocator::setIPv4(locator, runtime_config_.own_ip); locator.port = runtime_config_.participant_port; participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(locator); + locator.port = 0; + participant_qos.wire_protocol().default_unicast_locator_list.push_back(locator); + participant_qos.wire_protocol().builtin.avoid_builtin_multicast = true; + + if (!runtime_config_.initial_peers.empty()) + { + for (const auto& p : runtime_config_.initial_peers) + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, p.first); + peer.port = p.second; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } + } + else + { + for (uint16_t safety_port : {8001U, 8002U}) + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, runtime_config_.safety_ip); + peer.port = safety_port; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } + } eprosima::fastdds::dds::StatusMask participant_mask = eprosima::fastdds::dds::StatusMask::publication_matched(); diff --git a/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp b/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp index c7577c4..5f5e121 100644 --- a/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp +++ b/fast_dds/server/include/safe_edge/server/common/RuntimeConfig.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include namespace safe_edge { namespace server { @@ -13,6 +15,9 @@ struct RuntimeConfig std::string participant_name; uint32_t domain_id = 0U; uint16_t participant_port = 0U; + std::string own_ip = "127.0.0.1"; + std::string non_safety_ip = "127.0.0.1"; + std::vector> initial_peers; std::string pilot_server_base_url; std::string pilot_server_api_key; diff --git a/fast_dds/server/src/common/RuntimeConfig.cpp b/fast_dds/server/src/common/RuntimeConfig.cpp index 0034c96..bbd4d01 100644 --- a/fast_dds/server/src/common/RuntimeConfig.cpp +++ b/fast_dds/server/src/common/RuntimeConfig.cpp @@ -1,5 +1,9 @@ #include +#include +#include +#include + namespace safe_edge { namespace server { namespace common { @@ -10,6 +14,23 @@ RuntimeConfig make_server_runtime_config() config.participant_name = "SafeEdgeServerParticipant"; config.domain_id = 0U; config.participant_port = 8020U; + if (const char* v = std::getenv("SAFE_EDGE_OWN_IP")) { config.own_ip = v; } + if (const char* v = std::getenv("SAFE_EDGE_NON_SAFETY_IP")) { config.non_safety_ip = v; } + if (const char* v = std::getenv("SAFE_EDGE_INITIAL_PEERS")) + { + std::istringstream ss(v); + std::string token; + while (std::getline(ss, token, ',')) + { + const auto sep = token.rfind(':'); + if (sep != std::string::npos) + { + config.initial_peers.emplace_back( + token.substr(0, sep), + static_cast(std::stoul(token.substr(sep + 1)))); + } + } + } config.pilot_server_base_url = "https://pilot2.dumitru-alexandru.work"; // api_key moved to /etc/safe-edge/server.ini — do not hardcode here diff --git a/fast_dds/server/src/nodes/ServerNode.cpp b/fast_dds/server/src/nodes/ServerNode.cpp index 81c4ec7..70f3683 100644 --- a/fast_dds/server/src/nodes/ServerNode.cpp +++ b/fast_dds/server/src/nodes/ServerNode.cpp @@ -183,17 +183,37 @@ bool ServerNode::create_participant() participant_qos.name(runtime_config_.participant_name); eprosima::fastdds::rtps::Locator_t announced_locator; - eprosima::fastdds::rtps::IPLocator::setIPv4(announced_locator, "127.0.0.1"); + eprosima::fastdds::rtps::IPLocator::setIPv4(announced_locator, runtime_config_.own_ip); announced_locator.port = runtime_config_.participant_port; participant_qos.wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(announced_locator); + announced_locator.port = 0; + participant_qos.wire_protocol().default_unicast_locator_list.push_back(announced_locator); + participant_qos.wire_protocol().builtin.avoid_builtin_multicast = true; - static constexpr uint16_t peer_ports[] = { 8011U, 8030U }; - for (uint16_t port : peer_ports) + if (!runtime_config_.initial_peers.empty()) { - eprosima::fastdds::rtps::Locator_t peer; - eprosima::fastdds::rtps::IPLocator::setIPv4(peer, "127.0.0.1"); - peer.port = port; - participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + for (const auto& p : runtime_config_.initial_peers) + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, p.first); + peer.port = p.second; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } + } + else + { + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, runtime_config_.non_safety_ip); + peer.port = 8011U; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } + { + eprosima::fastdds::rtps::Locator_t peer; + eprosima::fastdds::rtps::IPLocator::setIPv4(peer, runtime_config_.own_ip); + peer.port = 8030U; + participant_qos.wire_protocol().builtin.initialPeersList.push_back(peer); + } } eprosima::fastdds::dds::StatusMask participant_mask = diff --git a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp index 56e76e5..a5c52f7 100644 --- a/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/non_safety/include/safe_edge/non_safety_domain/common/RuntimeConfig.hpp @@ -22,6 +22,8 @@ struct RuntimeConfig eprosima::safedds::transport::Locator::IPv4 cross_domain_peer_ip = {127, 0, 0, 1}; uint16_t initial_peer_ports[5] = {}; std::size_t initial_peer_count = 0U; + eprosima::safedds::transport::Locator initial_peer_locators[8] = {}; + std::size_t initial_peer_locator_count = 0U; }; RuntimeConfig make_cloud_gateway_runtime_config(); diff --git a/safe_dds/non_safety/src/common/RuntimeConfig.cpp b/safe_dds/non_safety/src/common/RuntimeConfig.cpp index cbd73a2..d39d90b 100644 --- a/safe_dds/non_safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/non_safety/src/common/RuntimeConfig.cpp @@ -48,12 +48,58 @@ void resolve_ip_from_env( } } +void parse_initial_peers(RuntimeConfig& config) noexcept +{ + const char* raw = std::getenv("SAFE_EDGE_INITIAL_PEERS"); + if (nullptr == raw || '\0' == raw[0]) { return; } + + std::string s(raw); + std::size_t pos = 0U; + while (pos < s.size() && config.initial_peer_locator_count < 8U) + { + const std::size_t comma = s.find(',', pos); + const std::size_t end = (comma == std::string::npos) ? s.size() : comma; + const std::string token = s.substr(pos, end - pos); + pos = (comma == std::string::npos) ? s.size() : comma + 1U; + + const std::size_t colon = token.rfind(':'); + if (colon == std::string::npos) { continue; } + + eprosima::safedds::transport::Locator::IPv4 ip4 = {}; + { + std::stringstream ss(token.substr(0, colon)); + int a = 0, b = 0, c = 0, d = 0; + char p1 = '\0', p2 = '\0', p3 = '\0'; + if (!(ss >> a >> p1 >> b >> p2 >> c >> p3 >> d) || p1 != '.' || p2 != '.' || p3 != '.') + { continue; } + if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) + { continue; } + ip4[0] = static_cast(a); + ip4[1] = static_cast(b); + ip4[2] = static_cast(c); + ip4[3] = static_cast(d); + } + + uint16_t port = 0U; + { + std::stringstream ss(token.substr(colon + 1U)); + unsigned int p = 0U; + if (!(ss >> p) || p > 65535U) { continue; } + port = static_cast(p); + } + + config.initial_peer_locators[config.initial_peer_locator_count++] = + eprosima::safedds::transport::Locator::from_ipv4(ip4, port); + } +} + void populate_common_network_config( RuntimeConfig& config) noexcept { const eprosima::safedds::transport::Locator::IPv4 localhost = {127, 0, 0, 1}; resolve_ip_from_env("SAFE_EDGE_OWN_IP", config.own_ip, localhost); resolve_ip_from_env("SAFE_EDGE_CROSS_DOMAIN_IP", config.cross_domain_peer_ip, localhost); + parse_initial_peers(config); } } // namespace diff --git a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp index b5d665b..2889cbb 100644 --- a/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp +++ b/safe_dds/non_safety/src/nodes/CloudGatewayNode.cpp @@ -411,13 +411,24 @@ bool CloudGatewayNode::create_participant() runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + if (runtime_config_.initial_peer_locator_count > 0U) { - const auto& peer_ip = - (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? - runtime_config_.own_ip : - runtime_config_.cross_domain_peer_ip; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_locator_count; ++i) + { + initial_peers_.add(runtime_config_.initial_peer_locators[i]); + } + } + else + { + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const uint16_t port = runtime_config_.initial_peer_ports[i]; + const auto& peer_ip = + (port >= 8011U && port <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, port)); + } } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp index 13cd7de..062a00e 100644 --- a/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp +++ b/safe_dds/non_safety/src/nodes/InfotainmentNode.cpp @@ -319,13 +319,24 @@ bool InfotainmentNode::create_participant() runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + if (runtime_config_.initial_peer_locator_count > 0U) { - const auto& peer_ip = - (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? - runtime_config_.own_ip : - runtime_config_.cross_domain_peer_ip; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_locator_count; ++i) + { + initial_peers_.add(runtime_config_.initial_peer_locators[i]); + } + } + else + { + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const uint16_t port = runtime_config_.initial_peer_ports[i]; + const auto& peer_ip = + (port >= 8011U && port <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, port)); + } } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp index 4aa2ec1..56c66df 100644 --- a/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp +++ b/safe_dds/non_safety/src/nodes/OtaServiceNode.cpp @@ -283,13 +283,24 @@ bool OtaServiceNode::create_participant() runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + if (runtime_config_.initial_peer_locator_count > 0U) { - const auto& peer_ip = - (runtime_config_.initial_peer_ports[i] >= 8011U && runtime_config_.initial_peer_ports[i] <= 8013U) ? - runtime_config_.own_ip : - runtime_config_.cross_domain_peer_ip; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_locator_count; ++i) + { + initial_peers_.add(runtime_config_.initial_peer_locators[i]); + } + } + else + { + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const uint16_t port = runtime_config_.initial_peer_ports[i]; + const auto& peer_ip = + (port >= 8011U && port <= 8013U) ? + runtime_config_.own_ip : + runtime_config_.cross_domain_peer_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, port)); + } } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp index a545426..d1dac5c 100644 --- a/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp +++ b/safe_dds/safety/include/safe_edge/safety_domain/common/RuntimeConfig.hpp @@ -20,8 +20,11 @@ struct RuntimeConfig uint16_t participant_port = 0U; eprosima::safedds::transport::Locator::IPv4 own_ip = {127, 0, 0, 1}; eprosima::safedds::transport::Locator::IPv4 cross_domain_peer_ip = {127, 0, 0, 1}; - uint16_t initial_peer_ports[5] = {}; + eprosima::safedds::transport::Locator::IPv4 host_ip = {127, 0, 0, 1}; + uint16_t initial_peer_ports[7] = {}; std::size_t initial_peer_count = 0U; + eprosima::safedds::transport::Locator initial_peer_locators[8] = {}; + std::size_t initial_peer_locator_count = 0U; }; RuntimeConfig make_safety_io_adapters_runtime_config(); diff --git a/safe_dds/safety/src/common/RuntimeConfig.cpp b/safe_dds/safety/src/common/RuntimeConfig.cpp index 0cf3e7c..5654024 100644 --- a/safe_dds/safety/src/common/RuntimeConfig.cpp +++ b/safe_dds/safety/src/common/RuntimeConfig.cpp @@ -48,12 +48,59 @@ void resolve_ip_from_env( } } +void parse_initial_peers(RuntimeConfig& config) noexcept +{ + const char* raw = std::getenv("SAFE_EDGE_INITIAL_PEERS"); + if (nullptr == raw || '\0' == raw[0]) { return; } + + std::string s(raw); + std::size_t pos = 0U; + while (pos < s.size() && config.initial_peer_locator_count < 8U) + { + const std::size_t comma = s.find(',', pos); + const std::size_t end = (comma == std::string::npos) ? s.size() : comma; + const std::string token = s.substr(pos, end - pos); + pos = (comma == std::string::npos) ? s.size() : comma + 1U; + + const std::size_t colon = token.rfind(':'); + if (colon == std::string::npos) { continue; } + + eprosima::safedds::transport::Locator::IPv4 ip4 = {}; + { + std::stringstream ss(token.substr(0, colon)); + int a = 0, b = 0, c = 0, d = 0; + char p1 = '\0', p2 = '\0', p3 = '\0'; + if (!(ss >> a >> p1 >> b >> p2 >> c >> p3 >> d) || p1 != '.' || p2 != '.' || p3 != '.') + { continue; } + if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) + { continue; } + ip4[0] = static_cast(a); + ip4[1] = static_cast(b); + ip4[2] = static_cast(c); + ip4[3] = static_cast(d); + } + + uint16_t port = 0U; + { + std::stringstream ss(token.substr(colon + 1U)); + unsigned int p = 0U; + if (!(ss >> p) || p > 65535U) { continue; } + port = static_cast(p); + } + + config.initial_peer_locators[config.initial_peer_locator_count++] = + eprosima::safedds::transport::Locator::from_ipv4(ip4, port); + } +} + void populate_common_network_config( RuntimeConfig& config) noexcept { const eprosima::safedds::transport::Locator::IPv4 localhost = {127, 0, 0, 1}; resolve_ip_from_env("SAFE_EDGE_OWN_IP", config.own_ip, localhost); resolve_ip_from_env("SAFE_EDGE_CROSS_DOMAIN_IP", config.cross_domain_peer_ip, localhost); + resolve_ip_from_env("SAFE_EDGE_HOST_IP", config.host_ip, localhost); + parse_initial_peers(config); } } // namespace @@ -68,7 +115,9 @@ RuntimeConfig make_safety_io_adapters_runtime_config() config.participant_port = 8001U; populate_common_network_config(config); config.initial_peer_ports[0] = 8002U; - config.initial_peer_count = 1U; + config.initial_peer_ports[1] = 8020U; + config.initial_peer_ports[2] = 8030U; + config.initial_peer_count = 3U; return config; } @@ -83,7 +132,9 @@ RuntimeConfig make_policy_engine_runtime_config() populate_common_network_config(config); config.initial_peer_ports[0] = 8001U; config.initial_peer_ports[1] = 8011U; - config.initial_peer_count = 2U; + config.initial_peer_ports[2] = 8020U; + config.initial_peer_ports[3] = 8030U; + config.initial_peer_count = 4U; return config; } diff --git a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp index 8afea47..0267dda 100644 --- a/safe_dds/safety/src/nodes/PolicyEngineNode.cpp +++ b/safe_dds/safety/src/nodes/PolicyEngineNode.cpp @@ -383,13 +383,24 @@ bool PolicyEngineNode::create_participant() runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + if (runtime_config_.initial_peer_locator_count > 0U) { - const auto& peer_ip = - (runtime_config_.initial_peer_ports[i] >= 8001U && runtime_config_.initial_peer_ports[i] <= 8003U) ? - runtime_config_.own_ip : - runtime_config_.cross_domain_peer_ip; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_locator_count; ++i) + { + initial_peers_.add(runtime_config_.initial_peer_locators[i]); + } + } + else + { + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const uint16_t port = runtime_config_.initial_peer_ports[i]; + const auto& peer_ip = + (port >= 8001U && port <= 8003U) ? runtime_config_.own_ip : + (port >= 8011U && port <= 8013U) ? runtime_config_.cross_domain_peer_ip : + runtime_config_.host_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, port)); + } } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp index a30ff75..36ee498 100644 --- a/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp +++ b/safe_dds/safety/src/nodes/SafetyIoAdaptersNode.cpp @@ -298,13 +298,24 @@ bool SafetyIoAdaptersNode::create_participant() runtime_config_.own_ip, runtime_config_.participant_port); participant_qos.wire_protocol_qos().use_multicast_discovery = false; - for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + if (runtime_config_.initial_peer_locator_count > 0U) { - const auto& peer_ip = - (runtime_config_.initial_peer_ports[i] >= 8001U && runtime_config_.initial_peer_ports[i] <= 8003U) ? - runtime_config_.own_ip : - runtime_config_.cross_domain_peer_ip; - initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, runtime_config_.initial_peer_ports[i])); + for (std::size_t i = 0U; i < runtime_config_.initial_peer_locator_count; ++i) + { + initial_peers_.add(runtime_config_.initial_peer_locators[i]); + } + } + else + { + for (std::size_t i = 0U; i < runtime_config_.initial_peer_count; ++i) + { + const uint16_t port = runtime_config_.initial_peer_ports[i]; + const auto& peer_ip = + (port >= 8001U && port <= 8003U) ? runtime_config_.own_ip : + (port >= 8011U && port <= 8013U) ? runtime_config_.cross_domain_peer_ip : + runtime_config_.host_ip; + initial_peers_.add(eprosima::safedds::transport::Locator::from_ipv4(peer_ip, port)); + } } participant_qos.wire_protocol_qos().initial_peers = &initial_peers_; diff --git a/scripts/launch_all.sh b/scripts/launch_all.sh new file mode 100755 index 0000000..a83ee16 --- /dev/null +++ b/scripts/launch_all.sh @@ -0,0 +1,247 @@ +#!/usr/bin/env bash +# Launch the full SafeEDGE stack and verify DDS connectivity. +# +# Order: +# 1. Stop any previous instances. +# 2. Build images/binaries if needed. +# 3. Start hypervisor (safety + non-safety guests). +# 4. Once guests are SSH-reachable, start edge and server Docker containers. +# 5. Wait for DDS connectivity and print a final verdict. +# +# Usage: bash scripts/launch_all.sh [--no-rebuild] [-h|--help] +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +HV_LOG="/tmp/safe-edge-hypervisor.log" +EDGE_LOG="/tmp/safe-edge-edge.log" +SERVER_LOG="/tmp/safe-edge-server.log" + +SAFETY_CTL="/tmp/ssh-guest-safety-ctl" +NON_SAFETY_CTL="/tmp/ssh-guest-non-safety-ctl" +SAFETY_IP="192.168.10.2" +NON_SAFETY_IP="192.168.20.2" + +VERIFY_TIMEOUT=60 # seconds to wait for DDS connectivity +OPT_NO_REBUILD=0 +OPT_STOP=0 + +log() { echo "$*"; } +ok() { echo "✓ $*"; } +fail() { echo "✗ $*" >&2; } + +_stop_all() { + for cname in safe-edge-edge safe-edge-server; do + if docker ps -q --filter name="^${cname}$" | grep -q .; then + log "Stopping ${cname}..." + docker stop "${cname}" > /dev/null 2>&1 || true + docker rm "${cname}" > /dev/null 2>&1 || true + ok "${cname} stopped." + fi + done + bash "${SCRIPT_DIR}/launch_hypervisor_split.sh" --stop 2>/dev/null || true + sleep 2 + pkill -f qemu-system 2>/dev/null || true +} + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +if [[ "${OPT_STOP}" -eq 1 ]]; then + _stop_all + ok "All stopped." + exit 0 +fi + +ssh_guest() { + local ctl="${1}" ip="${2}"; shift 2 + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + -o ControlPath="${ctl}" -o ConnectTimeout=5 \ + root@"${ip}" "$@" 2>/dev/null +} + +# ── 1. Stop previous instances ──────────────────────────────────────────────── + +log "Stopping any previous SafeEDGE instances..." +_stop_all +sleep 2 + +# ── 2. Build ────────────────────────────────────────────────────────────────── + +if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + log "Building Docker images..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" + log "Building QNX binaries..." + bash "${SCRIPT_DIR}/build_qnx.sh" +fi + +# ── Topology ───────────────────────────────────────────────────────────────── + +BRIDGE_IP="$(ip -o -f inet addr show virbr0 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)" +: "${BRIDGE_IP:=127.0.0.1}" +export SAFE_EDGE_OWN_IP="${BRIDGE_IP}" +export SAFE_EDGE_SAFETY_IP="${SAFETY_IP}" +export SAFE_EDGE_NON_SAFETY_IP="${NON_SAFETY_IP}" +export SAFE_EDGE_INITIAL_PEERS_EDGE="${SAFETY_IP}:8001,${SAFETY_IP}:8002,${NON_SAFETY_IP}:8011,${BRIDGE_IP}:8020" +export SAFE_EDGE_INITIAL_PEERS_SERVER="${NON_SAFETY_IP}:8011,${BRIDGE_IP}:8030" +log "Bridge IP: ${BRIDGE_IP} Safety: ${SAFETY_IP} Non-safety: ${NON_SAFETY_IP}" + +# ── 3. Start hypervisor ─────────────────────────────────────────────────────── + +log "Starting hypervisor (safety + non-safety guests)..." +HV_ARGS=() +[[ "${OPT_NO_REBUILD}" -eq 1 ]] && HV_ARGS+=(--no-rebuild) +bash "${SCRIPT_DIR}/launch_hypervisor_split.sh" "${HV_ARGS[@]}" > "${HV_LOG}" 2>&1 & +HV_PID=$! + +log "Waiting for guests to be ready..." +DEADLINE=$(( $(date +%s) + 300 )) +while ! grep -q "Both guests are running" "${HV_LOG}" 2>/dev/null; do + if [[ $(date +%s) -gt ${DEADLINE} ]]; then + fail "Timed out waiting for hypervisor guests." + tail -20 "${HV_LOG}" >&2 + exit 1 + fi + sleep 3 +done +ok "Guests are up." + +# ── 4. Start edge and server ────────────────────────────────────────────────── + +log "Starting edge and server..." +SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_EDGE}" bash "${SCRIPT_DIR}/launch_fast_edge_test.sh" > "${EDGE_LOG}" 2>&1 & +SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_SERVER}" bash "${SCRIPT_DIR}/launch_fast_server_test.sh" > "${SERVER_LOG}" 2>&1 & + +# Wait for both named containers to be running. +DEADLINE=$(( $(date +%s) + 30 )) +while [[ $(docker ps -q --filter name=^safe-edge-edge$ | wc -l) -eq 0 ]] || \ + [[ $(docker ps -q --filter name=^safe-edge-server$ | wc -l) -eq 0 ]]; do + if [[ $(date +%s) -gt ${DEADLINE} ]]; then + fail "Timed out waiting for Docker containers." + exit 1 + fi + sleep 2 +done +ok "Edge and server containers running (safe-edge-edge, safe-edge-server)." + +# ── 5. Verify DDS connectivity ──────────────────────────────────────────────── +# +# Checks in logical order: +# [1] safety ↔ non-safety (guest-guest, within hypervisor) +# [2] safety ↔ edge (safety domain → Docker) +# [3] edge ↔ server (Docker ↔ Docker) +# [4] non-safety ↔ server (non-safety domain → Docker) + +log "Waiting for DDS connectivity (up to ${VERIFY_TIMEOUT}s)..." +GUEST_GUEST_OK=0 +SAFETY_EDGE_OK=0 +EDGE_SERVER_OK=0 +NS_SERVER_OK=0 +DEADLINE=$(( $(date +%s) + VERIFY_TIMEOUT )) + +_edge_id() { docker ps -q --filter name=^safe-edge-edge$ 2>/dev/null | head -1; } + +while [[ $(date +%s) -le ${DEADLINE} ]]; do + + # [1] safety ↔ non-safety + # Signal: cloud_gateway published ServerAvailabilityStatus to policy_engine reader + # → "Publication matched on safe_edge.internal.server_availability_status" + if [[ "${GUEST_GUEST_OK}" -eq 0 ]]; then + if ssh_guest "${NON_SAFETY_CTL}" "${NON_SAFETY_IP}" \ + "grep -q 'Publication matched on safe_edge.internal.server_availability_status' \ + /data/safe-edge-non-safety/logs/safe_edge_cloud_gateway.log 2>/dev/null"; then + GUEST_GUEST_OK=1 + ok "[1] safety ↔ non-safety: connected (cloud_gateway→ServerAvailabilityStatus matched policy_engine reader)" + fi + fi + + # [2] safety ↔ edge + # Signal: edge reader matched safety writer on vehicle_edge_summary + # → "Subscription matched on safe_edge.edge.vehicle_edge_summary" + if [[ "${SAFETY_EDGE_OK}" -eq 0 ]]; then + local_edge_id="$(_edge_id)" + if [[ -n "${local_edge_id}" ]]; then + if docker logs "${local_edge_id}" 2>/dev/null \ + | grep -q "Subscription matched on safe_edge.edge.vehicle_edge_summary"; then + SAFETY_EDGE_OK=1 + ok "[2] safety ↔ edge: connected (edge subscribed to VehicleEdgeSummary from safety)" + fi + fi + fi + + # [3] edge ↔ server + # Signal: edge publishes EdgeGatewayStatus=OK (server heartbeat received, charger_locations matched) + if [[ "${EDGE_SERVER_OK}" -eq 0 ]]; then + local_edge_id="$(_edge_id)" + if [[ -n "${local_edge_id}" ]]; then + if docker logs "${local_edge_id}" 2>/dev/null \ + | grep -q "EdgeGatewayStatus status=OK"; then + EDGE_SERVER_OK=1 + ok "[3] edge ↔ server: connected (EdgeGatewayStatus=OK)" + fi + fi + fi + + # [4] non-safety ↔ server + # Signal: cloud_gateway received charger_locations from server and published ServerAvailabilityStatus=true + if [[ "${NS_SERVER_OK}" -eq 0 ]]; then + if ssh_guest "${NON_SAFETY_CTL}" "${NON_SAFETY_IP}" \ + "grep -q 'server_available=true' \ + /data/safe-edge-non-safety/logs/safe_edge_cloud_gateway.log 2>/dev/null"; then + NS_SERVER_OK=1 + ok "[4] non-safety ↔ server: connected (ServerAvailabilityStatus server_available=true)" + fi + fi + + [[ "${GUEST_GUEST_OK}" -eq 1 && "${SAFETY_EDGE_OK}" -eq 1 && \ + "${EDGE_SERVER_OK}" -eq 1 && "${NS_SERVER_OK}" -eq 1 ]] && break + sleep 3 +done + +# ── Result ─────────────────────────────────────────────────────────────────── + +echo "" +echo "──────────────────────────────────────────────────────" +ALL_OK=0 +[[ "${GUEST_GUEST_OK}" -eq 1 && "${SAFETY_EDGE_OK}" -eq 1 && \ + "${EDGE_SERVER_OK}" -eq 1 && "${NS_SERVER_OK}" -eq 1 ]] && ALL_OK=1 + +if [[ "${ALL_OK}" -eq 1 ]]; then + echo "ALL SYSTEMS CONNECTED" +else + [[ "${GUEST_GUEST_OK}" -eq 0 ]] && fail "[1] safety ↔ non-safety: NOT connected" + [[ "${SAFETY_EDGE_OK}" -eq 0 ]] && fail "[2] safety ↔ edge: NOT connected" + [[ "${EDGE_SERVER_OK}" -eq 0 ]] && fail "[3] edge ↔ server: NOT connected" + [[ "${NS_SERVER_OK}" -eq 0 ]] && fail "[4] non-safety ↔ server: NOT connected" +fi +echo "──────────────────────────────────────────────────────" +echo "" +echo "Logs:" +echo " Hypervisor : ${HV_LOG}" +echo " Edge : ${EDGE_LOG}" +echo " Server : ${SERVER_LOG}" +echo " Safety : ssh -o ControlPath=${SAFETY_CTL} root@${SAFETY_IP}" +echo " Non-safety : ssh -o ControlPath=${NON_SAFETY_CTL} root@${NON_SAFETY_IP}" +[[ "${ALL_OK}" -eq 0 ]] && exit 1 +echo "" +echo "Stop with: bash scripts/launch_all.sh --stop" diff --git a/scripts/launch_fast_edge_test.sh b/scripts/launch_fast_edge_test.sh index ba5be0e..8ea73ac 100755 --- a/scripts/launch_fast_edge_test.sh +++ b/scripts/launch_fast_edge_test.sh @@ -4,54 +4,92 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -IMAGE="safe-edge-edge:fast-test" LOG_DIR="${SCRIPT_DIR}/logs" -LOG_FILE="${LOG_DIR}/launch_fast_edge_test.log" +OPT_TEST=0 usage() { cat <&2; usage >&2; exit 1 ;; esac done -mkdir -p "${LOG_DIR}" +# Remove stale FastDDS shared-memory artifacts. +rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true + +if [[ "${OPT_TEST}" -eq 1 ]]; then + IMAGE="safe-edge-edge:fast-test" + LOG_FILE="${LOG_DIR}/launch_fast_edge_test.log" + mkdir -p "${LOG_DIR}" + + if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then + echo "[edge] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests + fi + + echo "[edge] Running integration test..." + echo "[edge] Log: ${LOG_FILE}" + + set +e + docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" + TEST_EXIT=${PIPESTATUS[0]} + set -e + + if [[ ${TEST_EXIT} -eq 0 ]]; then + echo "[edge] PASSED" + else + echo "[edge] FAILED (exit ${TEST_EXIT})" >&2 + fi + exit "${TEST_EXIT}" +fi + +# Service mode +IMAGE="safe-edge-edge:fast" if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then - echo "[launch_fast_edge_test] Image ${IMAGE} not found — building..." - bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests + echo "[edge] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" fi -# Remove stale FastDDS shared-memory artifacts that can cause false failures. -sudo rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true +# Auto-detect virbr0 IP (Linux↔QEMU bridge) when not overridden. +: "${SAFE_EDGE_OWN_IP:=$(ip -o -f inet addr show virbr0 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)}" +: "${SAFE_EDGE_OWN_IP:=127.0.0.1}" +# Safety guest IP and non-safety guest IP are fixed by the hypervisor split configuration. +: "${SAFE_EDGE_SAFETY_IP:=192.168.10.2}" +: "${SAFE_EDGE_NON_SAFETY_IP:=192.168.20.2}" +# Build explicit peer list (8011 = cloud_gateway, lives in non-safety). +: "${SAFE_EDGE_INITIAL_PEERS:=${SAFE_EDGE_SAFETY_IP}:8001,${SAFE_EDGE_SAFETY_IP}:8002,${SAFE_EDGE_NON_SAFETY_IP}:8011,${SAFE_EDGE_OWN_IP}:8020}" -echo "[launch_fast_edge_test] Running edge integration test..." -echo "[launch_fast_edge_test] Log: ${LOG_FILE}" +DOCKER_ENV_ARGS=( + -e "SAFE_EDGE_OWN_IP=${SAFE_EDGE_OWN_IP}" + -e "SAFE_EDGE_SAFETY_IP=${SAFE_EDGE_SAFETY_IP}" + -e "SAFE_EDGE_NON_SAFETY_IP=${SAFE_EDGE_NON_SAFETY_IP}" + -e "SAFE_EDGE_INITIAL_PEERS=${SAFE_EDGE_INITIAL_PEERS}" +) +[[ -n "${SAFE_EDGE_CROSS_DOMAIN_IP:-}" ]] && DOCKER_ENV_ARGS+=(-e "SAFE_EDGE_CROSS_DOMAIN_IP=${SAFE_EDGE_CROSS_DOMAIN_IP}") -set +e -docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" -TEST_EXIT=${PIPESTATUS[0]} -set -e +CONTAINER_NAME="safe-edge-edge" +docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true -if [[ ${TEST_EXIT} -eq 0 ]]; then - echo "[launch_fast_edge_test] PASSED" -else - echo "[launch_fast_edge_test] FAILED (exit ${TEST_EXIT})" >&2 -fi +echo "[edge] Starting safe_edge_edge_gateway (Ctrl+C to stop)" +echo "[edge] OWN_IP=${SAFE_EDGE_OWN_IP} INITIAL_PEERS=${SAFE_EDGE_INITIAL_PEERS}" -exit "${TEST_EXIT}" +exec docker run --name "${CONTAINER_NAME}" --network host "${DOCKER_ENV_ARGS[@]}" "${IMAGE}" diff --git a/scripts/launch_fast_server_test.sh b/scripts/launch_fast_server_test.sh index 4aa7be5..c79ca16 100755 --- a/scripts/launch_fast_server_test.sh +++ b/scripts/launch_fast_server_test.sh @@ -4,53 +4,90 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -IMAGE="safe-edge-server:fast-test" LOG_DIR="${SCRIPT_DIR}/logs" -LOG_FILE="${LOG_DIR}/launch_fast_server_test.log" +OPT_TEST=0 usage() { cat <&2; usage >&2; exit 1 ;; esac done -mkdir -p "${LOG_DIR}" +# Remove stale FastDDS shared-memory artifacts. +rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true + +if [[ "${OPT_TEST}" -eq 1 ]]; then + IMAGE="safe-edge-server:fast-test" + LOG_FILE="${LOG_DIR}/launch_fast_server_test.log" + mkdir -p "${LOG_DIR}" + + if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then + echo "[server] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests + fi + + echo "[server] Running integration test..." + echo "[server] Log: ${LOG_FILE}" + + set +e + docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" + TEST_EXIT=${PIPESTATUS[0]} + set -e + + if [[ ${TEST_EXIT} -eq 0 ]]; then + echo "[server] PASSED" + else + echo "[server] FAILED (exit ${TEST_EXIT})" >&2 + fi + exit "${TEST_EXIT}" +fi + +# Service mode +IMAGE="safe-edge-server:fast" if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then - echo "[launch_fast_server_test] Image ${IMAGE} not found — building..." - bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests + echo "[server] Image ${IMAGE} not found — building..." + bash "${SCRIPT_DIR}/build_ubuntu.sh" fi -# Remove stale FastDDS shared-memory artifacts that can cause false failures. -sudo rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true +# Auto-detect virbr0 IP (Linux↔QEMU bridge) when not overridden. +: "${SAFE_EDGE_OWN_IP:=$(ip -o -f inet addr show virbr0 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)}" +: "${SAFE_EDGE_OWN_IP:=127.0.0.1}" +# Non-safety guest IP is fixed by the hypervisor split configuration. +: "${SAFE_EDGE_NON_SAFETY_IP:=192.168.20.2}" +# Build explicit peer list if not already provided. +: "${SAFE_EDGE_INITIAL_PEERS:=${SAFE_EDGE_NON_SAFETY_IP}:8011,${SAFE_EDGE_OWN_IP}:8030}" -echo "[launch_fast_server_test] Running server integration test..." -echo "[launch_fast_server_test] Log: ${LOG_FILE}" +DOCKER_ENV_ARGS=( + -e "SAFE_EDGE_OWN_IP=${SAFE_EDGE_OWN_IP}" + -e "SAFE_EDGE_NON_SAFETY_IP=${SAFE_EDGE_NON_SAFETY_IP}" + -e "SAFE_EDGE_INITIAL_PEERS=${SAFE_EDGE_INITIAL_PEERS}" +) +[[ -n "${SAFE_EDGE_CROSS_DOMAIN_IP:-}" ]] && DOCKER_ENV_ARGS+=(-e "SAFE_EDGE_CROSS_DOMAIN_IP=${SAFE_EDGE_CROSS_DOMAIN_IP}") -set +e -docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" -TEST_EXIT=${PIPESTATUS[0]} -set -e +CONTAINER_NAME="safe-edge-server" +docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true -if [[ ${TEST_EXIT} -eq 0 ]]; then - echo "[launch_fast_server_test] PASSED" -else - echo "[launch_fast_server_test] FAILED (exit ${TEST_EXIT})" >&2 -fi +echo "[server] Starting safe_edge_server (Ctrl+C to stop)" +echo "[server] OWN_IP=${SAFE_EDGE_OWN_IP} INITIAL_PEERS=${SAFE_EDGE_INITIAL_PEERS}" -exit "${TEST_EXIT}" +exec docker run --name "${CONTAINER_NAME}" --network host "${DOCKER_ENV_ARGS[@]}" "${IMAGE}" diff --git a/scripts/launch_hypervisor_split.sh b/scripts/launch_hypervisor_split.sh index ea3900c..77a2821 100644 --- a/scripts/launch_hypervisor_split.sh +++ b/scripts/launch_hypervisor_split.sh @@ -48,6 +48,13 @@ OPT_NO_REBUILD=0 OPT_NO_RUN=0 OPT_STOP=0 +# Detect the Linux→QEMU bridge subnet so guests can route back to the Linux host. +# virbr0 (libvirt NAT bridge) is the standard QEMU bridge on this machine. +_BRIDGE_SUBNET="$(ip -o -f inet addr show virbr0 2>/dev/null | awk '{print $4}' | head -1)" +: "${_BRIDGE_SUBNET:=192.168.122.0/24}" +_BRIDGE_HOST_IP="$(ip -o -f inet addr show virbr0 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)" +: "${_BRIDGE_HOST_IP:=192.168.122.1}" + SAFETY_NODES=( safe_edge_vehicle_mock safe_edge_safety_io_adapters @@ -278,9 +285,11 @@ _refresh_guest_files() { fi first=0 if [[ "${env_prefix}" == "safety" ]]; then - echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${NON_SAFETY_GUEST_IP} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + local _safety_peers="${SAFETY_GUEST_IP}:8001,${SAFETY_GUEST_IP}:8002,${NON_SAFETY_GUEST_IP}:8011,${_BRIDGE_HOST_IP}:8020,${_BRIDGE_HOST_IP}:8030" + echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${NON_SAFETY_GUEST_IP} SAFE_EDGE_HOST_IP=${_BRIDGE_HOST_IP} SAFE_EDGE_INITIAL_PEERS=${_safety_peers} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" else - echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${SAFETY_GUEST_IP} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" + local _non_safety_peers="${NON_SAFETY_GUEST_IP}:8011,${NON_SAFETY_GUEST_IP}:8012,${SAFETY_GUEST_IP}:8001,${SAFETY_GUEST_IP}:8002,${_BRIDGE_HOST_IP}:8020,${_BRIDGE_HOST_IP}:8030" + echo "SAFE_EDGE_OWN_IP=${guest_ip} SAFE_EDGE_CROSS_DOMAIN_IP=${SAFETY_GUEST_IP} SAFE_EDGE_HOST_IP=${_BRIDGE_HOST_IP} SAFE_EDGE_INITIAL_PEERS=${_non_safety_peers} /system/bin/${name} > \"\${LOG_DIR}/${name}.log\" 2>&1 &" fi echo "echo \$! > \"\${LOG_DIR}/${name}.pid\"" done @@ -353,6 +362,7 @@ EOF # Generated by scripts/launch_hypervisor_split.sh route add -net 224.0.0.0/4 vtnet0 route add -net ${cross_subnet} ${gateway_ip} +route add -net ${_BRIDGE_SUBNET} ${gateway_ip} ${seed_line} mkdir -p /data/ssh echo "${pubkey}" > /data/ssh/authorized_keys @@ -667,6 +677,16 @@ echo "Waiting for hypervisor host IP address..." HOST_IP="$(_get_host_ip_address)" echo "Hypervisor host is up: ${HOST_IP}" +# Detect the Linux bridge IP (source IP used to reach the QNX hypervisor host). +# Docker containers with --network host will bind to this IP, so DDS nodes +# in the guests must use it as the peer IP for server (8020) and edge (8030). +LINUX_BRIDGE_IP="$(ip route get "${HOST_IP}" 2>/dev/null | awk '/src/{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}')" +if [[ -z "${LINUX_BRIDGE_IP}" ]]; then + echo "Warning: could not detect Linux bridge IP for host→${HOST_IP}; defaulting to 127.0.0.1" >&2 + LINUX_BRIDGE_IP="127.0.0.1" +fi +echo "Linux bridge IP (SAFE_EDGE_HOST_IP): ${LINUX_BRIDGE_IP}" + echo "Waiting for safety guest SSH reachability at ${SAFETY_GUEST_IP}..." _wait_for_guest_ssh "${HOST_IP}" "${SAFETY_GUEST_IP}" "${SAFETY_GUEST_CTL}" "${SAFETY_HOST_LOG}" "${SAFETY_HOST_LINK_IP}" echo "Safety guest is reachable over SSH." From be0ba0f8578090d326fb6974bea555fc6bca3775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 11 Jun 2026 12:53:25 +0200 Subject: [PATCH 14/17] Scripts' improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- scripts/launch_all.sh | 10 ++- ..._fast_edge_test.sh => launch_fast_edge.sh} | 6 +- ...t_server_test.sh => launch_fast_server.sh} | 6 +- scripts/launch_tests.sh | 62 +++++++++++++++++++ 4 files changed, 76 insertions(+), 8 deletions(-) rename scripts/{launch_fast_edge_test.sh => launch_fast_edge.sh} (95%) rename scripts/{launch_fast_server_test.sh => launch_fast_server.sh} (94%) create mode 100755 scripts/launch_tests.sh diff --git a/scripts/launch_all.sh b/scripts/launch_all.sh index a83ee16..4d31d60 100755 --- a/scripts/launch_all.sh +++ b/scripts/launch_all.sh @@ -129,8 +129,8 @@ ok "Guests are up." # ── 4. Start edge and server ────────────────────────────────────────────────── log "Starting edge and server..." -SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_EDGE}" bash "${SCRIPT_DIR}/launch_fast_edge_test.sh" > "${EDGE_LOG}" 2>&1 & -SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_SERVER}" bash "${SCRIPT_DIR}/launch_fast_server_test.sh" > "${SERVER_LOG}" 2>&1 & +SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_EDGE}" bash "${SCRIPT_DIR}/launch_fast_edge.sh" > "${EDGE_LOG}" 2>&1 & +SAFE_EDGE_INITIAL_PEERS="${SAFE_EDGE_INITIAL_PEERS_SERVER}" bash "${SCRIPT_DIR}/launch_fast_server.sh" > "${SERVER_LOG}" 2>&1 & # Wait for both named containers to be running. DEADLINE=$(( $(date +%s) + 30 )) @@ -242,6 +242,12 @@ echo " Edge : ${EDGE_LOG}" echo " Server : ${SERVER_LOG}" echo " Safety : ssh -o ControlPath=${SAFETY_CTL} root@${SAFETY_IP}" echo " Non-safety : ssh -o ControlPath=${NON_SAFETY_CTL} root@${NON_SAFETY_IP}" +echo "" +echo "Live log hints:" +echo " Server : docker logs -f safe-edge-server" +echo " Edge : docker logs -f safe-edge-edge" +echo " Safety : ssh -o ControlPath=${SAFETY_CTL} root@${SAFETY_IP} 'tail -f /data/safe-edge-safety/logs/*.log | grep -v \"^==> .* <==$\"'" +echo " Non-safety : ssh -o ControlPath=${NON_SAFETY_CTL} root@${NON_SAFETY_IP} 'tail -f /data/safe-edge-non-safety/logs/*.log | grep -v \"^==> .* <==$\"'" [[ "${ALL_OK}" -eq 0 ]] && exit 1 echo "" echo "Stop with: bash scripts/launch_all.sh --stop" diff --git a/scripts/launch_fast_edge_test.sh b/scripts/launch_fast_edge.sh similarity index 95% rename from scripts/launch_fast_edge_test.sh rename to scripts/launch_fast_edge.sh index 8ea73ac..ce39b63 100755 --- a/scripts/launch_fast_edge_test.sh +++ b/scripts/launch_fast_edge.sh @@ -9,7 +9,7 @@ OPT_TEST=0 usage() { cat </dev/null || true if [[ "${OPT_TEST}" -eq 1 ]]; then IMAGE="safe-edge-edge:fast-test" - LOG_FILE="${LOG_DIR}/launch_fast_edge_test.log" + LOG_FILE="${LOG_DIR}/launch_fast_edge.log" mkdir -p "${LOG_DIR}" if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then diff --git a/scripts/launch_fast_server_test.sh b/scripts/launch_fast_server.sh similarity index 94% rename from scripts/launch_fast_server_test.sh rename to scripts/launch_fast_server.sh index c79ca16..e91ea7e 100755 --- a/scripts/launch_fast_server_test.sh +++ b/scripts/launch_fast_server.sh @@ -9,7 +9,7 @@ OPT_TEST=0 usage() { cat </dev/null || true if [[ "${OPT_TEST}" -eq 1 ]]; then IMAGE="safe-edge-server:fast-test" - LOG_FILE="${LOG_DIR}/launch_fast_server_test.log" + LOG_FILE="${LOG_DIR}/launch_fast_server.log" mkdir -p "${LOG_DIR}" if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then diff --git a/scripts/launch_tests.sh b/scripts/launch_tests.sh new file mode 100755 index 0000000..f098b0c --- /dev/null +++ b/scripts/launch_tests.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +OPT_NO_REBUILD=0 + +usage() { + cat <&2; usage >&2; exit 1 ;; + esac +done + +FAILURES=0 + +run_test() { + local label="${1}" + shift + echo "" + echo "=== ${label} ===" + if "$@"; then + echo "[launch_tests] PASS: ${label}" + else + echo "[launch_tests] FAIL: ${label}" >&2 + FAILURES=$((FAILURES + 1)) + fi +} + +TPI_ARGS=() +if [[ "${OPT_NO_REBUILD}" -eq 1 ]]; then + TPI_ARGS+=(--no-rebuild) +fi + +run_test "Fast DDS server" bash "${SCRIPT_DIR}/launch_fast_server.sh" --test +run_test "Fast DDS edge" bash "${SCRIPT_DIR}/launch_fast_edge.sh" --test +run_test "TPI 2.3" bash "${SCRIPT_DIR}/launch_tpi_2_3_test.sh" +run_test "TPI 2.1" bash "${SCRIPT_DIR}/launch_tpi_2_1_test.sh" "${TPI_ARGS[@]}" +run_test "TPI 2.2" bash "${SCRIPT_DIR}/launch_tpi_2_2_test.sh" "${TPI_ARGS[@]}" +run_test "TPI 2.5" bash "${SCRIPT_DIR}/launch_tpi_2_5_test.sh" "${TPI_ARGS[@]}" +run_test "TPI 2.6" bash "${SCRIPT_DIR}/launch_tpi_2_6_test.sh" "${TPI_ARGS[@]}" + +echo "" +if [[ "${FAILURES}" -eq 0 ]]; then + echo "[launch_tests] All tests passed." +else + echo "[launch_tests] ${FAILURES} test suite(s) failed." >&2 + exit 1 +fi From ed6dbc3ddae27e1f9570091a29c5c511a62b0430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Thu, 11 Jun 2026 14:59:12 +0200 Subject: [PATCH 15/17] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- README.md | 571 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 334 insertions(+), 237 deletions(-) diff --git a/README.md b/README.md index fa791d8..751cfbf 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,146 @@ # SafeEDGE -This repository is self-contained for SafeEDGE source code, QNX target definitions, build scripts, and test launchers. -It does not require the old `~/Safe/SAFE-EDGE` repository. +## 1. Purpose + +SafeEDGE contains the source code, QNX target definitions, Docker packaging, +build scripts, and test launchers required to build and run the current system +from this repository alone. + +This repository does **not** vendor: + +- QNX SDP 8 +- Safe-DDS source tree + +Those must exist outside the repository and be provided through environment +variables. + +## 2. Repository Structure + +The most relevant repository areas are: + +- `common_server/` + Shared server-side code and tests used by the server path. +- `fast_dds/` + Fast DDS based implementations, including: + - `fast_dds/server/` + - `fast_dds/edge/` + - `fast_dds/docker/` +- `safe_dds/` + SafeDDS based implementations, including: + - `safe_dds/server/` + - `safe_dds/edge/` + - `safe_dds/safety/` + - `safe_dds/non_safety/` +- `idl/` + Shared IDL definitions. +- `qnx/` + QNX toolchains, targets, hypervisor artifacts, and guest definitions. +- `scripts/` + Build scripts, launchers, test launchers, setup helpers, and utilities. + +Use `scripts/` for operational entry points, `qnx/` for QNX/hypervisor layout, +`fast_dds/` for Docker/Fast DDS code, and `safe_dds/` for SafeDDS/QNX code. + +## 3. System Architecture and Endpoints + +### Runtime groups + +| Group | Runtime | Main process(es) | +|---|---|---| +| `server` | Docker container `safe-edge-server` | `safe_edge_server` | +| `edge` | Docker container `safe-edge-edge` | `safe_edge_edge_gateway` | +| `safety` | QNX guest VM | `safe_edge_vehicle_mock`, `safe_edge_safety_io_adapters`, `safe_edge_policy_engine` | +| `non-safety` | QNX guest VM | `safe_edge_cloud_gateway`, `safe_edge_ota_service`, `safe_edge_infotainment` | + +### High-level topology + +```text +Host Linux (virbr0, typically 192.168.122.1) +├── Docker container: safe-edge-server DDS port 8020 +├── Docker container: safe-edge-edge DDS port 8030 +└── QNX Hypervisor host + ├── safety guest 192.168.10.2 + │ ├── safety_io_adapters 8001 + │ ├── policy_engine 8002 + │ └── vehicle_mock 8003 + └── non-safety guest 192.168.20.2 + ├── cloud_gateway 8011 + ├── ota_service 8012 + └── infotainment 8013 +``` + +The QNX hypervisor host routes between: + +- `192.168.10.0/24` safety guest subnet +- `192.168.20.0/24` non-safety guest subnet +- host Linux bridge subnet, typically `192.168.122.0/24` + +### Interfaces and endpoints + +- Docker services run with `--network host` +- `server` participates on the host network stack at DDS participant port `8020` +- `edge` participates on the host network stack at DDS participant port `8030` +- safety guest IP: `192.168.10.2` +- non-safety guest IP: `192.168.20.2` +- SSH access to both guests is part of the normal operational flow + +### Current discovery configuration + +The current launcher-driven DDS discovery topology is: + +| Group | Current initial peers | +|---|---| +| `server` | `non-safety:8011`, `host:8030` | +| `edge` | `safety:8001`, `safety:8002`, `non-safety:8011`, `host:8020` | +| `safety` | `safety:8001,8002`, `non-safety:8011`, `host:8020,8030` | +| `non-safety` | `non-safety:8011,8012`, `safety:8001,8002`, `host:8020,8030` | + +This is the current discovery setup, not a protocol-level guarantee. + +## 4. Prerequisites + +### QNX / Safe-DDS + +Required to build QNX artifacts: + +- QNX SDP 8 installed on the host +- Safe-DDS source tree available on the host +- `QNX_SDP_ROOT` exported or available at `/home/$USER/qnx800` +- `SAFE_DDS_PATH` exported and pointing to the Safe-DDS source tree + +### Host tools + +- `cmake` +- host C/C++ toolchain +- `docker` +- `qemu-system-x86_64` +- `sshpass` +- `bridge-utils` (`brctl`) +- `file` -It does not vendor third-party SDKs or source trees. QNX SDP 8 and Safe-DDS source code must be installed/provided outside this repository and pointed to with environment variables. +### Linux development packages -## Running scripts +- `libcurl` development package for Linux-side builds/tests -All scripts live under `scripts/` and resolve paths relative to their own location. -**Always run them from inside the `scripts/` directory:** +Ubuntu/Debian: ```bash -cd scripts -bash .sh [options] +sudo apt install libcurl4-openssl-dev ``` -They also work when invoked from the repository root with a prefix (`bash scripts/.sh`), but the canonical form is from inside `scripts/`. +### Helper script -## Customer Quick Start - -For Ubuntu/Debian hosts, install the host packages that can be installed automatically: +Install what the repository can install automatically: ```bash -cd scripts -bash install_host_deps.sh +bash scripts/install_host_deps.sh ``` -Provide the two external inputs that are not stored in this repository: +This helper does **not** install QNX SDP or Safe-DDS. + +## 5. Configuration + +### Required build environment ```bash export QNX_SDP_ROOT="/path/to/qnx800" @@ -35,353 +149,336 @@ export QNX_TARGET="$QNX_SDP_ROOT/target/qnx" export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" ``` -`scripts/env.example` contains the same variables as a source-able template. - -Check the environment: +An example template is available at: ```bash -bash check_setup.sh +scripts/env.example ``` -Build and test (QNX): +Validate the environment: ```bash -cd scripts -bash build_safedds_qnx.sh -- -j2 -bash build_qnx.sh --idl -- -j2 -bash launch_tpi_2_3_test.sh -bash launch_tpi_2_1_test.sh -bash launch_tpi_2_2_test.sh -bash launch_tpi_2_5_test.sh +bash scripts/check_setup.sh ``` -## Hypervisor Path +### Runtime topology variables -Launch vehicle-side nodes inside a QNX Hypervisor guest (Linux host → QNX host VM → QNX guest VM): +The integrated stack currently relies mainly on: ```bash -bash scripts/launch_hypervisor_nodes.sh -``` +# Host-side announced IP, usually detected from virbr0 +SAFE_EDGE_OWN_IP=192.168.122.1 -Stop the hypervisor host VM: +# Fixed guest IPs in the current split hypervisor topology +SAFE_EDGE_SAFETY_IP=192.168.10.2 +SAFE_EDGE_NON_SAFETY_IP=192.168.20.2 -```bash -bash scripts/launch_hypervisor_nodes.sh --stop +# Explicit DDS discovery peers +SAFE_EDGE_INITIAL_PEERS=192.168.10.2:8001,192.168.20.2:8011,192.168.122.1:8020 ``` -Prerequisites: same host-side tools as the plain QEMU path. The launcher builds -the guest image first and the host image second. `--no-rebuild` reuses existing -artifacts. +Depending on binary family and launcher path, backward-compatible fallback +variables may still exist, but the current integrated stack is driven primarily +by: + +- `SAFE_EDGE_OWN_IP` +- `SAFE_EDGE_INITIAL_PEERS` -On successful launch, SSH access hints for host and guest are printed. +### Pilot server API key -What is committed vs generated: -- Committed: `qnx/targets/qemu-qnx800-x86_64-hypervisor/` and `qnx/targets/qvm-safe-edge-qnx800-x86_64/` skeletons (`options`, wrappers, stable snippets) -- Generated at runtime: `data_files.custom`, `system_files.custom`, `ifs_start.custom`, `post_start.custom`, `output/` +The server-side Pilot client does **not** take the API key from an environment +variable and it is not hardcoded in the source tree. -Build and test (FastDDS / Docker): +Both the Fast DDS server and the SafeDDS server read it from: ```bash -bash build_ubuntu.sh --tests -bash launch_fast_server_test.sh -bash launch_fast_edge_test.sh +/etc/safe-edge/server.ini ``` -For Linux-only validation of the common server component: +Expected format: -```bash -bash install_host_deps.sh --linux-only -bash check_setup.sh --linux-only -bash launch_tpi_2_3_test.sh -``` - -## Repository Paths - -- Shared IDL sources: `idl/` -- Safe DDS generated headers: `safe_dds/idl/` -- Shared server code: `common_server/` -- Safe DDS server: `safe_dds/server/` -- Safe DDS edge: `safe_dds/edge/` -- Safe DDS safety domain: `safe_dds/safety/` -- Safe DDS non-safety domain: `safe_dds/non_safety/` -- FastDDS server: `fast_dds/server/` -- FastDDS edge: `fast_dds/edge/` -- FastDDS generated headers: `fast_dds/idl/` -- FastDDS Dockerfiles: `fast_dds/docker/` -- QNX toolchain file: `qnx/toolchains/qnx8.cmake` -- Safe DDS QNX build script: `scripts/build_safedds_qnx.sh` -- Generated Safe DDS QNX package: `qnx/install/safedds-qnx8-x86_64/safedds` -- Bundled QNX QEMU target: `qnx/targets/qemu-qnx800-x86_64` -- Scripts: `scripts/` -- Test logs: `scripts/logs/` - -## Versioned QNX Assets - -These files are needed by the build and QNX test scripts and are intended to be kept in this repository: - -- `qnx/toolchains/qnx8.cmake` -- `scripts/build_safedds_qnx.sh` -- `qnx/targets/qemu-qnx800-x86_64/mkqnximage-wrapper.sh` -- `qnx/targets/qemu-qnx800-x86_64/local/options` -- `qnx/targets/qemu-qnx800-x86_64/local/valgrind.files` -- stable snippets under `qnx/targets/qemu-qnx800-x86_64/local/snippets/` - -These files are generated and should not be committed: - -- `qnx/build/` -- `qnx/install/` -- `qnx/targets/qemu-qnx800-x86_64/output/` -- `scripts/logs/` -- `qnx/targets/qemu-qnx800-x86_64/local/misc_files/*` -- `qnx/targets/qemu-qnx800-x86_64/local/ssh-ident` -- `qnx/targets/qemu-qnx800-x86_64/local/snippets/ifs_start.custom` -- `qnx/targets/qemu-qnx800-x86_64/local/snippets/post_start.custom` -- `qnx/targets/qemu-qnx800-x86_64/local/snippets/system_files.custom` - -The `misc_files` entries include QNX VM keys, `shadow`, and other local image files. The three generated snippets above are rewritten by `launch_tpi_2_1_test.sh` and `launch_tpi_2_2_test.sh`. - -## External Prerequisites - -### Required to build QNX targets - -- QNX SDP 8 installed on the host -- Safe-DDS source tree available on the host -- `SAFE_DDS_PATH` exported and pointing to the Safe-DDS source tree -- Default SDK path used by scripts if `QNX_SDP_ROOT` is not set: `/home/$USER/qnx800` -- Required SDK files/directories: - - `$QNX_SDP_ROOT/qnxsdp-env.sh` - - `$QNX_SDP_ROOT/host/linux/x86_64` - - `$QNX_SDP_ROOT/target/qnx` -- `cmake` -- a C/C++ build toolchain usable by CMake on the host -- Internet access during configure time if GTest is not already available locally +```ini +[pilot_server] +api_key = +``` -`scripts/install_host_deps.sh` installs the host packages listed above where possible. It does not install QNX SDP 8 or Safe-DDS sources. +Notes: -### Required to run QNX VM tests +- the key must live under the `[pilot_server]` section +- if the file is missing, server code that talks to the Pilot backend will log: + `Cannot open config file: /etc/safe-edge/server.ini` +- some real Pilot backend tests are skipped automatically when that file is not + present -- `qemu-system-x86_64` -- `sshpass` -- `brctl` from `bridge-utils` -- `file` +## 6. Build -### Required to build and run FastDDS Docker tests +### Docker / Fast DDS side -- Docker +```bash +bash scripts/build_ubuntu.sh +``` -FastDDS is provided by the base Docker image (`eprosima/vulcanexus:kilted-base`); no separate FastDDS installation is required on the host. +### QNX side -### Required to run the Linux test +```bash +bash scripts/build_qnx.sh --idl -- -j2 +``` -- `cmake` -- `libcurl` development package - - Ubuntu/Debian: `sudo apt install libcurl4-openssl-dev` +### SafeDDS QNX package only -## Environment Variables +```bash +bash scripts/build_safedds_qnx.sh +``` -Required for QNX builds: +### Recommended full build sequence ```bash -export QNX_SDP_ROOT="/home/$USER/qnx800" -export QNX_HOST="$QNX_SDP_ROOT/host/linux/x86_64" -export QNX_TARGET="$QNX_SDP_ROOT/target/qnx" -export SAFE_DDS_PATH="/path/to/Safe-DDS-source-release" +bash scripts/build_ubuntu.sh +bash scripts/build_qnx.sh --idl -- -j2 ``` -Optional variables: +## 7. Launchers + +Run all scripts from the repository root: ```bash -export QNX_ARCH="x86_64" -export CMAKE_BUILD_TYPE="Release" +bash scripts/.sh [options] ``` -## Pilot Server API Key +### Launcher overview -Requests to the Pilot Server read the API key from `/etc/safe-edge/server.ini`. -The key is not stored in this repository and should not be committed. +| Goal | Script | Notes | +|---|---|---| +| Launch full integrated stack | `scripts/launch_all.sh` | Main current end-to-end launcher | +| Stop full integrated stack | `scripts/launch_all.sh --stop` | Stops Docker services and split hypervisor stack | +| Launch Docker server service | `scripts/launch_fast_server.sh` | Service mode by default | +| Launch Docker edge service | `scripts/launch_fast_edge.sh` | Service mode by default | +| Run Fast DDS server integration test | `scripts/launch_fast_server.sh --test` | Docker test mode | +| Run Fast DDS edge integration test | `scripts/launch_fast_edge.sh --test` | Docker test mode | +| Launch split QNX hypervisor stack | `scripts/launch_hypervisor_split.sh` | Safety guest + non-safety guest | +| Stop split QNX hypervisor stack | `scripts/launch_hypervisor_split.sh --stop` | Stops hypervisor/QNX split flow | +| Launch single-guest QNX baseline | `scripts/launch_hypervisor_nodes.sh` | Alternative/baseline launcher | +| Run all wired tests | `scripts/launch_tests.sh` | Aggregates Docker tests and TPI launchers | -Example file: +### Full stack launcher -```ini -[pilot_server] -api_key = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +```bash +bash scripts/launch_all.sh [--no-rebuild] ``` -If the file does not exist, the real Pilot Server checks in the Linux test are skipped. +`launch_all.sh`: + +1. stops previous SafeEDGE instances +2. optionally rebuilds Docker and QNX artifacts +3. starts the split hypervisor guests +4. starts Docker `server` and `edge` +5. verifies DDS connectivity across all groups -## Setup Check +### Hypervisor launchers -Install Ubuntu/Debian host packages: +Split deployment: ```bash -cd scripts -bash install_host_deps.sh +bash scripts/launch_hypervisor_split.sh +bash scripts/launch_hypervisor_split.sh --no-rebuild +bash scripts/launch_hypervisor_split.sh --stop ``` -For only the Linux test dependencies: +Single-guest baseline: ```bash -bash install_host_deps.sh --linux-only +bash scripts/launch_hypervisor_nodes.sh ``` -Run: +## 8. Tests + +### Run all tests ```bash -bash check_setup.sh +bash scripts/launch_tests.sh +bash scripts/launch_tests.sh --no-rebuild ``` -What it does: +`launch_tests.sh` currently runs: -- checks for required host tools -- validates the expected QNX SDK path -- validates repo paths under `qnx/` -- validates that `SAFE_DDS_PATH` is defined for QNX builds -- reports local/generated QNX folders if they have not been created yet +- `launch_fast_server.sh --test` +- `launch_fast_edge.sh --test` +- `launch_tpi_2_3_test.sh` +- `launch_tpi_2_1_test.sh` +- `launch_tpi_2_2_test.sh` +- `launch_tpi_2_5_test.sh` +- `launch_tpi_2_6_test.sh` -For a Linux-only check: +### Run individual test launchers ```bash -bash check_setup.sh --linux-only +bash scripts/launch_fast_server.sh --test +bash scripts/launch_fast_edge.sh --test +bash scripts/launch_tpi_2_1_test.sh +bash scripts/launch_tpi_2_2_test.sh +bash scripts/launch_tpi_2_3_test.sh +bash scripts/launch_tpi_2_5_test.sh +bash scripts/launch_tpi_2_6_test.sh ``` -## Build +### Test intent -### QNX targets +- Fast DDS launchers with `--test` run Docker integration tests +- `launch_tpi_2_3_test.sh` exercises the Linux/common server path +- the QNX TPI launchers exercise QNX VM and SafeDDS flows -Build and install Safe DDS for QNX if `qnx/install/safedds-qnx8-x86_64/safedds` is missing: +## 9. Logs and Observability -```bash -cd scripts -bash build_safedds_qnx.sh -- -j2 -``` - -Build all QNX targets from this repository: +### Docker service logs ```bash -bash build_qnx.sh -- -j2 +docker logs -f safe-edge-server +docker logs -f safe-edge-edge ``` -If files under `idl/` changed, regenerate Safe DDS generated headers during the build: +### launch_all.sh logs ```bash -bash build_qnx.sh --idl -- -j2 +tail -f /tmp/safe-edge-hypervisor.log +tail -f /tmp/safe-edge-edge.log +tail -f /tmp/safe-edge-server.log ``` -This configures and installs: +### QNX guest access -- `common_server` -- `safe_dds/server` -- `safe_dds/edge` -- `safe_dds/safety` -- `safe_dds/non_safety` +After `launch_all.sh` or `launch_hypervisor_split.sh` succeeds: -Installed binaries end up in: - -- `common_server/install/server-common-qnx8-x86_64-Release/bin` -- `safe_dds/install/server-qnx8-x86_64-Release/bin` -- `safe_dds/install/edge-qnx8-x86_64-Release/bin` -- `safe_dds/install/safety-qnx8-x86_64-Release/bin` -- `safe_dds/install/non-safety-qnx8-x86_64-Release/bin` +```bash +ssh -o ControlPath=/tmp/ssh-guest-safety-ctl root@192.168.10.2 +ssh -o ControlPath=/tmp/ssh-guest-non-safety-ctl root@192.168.20.2 +``` -### FastDDS Docker images +### QNX guest logs -Build the runtime images: +Safety: ```bash -cd scripts -bash build_ubuntu.sh +ssh -o ControlPath=/tmp/ssh-guest-safety-ctl root@192.168.10.2 \ + 'tail -f /data/safe-edge-safety/logs/*.log | grep -v "^==> .* <==$"' ``` -Build runtime and test images: +Non-safety: ```bash -bash build_ubuntu.sh --tests +ssh -o ControlPath=/tmp/ssh-guest-non-safety-ctl root@192.168.20.2 \ + 'tail -f /data/safe-edge-non-safety/logs/*.log | grep -v "^==> .* <==$"' ``` -Images produced: +### Test logs -- `safe-edge-server:fast` -- `safe-edge-edge:fast` -- `safe-edge-server:fast-test` (with `--tests`) -- `safe-edge-edge:fast-test` (with `--tests`) +All test launchers write logs under: -## Test +```bash +scripts/logs/ +``` -All test scripts log their output under `scripts/logs/`. +## 10. Operational Flows -### KPI/TPI 2.3: Linux test +### Build and launch everything ```bash -cd scripts -bash launch_tpi_2_3_test.sh +bash scripts/build_ubuntu.sh +bash scripts/build_qnx.sh --idl -- -j2 +bash scripts/launch_all.sh --no-rebuild ``` -Log: `scripts/logs/launch_tpi_2_3.log` - -### TPI 2.1: QNX server test +### Run tests only ```bash -cd scripts -bash launch_tpi_2_1_test.sh +bash scripts/launch_tests.sh ``` -Log: `scripts/logs/launch_tpi_2_1.log` - -### TPI 2.2: QNX edge test +### Stop the stack ```bash -cd scripts -bash launch_tpi_2_2_test.sh +bash scripts/launch_all.sh --stop ``` -Log: `scripts/logs/launch_tpi_2_2.log` - -### TPI 2.5: QNX vehicle node smoke test +### Run one component only ```bash -cd scripts -bash launch_tpi_2_5_test.sh +bash scripts/launch_fast_server.sh +bash scripts/launch_fast_edge.sh +bash scripts/launch_hypervisor_split.sh ``` -This starts the SafeDDS safety and non-safety nodes on the QNX VM, verifies that all six processes stay alive during the smoke-test window, prints their logs, and stops the VM. +## 11. Troubleshooting -The underlying launcher can also leave the nodes running for manual inspection: +### `Cannot open config file: /etc/safe-edge/server.ini` -```bash -cd scripts -bash aux_vehicle_nodes.sh +Likely cause: + +- missing Pilot server config file for server-side backend access + +First thing to check: + +- create or verify `/etc/safe-edge/server.ini` +- ensure it contains: + +```ini +[pilot_server] +api_key = ``` -Logs: +### QNX build cannot start + +Likely cause: -- `scripts/logs/launch_tpi_2_5.log` -- `scripts/logs/aux_vehicle_nodes.log` +- missing or misconfigured `QNX_SDP_ROOT` +- missing `SAFE_DDS_PATH` -### FastDDS server integration test +First thing to check: ```bash -cd scripts -bash launch_fast_server_test.sh +bash scripts/check_setup.sh ``` -Log: `scripts/logs/launch_fast_server_test.log` +### Guests do not come up + +Likely cause: + +- QNX image build issue +- QEMU/hypervisor startup failure +- missing host dependencies such as `qemu-system-x86_64` or `sshpass` -Builds `safe-edge-server:fast-test` automatically if the image is not present. +First places to inspect: -### FastDDS edge integration test +- `/tmp/safe-edge-hypervisor.log` +- `bash scripts/check_setup.sh` + +### Docker services do not start + +Likely cause: + +- missing Docker image +- Docker not running +- bad runtime environment + +First places to inspect: ```bash -cd scripts -bash launch_fast_edge_test.sh +docker logs safe-edge-server +docker logs safe-edge-edge ``` -Log: `scripts/logs/launch_fast_edge_test.log` +### DDS connectivity does not converge + +Likely cause: -Builds `safe-edge-edge:fast-test` automatically if the image is not present. +- incorrect runtime topology variables +- guest routing issue +- missing peer in current discovery configuration -## Notes +First places to inspect: -- The QNX tests rebuild the QEMU image from `qnx/targets/qemu-qnx800-x86_64`. -- Generated target output is recreated under `qnx/targets/qemu-qnx800-x86_64/output/` and is ignored by git. -- The Linux test may execute real Pilot Server checks if `/etc/safe-edge/server.ini` exists on the host. -- The FastDDS Docker tests are self-contained: each test image spawns the component under test as a subprocess and communicates with it via DDS over the loopback interface. +- `bash scripts/launch_all.sh` +- `/tmp/safe-edge-hypervisor.log` +- `/tmp/safe-edge-server.log` +- `/tmp/safe-edge-edge.log` +- guest logs under `/data/safe-edge-safety/logs/` and + `/data/safe-edge-non-safety/logs/` From e7e4debcb2d8f5c3d6dfadc9497d1887d1ec73e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Mon, 15 Jun 2026 15:34:44 +0200 Subject: [PATCH 16/17] CI & update tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .github/workflows/ci-git.yaml | 58 +++- fast_dds/docker/server-test-entrypoint.sh | 8 + fast_dds/docker/server.test.Dockerfile | 4 +- safe_dds/safety/src/nodes/VehicleMockNode.cpp | 9 +- scripts/launch_fast_server.sh | 24 +- scripts/launch_tests.sh | 9 +- scripts/launch_tpi_2_1_test.sh | 67 +++- scripts/launch_tpi_2_2_test.sh | 66 +++- scripts/launch_tpi_2_5_test.sh | 276 +++++++++++---- scripts/launch_tpi_2_6_test.sh | 328 +++++++++++++----- 10 files changed, 687 insertions(+), 162 deletions(-) create mode 100644 fast_dds/docker/server-test-entrypoint.sh diff --git a/.github/workflows/ci-git.yaml b/.github/workflows/ci-git.yaml index 4bbceb4..c612752 100644 --- a/.github/workflows/ci-git.yaml +++ b/.github/workflows/ci-git.yaml @@ -36,5 +36,59 @@ jobs: with: ref: ${{ github.sha }} - - name: Hello World - run: echo "Hello World" + - name: Check prerequisites + run: | + ok=1 + check() { + if ! eval "$2" >/dev/null 2>&1; then + echo "MISSING: $1" >&2 + ok=0 + else + echo "OK: $1" + fi + } + + check "cmake" "command -v cmake" + check "g++" "command -v g++" + check "libcurl4-openssl-dev" "dpkg -s libcurl4-openssl-dev" + check "docker" "command -v docker" + check "python3" "command -v python3" + + [[ "${ok}" -eq 1 ]] || { echo "One or more prerequisites are missing. Aborting." >&2; exit 1; } + + - name: Build Ubuntu + run: bash scripts/build_native.sh + + - name: Inject pilot server config + env: + PILOT_API_KEY: ${{ secrets.PILOT_API_KEY }} + run: | + if [[ -z "${PILOT_API_KEY}" ]]; then + echo "Secret PILOT_API_KEY is not set or empty. Aborting." >&2 + exit 1 + fi + printf '[pilot_server]\napi_key = %s\n' "${PILOT_API_KEY}" \ + > /etc/safe-edge/server.ini + + - name: Test - Fast DDS server + env: + PILOT_API_KEY: ${{ secrets.PILOT_API_KEY }} + run: bash scripts/launch_fast_server.sh --test + + - name: Test - Fast DDS edge + run: bash scripts/launch_fast_edge.sh --test + + - name: Test - TPI 2.1 + run: bash scripts/launch_tpi_2_1_test.sh --linux --no-rebuild + + - name: Test - TPI 2.2 + run: bash scripts/launch_tpi_2_2_test.sh --linux --no-rebuild + + - name: Test - TPI 2.3 + run: bash scripts/launch_tpi_2_3_test.sh + + - name: Test - TPI 2.5 + run: bash scripts/launch_tpi_2_5_test.sh --linux --no-rebuild + + - name: Test - TPI 2.6 + run: bash scripts/launch_tpi_2_6_test.sh --linux --no-rebuild diff --git a/fast_dds/docker/server-test-entrypoint.sh b/fast_dds/docker/server-test-entrypoint.sh new file mode 100644 index 0000000..af4b36e --- /dev/null +++ b/fast_dds/docker/server-test-entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e +if [ -n "${PILOT_API_KEY:-}" ]; then + mkdir -p /etc/safe-edge + printf '[pilot_server]\napi_key = %s\n' "${PILOT_API_KEY}" \ + > /etc/safe-edge/server.ini +fi +exec "$@" diff --git a/fast_dds/docker/server.test.Dockerfile b/fast_dds/docker/server.test.Dockerfile index cf2dcf5..f60c42e 100644 --- a/fast_dds/docker/server.test.Dockerfile +++ b/fast_dds/docker/server.test.Dockerfile @@ -28,5 +28,7 @@ ENV FASTDDS_BUILTIN_TRANSPORTS=UDPv4 ENV SAFE_EDGE_FAST_SERVER_BIN=${SERVER_INSTALL_PREFIX}/bin/safe_edge_server COPY --from=build ${SERVER_INSTALL_PREFIX} ${SERVER_INSTALL_PREFIX} +COPY fast_dds/docker/server-test-entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh -ENTRYPOINT ["/opt/safe-edge/server/bin/test_server_integration"] +ENTRYPOINT ["/entrypoint.sh", "/opt/safe-edge/server/bin/test_server_integration"] diff --git a/safe_dds/safety/src/nodes/VehicleMockNode.cpp b/safe_dds/safety/src/nodes/VehicleMockNode.cpp index 3529f96..1345a94 100644 --- a/safe_dds/safety/src/nodes/VehicleMockNode.cpp +++ b/safe_dds/safety/src/nodes/VehicleMockNode.cpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace safe_edge { @@ -33,6 +34,12 @@ constexpr eprosima::safedds::execution::TimePeriod TIMEOUT = {0, 250'000'000}; static constexpr const char* INPUT_FILE_PATH = "/data/safe-edge-stage2/input.txt"; +static const char* resolve_input_file_path() noexcept +{ + const char* override_path = std::getenv("SAFE_EDGE_INPUT_FILE"); + return (nullptr != override_path && '\0' != override_path[0]) ? override_path : INPUT_FILE_PATH; +} + struct InputConfig { float soc; @@ -59,7 +66,7 @@ static InputConfig read_input_file() cfg.braking_available = true; cfg.steering_available = true; - std::FILE* f = std::fopen(INPUT_FILE_PATH, "r"); + std::FILE* f = std::fopen(resolve_input_file_path(), "r"); if (nullptr != f) { char line[64]; diff --git a/scripts/launch_fast_server.sh b/scripts/launch_fast_server.sh index e91ea7e..b3fff80 100755 --- a/scripts/launch_fast_server.sh +++ b/scripts/launch_fast_server.sh @@ -38,21 +38,31 @@ rm -f /dev/shm/fastdds_* /dev/shm/sem.fastdds_* 2>/dev/null || true if [[ "${OPT_TEST}" -eq 1 ]]; then IMAGE="safe-edge-server:fast-test" LOG_FILE="${LOG_DIR}/launch_fast_server.log" + DOCKER_ARGS=(--network host) mkdir -p "${LOG_DIR}" + docker rm -f safe-edge-server-test 2>/dev/null || true + if ! docker image inspect "${IMAGE}" > /dev/null 2>&1; then echo "[server] Image ${IMAGE} not found — building..." bash "${SCRIPT_DIR}/build_ubuntu.sh" --tests fi + if [[ -z "${PILOT_API_KEY:-}" && -f /etc/safe-edge/server.ini ]]; then + PILOT_API_KEY="$(grep -m1 'api_key' /etc/safe-edge/server.ini | cut -d'=' -f2 | tr -d ' ')" + fi + [[ -n "${PILOT_API_KEY:-}" ]] && DOCKER_ARGS+=(-e "PILOT_API_KEY=${PILOT_API_KEY}") + echo "[server] Running integration test..." echo "[server] Log: ${LOG_FILE}" set +e - docker run --rm --network host "${IMAGE}" 2>&1 | tee "${LOG_FILE}" + docker run --name safe-edge-server-test "${DOCKER_ARGS[@]}" "${IMAGE}" 2>&1 | tee "${LOG_FILE}" TEST_EXIT=${PIPESTATUS[0]} set -e + docker rm -f safe-edge-server-test 2>/dev/null || true + if [[ ${TEST_EXIT} -eq 0 ]]; then echo "[server] PASSED" else @@ -87,7 +97,17 @@ DOCKER_ENV_ARGS=( CONTAINER_NAME="safe-edge-server" docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true +DOCKER_ARGS=( + --name "${CONTAINER_NAME}" + --network host + "${DOCKER_ENV_ARGS[@]}" +) + +if [[ -d /etc/safe-edge ]]; then + DOCKER_ARGS+=(-v /etc/safe-edge:/etc/safe-edge:ro) +fi + echo "[server] Starting safe_edge_server (Ctrl+C to stop)" echo "[server] OWN_IP=${SAFE_EDGE_OWN_IP} INITIAL_PEERS=${SAFE_EDGE_INITIAL_PEERS}" -exec docker run --name "${CONTAINER_NAME}" --network host "${DOCKER_ENV_ARGS[@]}" "${IMAGE}" +exec docker run "${DOCKER_ARGS[@]}" "${IMAGE}" diff --git a/scripts/launch_tests.sh b/scripts/launch_tests.sh index f098b0c..564bb2d 100755 --- a/scripts/launch_tests.sh +++ b/scripts/launch_tests.sh @@ -4,15 +4,18 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" OPT_NO_REBUILD=0 +OPT_LINUX=0 usage() { cat <&2; usage >&2; exit 1 ;; esac @@ -44,6 +48,9 @@ TPI_ARGS=() if [[ "${OPT_NO_REBUILD}" -eq 1 ]]; then TPI_ARGS+=(--no-rebuild) fi +if [[ "${OPT_LINUX}" -eq 1 ]]; then + TPI_ARGS+=(--linux) +fi run_test "Fast DDS server" bash "${SCRIPT_DIR}/launch_fast_server.sh" --test run_test "Fast DDS edge" bash "${SCRIPT_DIR}/launch_fast_edge.sh" --test diff --git a/scripts/launch_tpi_2_1_test.sh b/scripts/launch_tpi_2_1_test.sh index 283c4c8..ad20afb 100755 --- a/scripts/launch_tpi_2_1_test.sh +++ b/scripts/launch_tpi_2_1_test.sh @@ -13,6 +13,7 @@ QNX_USER="${USER:-$(id -un)}" : "${CMAKE_BUILD_TYPE:=Release}" : "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" : "${SERVER_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/server-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${SERVER_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/server-native-${CMAKE_BUILD_TYPE}/bin}" _SSH_PASS="root" _SSH_USER="root" @@ -23,13 +24,16 @@ exec > >(tee "${LOG_FILE}") 2>&1 OPT_NO_REBUILD=0 OPT_STOP=0 +OPT_LINUX=0 usage() { cat <&2 + return 1 + fi + + if ! grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary does not look like a Linux executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_native.sh" >&2 + return 1 + fi +} + +if [[ "${OPT_LINUX}" -eq 1 ]]; then + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "No QNX VM is used in --linux mode. Nothing to stop." + exit 0 + fi + + pkill -f "safe_edge_server" 2>/dev/null || true + + for bin in \ + "${SERVER_BIN_DIR}/safe_edge_server" \ + "${SERVER_BIN_DIR}/test_server_integration"; do + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_native.sh" >&2 + exit 1 + fi + _validate_linux_binary "${bin}" + done + + echo "" + echo "Running test_server_integration on Linux..." + echo "---------------------------------------------" + + TEST_RC=0 + SAFE_EDGE_SERVER_BIN="${SERVER_BIN_DIR}/safe_edge_server" \ + "${SERVER_BIN_DIR}/test_server_integration" || TEST_RC=$? + + echo "---------------------------------------------" + if [[ "${TEST_RC}" -eq 0 ]]; then + echo "PASSED" + exit 0 + else + echo "FAILED (exit code ${TEST_RC})" + exit "${TEST_RC}" + fi +fi + if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 exit 1 @@ -248,6 +312,7 @@ mkqnximage --stop 2>/dev/null || true if [[ "${TEST_RC}" -eq 0 ]]; then echo "PASSED" + exit 0 else echo "FAILED (exit code ${TEST_RC})" exit "${TEST_RC}" diff --git a/scripts/launch_tpi_2_2_test.sh b/scripts/launch_tpi_2_2_test.sh index 4e6b41e..2224507 100755 --- a/scripts/launch_tpi_2_2_test.sh +++ b/scripts/launch_tpi_2_2_test.sh @@ -13,6 +13,7 @@ QNX_USER="${USER:-$(id -un)}" : "${CMAKE_BUILD_TYPE:=Release}" : "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" : "${EDGE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/edge-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${EDGE_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/edge-native-${CMAKE_BUILD_TYPE}/bin}" _SSH_PASS="root" _SSH_USER="root" @@ -23,13 +24,16 @@ exec > >(tee "${LOG_FILE}") 2>&1 OPT_NO_REBUILD=0 OPT_STOP=0 +OPT_LINUX=0 usage() { cat <&2 + return 1 + fi + + if ! grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary does not look like a Linux executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_native.sh" >&2 + return 1 + fi +} + +if [[ "${OPT_LINUX}" -eq 1 ]]; then + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "No QNX VM is used in --linux mode. Nothing to stop." + exit 0 + fi + + pkill -f "safe_edge_edge_gateway" 2>/dev/null || true + + for bin in \ + "${EDGE_BIN_DIR}/safe_edge_edge_gateway" \ + "${EDGE_BIN_DIR}/test_edge_integration"; do + if [[ ! -f "${bin}" ]]; then + echo "Binary not found: ${bin}" >&2 + echo "Build with: bash scripts/build_native.sh" >&2 + exit 1 + fi + _validate_linux_binary "${bin}" + done + + echo "" + echo "Running test_edge_integration on Linux..." + echo "---------------------------------------------" + + TEST_RC=0 + SAFE_EDGE_EDGE_BIN="${EDGE_BIN_DIR}/safe_edge_edge_gateway" \ + "${EDGE_BIN_DIR}/test_edge_integration" || TEST_RC=$? + + echo "---------------------------------------------" + if [[ "${TEST_RC}" -eq 0 ]]; then + echo "PASSED" + else + echo "FAILED (exit code ${TEST_RC})" + exit "${TEST_RC}" + fi + exit 0 +fi + if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 exit 1 diff --git a/scripts/launch_tpi_2_5_test.sh b/scripts/launch_tpi_2_5_test.sh index ffab568..94df501 100644 --- a/scripts/launch_tpi_2_5_test.sh +++ b/scripts/launch_tpi_2_5_test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Run TPI 2.5 smoke test for SafeDDS safety and non-safety QNX nodes. +# Run TPI 2.5 smoke test for SafeDDS safety and non-safety nodes. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -14,6 +14,8 @@ QNX_USER="${USER:-$(id -un)}" : "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" : "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" : "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${SAFETY_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-native-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-native-${CMAKE_BUILD_TYPE}/bin}" : "${SMOKE_TEST_SECONDS:=10}" _SSH_PASS="root" @@ -25,6 +27,11 @@ exec > >(tee "${LOG_FILE}") 2>&1 OPT_NO_REBUILD=0 OPT_STOP=0 +OPT_LINUX=0 + +TEST_PLATFORM="qnx" +RUNTIME_DIR="${LOG_DIR}/tpi_2_5_runtime" +INPUT_FILE="${RUNTIME_DIR}/input.txt" VEHICLE_NODE_BINS=( safe_edge_safety_io_adapters @@ -37,11 +44,13 @@ VEHICLE_NODE_BINS=( usage() { cat <&2 exit 1 fi -if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then +_validate_linux_binary() { + local bin="$1" + local description + + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + + if ! grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary does not look like a Linux executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_native.sh" >&2 + return 1 + fi +} + +if [[ "${TEST_PLATFORM}" == "qnx" && ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2 exit 1 fi -if [[ ! -d "${TARGET_DIR}" ]]; then +if [[ "${TEST_PLATFORM}" == "qnx" && ! -d "${TARGET_DIR}" ]]; then echo "QNX target directory not found: ${TARGET_DIR}" >&2 exit 1 fi -if ! command -v sshpass >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v sshpass >/dev/null 2>&1; then echo "sshpass not found. Install with: sudo apt install sshpass" >&2 exit 1 fi -# shellcheck source=/dev/null -source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + # shellcheck source=/dev/null + source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 +fi -if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v qemu-system-x86_64 >/dev/null 2>&1; then echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2 exit 1 fi -if ! command -v brctl >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v brctl >/dev/null 2>&1; then echo "brctl not found. Install with: sudo apt install bridge-utils" >&2 exit 1 fi @@ -238,10 +278,18 @@ _validate_vehicle_binaries() { bin="$(_host_bin_path "${name}")" if [[ ! -f "${bin}" ]]; then echo "Binary not found: ${bin}" >&2 - echo "Build with: bash scripts/build_qnx.sh" >&2 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + echo "Build with: bash scripts/build_native.sh" >&2 + else + echo "Build with: bash scripts/build_qnx.sh" >&2 + fi exit 1 fi - _validate_qnx_binary "${bin}" + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + _validate_linux_binary "${bin}" + else + _validate_qnx_binary "${bin}" + fi done } @@ -285,12 +333,20 @@ _reset_generated_target_output() { } _prepare_local_target_dirs() { - mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" + if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" + else + mkdir -p "${RUNTIME_DIR}" + fi } _remote_log_path_for_bin() { local bin="$1" - echo "/tmp/safe_edge_vehicle_nodes/${bin}.log" + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + echo "${RUNTIME_DIR}/${bin}.log" + else + echo "/tmp/safe_edge_vehicle_nodes/${bin}.log" + fi } _escape_single_quotes() { @@ -301,11 +357,18 @@ _escape_single_quotes() { _remote_pid_is_running() { local ip="$1" local bin="$2" - local pid_file="/tmp/safe_edge_vehicle_nodes/${bin}.pid" + local pid_file local escaped_pid_file - escaped_pid_file="$(_escape_single_quotes "${pid_file}")" - _ssh_run "${ip}" "test -f '${escaped_pid_file}' && pid=\$(cat '${escaped_pid_file}') && kill -0 \"\$pid\" 2>/dev/null" >/dev/null 2>&1 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + pid_file="${RUNTIME_DIR}/${bin}.pid" + [[ -f "${pid_file}" ]] || return 1 + kill -0 "$(cat "${pid_file}")" 2>/dev/null + else + pid_file="/tmp/safe_edge_vehicle_nodes/${bin}.pid" + escaped_pid_file="$(_escape_single_quotes "${pid_file}")" + _ssh_run "${ip}" "test -f '${escaped_pid_file}' && pid=\$(cat '${escaped_pid_file}') && kill -0 \"\$pid\" 2>/dev/null" >/dev/null 2>&1 + fi } _remote_file_nonempty() { @@ -313,8 +376,12 @@ _remote_file_nonempty() { local file="$2" local escaped_file - escaped_file="$(_escape_single_quotes "${file}")" - _ssh_run "${ip}" "test -s '${escaped_file}'" >/dev/null 2>&1 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + [[ -s "${file}" ]] + else + escaped_file="$(_escape_single_quotes "${file}")" + _ssh_run "${ip}" "test -s '${escaped_file}'" >/dev/null 2>&1 + fi } _remote_file_contains() { @@ -323,9 +390,13 @@ _remote_file_contains() { local pattern="$3" local escaped_file escaped_pattern - escaped_file="$(_escape_single_quotes "${file}")" - escaped_pattern="$(_escape_single_quotes "${pattern}")" - _ssh_run "${ip}" "grep -Fq -- '${escaped_pattern}' '${escaped_file}'" >/dev/null 2>&1 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + grep -Fq -- "${pattern}" "${file}" >/dev/null 2>&1 + else + escaped_file="$(_escape_single_quotes "${file}")" + escaped_pattern="$(_escape_single_quotes "${pattern}")" + _ssh_run "${ip}" "grep -Fq -- '${escaped_pattern}' '${escaped_file}'" >/dev/null 2>&1 + fi } _wait_for_remote_file_contains() { @@ -351,8 +422,15 @@ _print_remote_log_tail() { local file="$2" local escaped_file - escaped_file="$(_escape_single_quotes "${file}")" - _ssh_run "${ip}" "if [ -f '${escaped_file}' ]; then echo '--- ${file} ---'; tail -40 '${escaped_file}'; fi" || true + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + if [[ -f "${file}" ]]; then + echo "--- ${file} ---" + tail -40 "${file}" + fi + else + escaped_file="$(_escape_single_quotes "${file}")" + _ssh_run "${ip}" "if [ -f '${escaped_file}' ]; then echo '--- ${file} ---'; tail -40 '${escaped_file}'; fi" || true + fi } _start_vehicle_nodes() { @@ -360,6 +438,26 @@ _start_vehicle_nodes() { local cmd local name + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + mkdir -p "${RUNTIME_DIR}" + printf "soc=50.0\nemergency_stop=0\nadas_fault=0\navailable_charge_kw=50.0\navailable_discharge_kw=50.0\nv2g_ready=1\nspeed_mps=0.0\nbraking_available=1\nsteering_available=1\n" > "${INPUT_FILE}" + rm -f "${RUNTIME_DIR}"/*.pid "${RUNTIME_DIR}"/*.log + + "${NON_SAFETY_BIN_DIR}/safe_edge_infotainment" > "${RUNTIME_DIR}/safe_edge_infotainment.log" 2>&1 & + echo $! > "${RUNTIME_DIR}/safe_edge_infotainment.pid" + sleep 2 + for name in "${VEHICLE_NODE_BINS[@]}"; do + [[ "${name}" == "safe_edge_infotainment" ]] && continue + if [[ "${name}" == "safe_edge_vehicle_mock" ]]; then + SAFE_EDGE_INPUT_FILE="${INPUT_FILE}" "${SAFETY_BIN_DIR}/${name}" > "${RUNTIME_DIR}/${name}.log" 2>&1 & + else + "$(_host_bin_path "${name}")" > "${RUNTIME_DIR}/${name}.log" 2>&1 & + fi + echo $! > "${RUNTIME_DIR}/${name}.pid" + done + return 0 + fi + cmd='set -e; mkdir -p /tmp/safe_edge_vehicle_nodes /data/safe-edge-stage2;' cmd+=' printf "soc=50.0\nemergency_stop=0\nadas_fault=0\navailable_charge_kw=50.0\navailable_discharge_kw=50.0\nv2g_ready=1\nspeed_mps=0.0\nbraking_available=1\nsteering_available=1\n" > /data/safe-edge-stage2/input.txt;' cmd+=' rm -f /tmp/safe_edge_vehicle_nodes/*.pid /tmp/safe_edge_vehicle_nodes/*.log;' @@ -375,16 +473,33 @@ _start_vehicle_nodes() { _stop_vehicle_nodes() { local ip="$1" - _ssh_run "${ip}" \ - "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; pid=\$(cat \"\$f\"); kill \"\$pid\" 2>/dev/null || true; done" \ - >/dev/null 2>&1 || true + local pid_file + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + for pid_file in "${RUNTIME_DIR}"/*.pid; do + [[ -f "${pid_file}" ]] || continue + kill "$(cat "${pid_file}")" 2>/dev/null || true + done + else + _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; pid=\$(cat \"\$f\"); kill \"\$pid\" 2>/dev/null || true; done" \ + >/dev/null 2>&1 || true + fi } _print_vehicle_node_logs() { local ip="$1" - _ssh_run "${ip}" \ - "for f in /tmp/safe_edge_vehicle_nodes/*.log; do [ -f \"\$f\" ] || continue; echo \"--- \$f ---\"; tail -40 \"\$f\"; done" \ - || true + local log_file + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + for log_file in "${RUNTIME_DIR}"/*.log; do + [[ -f "${log_file}" ]] || continue + echo "--- ${log_file} ---" + tail -40 "${log_file}" + done + else + _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.log; do [ -f \"\$f\" ] || continue; echo \"--- \$f ---\"; tail -40 \"\$f\"; done" \ + || true + fi } _service_name_for_bin() { @@ -423,16 +538,17 @@ _run_liveliness_test_case() { local ip="$1" local name="$2" local test_name="$3" - local infotainment_log="/tmp/safe_edge_vehicle_nodes/safe_edge_infotainment.log" + local infotainment_log + infotainment_log="$(_remote_log_path_for_bin "safe_edge_infotainment")" local node_log service_name hb_line failed=0 node_log="$(_remote_log_path_for_bin "${name}")" echo "[ RUN ] ${test_name}" if _remote_pid_is_running "${ip}" "${name}"; then - echo " [qemu] pid exists and process is alive" + echo " [proc] pid exists and process is alive" else - echo " [qemu] FAIL pid missing or process not alive" + echo " [proc] FAIL pid missing or process not alive" failed=1 fi @@ -529,54 +645,66 @@ _test_smoke_logs() { _prepare_local_target_dirs -CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" -if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then - echo "A QNX VM from another repo target is still running." >&2 - echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 - echo >&2 - echo "Conflicts detected:" >&2 - while IFS=$'\t' read -r pid cwd; do - [[ -n "${pid}" ]] || continue - echo " PID ${pid}: ${cwd}" >&2 - done <<< "${CONFLICTING_QEMU_TARGETS}" - exit 1 -fi +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" + if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then + echo "A QNX VM from another repo target is still running." >&2 + echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 + echo >&2 + echo "Conflicts detected:" >&2 + while IFS=$'\t' read -r pid cwd; do + [[ -n "${pid}" ]] || continue + echo " PID ${pid}: ${cwd}" >&2 + done <<< "${CONFLICTING_QEMU_TARGETS}" + exit 1 + fi -cd "${TARGET_DIR}" + cd "${TARGET_DIR}" + + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 + fi -if [[ "${OPT_STOP}" -eq 1 ]]; then - echo "Stopping QNX VM..." - mkqnximage --stop 2>/dev/null || true kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true - echo "VM stopped." - exit 0 -fi -kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_vehicle_binaries + _refresh_vehicle_ifs_start_snippet + _refresh_vehicle_post_start_snippet + _refresh_vehicle_system_files_snippet + _reset_generated_target_output + fi -if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then - _validate_vehicle_binaries - _refresh_vehicle_ifs_start_snippet - _refresh_vehicle_post_start_snippet - _refresh_vehicle_system_files_snippet - _reset_generated_target_output -fi + if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 + else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 + fi -if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then - echo "Building QNX image and starting QEMU..." - mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 + echo "Waiting for VM IP..." + VM_IP="$(_get_ip_address)" + echo "VM is up: ${VM_IP}" + echo "Waiting for SSH..." + _wait_for_ssh "${VM_IP}" + echo "VM is reachable." else - echo "Starting QNX QEMU (skipping rebuild)..." - mkqnximage --noprompt --run=-h >/dev/null 2>&1 + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping local Linux node processes..." + _stop_vehicle_nodes "" + echo "Processes stopped." + exit 0 + fi + _validate_vehicle_binaries + VM_IP="" + echo "Running native Linux mode. No QNX image will be built." fi -echo "Waiting for VM IP..." -VM_IP="$(_get_ip_address)" -echo "VM is up: ${VM_IP}" -echo "Waiting for SSH..." -_wait_for_ssh "${VM_IP}" -echo "VM is reachable." - echo "Starting vehicle nodes..." _start_vehicle_nodes "${VM_IP}" echo "Vehicle nodes launched." @@ -586,7 +714,9 @@ _test_nodes_launch_and_stay_alive "${VM_IP}" || TEST_RC=1 _test_smoke_logs "${VM_IP}" || TEST_RC=1 _stop_vehicle_nodes "${VM_IP}" -echo -e "\nStopping QNX VM..." -mkqnximage --stop 2>/dev/null || true +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + echo -e "\nStopping QNX VM..." + mkqnximage --stop 2>/dev/null || true +fi exit "${TEST_RC}" diff --git a/scripts/launch_tpi_2_6_test.sh b/scripts/launch_tpi_2_6_test.sh index 67fbadf..b64c82a 100644 --- a/scripts/launch_tpi_2_6_test.sh +++ b/scripts/launch_tpi_2_6_test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Run TPI 2.6 safety communication path latency benchmark for SafeDDS QNX nodes. +# Run TPI 2.6 safety communication path latency benchmark for SafeDDS nodes. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -15,6 +15,8 @@ QNX_USER="${USER:-$(id -un)}" : "${TARGET_DIR:=${WORKSPACE_ROOT}/qnx/targets/qemu-qnx800-${QNX_ARCH}}" : "${SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" : "${NON_SAFETY_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-qnx8-${QNX_ARCH}-${CMAKE_BUILD_TYPE}/bin}" +: "${SAFETY_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/safety-native-${CMAKE_BUILD_TYPE}/bin}" +: "${NON_SAFETY_NATIVE_BIN_DIR:=${WORKSPACE_ROOT}/safe_dds/install/non-safety-native-${CMAKE_BUILD_TYPE}/bin}" _SSH_PASS="root" _SSH_USER="root" @@ -25,6 +27,11 @@ OPT_STOP=0 OPT_SAMPLES=100 OPT_LOAD=0 OPT_PRIO=0 +OPT_LINUX=0 + +TEST_PLATFORM="qnx" +RUNTIME_DIR="${LOG_DIR}/tpi_2_6_runtime" +INPUT_FILE="${RUNTIME_DIR}/input.txt" VEHICLE_NODE_BINS=( safe_edge_safety_io_adapters @@ -54,6 +61,8 @@ Usage: bash scripts/launch_tpi_2_6_test.sh [options] Options: --no-rebuild skip QNX image rebuild + --linux run native Linux binaries instead of a QNX VM + --ubuntu alias for --linux --samples N number of measurement samples (default: ${OPT_SAMPLES}) --load LEVEL load level: 0=baseline, 1=medium (CPU), 2=high (CPU+I/O) (default: ${OPT_LOAD}) --prio start VM + nodes, print thread priorities via pidin, then exit @@ -67,6 +76,8 @@ Environment variables: TARGET_DIR mkqnximage target directory SAFETY_BIN_DIR directory containing safety domain binaries NON_SAFETY_BIN_DIR directory containing non-safety domain binaries + SAFETY_NATIVE_BIN_DIR directory containing native safety domain binaries + NON_SAFETY_NATIVE_BIN_DIR directory containing native non-safety domain binaries EOF } @@ -76,6 +87,10 @@ while [[ $# -gt 0 ]]; do OPT_NO_REBUILD=1 shift ;; + --linux|--ubuntu) + OPT_LINUX=1 + shift + ;; --samples) if [[ $# -lt 2 ]]; then echo "--samples requires a value" >&2; exit 1 @@ -107,6 +122,12 @@ while [[ $# -gt 0 ]]; do esac done +if [[ "${OPT_LINUX}" -eq 1 ]]; then + TEST_PLATFORM="linux" + SAFETY_BIN_DIR="${SAFETY_NATIVE_BIN_DIR}" + NON_SAFETY_BIN_DIR="${NON_SAFETY_NATIVE_BIN_DIR}" +fi + if [[ ! "${OPT_SAMPLES}" =~ ^[0-9]+$ || "${OPT_SAMPLES}" -lt 1 ]]; then echo "Invalid --samples value: ${OPT_SAMPLES}" >&2; exit 1 fi @@ -116,6 +137,21 @@ fi mkdir -p "${LOG_DIR}" +_validate_linux_binary() { + local bin="$1" + local description + if ! description="$(file "${bin}")"; then + echo "Failed to inspect binary: ${bin}" >&2 + return 1 + fi + if ! grep -Fq "GNU/Linux" <<<"${description}"; then + echo "Binary does not look like a Linux executable:" >&2 + echo " ${description}" >&2 + echo "Rebuild with: bash scripts/build_native.sh" >&2 + return 1 + fi +} + _ssh_run() { local ip="$1" local cmd="$2" @@ -204,9 +240,18 @@ _validate_vehicle_binaries() { bin="$(_host_bin_path "${name}")" if [[ ! -f "${bin}" ]]; then echo "Binary not found: ${bin}" >&2 - echo "Build with: bash scripts/build_qnx.sh" >&2; exit 1 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + echo "Build with: bash scripts/build_native.sh" >&2 + else + echo "Build with: bash scripts/build_qnx.sh" >&2 + fi + exit 1 + fi + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + _validate_linux_binary "${bin}" + else + _validate_qnx_binary "${bin}" fi - _validate_qnx_binary "${bin}" done } @@ -248,7 +293,11 @@ _reset_generated_target_output() { } _prepare_local_target_dirs() { - mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" + if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + mkdir -p "${TARGET_DIR}/local/misc_files" "${TARGET_DIR}/local/snippets" + else + mkdir -p "${RUNTIME_DIR}" + fi } _escape_single_quotes() { @@ -259,9 +308,13 @@ _escape_single_quotes() { _remote_file_contains() { local ip="$1" file="$2" pattern="$3" local ef ep - ef="$(_escape_single_quotes "${file}")" - ep="$(_escape_single_quotes "${pattern}")" - _ssh_run "${ip}" "grep -Fq -- '${ep}' '${ef}'" >/dev/null 2>&1 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + grep -Fq -- "${pattern}" "${file}" >/dev/null 2>&1 + else + ef="$(_escape_single_quotes "${file}")" + ep="$(_escape_single_quotes "${pattern}")" + _ssh_run "${ip}" "grep -Fq -- '${ep}' '${ef}'" >/dev/null 2>&1 + fi } _wait_for_remote_file_contains() { @@ -278,6 +331,25 @@ _start_vehicle_nodes() { local ip="$1" local cmd name local input_escaped + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + mkdir -p "${RUNTIME_DIR}" + printf '%s' "${INPUT_FILE_CONTENT}" > "${INPUT_FILE}" + rm -f "${RUNTIME_DIR}"/*.pid "${RUNTIME_DIR}"/*.log + "${NON_SAFETY_BIN_DIR}/safe_edge_infotainment" > "${RUNTIME_DIR}/safe_edge_infotainment.log" 2>&1 & + echo $! > "${RUNTIME_DIR}/safe_edge_infotainment.pid" + sleep 2 + for name in "${VEHICLE_NODE_BINS[@]}"; do + [[ "${name}" == "safe_edge_infotainment" ]] && continue + if [[ "${name}" == "safe_edge_vehicle_mock" ]]; then + SAFE_EDGE_INPUT_FILE="${INPUT_FILE}" "${SAFETY_BIN_DIR}/${name}" > "${RUNTIME_DIR}/${name}.log" 2>&1 & + else + "$(_host_bin_path "${name}")" > "${RUNTIME_DIR}/${name}.log" 2>&1 & + fi + echo $! > "${RUNTIME_DIR}/${name}.pid" + done + return 0 + fi + input_escaped="$(_escape_single_quotes "${INPUT_FILE_CONTENT}")" cmd="set -e; mkdir -p /tmp/safe_edge_vehicle_nodes /data/safe-edge-stage2;" @@ -296,6 +368,19 @@ _start_vehicle_nodes() { _start_load() { local ip="$1" level="$2" + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + local count=0 + if [[ "${level}" -ge 1 ]]; then + bash -lc 'while true; do awk "BEGIN{for(i=0;i<2000000;i++){x=sin(i)+cos(i)}}" >/dev/null; sleep 0.005; done' >/dev/null 2>&1 & + echo $! > "${RUNTIME_DIR}/load_$((++count)).pid" + fi + if [[ "${level}" -ge 2 ]]; then + bash -lc 'while true; do awk "BEGIN{for(i=0;i<50000;i++){x=sin(i)+cos(i)}}" >/dev/null; sleep 0.3; done' >/dev/null 2>&1 & + echo $! > "${RUNTIME_DIR}/load_$((++count)).pid" + fi + return 0 + fi + # vCPU warmer: always launched at priority 1:r (minimum user priority). # Preempted instantly by any SafeDDS thread (10:r) so it never competes. # Keeps the QEMU vCPU thread scheduled on a host core at all levels, @@ -329,6 +414,14 @@ _start_load() { _stop_vehicle_nodes() { local ip="$1" + local pid_file + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + for pid_file in "${RUNTIME_DIR}"/*.pid; do + [[ -f "${pid_file}" ]] || continue + kill "$(cat "${pid_file}")" 2>/dev/null || true + done + return 0 + fi # Kill tracked node pids _ssh_run "${ip}" \ "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; pid=\$(cat \"\$f\"); kill \"\$pid\" 2>/dev/null || true; done" \ @@ -342,6 +435,12 @@ _stop_vehicle_nodes() { _trigger_sample_cycle() { local ip="$1" local esc1 esc0 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + printf '%s' "${INPUT_FILE_CONTENT/emergency_stop=0/emergency_stop=1}" > "${INPUT_FILE}" + sleep 0.25 + printf '%s' "${INPUT_FILE_CONTENT}" > "${INPUT_FILE}" + return 0 + fi esc1="$(_escape_single_quotes "${INPUT_FILE_CONTENT/emergency_stop=0/emergency_stop=1}")" esc0="$(_escape_single_quotes "${INPUT_FILE_CONTENT}")" # Single SSH call: write trigger, hold for one vehicle_mock tick, reset — avoids 3×handshake overhead @@ -350,7 +449,12 @@ _trigger_sample_cycle() { _run_measurement_loop() { local ip="$1" - local pe_log="/tmp/safe_edge_vehicle_nodes/safe_edge_policy_engine.log" + local pe_log + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + pe_log="${RUNTIME_DIR}/safe_edge_policy_engine.log" + else + pe_log="/tmp/safe_edge_vehicle_nodes/safe_edge_policy_engine.log" + fi local i echo "Waiting for nodes to initialize..." @@ -375,13 +479,21 @@ _collect_logs() { echo "Collecting logs..." : > "${RAW_LOG}" for name in "${VEHICLE_NODE_BINS[@]}"; do - tmp="$(mktemp)" - if _scp_get "${ip}" "/tmp/safe_edge_vehicle_nodes/${name}.log" "${tmp}" 2>/dev/null; then - cat "${tmp}" >> "${RAW_LOG}" + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + if [[ -f "${RUNTIME_DIR}/${name}.log" ]]; then + cat "${RUNTIME_DIR}/${name}.log" >> "${RAW_LOG}" + else + echo "[${name}] (log not found on host)" >> "${RAW_LOG}" + fi else - echo "[${name}] (log not found on VM)" >> "${RAW_LOG}" + tmp="$(mktemp)" + if _scp_get "${ip}" "/tmp/safe_edge_vehicle_nodes/${name}.log" "${tmp}" 2>/dev/null; then + cat "${tmp}" >> "${RAW_LOG}" + else + echo "[${name}] (log not found on VM)" >> "${RAW_LOG}" + fi + rm -f "${tmp}" fi - rm -f "${tmp}" done echo "Raw log: ${RAW_LOG}" } @@ -390,17 +502,41 @@ _dump_node_diagnostics() { local ip="$1" local name echo "--- node diagnostics ---" >&2 - { _ssh_run "${ip}" \ - "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; n=\${f##*/}; n=\${n%.pid}; pid=\$(cat \"\$f\"); alive=dead; kill -0 \"\$pid\" 2>/dev/null && alive=running; echo \"\$n: pid=\$pid [\$alive]\"; done" \ - 2>/dev/null || true; } >&2 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + for name in "${VEHICLE_NODE_BINS[@]}"; do + local pid_file="${RUNTIME_DIR}/${name}.pid" + [[ -f "${pid_file}" ]] || continue + local pid + pid="$(cat "${pid_file}")" + if kill -0 "${pid}" 2>/dev/null; then + echo "${name}: pid=${pid} [running]" >&2 + else + echo "${name}: pid=${pid} [dead]" >&2 + fi + done + else + { _ssh_run "${ip}" \ + "for f in /tmp/safe_edge_vehicle_nodes/*.pid; do [ -f \"\$f\" ] || continue; n=\${f##*/}; n=\${n%.pid}; pid=\$(cat \"\$f\"); alive=dead; kill -0 \"\$pid\" 2>/dev/null && alive=running; echo \"\$n: pid=\$pid [\$alive]\"; done" \ + 2>/dev/null || true; } >&2 + fi for name in "${VEHICLE_NODE_BINS[@]}"; do echo "--- ${name}.log (tail 10) ---" >&2 - { _ssh_run "${ip}" "tail -10 /tmp/safe_edge_vehicle_nodes/${name}.log 2>/dev/null || echo '(empty)'" || true; } >&2 + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + { tail -10 "${RUNTIME_DIR}/${name}.log" 2>/dev/null || echo '(empty)'; } >&2 + else + { _ssh_run "${ip}" "tail -10 /tmp/safe_edge_vehicle_nodes/${name}.log 2>/dev/null || echo '(empty)'" || true; } >&2 + fi done } _generate_report() { - python3 - "${RAW_LOG}" "${REPORT}" "${OPT_SAMPLES}" "${E2E_THRESHOLD_MS}" "${REACTION_THRESHOLD_MS}" <<'PYEOF' + local environment_label + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + environment_label="Linux native" + else + environment_label="QNX 8.0 / QEMU x86_64" + fi + python3 - "${RAW_LOG}" "${REPORT}" "${OPT_SAMPLES}" "${E2E_THRESHOLD_MS}" "${REACTION_THRESHOLD_MS}" "${environment_label}" <<'PYEOF' import sys import re @@ -409,6 +545,7 @@ report_path = sys.argv[2] n_samples = int(sys.argv[3]) e2e_limit = float(sys.argv[4]) rxn_limit = float(sys.argv[5]) +environment = sys.argv[6] def parse_ts(s, ns): return int(s) * 1_000_000_000 + int(ns) @@ -508,7 +645,7 @@ overall = e2e_pass and rxn_pass report = [] report.append("SafeEDGE TPI 2.6 — Safety Path Latency Benchmark") -report.append("Environment: QNX 8.0 / QEMU x86_64") +report.append(f"Environment: {environment}") report.append(f"Raw log: {raw_log}") report.append(f"Samples collected: {len(e2e_samples)} E2E, {len(rxn_samples)} reaction (requested: {n_samples})") report.append("") @@ -543,74 +680,90 @@ PYEOF _prepare_local_target_dirs -CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" -if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then - echo "A QNX VM from another repo target is still running." >&2 - echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 - echo >&2 - echo "Conflicts detected:" >&2 - while IFS=$'\t' read -r pid cwd; do - [[ -n "${pid}" ]] || continue - echo " PID ${pid}: ${cwd}" >&2 - done <<< "${CONFLICTING_QEMU_TARGETS}" - exit 1 +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + CONFLICTING_QEMU_TARGETS="$(_find_conflicting_qemu_targets)" + if [[ -n "${CONFLICTING_QEMU_TARGETS}" ]]; then + echo "A QNX VM from another repo target is still running." >&2 + echo "Stop it first, otherwise SSH may connect to the wrong guest image." >&2 + echo >&2 + echo "Conflicts detected:" >&2 + while IFS=$'\t' read -r pid cwd; do + [[ -n "${pid}" ]] || continue + echo " PID ${pid}: ${cwd}" >&2 + done <<< "${CONFLICTING_QEMU_TARGETS}" + exit 1 + fi fi -if [[ ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then +if [[ "${TEST_PLATFORM}" == "qnx" && ! -f "${QNX_SDP_ROOT}/qnxsdp-env.sh" ]]; then echo "QNX SDK not found at QNX_SDP_ROOT='${QNX_SDP_ROOT}'" >&2; exit 1 fi -if [[ ! -d "${TARGET_DIR}" ]]; then +if [[ "${TEST_PLATFORM}" == "qnx" && ! -d "${TARGET_DIR}" ]]; then echo "QNX target directory not found: ${TARGET_DIR}" >&2; exit 1 fi -if ! command -v sshpass >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v sshpass >/dev/null 2>&1; then echo "sshpass not found. Install with: sudo apt install sshpass" >&2; exit 1 fi if ! command -v python3 >/dev/null 2>&1; then echo "python3 not found on host — required for post-processing" >&2; exit 1 fi -# shellcheck source=/dev/null -source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + # shellcheck source=/dev/null + source "${QNX_SDP_ROOT}/qnxsdp-env.sh" >/dev/null 2>&1 +fi -if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v qemu-system-x86_64 >/dev/null 2>&1; then echo "qemu-system-x86_64 not found. Install with: sudo apt install qemu-system-x86" >&2; exit 1 fi -if ! command -v brctl >/dev/null 2>&1; then +if [[ "${TEST_PLATFORM}" == "qnx" ]] && ! command -v brctl >/dev/null 2>&1; then echo "brctl not found. Install with: sudo apt install bridge-utils" >&2; exit 1 fi -cd "${TARGET_DIR}" +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + cd "${TARGET_DIR}" + + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true + kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + echo "VM stopped." + exit 0 + fi -if [[ "${OPT_STOP}" -eq 1 ]]; then - echo "Stopping QNX VM..." - mkqnximage --stop 2>/dev/null || true kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true - echo "VM stopped." - exit 0 -fi -kill "$(pgrep -f qemu-system-x86_64)" 2>/dev/null || true + if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then + _validate_vehicle_binaries + _refresh_vehicle_ifs_start_snippet + _refresh_vehicle_post_start_snippet + _refresh_vehicle_system_files_snippet + _reset_generated_target_output + echo "Building QNX image and starting QEMU..." + mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 + else + echo "Starting QNX QEMU (skipping rebuild)..." + mkqnximage --noprompt --run=-h >/dev/null 2>&1 + fi -if [[ "${OPT_NO_REBUILD}" -eq 0 ]]; then - _validate_vehicle_binaries - _refresh_vehicle_ifs_start_snippet - _refresh_vehicle_post_start_snippet - _refresh_vehicle_system_files_snippet - _reset_generated_target_output - echo "Building QNX image and starting QEMU..." - mkqnximage --noprompt --run=-h --clean >/dev/null 2>&1 + echo "Waiting for VM IP..." + VM_IP="$(_get_ip_address)" + echo "VM is up: ${VM_IP}" + echo "Waiting for SSH..." + _wait_for_ssh "${VM_IP}" + echo "VM is reachable." else - echo "Starting QNX QEMU (skipping rebuild)..." - mkqnximage --noprompt --run=-h >/dev/null 2>&1 + if [[ "${OPT_STOP}" -eq 1 ]]; then + echo "Stopping local Linux node processes..." + _stop_vehicle_nodes "" + echo "Processes stopped." + exit 0 + fi + _validate_vehicle_binaries + VM_IP="" + echo "Running native Linux mode. No QNX image will be built." fi -echo "Waiting for VM IP..." -VM_IP="$(_get_ip_address)" -echo "VM is up: ${VM_IP}" -echo "Waiting for SSH..." -_wait_for_ssh "${VM_IP}" -echo "VM is reachable." - echo "Starting vehicle nodes..." _start_vehicle_nodes "${VM_IP}" echo "Vehicle nodes launched." @@ -622,7 +775,11 @@ if [[ "${OPT_PRIO}" -eq 1 ]]; then echo "Load stressors launched." fi echo "Waiting for policy_engine heartbeat (up to 60s)..." - NODE_LOG_DIR="/tmp/safe_edge_vehicle_nodes" + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + NODE_LOG_DIR="${RUNTIME_DIR}" + else + NODE_LOG_DIR="/tmp/safe_edge_vehicle_nodes" + fi if ! _wait_for_remote_file_contains "${VM_IP}" \ "${NODE_LOG_DIR}/safe_edge_policy_engine.log" \ "Published ServiceHeartbeat" \ @@ -631,21 +788,30 @@ if [[ "${OPT_PRIO}" -eq 1 ]]; then else echo "Nodes up. Querying priorities..." fi - echo "" - echo "=== pidin header (raw) ===" - _ssh_run "${VM_IP}" "pidin 2>&1 | head -3" 2>/dev/null || true - echo "" - echo "=== all processes (pidin, first 40 lines) ===" - _ssh_run "${VM_IP}" "pidin 2>&1 | head -40" 2>/dev/null || true - echo "" - echo "=== safe_edge grep ===" - _ssh_run "${VM_IP}" "pidin 2>&1 | grep -i safe" 2>/dev/null || true - echo "" - echo "=== stressor grep ===" - _ssh_run "${VM_IP}" "pidin 2>&1 | grep -iE 'awk|nc '" 2>/dev/null || true - echo "" - echo "Stopping VM..." - mkqnximage --stop 2>/dev/null || true + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + echo "" + echo "=== local processes ===" + ps -eo pid,ppid,pri,ni,cls,rtprio,comm | grep -E 'safe_edge|awk' || true + else + echo "" + echo "=== pidin header (raw) ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | head -3" 2>/dev/null || true + echo "" + echo "=== all processes (pidin, first 40 lines) ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | head -40" 2>/dev/null || true + echo "" + echo "=== safe_edge grep ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | grep -i safe" 2>/dev/null || true + echo "" + echo "=== stressor grep ===" + _ssh_run "${VM_IP}" "pidin 2>&1 | grep -iE 'awk|nc '" 2>/dev/null || true + echo "" + echo "Stopping VM..." + mkqnximage --stop 2>/dev/null || true + fi + if [[ "${TEST_PLATFORM}" == "linux" ]]; then + _stop_vehicle_nodes "${VM_IP}" + fi exit 0 fi @@ -661,8 +827,10 @@ _run_measurement_loop "${VM_IP}" || TEST_RC=1 _collect_logs "${VM_IP}" _stop_vehicle_nodes "${VM_IP}" -echo "Stopping QNX VM..." -mkqnximage --stop 2>/dev/null || true +if [[ "${TEST_PLATFORM}" == "qnx" ]]; then + echo "Stopping QNX VM..." + mkqnximage --stop 2>/dev/null || true +fi echo "Generating latency report..." _generate_report || TEST_RC=1 From 66dc9a17cd6ef303817beb493f752db33b275448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Peix=20del=20R=C3=ADo?= Date: Tue, 16 Jun 2026 14:36:51 +0200 Subject: [PATCH 17/17] CI - Remove docker images and fix fast build warns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Peix del Río --- .github/workflows/ci-git.yaml | 7 +++++++ fast_dds/edge/test/test_edge_integration.cpp | 3 ++- fast_dds/server/test/test_server_integration.cpp | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-git.yaml b/.github/workflows/ci-git.yaml index c612752..5cb04b4 100644 --- a/.github/workflows/ci-git.yaml +++ b/.github/workflows/ci-git.yaml @@ -78,6 +78,13 @@ jobs: - name: Test - Fast DDS edge run: bash scripts/launch_fast_edge.sh --test + - name: Clear - Docker Images + run: | + docker stop safe_edge_server:fast || true + docker stop safe_edge_edge:fast || true + docker rmi -f safe-edge-server:fast || true + docker rmi -f safe-edge-edge:fast || true + - name: Test - TPI 2.1 run: bash scripts/launch_tpi_2_1_test.sh --linux --no-rebuild diff --git a/fast_dds/edge/test/test_edge_integration.cpp b/fast_dds/edge/test/test_edge_integration.cpp index 5bcbf7b..870cfb5 100644 --- a/fast_dds/edge/test/test_edge_integration.cpp +++ b/fast_dds/edge/test/test_edge_integration.cpp @@ -96,7 +96,8 @@ class EdgeEnvironment : public ::testing::Environment void TearDown() override { std::cout << "[env] Stopping safe_edge_edge_gateway...\n"; - std::system("pkill -f safe_edge_edge_gateway 2>/dev/null || true"); + const int res = std::system("pkill -f safe_edge_edge_gateway 2>/dev/null || true"); + (void) res; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }; diff --git a/fast_dds/server/test/test_server_integration.cpp b/fast_dds/server/test/test_server_integration.cpp index efd54d8..b0720ca 100644 --- a/fast_dds/server/test/test_server_integration.cpp +++ b/fast_dds/server/test/test_server_integration.cpp @@ -119,7 +119,8 @@ class ServerEnvironment : public ::testing::Environment void TearDown() override { std::cout << "[env] Stopping safe_edge_server...\n"; - std::system("pkill -f safe_edge_server 2>/dev/null || true"); + cont int res = std::system("pkill -f safe_edge_server 2>/dev/null || true"); + (void) res; std::this_thread::sleep_for(std::chrono::milliseconds(500)); } };