From bc45ce1dca9c38290b49b25df00134cc4381f13b Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Mon, 17 Feb 2025 12:07:22 +0200 Subject: [PATCH 1/9] added macos support --- .gitmodules | 4 + libcyphal_demo/CMakeLists.txt | 79 +++-- libcyphal_demo/CMakePresets.json | 134 +++++++++ libcyphal_demo/cmake/modules/Findnnvg.cmake | 118 -------- libcyphal_demo/src/CMakeLists.txt | 43 ++- libcyphal_demo/src/any_transport_bag.hpp | 34 +++ libcyphal_demo/src/application.hpp | 18 +- libcyphal_demo/src/main.cpp | 46 ++- .../bsd/kqueue_single_threaded_executor.hpp | 272 ++++++++++++++++++ .../src/platform/common_helpers.hpp | 9 +- libcyphal_demo/src/platform/defines.hpp | 27 ++ .../src/platform/linux/can/can_media.hpp | 6 +- .../linux/epoll_single_threaded_executor.hpp | 10 +- .../posix/posix_executor_extension.hpp | 9 + .../platform/posix/posix_platform_error.hpp | 4 + libcyphal_demo/src/transport_bag_can.hpp | 81 ++++-- libcyphal_demo/src/transport_bag_udp.hpp | 80 ++++-- submodules/nunavut | 1 + 18 files changed, 728 insertions(+), 247 deletions(-) create mode 100644 libcyphal_demo/CMakePresets.json delete mode 100644 libcyphal_demo/cmake/modules/Findnnvg.cmake create mode 100644 libcyphal_demo/src/any_transport_bag.hpp create mode 100644 libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp create mode 100644 libcyphal_demo/src/platform/defines.hpp create mode 160000 submodules/nunavut diff --git a/.gitmodules b/.gitmodules index 1d38cb7..e9c8315 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,3 +18,7 @@ [submodule "submodules/libcyphal"] path = submodules/libcyphal url = https://github.com/OpenCyphal-Garage/libcyphal.git +[submodule "submodules/nunavut"] + path = submodules/nunavut + url = https://github.com/OpenCyphal/nunavut.git + branch = sshirokov/rpi32 diff --git a/libcyphal_demo/CMakeLists.txt b/libcyphal_demo/CMakeLists.txt index e5fa206..70be951 100644 --- a/libcyphal_demo/CMakeLists.txt +++ b/libcyphal_demo/CMakeLists.txt @@ -4,7 +4,7 @@ # SPDX-License-Identifier: MIT # Author: Sergei Shirokov -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.25) project(libcyphal_demo LANGUAGES CXX C @@ -15,6 +15,7 @@ set(DISABLE_CPP_EXCEPTIONS ON CACHE STRING "Disable C++ exceptions.") option(CETL_ENABLE_DEBUG_ASSERT "Enable or disable runtime CETL asserts." ON) +set(CXX_FLAG_SET "") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (DISABLE_CPP_EXCEPTIONS) message(STATUS "DISABLE_CPP_EXCEPTIONS is true. Adding -fno-exceptions to compiler flags.") @@ -25,15 +26,16 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # Disable PSABI warnings in GCC (on RPi). list(APPEND CXX_FLAG_SET "-Wno-psabi") endif() - add_compile_options("$<$:${CXX_FLAG_SET}>") if (CETL_ENABLE_DEBUG_ASSERT) add_compile_definitions("CETL_ENABLE_DEBUG_ASSERT=1") endif() -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") -set(submodules "${CMAKE_SOURCE_DIR}/../submodules") +# Set the output binary directory +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +set(submodules_dir "${CMAKE_SOURCE_DIR}/../submodules") # Set up static analysis. set(STATIC_ANALYSIS ON CACHE BOOL "enable static analysis") @@ -46,6 +48,16 @@ if (STATIC_ANALYSIS) message(STATUS "Using clang-tidy: ${clang_tidy}") endif () +# Pull in Nunavut's cmake integration +find_package("Nunavut" 3.0 REQUIRED) + +# libcyphal requires PMR support for Nunavut generated code. +if (${CMAKE_CXX_STANDARD} STREQUAL "14") + set(CYPHAL_LANGUAGE_STANDARD "cetl++14-17") +else () + set(CYPHAL_LANGUAGE_STANDARD "c++${CMAKE_CXX_STANDARD}-pmr") +endif () + # Forward the revision information to the compiler so that we could expose it at runtime. This is entirely optional. execute_process( COMMAND git rev-parse --short=16 HEAD @@ -60,53 +72,30 @@ add_definitions( -DVCS_REVISION_ID=0x${vcs_revision_id}ULL -DNODE_NAME="org.opencyphal.demos.libcyphal" ) +if (DEFINED PLATFORM_OS_TYPE) + if (${PLATFORM_OS_TYPE} STREQUAL "bsd") + add_definitions(-DPLATFORM_OS_TYPE_BSD) + elseif (${PLATFORM_OS_TYPE} STREQUAL "linux") + add_definitions(-DPLATFORM_OS_TYPE_LINUX) + endif () +endif () -# Transpile DSDL into C++ using Nunavut. Install Nunavut as follows: pip install nunavut. -# Alternatively, you can invoke the transpiler manually or use https://nunaweb.opencyphal.org. -find_package(nnvg REQUIRED) -create_dsdl_target( # Generate the support library for generated C++ headers, which is "nunavut.h". - "nunavut_support" - cpp - ${CMAKE_BINARY_DIR}/transpiled - "" - OFF - little - "only" -) -set(dsdl_root_namespace_dirs # List all DSDL root namespaces to transpile here. - ${submodules}/public_regulated_data_types/uavcan -) -foreach (ns_dir ${dsdl_root_namespace_dirs}) - get_filename_component(ns ${ns_dir} NAME) - message(STATUS "DSDL namespace ${ns} at ${ns_dir}") - create_dsdl_target( - "dsdl_${ns}" # CMake target name - cpp # Target language to transpile into - ${CMAKE_BINARY_DIR}/transpiled # Destination directory (add it to the includes) - ${ns_dir} # Source directory - OFF # Disable variable array capacity override - little # Endianness of the target platform (alternatives: "big", "any") - "never" # Support files are generated once in the nunavut_support target (above) - ${dsdl_root_namespace_dirs} # Look-up DSDL namespaces - ) - add_dependencies("dsdl_${ns}" nunavut_support) -endforeach () -include_directories(SYSTEM ${CMAKE_BINARY_DIR}/transpiled) # Make the transpiled headers available for inclusion. add_definitions(-DNUNAVUT_ASSERT=assert) # Define the LibUDPard static library build target. -add_library(udpard STATIC ${submodules}/libudpard/libudpard/udpard.c) -target_include_directories(udpard INTERFACE SYSTEM ${submodules}/libudpard/libudpard) +add_library(udpard STATIC ${submodules_dir}/libudpard/libudpard/udpard.c) +target_include_directories(udpard INTERFACE SYSTEM ${submodules_dir}/libudpard/libudpard) +include(${CMAKE_SOURCE_DIR}/../shared/udp/udp.cmake) -# Define the LibCANard static library build target. -add_library(canard STATIC ${submodules}/libcanard/libcanard/canard.c) -target_include_directories(canard INTERFACE SYSTEM ${submodules}/libcanard/libcanard) +if (${PLATFORM_OS_TYPE} STREQUAL "linux") + # Define the LibCANard static library build target. + add_library(canard STATIC ${submodules_dir}/libcanard/libcanard/canard.c) + target_include_directories(canard INTERFACE SYSTEM ${submodules_dir}/libcanard/libcanard) + include(${CMAKE_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) +endif () # Build o1heap -- a hard real-time deterministic memory allocator for embedded systems. -add_library(o1heap STATIC ${submodules}/o1heap/o1heap/o1heap.c) -target_include_directories(o1heap INTERFACE SYSTEM ${submodules}/o1heap/o1heap) - -include(${CMAKE_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) -include(${CMAKE_SOURCE_DIR}/../shared/udp/udp.cmake) +add_library(o1heap STATIC ${submodules_dir}/o1heap/o1heap/o1heap.c) +target_include_directories(o1heap INTERFACE SYSTEM ${submodules_dir}/o1heap/o1heap) add_subdirectory(src) diff --git a/libcyphal_demo/CMakePresets.json b/libcyphal_demo/CMakePresets.json new file mode 100644 index 0000000..8a89163 --- /dev/null +++ b/libcyphal_demo/CMakePresets.json @@ -0,0 +1,134 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 25, + "patch": 0 + }, + "configurePresets": [ + { + "name": "config-common", + "hidden": true, + "description": "Common configuration", + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/build", + "warnings": { + "deprecated": true, + "uninitialized": true + }, + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_CONFIGURATION_TYPES": "Release;Debug", + "CMAKE_CROSS_CONFIGS": "all", + "CMAKE_DEFAULT_BUILD_TYPE": "Release", + "CMAKE_DEFAULT_CONFIGS": "Release", + "CMAKE_PREFIX_PATH": "${sourceDir}/../submodules/nunavut", + "CMAKE_CXX_FLAGS": "-DCETL_ENABLE_DEBUG_ASSERT=1" + } + }, + { + "name": "config-linux", + "hidden": true, + "cacheVariables": { + "PLATFORM_OS_TYPE": "linux" + } + }, + { + "name": "config-bsd", + "hidden": true, + "cacheVariables": { + "PLATFORM_OS_TYPE": "bsd" + } + }, + { + "name": "OCVSMD-Linux", + "displayName": "Linux OCVSMD", + "description": "Configures OCVSMD for Linux.", + "inherits": [ + "config-common", + "config-linux" + ] + }, + { + "name": "OCVSMD-Linux-Coverage", + "displayName": "Linux OCVSMD (Coverage)", + "description": "Configures OCVSMD for Linux with coverage.", + "inherits": [ + "config-common", + "config-linux" + ], + "binaryDir": "${sourceDir}/cmake-build-coverage", + "cacheVariables": { + "CMAKE_C_FLAGS": "--coverage", + "CMAKE_CXX_FLAGS": "--coverage", + "NO_STATIC_ANALYSIS": "ON" + } + }, + { + "name": "OCVSMD-BSD", + "displayName": "BSD OCVSMD", + "description": "Configures OCVSMD for BSD", + "inherits": [ + "config-common", + "config-bsd" + ], + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + } + ], + "buildPresets": [ + { + "name": "OCVSMD-Linux-Debug", + "displayName": "Linux OCVSMD (Debug)", + "description": "Builds OCVSMD for Linux", + "configurePreset": "OCVSMD-Linux", + "configuration": "Debug" + }, + { + "name": "OCVSMD-Linux-Debug-Coverage", + "displayName": "Linux OCVSMD (Debug, Coverage)", + "description": "Builds OCVSMD for Linux with coverage", + "configurePreset": "OCVSMD-Linux-Coverage", + "configuration": "Debug" + }, + { + "name": "OCVSMD-Linux-Release", + "displayName": "Linux OCVSMD (Release)", + "description": "Builds OCVSMD for Linux", + "configurePreset": "OCVSMD-Linux", + "configuration": "Release" + }, + { + "name": "OCVSMD-BSD-Debug", + "displayName": "BSD OCVSMD (Debug)", + "description": "Builds OCVSMD for BSD", + "configurePreset": "OCVSMD-BSD", + "configuration": "Debug" + }, + { + "name": "OCVSMD-BSD-Release", + "displayName": "BSD OCVSMD (Release)", + "description": "Builds OCVSMD for BSD", + "configurePreset": "OCVSMD-BSD", + "configuration": "Release" + } + ], + "testPresets": [ + { + "name": "OCVSMD-Debug", + "displayName": "Test OCVSMD (Debug)", + "description": "Tests OCVSMD", + "configurePreset": "OCVSMD-Linux", + "configuration": "Debug" + }, + { + "name": "OCVSMD-Release", + "displayName": "Test OCVSMD (Release)", + "description": "Tests OCVSMD", + "configurePreset": "OCVSMD-Linux", + "configuration": "Release" + } + ] +} diff --git a/libcyphal_demo/cmake/modules/Findnnvg.cmake b/libcyphal_demo/cmake/modules/Findnnvg.cmake deleted file mode 100644 index 2a5c3c9..0000000 --- a/libcyphal_demo/cmake/modules/Findnnvg.cmake +++ /dev/null @@ -1,118 +0,0 @@ -# Find nnvg and setup python environment to generate C++ from DSDL. -# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Copyright 2021 OpenCyphal -# This file is adapted from: https://github.com/OpenCyphal/nunavut/blob/main/verification/cmake/modules/Findnnvg.cmake - -# Creates a target that will generate source code from dsdl definitions. -# -# Extra command line arguments can be passed to nnvg by setting the string variable NNVG_FLAGS. -# -# :param str ARG_TARGET_NAME: The name to give the target. -# :param str ARG_OUTPUT_LANGUAGE The language to generate for this target. -# :param Path ARG_OUTPUT_FOLDER: The directory to generate all source under. -# :param Path ARG_DSDL_ROOT_DIR: A directory containing the root namespace dsdl. -# :param bool ARG_ENABLE_OVR_VAR_ARRAY: Generates code with variable array capacity override enabled -# :param str ARG_SER_ENDIANNESS: One of 'any', 'big', or 'little' to pass as the value of the -# nnvg `--target-endianness` argument. Set to an empty string -# to omit this argument. -# :param str ARG_GENERATE_SUPPORT: Value for the nnvg --generate-support argument. Valid values are: -# as-needed (default) - generate support code if serialization is enabled. -# always - always generate support code. -# never - never generate support code. -# only - only generate support code. -# :param ...: A list of paths to use when looking up dependent DSDL types. -# :returns: Sets a variable "ARG_TARGET_NAME"-OUTPUT in the parent scope to the list of files the target -# will generate. For example, if ARG_TARGET_NAME == 'foo-bar' then after calling this function -# ${foo-bar-OUTPUT} will be set to the list of output files. -function(create_dsdl_target ARG_TARGET_NAME - ARG_OUTPUT_LANGUAGE - ARG_OUTPUT_FOLDER - ARG_DSDL_ROOT_DIR - ARG_ENABLE_OVR_VAR_ARRAY - ARG_SER_ENDIANNESS - ARG_GENERATE_SUPPORT) - - separate_arguments(NNVG_CMD_ARGS UNIX_COMMAND "${NNVG_FLAGS}") - - if (${ARGC} GREATER 7) - math(EXPR ARG_N_LAST "${ARGC}-1") - foreach (ARG_N RANGE 7 ${ARG_N_LAST}) - list(APPEND NNVG_CMD_ARGS "-I") - list(APPEND NNVG_CMD_ARGS "${ARGV${ARG_N}}") - endforeach (ARG_N) - endif () - - list(APPEND NNVG_CMD_ARGS --experimental-languages --target-language) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_LANGUAGE}) - list(APPEND NNVG_CMD_ARGS -O) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_FOLDER}) - list(APPEND NNVG_CMD_ARGS ${ARG_DSDL_ROOT_DIR}) - list(APPEND NNVG_CMD_ARGS "--target-endianness") - list(APPEND NNVG_CMD_ARGS ${ARG_SER_ENDIANNESS}) - list(APPEND NNVG_CMD_ARGS "--enable-serialization-asserts") - list(APPEND NNVG_CMD_ARGS "--generate-support") - list(APPEND NNVG_CMD_ARGS ${ARG_GENERATE_SUPPORT}) - if (CMAKE_CXX_STANDARD STREQUAL "14") - list(APPEND NNVG_CMD_ARGS "--language-standard=cetl++14-17") - else () - list(APPEND NNVG_CMD_ARGS "--language-standard=c++${CMAKE_CXX_STANDARD}-pmr") - endif () - - if (ARG_ENABLE_OVR_VAR_ARRAY) - list(APPEND NNVG_CMD_ARGS "--enable-override-variable-array-capacity") - message(STATUS "Enabling variable array capacity override option in generated code.") - endif () - - execute_process(COMMAND ${NNVG} --list-outputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE OUTPUT_FILES - RESULT_VARIABLE LIST_OUTPUTS_RESULT) - - if (NOT LIST_OUTPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to retrieve a list of headers nnvg would " - "generate for the ${ARG_TARGET_NAME} target (${LIST_OUTPUTS_RESULT})" - " (${NNVG})") - endif () - - execute_process(COMMAND ${NNVG} --list-inputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE INPUT_FILES - RESULT_VARIABLE LIST_INPUTS_RESULT) - - if (NOT LIST_INPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to resolve inputs using nnvg for the ${ARG_TARGET_NAME} " - "target (${LIST_INPUTS_RESULT})" - " (${NNVG})") - endif () - - add_custom_command(OUTPUT ${OUTPUT_FILES} - COMMAND ${NNVG} ${NNVG_CMD_ARGS} - DEPENDS ${INPUT_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Running nnvg ${NNVG_CMD_ARGS}") - - add_custom_target(${ARG_TARGET_NAME}-gen - DEPENDS ${OUTPUT_FILES}) - - add_library(${ARG_TARGET_NAME} INTERFACE) - - add_dependencies(${ARG_TARGET_NAME} ${ARG_TARGET_NAME}-gen) - - target_include_directories(${ARG_TARGET_NAME} INTERFACE ${ARG_OUTPUT_FOLDER}) - - set(${ARG_TARGET_NAME}-OUTPUT ${OUTPUT_FILES} PARENT_SCOPE) - -endfunction(create_dsdl_target) - - -find_program(NNVG nnvg) - -if (NNVG) - execute_process(COMMAND ${NNVG} --version OUTPUT_VARIABLE NNVG_VERSION RESULT_VARIABLE NNVG_VERSION_RESULT) - if (NNVG_VERSION_RESULT EQUAL 0) - string(STRIP ${NNVG_VERSION} NNVG_VERSION) - message(STATUS "${NNVG} --version: ${NNVG_VERSION}") - endif () -endif () - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(nnvg REQUIRED_VARS NNVG_VERSION) diff --git a/libcyphal_demo/src/CMakeLists.txt b/libcyphal_demo/src/CMakeLists.txt index 33b226b..84281e7 100644 --- a/libcyphal_demo/src/CMakeLists.txt +++ b/libcyphal_demo/src/CMakeLists.txt @@ -4,20 +4,49 @@ # SPDX-License-Identifier: MIT # Author: Sergei Shirokov -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.25) + +# Define type generation and header library all in one go. +# +set(dsdl_types_in_demo # List all the DSDL types used in the engine + uavcan/file/405.GetInfo.0.2.dsdl + uavcan/file/408.Read.1.1.dsdl + uavcan/node/430.GetInfo.1.0.dsdl + uavcan/node/435.ExecuteCommand.1.3.dsdl + uavcan/node/7509.Heartbeat.1.0.dsdl + uavcan/register/384.Access.1.0.dsdl + uavcan/register/385.List.1.0.dsdl +) +add_cyphal_library( + NAME demo + DSDL_FILES ${dsdl_types_in_demo} + ALLOW_EXPERIMENTAL_LANGUAGES + LANGUAGE cpp + LANGUAGE_STANDARD ${CYPHAL_LANGUAGE_STANDARD} + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/dsdl_transpiled + OUT_LIBRARY_TARGET demo_transpiled +) # Define the demo application build target and link it with the library. -add_executable( - demo +add_executable(demo ${CMAKE_SOURCE_DIR}/src/application.cpp ${CMAKE_SOURCE_DIR}/src/main.cpp ${CMAKE_SOURCE_DIR}/src/no_cpp_heap.cpp ) -target_link_libraries(demo PRIVATE canard o1heap udpard shared_socketcan shared_udp) + +target_link_libraries(demo + PRIVATE o1heap udpard shared_udp + PRIVATE ${demo_transpiled} +) +if (${PLATFORM_OS_TYPE} STREQUAL "linux") + target_link_libraries(demo + PRIVATE canard shared_socketcan + ) +endif () + target_include_directories(demo PRIVATE ${CMAKE_SOURCE_DIR}/src) -target_include_directories(demo PRIVATE ${submodules}/cetl/include) -target_include_directories(demo PRIVATE ${submodules}/libcyphal/include) -add_dependencies(demo dsdl_uavcan) +target_include_directories(demo PRIVATE ${submodules_dir}/cetl/include) +target_include_directories(demo PRIVATE ${submodules_dir}/libcyphal/include) if (STATIC_ANALYSIS) set_target_properties(demo PROPERTIES C_CLANG_TIDY "${clang_tidy}") diff --git a/libcyphal_demo/src/any_transport_bag.hpp b/libcyphal_demo/src/any_transport_bag.hpp new file mode 100644 index 0000000..7a9cfc7 --- /dev/null +++ b/libcyphal_demo/src/any_transport_bag.hpp @@ -0,0 +1,34 @@ +// This software is distributed under the terms of the MIT License. +// Copyright (C) OpenCyphal Development Team +// Copyright Amazon.com Inc. or its affiliates. +// SPDX-License-Identifier: MIT +// Author: Sergei Shirokov + +#ifndef ANY_TRANSPORT_BAG_HPP_INCLUDED +#define ANY_TRANSPORT_BAG_HPP_INCLUDED + +#include +#include + +/// Represents storage of some (UDP, CAN) libcyphal transport and its media. +/// +class AnyTransportBag +{ +public: + using Ptr = libcyphal::UniquePtr; + using Transport = libcyphal::transport::ITransport; + + AnyTransportBag(const AnyTransportBag&) = delete; + AnyTransportBag(AnyTransportBag&&) noexcept = delete; + AnyTransportBag& operator=(const AnyTransportBag&) = delete; + AnyTransportBag& operator=(AnyTransportBag&&) noexcept = delete; + + virtual Transport& getTransport() const = 0; + +protected: + AnyTransportBag() = default; + ~AnyTransportBag() = default; + +}; // AnyTransportBag + +#endif // ANY_TRANSPORT_BAG_HPP_INCLUDED diff --git a/libcyphal_demo/src/application.hpp b/libcyphal_demo/src/application.hpp index 0909412..1185333 100644 --- a/libcyphal_demo/src/application.hpp +++ b/libcyphal_demo/src/application.hpp @@ -8,7 +8,7 @@ #define APPLICATION_HPP #include "platform/block_memory_resource.hpp" -#include "platform/linux/epoll_single_threaded_executor.hpp" +#include "platform/defines.hpp" #include "platform/o1_heap_memory_resource.hpp" #include "platform/storage.hpp" #include "platform/string.hpp" @@ -206,7 +206,7 @@ class Application final Application(Application&&) = delete; Application& operator=(Application&&) = delete; - CETL_NODISCARD platform::Linux::EpollSingleThreadedExecutor& executor() noexcept + CETL_NODISCARD platform::SingleThreadedExecutor& executor() noexcept { return executor_; } @@ -244,13 +244,13 @@ class Application final private: // MARK: Data members: - platform::Linux::EpollSingleThreadedExecutor executor_; - platform::O1HeapMemoryResource o1_heap_mr_; - platform::O1HeapMemoryResource o1_block_heap_mr_; - platform::BlockMemoryResource media_block_mr_; - platform::storage::KeyValue storage_; - libcyphal::application::registry::Registry registry_; - Regs regs_; + platform::SingleThreadedExecutor executor_; + platform::O1HeapMemoryResource o1_heap_mr_; + platform::O1HeapMemoryResource o1_block_heap_mr_; + platform::BlockMemoryResource media_block_mr_; + platform::storage::KeyValue storage_; + libcyphal::application::registry::Registry registry_; + Regs regs_; }; // Application diff --git a/libcyphal_demo/src/main.cpp b/libcyphal_demo/src/main.cpp index 9571004..592f34b 100644 --- a/libcyphal_demo/src/main.cpp +++ b/libcyphal_demo/src/main.cpp @@ -4,10 +4,10 @@ // SPDX-License-Identifier: MIT // Author: Sergei Shirokov +#include "any_transport_bag.hpp" #include "application.hpp" #include "exec_cmd_provider.hpp" #include "file_downloader.hpp" -#include "transport_bag_can.hpp" #include "transport_bag_udp.hpp" #include @@ -33,6 +33,15 @@ #include #include +#ifdef __linux__ +#include "transport_bag_can.hpp" +#endif + +#if defined (__APPLE__) +#include +#define environ (*_NSGetEnviron ()) +#endif + using namespace std::chrono_literals; // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers) @@ -187,34 +196,41 @@ libcyphal::Expected run_application(const char* const root_path) auto node_params = application.getNodeParams(); auto iface_params = application.getIfaceParams(); - // 1. Create the transport layer object. First try CAN, then UDP. - // - TransportBagCan transport_bag_can{general_mr, executor, media_block_mr}; - TransportBagUdp transport_bag_udp{general_mr, executor, media_block_mr}; + // 1. Create the transport layer object (try first UDP, then CAN). // - libcyphal::transport::ITransport* transport_iface = transport_bag_can.create(iface_params); - if (transport_iface == nullptr) + AnyTransportBag::Ptr any_transport_bag; + if (auto maybe_udp_transport_bag = TransportBagUdp::make(general_mr, executor, media_block_mr, iface_params)) { - transport_iface = transport_bag_udp.create(iface_params); + any_transport_bag = std::move(maybe_udp_transport_bag); } - if (transport_iface == nullptr) + else { - std::cerr << "❌ Failed to create any transport.\n"; - return ExitCode::TransportCreationFailure; +#ifdef __linux__ + if (auto maybe_can_transport_bag = TransportBagCan::make(general_mr, executor, media_block_mr, iface_params)) + { + any_transport_bag = std::move(maybe_can_transport_bag); + } + else +#endif // __linux__ + { + std::cerr << "❌ Failed to create any transport.\n"; + return ExitCode::TransportCreationFailure; + } } + auto& transport = any_transport_bag->getTransport(); TransferIdMap transfer_id_map{general_mr}; // 2. Create the presentation layer object. // const auto unique_id = application.getUniqueId(); - (void) transport_iface->setLocalNodeId(node_params.id.value()[0]); - std::cout << "Node ID : " << transport_iface->getLocalNodeId().value_or(65535) << "\n"; + (void) transport.setLocalNodeId(node_params.id.value()[0]); + std::cout << "Node ID : " << transport.getLocalNodeId().value_or(65535) << "\n"; std::cout << "Node Name : '" << node_params.description.value().c_str() << "'\n"; std::cout << "Unique-ID : "; PrintUniqueIdTo(unique_id, std::cout); std::cout << "\n"; // - libcyphal::presentation::Presentation presentation{general_mr, executor, *transport_iface}; + libcyphal::presentation::Presentation presentation{general_mr, executor, transport}; presentation.setTransferIdMap(&transfer_id_map); // 3. Create the node object with name. @@ -314,7 +330,7 @@ int main(const int argc, char* const argv[]) // Should we restart? if (cetl::get(result)) { - (void) ::execve(argv[0], argv, ::environ); // NOLINT + (void) ::execve(argv[0], argv, environ); // NOLINT return static_cast(ExitCode::RestartFailure); } diff --git a/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp new file mode 100644 index 0000000..412fcfc --- /dev/null +++ b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp @@ -0,0 +1,272 @@ +// This software is distributed under the terms of the MIT License. +// Copyright (C) OpenCyphal Development Team +// Copyright Amazon.com Inc. or its affiliates. +// SPDX-License-Identifier: MIT +// Author: Sergei Shirokov + +#ifndef PLATFORM_BSD_KQUEUE_SINGLE_THREADED_EXECUTOR_HPP_INCLUDED +#define PLATFORM_BSD_KQUEUE_SINGLE_THREADED_EXECUTOR_HPP_INCLUDED + +#include "platform/posix/posix_executor_extension.hpp" +#include "platform/posix/posix_platform_error.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace platform +{ +namespace bsd +{ + +/// @brief Defines BSD Linux platform-specific single-threaded executor based on `kqueue` mechanism. +/// +class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThreadedExecutor, + public posix::IPosixExecutorExtension +{ +public: + KqueueSingleThreadedExecutor() + : kqueuefd_{::kqueue()} + , total_awaitables_{0} + { + } + + KqueueSingleThreadedExecutor(const KqueueSingleThreadedExecutor&) = delete; + KqueueSingleThreadedExecutor(KqueueSingleThreadedExecutor&&) noexcept = delete; + KqueueSingleThreadedExecutor& operator=(const KqueueSingleThreadedExecutor&) = delete; + KqueueSingleThreadedExecutor& operator=(KqueueSingleThreadedExecutor&&) noexcept = delete; + + ~KqueueSingleThreadedExecutor() override + { + if (kqueuefd_ >= 0) + { + ::close(kqueuefd_); + } + } + + CETL_NODISCARD cetl::optional pollAwaitableResourcesFor( + const cetl::optional timeout) const override + { + CETL_DEBUG_ASSERT((total_awaitables_ > 0) || timeout, + "Infinite timeout without awaitables means that we will sleep forever."); + + if (total_awaitables_ == 0) + { + if (!timeout) + { + return libcyphal::ArgumentError{}; + } + + std::this_thread::sleep_for(*timeout); + return cetl::nullopt; + } + + // Convert libcyphal timeout (if any) to the `struct timespec` timeout in ns. + // Any possible negative timeout will be treated as zero (return immediately from the `::kevent`). + // + struct timespec timeout_spec + {}; + const struct timespec* timeout_spec_ptr = nullptr; + if (timeout) + { + using PollDuration = std::chrono::nanoseconds; + using TimeoutNsType = decltype(timespec::tv_nsec); + + // Fill nanoseconds part of the timeout spec taking into account the maximum possible value. + // + timeout_spec.tv_nsec = static_cast( // + std::max(static_cast(0), + std::min(std::chrono::duration_cast(*timeout).count(), + static_cast(std::numeric_limits::max())))); + + timeout_spec_ptr = &timeout_spec; + } + + std::array evs{}; + const int kqueue_result = ::kevent(kqueuefd_, nullptr, 0, evs.data(), evs.size(), timeout_spec_ptr); + if (kqueue_result < 0) + { + const auto err = errno; + return libcyphal::transport::PlatformError{posix::PosixPlatformError{err}}; + } + if (kqueue_result == 0) + { + return cetl::nullopt; + } + const auto kqueue_nfds = static_cast(kqueue_result); + + const auto now_time = now(); + for (std::size_t index = 0; index < kqueue_nfds; ++index) + { + const KEvent& ev = evs[index]; + if (auto* const cb_interface = static_cast(ev.udata)) + { + cb_interface->schedule(Callback::Schedule::Once{now_time}); + } + } + + return cetl::nullopt; + } + +protected: + // MARK: - IPosixExecutorExtension + + CETL_NODISCARD Callback::Any registerAwaitableCallback(Callback::Function&& function, + const Trigger::Variant& trigger) override + { + AwaitableNode new_cb_node{*this, std::move(function)}; + + cetl::visit( // + cetl::make_overloaded( + [&new_cb_node](const Trigger::Readable& readable) { + // + new_cb_node.setup(readable.fd, EVFILT_READ); + }, + [&new_cb_node](const Trigger::Writable& writable) { + // + new_cb_node.setup(writable.fd, EVFILT_WRITE); + }), + trigger); + + insertCallbackNode(new_cb_node); + return {std::move(new_cb_node)}; + } + + // MARK: - RTTI + + CETL_NODISCARD void* _cast_(const cetl::type_id& id) & noexcept override + { + if (id == IPosixExecutorExtension::_get_type_id_()) + { + return static_cast(this); + } + return Base::_cast_(id); + } + CETL_NODISCARD const void* _cast_(const cetl::type_id& id) const& noexcept override + { + if (id == IPosixExecutorExtension::_get_type_id_()) + { + return static_cast(this); + } + return Base::_cast_(id); + } + +private: + using KEvent = struct kevent; + using Base = SingleThreadedExecutor; + using Self = KqueueSingleThreadedExecutor; + + /// No Sonar cpp:S4963 b/c `AwaitableNode` supports move operation. + /// + class AwaitableNode final : public CallbackNode // NOSONAR cpp:S4963 + { + public: + AwaitableNode(Self& executor, Callback::Function&& function) + : CallbackNode{executor, std::move(function)} + , fd_{-1} + , events_{0} + { + } + + ~AwaitableNode() override + { + if (fd_ >= 0) + { + KEvent ev{}; + EV_SET(&ev, fd_, events_, EV_DELETE, NOTE_DELETE, 0, 0); + ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); + getExecutor().total_awaitables_--; + } + } + + AwaitableNode(AwaitableNode&& other) noexcept + : CallbackNode(std::move(static_cast(other))) + , fd_{std::exchange(other.fd_, -1)} + , events_{std::exchange(other.events_, 0)} + { + if (fd_ >= 0) + { + KEvent ev{}; + EV_SET(&ev, fd_, events_, EV_DELETE, NOTE_DELETE, 0, 0); + ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); + EV_SET(&ev, fd_, events_, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, this); + ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); + } + } + + AwaitableNode(const AwaitableNode&) = delete; + AwaitableNode& operator=(const AwaitableNode&) = delete; + AwaitableNode& operator=(AwaitableNode&& other) noexcept = delete; + + int fd() const noexcept + { + return fd_; + } + + std::uint32_t events() const noexcept + { + return events_; + } + + void setup(const int fd, const std::uint32_t events) noexcept + { + CETL_DEBUG_ASSERT(fd >= 0, ""); + CETL_DEBUG_ASSERT(events != 0, ""); + + fd_ = fd; + events_ = events | EVFILT_VNODE; + + getExecutor().total_awaitables_++; + KEvent ev{}; + EV_SET(&ev, fd, events_, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, this); + ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); + } + + private: + Self& getExecutor() noexcept + { + // No lint b/c we know for sure that the executor is of type `Self`. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) + return static_cast(executor()); + } + + // MARK: Data members: + + int fd_; + std::uint32_t events_; + + }; // AwaitableNode + + // MARK: - Data members: + + static constexpr int MaxEvents = 16; + + int kqueuefd_; + std::size_t total_awaitables_; + +}; // KqueueSingleThreadedExecutor + +} // namespace bsd +} // namespace platform + +#endif // PLATFORM_BSD_KQUEUE_SINGLE_THREADED_EXECUTOR_HPP_INCLUDED diff --git a/libcyphal_demo/src/platform/common_helpers.hpp b/libcyphal_demo/src/platform/common_helpers.hpp index f1bb958..97fdc76 100644 --- a/libcyphal_demo/src/platform/common_helpers.hpp +++ b/libcyphal_demo/src/platform/common_helpers.hpp @@ -10,10 +10,13 @@ #include #include #include -#include #include #include +#ifdef __linux__ +#include +#endif + #include namespace platform @@ -54,6 +57,8 @@ struct CommonHelpers } }; +#ifdef __linux__ + struct Can { static cetl::optional transientErrorReporter( @@ -101,6 +106,8 @@ struct CommonHelpers }; // Can +#endif // __linux__ + struct Udp { static cetl::optional transientErrorReporter( diff --git a/libcyphal_demo/src/platform/defines.hpp b/libcyphal_demo/src/platform/defines.hpp new file mode 100644 index 0000000..4850cf4 --- /dev/null +++ b/libcyphal_demo/src/platform/defines.hpp @@ -0,0 +1,27 @@ +// This software is distributed under the terms of the MIT License. +// Copyright (C) OpenCyphal Development Team +// Copyright Amazon.com Inc. or its affiliates. +// SPDX-License-Identifier: MIT +// Author: Sergei Shirokov + +#ifndef PLATFORM_DEFINES_HPP_INCLUDED +#define PLATFORM_DEFINES_HPP_INCLUDED + +#ifdef PLATFORM_OS_TYPE_BSD +# include "bsd/kqueue_single_threaded_executor.hpp" +#else +# include "linux/epoll_single_threaded_executor.hpp" +#endif + +namespace platform +{ + +#ifdef PLATFORM_OS_TYPE_BSD +using SingleThreadedExecutor = bsd::KqueueSingleThreadedExecutor; +#else +using SingleThreadedExecutor = Linux::EpollSingleThreadedExecutor; +#endif + +} // namespace platform + +#endif // PLATFORM_DEFINES_HPP_INCLUDED diff --git a/libcyphal_demo/src/platform/linux/can/can_media.hpp b/libcyphal_demo/src/platform/linux/can/can_media.hpp index af3dc60..493b67f 100644 --- a/libcyphal_demo/src/platform/linux/can/can_media.hpp +++ b/libcyphal_demo/src/platform/linux/can/can_media.hpp @@ -80,8 +80,9 @@ class CanMedia final : public libcyphal::transport::can::IMedia } } - CanMedia(const CanMedia&) = delete; - CanMedia& operator=(const CanMedia&) = delete; + CanMedia(const CanMedia&) = delete; + CanMedia& operator=(const CanMedia&) = delete; + CanMedia* operator=(CanMedia&&) noexcept = delete; CanMedia(CanMedia&& other) noexcept : general_mr_{other.general_mr_} @@ -92,7 +93,6 @@ class CanMedia final : public libcyphal::transport::can::IMedia , tx_mr_{other.tx_mr_} { } - CanMedia* operator=(CanMedia&&) noexcept = delete; void tryReopen() { diff --git a/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp b/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp index 6ef5455..cd828e7 100644 --- a/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp +++ b/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp @@ -10,6 +10,7 @@ #include "platform/posix/posix_executor_extension.hpp" #include "platform/posix/posix_platform_error.hpp" +#include #include #include #include @@ -36,7 +37,7 @@ namespace platform namespace Linux { -/// @brief Defines Linux platform specific single-threaded executor based on `epoll` mechanism. +/// @brief Defines Linux platform-specific single-threaded executor based on `epoll` mechanism. /// class EpollSingleThreadedExecutor final : public libcyphal::platform::SingleThreadedExecutor, public posix::IPosixExecutorExtension @@ -61,9 +62,8 @@ class EpollSingleThreadedExecutor final : public libcyphal::platform::SingleThre } } - using PollFailure = cetl::variant; - - cetl::optional pollAwaitableResourcesFor(const cetl::optional timeout) const + CETL_NODISCARD cetl::optional pollAwaitableResourcesFor( + const cetl::optional timeout) const override { CETL_DEBUG_ASSERT((total_awaitables_ > 0) || timeout, "Infinite timeout without awaitables means that we will sleep forever."); @@ -229,6 +229,8 @@ class EpollSingleThreadedExecutor final : public libcyphal::platform::SingleThre private: Self& getExecutor() noexcept { + // No lint b/c we know for sure that the executor is of type `Self`. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast) return static_cast(executor()); } diff --git a/libcyphal_demo/src/platform/posix/posix_executor_extension.hpp b/libcyphal_demo/src/platform/posix/posix_executor_extension.hpp index 068e884..e33b9d9 100644 --- a/libcyphal_demo/src/platform/posix/posix_executor_extension.hpp +++ b/libcyphal_demo/src/platform/posix/posix_executor_extension.hpp @@ -7,9 +7,13 @@ #ifndef PLATFORM_POSIX_EXECUTOR_EXTENSION_HPP_INCLUDED #define PLATFORM_POSIX_EXECUTOR_EXTENSION_HPP_INCLUDED +#include #include #include +#include #include +#include +#include namespace platform { @@ -47,6 +51,11 @@ class IPosixExecutorExtension libcyphal::IExecutor::Callback::Function&& function, const Trigger::Variant& trigger) = 0; + using PollFailure = cetl::variant; + + CETL_NODISCARD virtual cetl::optional pollAwaitableResourcesFor( + const cetl::optional timeout) const = 0; + // MARK: RTTI static constexpr cetl::type_id _get_type_id_() noexcept diff --git a/libcyphal_demo/src/platform/posix/posix_platform_error.hpp b/libcyphal_demo/src/platform/posix/posix_platform_error.hpp index c8fc7d2..ccdfc57 100644 --- a/libcyphal_demo/src/platform/posix/posix_platform_error.hpp +++ b/libcyphal_demo/src/platform/posix/posix_platform_error.hpp @@ -33,6 +33,10 @@ class PosixPlatformError final : public libcyphal::transport::IPlatformError // MARK: IPlatformError + /// Gets platform-specific error code. + /// + /// In this case, the error code is the POSIX error code (aka `errno`). + /// std::uint32_t code() const noexcept override { return static_cast(code_); diff --git a/libcyphal_demo/src/transport_bag_can.hpp b/libcyphal_demo/src/transport_bag_can.hpp index 536628a..be92156 100644 --- a/libcyphal_demo/src/transport_bag_can.hpp +++ b/libcyphal_demo/src/transport_bag_can.hpp @@ -7,12 +7,15 @@ #ifndef TRANSPORT_BAG_CAN_HPP_INCLUDED #define TRANSPORT_BAG_CAN_HPP_INCLUDED +#include "any_transport_bag.hpp" #include "application.hpp" #include "platform/block_memory_resource.hpp" #include "platform/common_helpers.hpp" #include "platform/linux/can/can_media.hpp" +#include #include +#include #include #include #include @@ -26,45 +29,64 @@ /// Holds (internally) instance of the CAN transport and its media (if any). /// -struct TransportBagCan final +class TransportBagCan final : public AnyTransportBag { - TransportBagCan(cetl::pmr::memory_resource& general_mr, - libcyphal::IExecutor& executor, - platform::BlockMemoryResource& media_block_mr) - : general_mr_{general_mr} - , executor_{executor} - , media_block_mr_{media_block_mr} - , media_collection_{general_mr, executor, media_block_mr} + /// Defines private specification for making interface unique ptr. + /// + struct Spec { + explicit Spec() = default; + }; + +public: + Transport& getTransport() const override + { + CETL_DEBUG_ASSERT(transport_, ""); + return *transport_; } - libcyphal::transport::can::ICanTransport* create(const Application::IfaceParams& params) + static Ptr make(cetl::pmr::memory_resource& general_mr, + libcyphal::IExecutor& executor, + platform::BlockMemoryResource& media_block_mr, + const Application::IfaceParams& params) { if (params.can_iface.value().empty()) { return nullptr; } - media_collection_.parse(params.can_iface.value()); - auto maybe_can_transport = makeTransport({general_mr_}, executor_, media_collection_.span(), TxQueueCapacity); + auto any_transport_bag = cetl::pmr::InterfaceFactory::make_unique( // + cetl::pmr::polymorphic_allocator{&general_mr}, + Spec{}, + general_mr, + executor, + media_block_mr); + if (!any_transport_bag) + { + return nullptr; + } + auto& bag = static_cast(*any_transport_bag); // NOLINT + + bag.media_collection_.parse(params.can_iface.value()); + auto maybe_can_transport = makeTransport({general_mr}, executor, bag.media_collection_.span(), TxQueueCapacity); if (const auto* failure = cetl::get_if(&maybe_can_transport)) { std::cerr << "❌ Failed to create CAN transport (iface='" << static_cast(params.can_iface.value()) << "').\n"; return nullptr; } - transport_ = cetl::get>( // + bag.transport_ = cetl::get>( // std::move(maybe_can_transport)); std::cout << "CAN Iface : '" << params.can_iface.value().c_str() << "'\n"; - const std::size_t mtu = transport_->getProtocolParams().mtu_bytes; + const std::size_t mtu = bag.transport_->getProtocolParams().mtu_bytes; std::cout << "Iface MTU : " << mtu << "\n"; // Canard allocates memory for raw bytes block only, so there is no alignment requirement. constexpr std::size_t block_alignment = 1; const std::size_t block_size = mtu; - const std::size_t pool_size = media_collection_.count() * TxQueueCapacity * block_size; - media_block_mr_.setup(pool_size, block_size, block_alignment); + const std::size_t pool_size = bag.media_collection_.count() * TxQueueCapacity * block_size; + bag.media_block_mr_.setup(pool_size, block_size, block_alignment); // To support redundancy (multiple homogeneous interfaces), it's important to have a non-default // handler which "swallows" expected transient failures (by returning `nullopt` result). @@ -72,23 +94,36 @@ struct TransportBagCan final // if some of its media encounter transient failures - thus breaking the whole redundancy goal, // namely, maintain communication if at least one of the interfaces is still up and running. // - transport_->setTransientErrorHandler([](auto&) { return cetl::nullopt; }); - // transport_->setTransientErrorHandler(platform::CommonHelpers::Can::transientErrorReporter); + bag.transport_->setTransientErrorHandler([](auto&) { return cetl::nullopt; }); + // bag.transport_->setTransientErrorHandler(platform::CommonHelpers::Can::transientErrorReporter); - return transport_.get(); + return any_transport_bag; + } + + TransportBagCan(Spec, + cetl::pmr::memory_resource& general_mr, + libcyphal::IExecutor& executor, + platform::BlockMemoryResource& media_block_mr) + : general_mr_{general_mr} + , executor_{executor} + , media_block_mr_{media_block_mr} + , media_collection_{general_mr, executor, media_block_mr} + { } private: + using TransportPtr = libcyphal::UniquePtr; + // Our current max `SerializationBufferSizeBytes` is 515 bytes (for `uavcan.register.Access.Request.1.0`) // Assuming CAN classic presentation MTU of 7 bytes (plus a bit of overhead like CRC and stuff), // let's calculate the required TX queue capacity, and make it twice to accommodate 2 such messages. static constexpr std::size_t TxQueueCapacity = 2 * (515U + 8U) / 7U; - cetl::pmr::memory_resource& general_mr_; - libcyphal::IExecutor& executor_; - platform::BlockMemoryResource& media_block_mr_; - platform::Linux::CanMediaCollection media_collection_; - libcyphal::UniquePtr transport_; + cetl::pmr::memory_resource& general_mr_; + libcyphal::IExecutor& executor_; + platform::BlockMemoryResource& media_block_mr_; + platform::Linux::CanMediaCollection media_collection_; + TransportPtr transport_; }; // TransportBagCan diff --git a/libcyphal_demo/src/transport_bag_udp.hpp b/libcyphal_demo/src/transport_bag_udp.hpp index 66db8e6..71d5047 100644 --- a/libcyphal_demo/src/transport_bag_udp.hpp +++ b/libcyphal_demo/src/transport_bag_udp.hpp @@ -7,12 +7,15 @@ #ifndef TRANSPORT_BAG_UDP_HPP_INCLUDED #define TRANSPORT_BAG_UDP_HPP_INCLUDED +#include "any_transport_bag.hpp" #include "application.hpp" #include "platform/block_memory_resource.hpp" #include "platform/common_helpers.hpp" #include "platform/posix/udp/udp_media.hpp" +#include #include +#include #include #include #include @@ -25,59 +28,92 @@ /// Holds (internally) instance of the UDP transport and its media (if any). /// -struct TransportBagUdp final +class TransportBagUdp final : public AnyTransportBag { - TransportBagUdp(cetl::pmr::memory_resource& general_memory, - libcyphal::IExecutor& executor, - platform::BlockMemoryResource& media_block_mr) - : general_mr_{general_memory} - , executor_{executor} - , media_block_mr_{media_block_mr} - , media_collection_{general_memory, executor, media_block_mr} + /// Defines private specification for making interface unique ptr. + /// + struct Spec : libcyphal::detail::UniquePtrSpec { + explicit Spec() = default; + }; + +public: + Transport& getTransport() const override + { + CETL_DEBUG_ASSERT(transport_, ""); + return *transport_; } - libcyphal::transport::udp::IUdpTransport* create(const Application::IfaceParams& params) + static Ptr make(cetl::pmr::memory_resource& general_mr, + libcyphal::IExecutor& executor, + platform::BlockMemoryResource& media_block_mr, + const Application::IfaceParams& params) + { if (params.udp_iface.value().empty()) { return nullptr; } - media_collection_.parse(params.udp_iface.value()); - auto maybe_udp_transport = makeTransport({general_mr_}, executor_, media_collection_.span(), TxQueueCapacity); + auto any_transport_bag = cetl::pmr::InterfaceFactory::make_unique( // + cetl::pmr::polymorphic_allocator{&general_mr}, + Spec{}, + general_mr, + executor, + media_block_mr); + if (!any_transport_bag) + { + return nullptr; + } + auto& bag = static_cast(*any_transport_bag); // NOLINT + + bag.media_collection_.parse(params.udp_iface.value()); + auto maybe_udp_transport = makeTransport({general_mr}, executor, bag.media_collection_.span(), TxQueueCapacity); if (const auto* failure = cetl::get_if(&maybe_udp_transport)) { std::cerr << "❌ Failed to create UDP transport (iface='" << static_cast(params.udp_iface.value()) << "').\n"; return nullptr; } - transport_ = cetl::get>( // + bag.transport_ = cetl::get>( // std::move(maybe_udp_transport)); std::cout << "UDP Iface : '" << params.udp_iface.value().c_str() << "'\n"; - const std::size_t mtu = transport_->getProtocolParams().mtu_bytes; + const std::size_t mtu = bag.transport_->getProtocolParams().mtu_bytes; std::cout << "Iface MTU : " << mtu << "\n"; // Udpard allocates memory for raw bytes block only, so there is no alignment requirement. constexpr std::size_t block_alignment = 1; const std::size_t block_size = mtu; - const std::size_t pool_size = media_collection_.count() * TxQueueCapacity * block_size; - media_block_mr_.setup(pool_size, block_size, block_alignment); + const std::size_t pool_size = bag.media_collection_.count() * TxQueueCapacity * block_size; + bag.media_block_mr_.setup(pool_size, block_size, block_alignment); - transport_->setTransientErrorHandler(platform::CommonHelpers::Udp::transientErrorReporter); + bag.transport_->setTransientErrorHandler(platform::CommonHelpers::Udp::transientErrorReporter); - return transport_.get(); + return any_transport_bag; + } + + TransportBagUdp(Spec, + cetl::pmr::memory_resource& general_memory, + libcyphal::IExecutor& executor, + platform::BlockMemoryResource& media_block_mr) + : general_mr_{general_memory} + , executor_{executor} + , media_block_mr_{media_block_mr} + , media_collection_{general_memory, executor, media_block_mr} + { } private: + using TransportPtr = libcyphal::UniquePtr; + static constexpr std::size_t TxQueueCapacity = 16; - cetl::pmr::memory_resource& general_mr_; - libcyphal::IExecutor& executor_; - platform::BlockMemoryResource& media_block_mr_; - platform::posix::UdpMediaCollection media_collection_; - libcyphal::UniquePtr transport_; + cetl::pmr::memory_resource& general_mr_; + libcyphal::IExecutor& executor_; + platform::BlockMemoryResource& media_block_mr_; + platform::posix::UdpMediaCollection media_collection_; + TransportPtr transport_; }; // TransportBagUdp diff --git a/submodules/nunavut b/submodules/nunavut new file mode 160000 index 0000000..cd5f563 --- /dev/null +++ b/submodules/nunavut @@ -0,0 +1 @@ +Subproject commit cd5f563bd1587990cfa59fa1a7932c42be7f0dca From 14aead5fef90dc7027b98d9afdc84ce38e756fb5 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Mon, 17 Feb 2025 15:26:32 +0200 Subject: [PATCH 2/9] fixed incorrect kqueue executor implementation a) can't combine filters (like `| EVFILT_VNODE`) b) wrong `EV_CLEAR` usage c) wrong `NOTE_xxx` usage --- .../bsd/kqueue_single_threaded_executor.hpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp index 412fcfc..5614f73 100644 --- a/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp +++ b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp @@ -184,7 +184,7 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr AwaitableNode(Self& executor, Callback::Function&& function) : CallbackNode{executor, std::move(function)} , fd_{-1} - , events_{0} + , filter_{0} { } @@ -193,7 +193,7 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr if (fd_ >= 0) { KEvent ev{}; - EV_SET(&ev, fd_, events_, EV_DELETE, NOTE_DELETE, 0, 0); + EV_SET(&ev, fd_, filter_, EV_DELETE, 0, 0, 0); ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); getExecutor().total_awaitables_--; } @@ -202,14 +202,14 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr AwaitableNode(AwaitableNode&& other) noexcept : CallbackNode(std::move(static_cast(other))) , fd_{std::exchange(other.fd_, -1)} - , events_{std::exchange(other.events_, 0)} + , filter_{std::exchange(other.filter_, 0)} { if (fd_ >= 0) { KEvent ev{}; - EV_SET(&ev, fd_, events_, EV_DELETE, NOTE_DELETE, 0, 0); + EV_SET(&ev, fd_, filter_, EV_DELETE, 0, 0, 0); ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); - EV_SET(&ev, fd_, events_, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, this); + EV_SET(&ev, fd_, filter_, EV_ADD, 0, 0, this); ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); } } @@ -223,22 +223,22 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr return fd_; } - std::uint32_t events() const noexcept + std::int16_t filter() const noexcept { - return events_; + return filter_; } - void setup(const int fd, const std::uint32_t events) noexcept + void setup(const int fd, const std::int16_t filter) noexcept { CETL_DEBUG_ASSERT(fd >= 0, ""); - CETL_DEBUG_ASSERT(events != 0, ""); + CETL_DEBUG_ASSERT(filter != 0, ""); fd_ = fd; - events_ = events | EVFILT_VNODE; + filter_ = filter; getExecutor().total_awaitables_++; KEvent ev{}; - EV_SET(&ev, fd, events_, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, this); + EV_SET(&ev, fd, filter_, EV_ADD, 0, 0, this); ::kevent(getExecutor().kqueuefd_, &ev, 1, nullptr, 0, nullptr); } @@ -253,7 +253,7 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr // MARK: Data members: int fd_; - std::uint32_t events_; + std::int16_t filter_; }; // AwaitableNode From 86cb18340659cb3ed963209eafcc36355fe21a53 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 18 Feb 2025 19:24:20 +0200 Subject: [PATCH 3/9] silence `EINTR` "error" condition --- libcyphal_demo/src/main.cpp | 6 +++++- .../src/platform/bsd/kqueue_single_threaded_executor.hpp | 6 ++++++ .../src/platform/linux/epoll_single_threaded_executor.hpp | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libcyphal_demo/src/main.cpp b/libcyphal_demo/src/main.cpp index 592f34b..76c45ca 100644 --- a/libcyphal_demo/src/main.cpp +++ b/libcyphal_demo/src/main.cpp @@ -302,7 +302,11 @@ libcyphal::Expected run_application(const char* const root_path) { timeout = std::min(timeout, spin_result.next_exec_time.value() - executor.now()); } - (void) executor.pollAwaitableResourcesFor(cetl::make_optional(timeout)); + if (const auto failure = executor.pollAwaitableResourcesFor(cetl::make_optional(timeout))) + { + (void) failure; + std::cerr << "❌ Poll failure.\n"; + } } // std::cout << "🏁 Done.\n-----------\nRun Stats:\n"; diff --git a/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp index 5614f73..c3941ea 100644 --- a/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp +++ b/libcyphal_demo/src/platform/bsd/kqueue_single_threaded_executor.hpp @@ -107,6 +107,12 @@ class KqueueSingleThreadedExecutor final : public libcyphal::platform::SingleThr if (kqueue_result < 0) { const auto err = errno; + if (err == EINTR) + { + // Normally, we would just retry a system call (`::kevent`), + // but we need updated timeout (from the main loop). + return cetl::nullopt; + } return libcyphal::transport::PlatformError{posix::PosixPlatformError{err}}; } if (kqueue_result == 0) diff --git a/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp b/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp index cd828e7..695c396 100644 --- a/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp +++ b/libcyphal_demo/src/platform/linux/epoll_single_threaded_executor.hpp @@ -98,6 +98,12 @@ class EpollSingleThreadedExecutor final : public libcyphal::platform::SingleThre if (epoll_result < 0) { const auto err = errno; + if (err == EINTR) + { + // Normally, we would just retry a system call (`::epoll_wait`), + // but we need updated timeout (from the main loop). + return cetl::nullopt; + } return libcyphal::transport::PlatformError{posix::PosixPlatformError{err}}; } if (epoll_result == 0) From f4caf23f125c22b764c2dbd59b1f94bcbc608674 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Tue, 18 Feb 2025 19:57:32 +0200 Subject: [PATCH 4/9] minor cmake fixes --- libcyphal_demo/CMakeLists.txt | 2 +- libcyphal_demo/CMakePresets.json | 74 ++++++++++++++++---------------- libcyphal_demo/README.md | 14 ++++-- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/libcyphal_demo/CMakeLists.txt b/libcyphal_demo/CMakeLists.txt index 70be951..30b06b7 100644 --- a/libcyphal_demo/CMakeLists.txt +++ b/libcyphal_demo/CMakeLists.txt @@ -43,7 +43,7 @@ if (STATIC_ANALYSIS) # clang-tidy (separate config files per directory) find_program(clang_tidy NAMES clang-tidy) if (NOT clang_tidy) - message(FATAL_ERROR "Could not locate clang-tidy") + message(WARNING "Could not locate clang-tidy") endif () message(STATUS "Using clang-tidy: ${clang_tidy}") endif () diff --git a/libcyphal_demo/CMakePresets.json b/libcyphal_demo/CMakePresets.json index 8a89163..dfb3cbe 100644 --- a/libcyphal_demo/CMakePresets.json +++ b/libcyphal_demo/CMakePresets.json @@ -41,18 +41,18 @@ } }, { - "name": "OCVSMD-Linux", - "displayName": "Linux OCVSMD", - "description": "Configures OCVSMD for Linux.", + "name": "Demo-Linux", + "displayName": "Linux Demo", + "description": "Configures Demo for Linux.", "inherits": [ "config-common", "config-linux" ] }, { - "name": "OCVSMD-Linux-Coverage", - "displayName": "Linux OCVSMD (Coverage)", - "description": "Configures OCVSMD for Linux with coverage.", + "name": "Demo-Linux-Coverage", + "displayName": "Linux Demo (Coverage)", + "description": "Configures Demo for Linux with coverage.", "inherits": [ "config-common", "config-linux" @@ -65,9 +65,9 @@ } }, { - "name": "OCVSMD-BSD", - "displayName": "BSD OCVSMD", - "description": "Configures OCVSMD for BSD", + "name": "Demo-BSD", + "displayName": "BSD Demo", + "description": "Configures Demo for BSD", "inherits": [ "config-common", "config-bsd" @@ -80,54 +80,54 @@ ], "buildPresets": [ { - "name": "OCVSMD-Linux-Debug", - "displayName": "Linux OCVSMD (Debug)", - "description": "Builds OCVSMD for Linux", - "configurePreset": "OCVSMD-Linux", + "name": "Demo-Linux-Debug", + "displayName": "Linux Demo (Debug)", + "description": "Builds Demo for Linux", + "configurePreset": "Demo-Linux", "configuration": "Debug" }, { - "name": "OCVSMD-Linux-Debug-Coverage", - "displayName": "Linux OCVSMD (Debug, Coverage)", - "description": "Builds OCVSMD for Linux with coverage", - "configurePreset": "OCVSMD-Linux-Coverage", + "name": "Demo-Linux-Debug-Coverage", + "displayName": "Linux Demo (Debug, Coverage)", + "description": "Builds Demo for Linux with coverage", + "configurePreset": "Demo-Linux-Coverage", "configuration": "Debug" }, { - "name": "OCVSMD-Linux-Release", - "displayName": "Linux OCVSMD (Release)", - "description": "Builds OCVSMD for Linux", - "configurePreset": "OCVSMD-Linux", + "name": "Demo-Linux-Release", + "displayName": "Linux Demo (Release)", + "description": "Builds Demo for Linux", + "configurePreset": "Demo-Linux", "configuration": "Release" }, { - "name": "OCVSMD-BSD-Debug", - "displayName": "BSD OCVSMD (Debug)", - "description": "Builds OCVSMD for BSD", - "configurePreset": "OCVSMD-BSD", + "name": "Demo-BSD-Debug", + "displayName": "BSD Demo (Debug)", + "description": "Builds Demo for BSD", + "configurePreset": "Demo-BSD", "configuration": "Debug" }, { - "name": "OCVSMD-BSD-Release", - "displayName": "BSD OCVSMD (Release)", - "description": "Builds OCVSMD for BSD", - "configurePreset": "OCVSMD-BSD", + "name": "Demo-BSD-Release", + "displayName": "BSD Demo (Release)", + "description": "Builds Demo for BSD", + "configurePreset": "Demo-BSD", "configuration": "Release" } ], "testPresets": [ { - "name": "OCVSMD-Debug", - "displayName": "Test OCVSMD (Debug)", - "description": "Tests OCVSMD", - "configurePreset": "OCVSMD-Linux", + "name": "Demo-Debug", + "displayName": "Test Demo (Debug)", + "description": "Tests Demo", + "configurePreset": "Demo-Linux", "configuration": "Debug" }, { - "name": "OCVSMD-Release", - "displayName": "Test OCVSMD (Release)", - "description": "Tests OCVSMD", - "configurePreset": "OCVSMD-Linux", + "name": "Demo-Release", + "displayName": "Test Demo (Release)", + "description": "Tests Demo", + "configurePreset": "Demo-Linux", "configuration": "Release" } ] diff --git a/libcyphal_demo/README.md b/libcyphal_demo/README.md index 0341b11..3ef3f1b 100644 --- a/libcyphal_demo/README.md +++ b/libcyphal_demo/README.md @@ -18,7 +18,8 @@ by replacing the POSIX socket API and stdio with suitable alternatives; for details, please consult with: - `platform/posix/udp/*` files regarding UDP transport - `platform/linux/can/*` files regarding CAN transport -- `platform/linux/epoll_single_threaded_executor.hpp` file regarding the executor +- `platform/linux/epoll_single_threaded_executor.hpp` file regarding the executor using epoll (Debian) +- `platform/linux/kqueue_single_threaded_executor.hpp` file regarding the executor using kqueue (BSD/Darwin) - `platform/storage.hpp` file regarding the non-volatile storage - `platform/o1_heap_memory_resource.hpp` file regarding the memory resource @@ -35,6 +36,13 @@ Build the demo: ```shell git clone --recursive https://github.com/OpenCyphal/demos cd demos/libcyphal_demo -mkdir build && cd build -cmake .. && make +``` +Then one of the two presets depending on your system: + +- `Demo-Linux` - Debian-based Linux distros like Ubuntu. +- `Demo-BSD` – Mac OS + +``` +cmake --preset Demo-Linux +cmake --build --preset Demo-Linux-Debug ``` From 74d091f5f7552edd729e981e40b0e768fa060d2a Mon Sep 17 00:00:00 2001 From: Scott Dixon Date: Fri, 20 Dec 2024 14:17:38 -0800 Subject: [PATCH 5/9] Cherry-pick of 54cee7d2 by Scott Dixon, plus some build fixes Simplifying Nunavut integration Using Nunavut as a submodule now provides all dependencies including pydsdl, public_regulated_data_types, and cmake integration. --- .gitmodules | 3 - differential_pressure_sensor/CMakeLists.txt | 80 +++++++------ .../cmake/modules/Findnnvg.cmake | 113 ------------------ libudpard_demo/CMakeLists.txt | 80 ++++++------- libudpard_demo/cmake/modules/Findnnvg.cmake | 113 ------------------ shared/register/register.cmake | 1 - submodules/public_regulated_data_types | 1 - udral_servo/CMakeLists.txt | 88 +++++++------- udral_servo/cmake/modules/Findnnvg.cmake | 113 ------------------ 9 files changed, 126 insertions(+), 466 deletions(-) delete mode 100644 differential_pressure_sensor/cmake/modules/Findnnvg.cmake delete mode 100644 libudpard_demo/cmake/modules/Findnnvg.cmake delete mode 160000 submodules/public_regulated_data_types delete mode 100644 udral_servo/cmake/modules/Findnnvg.cmake diff --git a/.gitmodules b/.gitmodules index e9c8315..d1f3572 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,9 +2,6 @@ path = submodules/libcanard url = https://github.com/UAVCAN/libcanard branch = v4 -[submodule "submodules/public_regulated_data_types"] - path = submodules/public_regulated_data_types - url = https://github.com/UAVCAN/public_regulated_data_types [submodule "submodules/o1heap"] path = submodules/o1heap url = https://github.com/pavel-kirienko/o1heap diff --git a/differential_pressure_sensor/CMakeLists.txt b/differential_pressure_sensor/CMakeLists.txt index 3e37e47..8878dde 100644 --- a/differential_pressure_sensor/CMakeLists.txt +++ b/differential_pressure_sensor/CMakeLists.txt @@ -3,10 +3,10 @@ # Author: Pavel Kirienko cmake_minimum_required(VERSION 3.17) -project(differential_pressure_sensor C CXX) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +project(differential_pressure_sensor C) -set(submodules "${CMAKE_SOURCE_DIR}/../submodules") +set(submodules "${CMAKE_CURRENT_SOURCE_DIR}/../submodules") +set(CMAKE_PREFIX_PATH "${submodules}/nunavut") set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic -fstrict-aliasing") @@ -17,7 +17,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-conversion -Wcast-align -Wmissing-dec # Forward the revision information to the compiler so that we could expose it at runtime. This is entirely optional. execute_process( COMMAND git rev-parse --short=16 HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE vcs_revision_id OUTPUT_STRIP_TRAILING_WHITESPACE ) @@ -29,39 +29,32 @@ add_definitions( -DNODE_NAME="org.opencyphal.demos.differential_pressure" ) -# Transpile DSDL into C using Nunavut. Install Nunavut as follows: pip install nunavut. -# Alternatively, you can invoke the transpiler manually or use https://nunaweb.opencyphal.org. -find_package(nnvg REQUIRED) -create_dsdl_target( # Generate the support library for generated C headers, which is "nunavut.h". - "nunavut_support" - c - ${CMAKE_BINARY_DIR}/transpiled - "" - OFF - little - "only" +## Transpile DSDL into C using Nunavut. This uses this repo's built-in submodules to setup Nunavut. See +# CMAKE_PREFIX_PATH above for how this is resolved to the local submodules. +find_package(Nunavut 3.0 REQUIRED) + +set(LOCAL_PUBLIC_TYPES + uavcan/node/430.GetInfo.1.0.dsdl + uavcan/node/435.ExecuteCommand.1.1.dsdl + uavcan/node/7509.Heartbeat.1.0.dsdl + uavcan/node/port/7510.List.0.1.dsdl + uavcan/pnp/8165.NodeIDAllocationData.2.0.dsdl + uavcan/register/384.Access.1.0.dsdl + uavcan/register/385.List.1.0.dsdl + uavcan/si/unit/pressure/Scalar.1.0.dsdl + uavcan/si/unit/temperature/Scalar.1.0.dsdl ) -set(dsdl_root_namespace_dirs # List all DSDL root namespaces to transpile here. - ${submodules}/public_regulated_data_types/uavcan - ${submodules}/public_regulated_data_types/reg + +add_cyphal_library( + NAME dsdl_uavcan + EXACT_NAME + LANGUAGE c + LANGUAGE_STANDARD c${CMAKE_C_STANDARD} + DSDL_FILES ${LOCAL_PUBLIC_TYPES} + SERIALIZATION_ASSERT assert + EXPORT_MANIFEST + OUT_LIBRARY_TARGET LOCAL_TYPES_C_LIBRARY ) -foreach (ns_dir ${dsdl_root_namespace_dirs}) - get_filename_component(ns ${ns_dir} NAME) - message(STATUS "DSDL namespace ${ns} at ${ns_dir}") - create_dsdl_target( - "dsdl_${ns}" # CMake target name - c # Target language to transpile into - ${CMAKE_BINARY_DIR}/transpiled # Destination directory (add it to the includes) - ${ns_dir} # Source directory - OFF # Disable variable array capacity override - little # Endianness of the target platform (alternatives: "big", "any") - "never" # Support files are generated once in the nunavut_support target (above) - ${dsdl_root_namespace_dirs} # Look-up DSDL namespaces - ) - add_dependencies("dsdl_${ns}" nunavut_support) -endforeach () -include_directories(SYSTEM ${CMAKE_BINARY_DIR}/transpiled) # Make the transpiled headers available for inclusion. -add_definitions(-DNUNAVUT_ASSERT=assert) # Build libcanard. add_library(canard STATIC ${submodules}/libcanard/libcanard/canard.c) @@ -71,12 +64,21 @@ include_directories(SYSTEM ${submodules}/libcanard/libcanard) add_library(o1heap STATIC ${submodules}/o1heap/o1heap/o1heap.c) include_directories(SYSTEM ${submodules}/o1heap/o1heap/) -include(${CMAKE_SOURCE_DIR}/../shared/register/register.cmake) -include(${CMAKE_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../shared/register/register.cmake) +target_link_libraries(shared_register + PRIVATE ${LOCAL_TYPES_C_LIBRARY} +) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) # Build the application. add_executable(differential_pressure_sensor src/main.c ) -add_dependencies(differential_pressure_sensor dsdl_uavcan dsdl_reg) -target_link_libraries(differential_pressure_sensor canard o1heap shared_register shared_socketcan) +target_link_libraries(differential_pressure_sensor + ${LOCAL_TYPES_C_LIBRARY} + canard + o1heap + shared_register + shared_socketcan +) diff --git a/differential_pressure_sensor/cmake/modules/Findnnvg.cmake b/differential_pressure_sensor/cmake/modules/Findnnvg.cmake deleted file mode 100644 index f69e2f0..0000000 --- a/differential_pressure_sensor/cmake/modules/Findnnvg.cmake +++ /dev/null @@ -1,113 +0,0 @@ -# Find nnvg and setup python environment to generate C++ from DSDL. -# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Copyright 2021 OpenCyphal -# This file is adapted from: https://github.com/OpenCyphal/nunavut/blob/main/verification/cmake/modules/Findnnvg.cmake - -# Creates a target that will generate source code from dsdl definitions. -# -# Extra command line arguments can be passed to nnvg by setting the string variable NNVG_FLAGS. -# -# :param str ARG_TARGET_NAME: The name to give the target. -# :param str ARG_OUTPUT_LANGUAGE The language to generate for this target. -# :param Path ARG_OUTPUT_FOLDER: The directory to generate all source under. -# :param Path ARG_DSDL_ROOT_DIR: A directory containing the root namespace dsdl. -# :param bool ARG_ENABLE_OVR_VAR_ARRAY: Generates code with variable array capacity override enabled -# :param str ARG_SER_ENDIANNESS: One of 'any', 'big', or 'little' to pass as the value of the -# nnvg `--target-endianness` argument. Set to an empty string -# to omit this argument. -# :param str ARG_GENERATE_SUPPORT: Value for the nnvg --generate-support argument. Valid values are: -# as-needed (default) - generate support code if serialization is enabled. -# always - always generate support code. -# never - never generate support code. -# only - only generate support code. -# :param ...: A list of paths to use when looking up dependent DSDL types. -# :returns: Sets a variable "ARG_TARGET_NAME"-OUTPUT in the parent scope to the list of files the target -# will generate. For example, if ARG_TARGET_NAME == 'foo-bar' then after calling this function -# ${foo-bar-OUTPUT} will be set to the list of output files. -function(create_dsdl_target ARG_TARGET_NAME - ARG_OUTPUT_LANGUAGE - ARG_OUTPUT_FOLDER - ARG_DSDL_ROOT_DIR - ARG_ENABLE_OVR_VAR_ARRAY - ARG_SER_ENDIANNESS - ARG_GENERATE_SUPPORT) - - separate_arguments(NNVG_CMD_ARGS UNIX_COMMAND "${NNVG_FLAGS}") - - if (${ARGC} GREATER 7) - math(EXPR ARG_N_LAST "${ARGC}-1") - foreach (ARG_N RANGE 7 ${ARG_N_LAST}) - list(APPEND NNVG_CMD_ARGS "-I") - list(APPEND NNVG_CMD_ARGS "${ARGV${ARG_N}}") - endforeach (ARG_N) - endif () - - list(APPEND NNVG_CMD_ARGS --target-language) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_LANGUAGE}) - list(APPEND NNVG_CMD_ARGS -O) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_FOLDER}) - list(APPEND NNVG_CMD_ARGS ${ARG_DSDL_ROOT_DIR}) - list(APPEND NNVG_CMD_ARGS "--target-endianness") - list(APPEND NNVG_CMD_ARGS ${ARG_SER_ENDIANNESS}) - list(APPEND NNVG_CMD_ARGS "--enable-serialization-asserts") - list(APPEND NNVG_CMD_ARGS "--generate-support") - list(APPEND NNVG_CMD_ARGS ${ARG_GENERATE_SUPPORT}) - - if (ARG_ENABLE_OVR_VAR_ARRAY) - list(APPEND NNVG_CMD_ARGS "--enable-override-variable-array-capacity") - message(STATUS "Enabling variable array capacity override option in generated code.") - endif () - - execute_process(COMMAND ${NNVG} --list-outputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE OUTPUT_FILES - RESULT_VARIABLE LIST_OUTPUTS_RESULT) - - if (NOT LIST_OUTPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to retrieve a list of headers nnvg would " - "generate for the ${ARG_TARGET_NAME} target (${LIST_OUTPUTS_RESULT})" - " (${NNVG})") - endif () - - execute_process(COMMAND ${NNVG} --list-inputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE INPUT_FILES - RESULT_VARIABLE LIST_INPUTS_RESULT) - - if (NOT LIST_INPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to resolve inputs using nnvg for the ${ARG_TARGET_NAME} " - "target (${LIST_INPUTS_RESULT})" - " (${NNVG})") - endif () - - add_custom_command(OUTPUT ${OUTPUT_FILES} - COMMAND ${NNVG} ${NNVG_CMD_ARGS} - DEPENDS ${INPUT_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Running nnvg ${NNVG_CMD_ARGS}") - - add_custom_target(${ARG_TARGET_NAME}-gen - DEPENDS ${OUTPUT_FILES}) - - add_library(${ARG_TARGET_NAME} INTERFACE) - - add_dependencies(${ARG_TARGET_NAME} ${ARG_TARGET_NAME}-gen) - - target_include_directories(${ARG_TARGET_NAME} INTERFACE ${ARG_OUTPUT_FOLDER}) - - set(${ARG_TARGET_NAME}-OUTPUT ${OUTPUT_FILES} PARENT_SCOPE) - -endfunction(create_dsdl_target) - - -find_program(NNVG nnvg) - -if (NNVG) - execute_process(COMMAND ${NNVG} --version OUTPUT_VARIABLE NNVG_VERSION RESULT_VARIABLE NNVG_VERSION_RESULT) - if (NNVG_VERSION_RESULT EQUAL 0) - string(STRIP ${NNVG_VERSION} NNVG_VERSION) - message(STATUS "${NNVG} --version: ${NNVG_VERSION}") - endif () -endif () - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(nnvg REQUIRED_VARS NNVG_VERSION) diff --git a/libudpard_demo/CMakeLists.txt b/libudpard_demo/CMakeLists.txt index ca68b9e..76a1d6a 100644 --- a/libudpard_demo/CMakeLists.txt +++ b/libudpard_demo/CMakeLists.txt @@ -4,11 +4,12 @@ # SPDX-License-Identifier: MIT # Author: Pavel Kirienko -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.25) project(libudpard_demo C) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") -set(submodules "${CMAKE_SOURCE_DIR}/../submodules") +set(CMAKE_C_STANDARD 11) +set(submodules "${CMAKE_CURRENT_SOURCE_DIR}/../submodules") +set(CMAKE_PREFIX_PATH "${submodules}/nunavut") # Set up static analysis. set(STATIC_ANALYSIS ON CACHE BOOL "enable static analysis") @@ -16,15 +17,17 @@ if (STATIC_ANALYSIS) # clang-tidy (separate config files per directory) find_program(clang_tidy NAMES clang-tidy) if (NOT clang_tidy) - message(FATAL_ERROR "Could not locate clang-tidy") + message(WARNING "Could not locate clang-tidy") + set(STATIC_ANALYSIS OFF) + else() + message(STATUS "Using clang-tidy: ${clang_tidy}") endif () - message(STATUS "Using clang-tidy: ${clang_tidy}") endif () # Forward the revision information to the compiler so that we could expose it at runtime. This is entirely optional. execute_process( COMMAND git rev-parse --short=16 HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE vcs_revision_id OUTPUT_STRIP_TRAILING_WHITESPACE ) @@ -36,56 +39,47 @@ add_definitions( -DNODE_NAME="org.opencyphal.demos.libudpard" ) -# Transpile DSDL into C using Nunavut. Install Nunavut as follows: pip install nunavut. -# Alternatively, you can invoke the transpiler manually or use https://nunaweb.opencyphal.org. -find_package(nnvg REQUIRED) -create_dsdl_target( # Generate the support library for generated C headers, which is "nunavut.h". - "nunavut_support" - c - ${CMAKE_BINARY_DIR}/transpiled - "" - OFF - little - "only" +# Transpile DSDL into C using Nunavut. This uses this repo's built-in submodules to setup Nunavut. See +# CMAKE_PREFIX_PATH above for how this is resolved to the local submodules. +find_package(Nunavut 3.0 REQUIRED) + +set(LOCAL_PUBLIC_TYPES + uavcan/node/7509.Heartbeat.1.0.dsdl + uavcan/node/port/7510.List.1.0.dsdl + uavcan/node/430.GetInfo.1.0.dsdl + uavcan/node/435.ExecuteCommand.1.1.dsdl + uavcan/register/384.Access.1.0.dsdl + uavcan/register/385.List.1.0.dsdl + uavcan/pnp/8165.NodeIDAllocationData.2.0.dsdl + uavcan/primitive/array/Real32.1.0.dsdl ) -set(dsdl_root_namespace_dirs # List all DSDL root namespaces to transpile here. - ${submodules}/public_regulated_data_types/uavcan - ${submodules}/public_regulated_data_types/reg + +add_cyphal_library( + NAME dsdl_uavcan + EXACT_NAME + LANGUAGE c + LANGUAGE_STANDARD c${CMAKE_C_STANDARD} + DSDL_FILES ${LOCAL_PUBLIC_TYPES} + SERIALIZATION_ASSERT assert + EXPORT_MANIFEST + OUT_LIBRARY_TARGET LOCAL_TYPES_C_LIBRARY ) -foreach (ns_dir ${dsdl_root_namespace_dirs}) - get_filename_component(ns ${ns_dir} NAME) - message(STATUS "DSDL namespace ${ns} at ${ns_dir}") - create_dsdl_target( - "dsdl_${ns}" # CMake target name - c # Target language to transpile into - ${CMAKE_BINARY_DIR}/transpiled # Destination directory (add it to the includes) - ${ns_dir} # Source directory - OFF # Disable variable array capacity override - little # Endianness of the target platform (alternatives: "big", "any") - "never" # Support files are generated once in the nunavut_support target (above) - ${dsdl_root_namespace_dirs} # Look-up DSDL namespaces - ) - add_dependencies("dsdl_${ns}" nunavut_support) -endforeach () -include_directories(SYSTEM ${CMAKE_BINARY_DIR}/transpiled) # Make the transpiled headers available for inclusion. -add_definitions(-DNUNAVUT_ASSERT=assert) # Define the LibUDPard static library build target. No special options are needed to use the library, it's very simple. add_library(udpard_demo STATIC ${submodules}/libudpard/libudpard/udpard.c) target_include_directories(udpard_demo INTERFACE SYSTEM ${submodules}/libudpard/libudpard) -include(${CMAKE_SOURCE_DIR}/../shared/udp/udp.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../shared/udp/udp.cmake) # Define the demo application build target and link it with the library. add_executable( demo - ${CMAKE_SOURCE_DIR}/src/main.c - ${CMAKE_SOURCE_DIR}/src/storage.c - ${CMAKE_SOURCE_DIR}/src/register.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/storage.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/register.c ) target_include_directories(demo PRIVATE ${submodules}/cavl) -target_link_libraries(demo PRIVATE udpard_demo shared_udp) -add_dependencies(demo dsdl_uavcan dsdl_reg) +target_link_libraries(demo PRIVATE ${LOCAL_TYPES_C_LIBRARY} udpard_demo shared_udp) set_target_properties( demo PROPERTIES diff --git a/libudpard_demo/cmake/modules/Findnnvg.cmake b/libudpard_demo/cmake/modules/Findnnvg.cmake deleted file mode 100644 index f69e2f0..0000000 --- a/libudpard_demo/cmake/modules/Findnnvg.cmake +++ /dev/null @@ -1,113 +0,0 @@ -# Find nnvg and setup python environment to generate C++ from DSDL. -# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Copyright 2021 OpenCyphal -# This file is adapted from: https://github.com/OpenCyphal/nunavut/blob/main/verification/cmake/modules/Findnnvg.cmake - -# Creates a target that will generate source code from dsdl definitions. -# -# Extra command line arguments can be passed to nnvg by setting the string variable NNVG_FLAGS. -# -# :param str ARG_TARGET_NAME: The name to give the target. -# :param str ARG_OUTPUT_LANGUAGE The language to generate for this target. -# :param Path ARG_OUTPUT_FOLDER: The directory to generate all source under. -# :param Path ARG_DSDL_ROOT_DIR: A directory containing the root namespace dsdl. -# :param bool ARG_ENABLE_OVR_VAR_ARRAY: Generates code with variable array capacity override enabled -# :param str ARG_SER_ENDIANNESS: One of 'any', 'big', or 'little' to pass as the value of the -# nnvg `--target-endianness` argument. Set to an empty string -# to omit this argument. -# :param str ARG_GENERATE_SUPPORT: Value for the nnvg --generate-support argument. Valid values are: -# as-needed (default) - generate support code if serialization is enabled. -# always - always generate support code. -# never - never generate support code. -# only - only generate support code. -# :param ...: A list of paths to use when looking up dependent DSDL types. -# :returns: Sets a variable "ARG_TARGET_NAME"-OUTPUT in the parent scope to the list of files the target -# will generate. For example, if ARG_TARGET_NAME == 'foo-bar' then after calling this function -# ${foo-bar-OUTPUT} will be set to the list of output files. -function(create_dsdl_target ARG_TARGET_NAME - ARG_OUTPUT_LANGUAGE - ARG_OUTPUT_FOLDER - ARG_DSDL_ROOT_DIR - ARG_ENABLE_OVR_VAR_ARRAY - ARG_SER_ENDIANNESS - ARG_GENERATE_SUPPORT) - - separate_arguments(NNVG_CMD_ARGS UNIX_COMMAND "${NNVG_FLAGS}") - - if (${ARGC} GREATER 7) - math(EXPR ARG_N_LAST "${ARGC}-1") - foreach (ARG_N RANGE 7 ${ARG_N_LAST}) - list(APPEND NNVG_CMD_ARGS "-I") - list(APPEND NNVG_CMD_ARGS "${ARGV${ARG_N}}") - endforeach (ARG_N) - endif () - - list(APPEND NNVG_CMD_ARGS --target-language) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_LANGUAGE}) - list(APPEND NNVG_CMD_ARGS -O) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_FOLDER}) - list(APPEND NNVG_CMD_ARGS ${ARG_DSDL_ROOT_DIR}) - list(APPEND NNVG_CMD_ARGS "--target-endianness") - list(APPEND NNVG_CMD_ARGS ${ARG_SER_ENDIANNESS}) - list(APPEND NNVG_CMD_ARGS "--enable-serialization-asserts") - list(APPEND NNVG_CMD_ARGS "--generate-support") - list(APPEND NNVG_CMD_ARGS ${ARG_GENERATE_SUPPORT}) - - if (ARG_ENABLE_OVR_VAR_ARRAY) - list(APPEND NNVG_CMD_ARGS "--enable-override-variable-array-capacity") - message(STATUS "Enabling variable array capacity override option in generated code.") - endif () - - execute_process(COMMAND ${NNVG} --list-outputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE OUTPUT_FILES - RESULT_VARIABLE LIST_OUTPUTS_RESULT) - - if (NOT LIST_OUTPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to retrieve a list of headers nnvg would " - "generate for the ${ARG_TARGET_NAME} target (${LIST_OUTPUTS_RESULT})" - " (${NNVG})") - endif () - - execute_process(COMMAND ${NNVG} --list-inputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE INPUT_FILES - RESULT_VARIABLE LIST_INPUTS_RESULT) - - if (NOT LIST_INPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to resolve inputs using nnvg for the ${ARG_TARGET_NAME} " - "target (${LIST_INPUTS_RESULT})" - " (${NNVG})") - endif () - - add_custom_command(OUTPUT ${OUTPUT_FILES} - COMMAND ${NNVG} ${NNVG_CMD_ARGS} - DEPENDS ${INPUT_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Running nnvg ${NNVG_CMD_ARGS}") - - add_custom_target(${ARG_TARGET_NAME}-gen - DEPENDS ${OUTPUT_FILES}) - - add_library(${ARG_TARGET_NAME} INTERFACE) - - add_dependencies(${ARG_TARGET_NAME} ${ARG_TARGET_NAME}-gen) - - target_include_directories(${ARG_TARGET_NAME} INTERFACE ${ARG_OUTPUT_FOLDER}) - - set(${ARG_TARGET_NAME}-OUTPUT ${OUTPUT_FILES} PARENT_SCOPE) - -endfunction(create_dsdl_target) - - -find_program(NNVG nnvg) - -if (NNVG) - execute_process(COMMAND ${NNVG} --version OUTPUT_VARIABLE NNVG_VERSION RESULT_VARIABLE NNVG_VERSION_RESULT) - if (NNVG_VERSION_RESULT EQUAL 0) - string(STRIP ${NNVG_VERSION} NNVG_VERSION) - message(STATUS "${NNVG} --version: ${NNVG_VERSION}") - endif () -endif () - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(nnvg REQUIRED_VARS NNVG_VERSION) diff --git a/shared/register/register.cmake b/shared/register/register.cmake index eae7a2f..90aaf8a 100644 --- a/shared/register/register.cmake +++ b/shared/register/register.cmake @@ -11,5 +11,4 @@ add_library( shared_register ${CMAKE_CURRENT_LIST_DIR}/register.c ) -add_dependencies(shared_register dsdl_uavcan) target_include_directories(shared_register PUBLIC ${CMAKE_CURRENT_LIST_DIR}) diff --git a/submodules/public_regulated_data_types b/submodules/public_regulated_data_types deleted file mode 160000 index f9f6790..0000000 --- a/submodules/public_regulated_data_types +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f9f67906cc0ca5d7c1b429924852f6b28f313cbf diff --git a/udral_servo/CMakeLists.txt b/udral_servo/CMakeLists.txt index b688b6a..7c25f0e 100644 --- a/udral_servo/CMakeLists.txt +++ b/udral_servo/CMakeLists.txt @@ -2,11 +2,11 @@ # Copyright (C) 2021 OpenCyphal # Author: Pavel Kirienko -cmake_minimum_required(VERSION 3.17) -project(udral_servo_demo C CXX) -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +cmake_minimum_required(VERSION 3.25) +project(udral_servo_demo C) -set(submodules "${CMAKE_SOURCE_DIR}/../submodules") +set(submodules "${CMAKE_CURRENT_SOURCE_DIR}/../submodules") +list(APPEND CMAKE_PREFIX_PATH "${submodules}/nunavut") set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -pedantic -fstrict-aliasing") @@ -17,7 +17,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-conversion -Wcast-align -Wmissing-dec # Forward the revision information to the compiler so that we could expose it at runtime. This is entirely optional. execute_process( COMMAND git rev-parse --short=16 HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE vcs_revision_id OUTPUT_STRIP_TRAILING_WHITESPACE ) @@ -29,39 +29,38 @@ add_definitions( -DNODE_NAME="org.opencyphal.demos.udral.servo" ) -# Transpile DSDL into C using Nunavut. Install Nunavut as follows: pip install nunavut. -# Alternatively, you can invoke the transpiler manually or use https://nunaweb.opencyphal.org. -find_package(nnvg REQUIRED) -create_dsdl_target( # Generate the support library for generated C headers, which is "nunavut.h". - "nunavut_support" - c - ${CMAKE_BINARY_DIR}/transpiled - "" - OFF - little - "only" +# Transpile DSDL into C using Nunavut. This uses this repo's built-in submodules to setup Nunavut. See +# CMAKE_PREFIX_PATH above for how this is resolved to the local submodules. +find_package(Nunavut 3.0 REQUIRED) + +set(LOCAL_PUBLIC_REG_TYPES + reg/udral/service/common/Readiness.0.1.dsdl + reg/udral/service/actuator/common/_.0.1.dsdl + reg/udral/service/actuator/common/Feedback.0.1.dsdl + reg/udral/service/actuator/common/Status.0.1.dsdl + reg/udral/physics/dynamics/translation/LinearTs.0.1.dsdl + reg/udral/physics/electricity/PowerTs.0.1.dsdl + uavcan/node/430.GetInfo.1.0.dsdl + uavcan/node/435.ExecuteCommand.1.1.dsdl + uavcan/node/7509.Heartbeat.1.0.dsdl + uavcan/node/port/7510.List.0.1.dsdl + uavcan/pnp/8165.NodeIDAllocationData.2.0.dsdl + uavcan/register/384.Access.1.0.dsdl + uavcan/register/385.List.1.0.dsdl ) -set(dsdl_root_namespace_dirs # List all DSDL root namespaces to transpile here. - ${submodules}/public_regulated_data_types/uavcan - ${submodules}/public_regulated_data_types/reg + +add_cyphal_library( + NAME dsdl_uavcan + EXACT_NAME + LANGUAGE c + LANGUAGE_STANDARD c${CMAKE_C_STANDARD} + DSDL_FILES ${LOCAL_PUBLIC_REG_TYPES} + DSDL_NAMESPACES + ${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/reg + SERIALIZATION_ASSERT assert + EXPORT_MANIFEST + OUT_LIBRARY_TARGET LOCAL_TYPES_C_LIBRARY ) -foreach (ns_dir ${dsdl_root_namespace_dirs}) - get_filename_component(ns ${ns_dir} NAME) - message(STATUS "DSDL namespace ${ns} at ${ns_dir}") - create_dsdl_target( - "dsdl_${ns}" # CMake target name - c # Target language to transpile into - ${CMAKE_BINARY_DIR}/transpiled # Destination directory (add it to the includes) - ${ns_dir} # Source directory - OFF # Disable variable array capacity override - little # Endianness of the target platform (alternatives: "big", "any") - "never" # Support files are generated once in the nunavut_support target (above) - ${dsdl_root_namespace_dirs} # Look-up DSDL namespaces - ) - add_dependencies("dsdl_${ns}" nunavut_support) -endforeach () -include_directories(SYSTEM ${CMAKE_BINARY_DIR}/transpiled) # Make the transpiled headers available for inclusion. -add_definitions(-DNUNAVUT_ASSERT=assert) # Build libcanard. add_library(canard STATIC ${submodules}/libcanard/libcanard/canard.c) @@ -71,12 +70,21 @@ include_directories(SYSTEM ${submodules}/libcanard/libcanard) add_library(o1heap STATIC ${submodules}/o1heap/o1heap/o1heap.c) include_directories(SYSTEM ${submodules}/o1heap/o1heap/) -include(${CMAKE_SOURCE_DIR}/../shared/register/register.cmake) -include(${CMAKE_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/../shared/register/register.cmake) +target_link_libraries(shared_register + PRIVATE ${LOCAL_TYPES_C_LIBRARY} +) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../shared/socketcan/socketcan.cmake) # Build the application. add_executable(udral_servo_demo src/main.c ) -add_dependencies(udral_servo_demo dsdl_uavcan dsdl_reg) -target_link_libraries(udral_servo_demo canard o1heap shared_register shared_socketcan) +target_link_libraries(udral_servo_demo + ${LOCAL_TYPES_C_LIBRARY} + canard + o1heap + shared_register + shared_socketcan +) diff --git a/udral_servo/cmake/modules/Findnnvg.cmake b/udral_servo/cmake/modules/Findnnvg.cmake deleted file mode 100644 index c465347..0000000 --- a/udral_servo/cmake/modules/Findnnvg.cmake +++ /dev/null @@ -1,113 +0,0 @@ -# Find nnvg and setup python environment to generate C++ from DSDL. -# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# Copyright 2021 OpenCyphal -# This file is adapted from: https://github.com/OpenCyphal/nunavut/blob/main/verification/cmake/modules/Findnnvg.cmake - -# Creates a target that will generate source code from dsdl definitions. -# -# Extra command line arguments can be passed to nnvg by setting the string variable NNVG_FLAGS. -# -# :param str ARG_TARGET_NAME: The name to give the target. -# :param str ARG_OUTPUT_LANGUAGE The language to generate for this target. -# :param Path ARG_OUTPUT_FOLDER: The directory to generate all source under. -# :param Path ARG_DSDL_ROOT_DIR: A directory containing the root namespace dsdl. -# :param bool ARG_ENABLE_OVR_VAR_ARRAY: Generates code with variable array capacity override enabled -# :param str ARG_SER_ENDIANNESS: One of 'any', 'big', or 'little' to pass as the value of the -# nnvg `--target-endianness` argument. Set to an empty string -# to omit this argument. -# :param str ARG_GENERATE_SUPPORT: Value for the nnvg --generate-support argument. Valid values are: -# as-needed (default) - generate support code if serialization is enabled. -# always - always generate support code. -# never - never generate support code. -# only - only generate support code. -# :param ...: A list of paths to use when looking up dependent DSDL types. -# :returns: Sets a variable "ARG_TARGET_NAME"-OUTPUT in the parent scope to the list of files the target -# will generate. For example, if ARG_TARGET_NAME == 'foo-bar' then after calling this function -# ${foo-bar-OUTPUT} will be set to the list of output files. -function (create_dsdl_target ARG_TARGET_NAME - ARG_OUTPUT_LANGUAGE - ARG_OUTPUT_FOLDER - ARG_DSDL_ROOT_DIR - ARG_ENABLE_OVR_VAR_ARRAY - ARG_SER_ENDIANNESS - ARG_GENERATE_SUPPORT) - - separate_arguments(NNVG_CMD_ARGS UNIX_COMMAND "${NNVG_FLAGS}") - - if (${ARGC} GREATER 7) - math(EXPR ARG_N_LAST "${ARGC}-1") - foreach(ARG_N RANGE 7 ${ARG_N_LAST}) - list(APPEND NNVG_CMD_ARGS "-I") - list(APPEND NNVG_CMD_ARGS "${ARGV${ARG_N}}") - endforeach(ARG_N) - endif() - - list(APPEND NNVG_CMD_ARGS --target-language) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_LANGUAGE}) - list(APPEND NNVG_CMD_ARGS -O) - list(APPEND NNVG_CMD_ARGS ${ARG_OUTPUT_FOLDER}) - list(APPEND NNVG_CMD_ARGS ${ARG_DSDL_ROOT_DIR}) - list(APPEND NNVG_CMD_ARGS "--target-endianness") - list(APPEND NNVG_CMD_ARGS ${ARG_SER_ENDIANNESS}) - list(APPEND NNVG_CMD_ARGS "--enable-serialization-asserts") - list(APPEND NNVG_CMD_ARGS "--generate-support") - list(APPEND NNVG_CMD_ARGS ${ARG_GENERATE_SUPPORT}) - - if (ARG_ENABLE_OVR_VAR_ARRAY) - list(APPEND NNVG_CMD_ARGS "--enable-override-variable-array-capacity") - message(STATUS "Enabling variable array capacity override option in generated code.") - endif() - - execute_process(COMMAND ${NNVG} --list-outputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE OUTPUT_FILES - RESULT_VARIABLE LIST_OUTPUTS_RESULT) - - if(NOT LIST_OUTPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to retrieve a list of headers nnvg would " - "generate for the ${ARG_TARGET_NAME} target (${LIST_OUTPUTS_RESULT})" - " (${NNVG})") - endif() - - execute_process(COMMAND ${NNVG} --list-inputs ${NNVG_CMD_ARGS} - OUTPUT_VARIABLE INPUT_FILES - RESULT_VARIABLE LIST_INPUTS_RESULT) - - if(NOT LIST_INPUTS_RESULT EQUAL 0) - message(FATAL_ERROR "Failed to resolve inputs using nnvg for the ${ARG_TARGET_NAME} " - "target (${LIST_INPUTS_RESULT})" - " (${NNVG})") - endif() - - add_custom_command(OUTPUT ${OUTPUT_FILES} - COMMAND ${NNVG} ${NNVG_CMD_ARGS} - DEPENDS ${INPUT_FILES} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Running nnvg ${NNVG_CMD_ARGS}") - - add_custom_target(${ARG_TARGET_NAME}-gen - DEPENDS ${OUTPUT_FILES}) - - add_library(${ARG_TARGET_NAME} INTERFACE) - - add_dependencies(${ARG_TARGET_NAME} ${ARG_TARGET_NAME}-gen) - - target_include_directories(${ARG_TARGET_NAME} INTERFACE ${ARG_OUTPUT_FOLDER}) - - set(${ARG_TARGET_NAME}-OUTPUT ${OUTPUT_FILES} PARENT_SCOPE) - -endfunction(create_dsdl_target) - - -find_program(NNVG nnvg) - -if (NNVG) - execute_process(COMMAND ${NNVG} --version OUTPUT_VARIABLE NNVG_VERSION RESULT_VARIABLE NNVG_VERSION_RESULT) - if(NNVG_VERSION_RESULT EQUAL 0) - string(STRIP ${NNVG_VERSION} NNVG_VERSION) - message(STATUS "${NNVG} --version: ${NNVG_VERSION}") - endif() -endif() - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(nnvg REQUIRED_VARS NNVG_VERSION) From b7c5fc137a81d152bc75300167abb5a1b22b5188 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 18 Feb 2025 22:40:46 +0200 Subject: [PATCH 6/9] try fix CI --- .github/workflows/build.yml | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12ee499..67a670c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,24 +18,20 @@ jobs: build_libcyphal_demo: name: Build LibCyphal demo runs-on: ubuntu-latest - strategy: - matrix: - build_type: [Release, Debug] steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: get nunavut - run: > - pip install git+https://github.com/OpenCyphal/nunavut.git@3.0.preview - - name: Configure CMake - run: cmake -B ${{github.workspace}}/libcyphal_demo/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} ${{github.workspace}}/libcyphal_demo + run: cd ${{github.workspace}}/libcyphal_demo && cmake --preset Demo-Linux - - name: Build - run: cmake --build ${{github.workspace}}/libcyphal_demo/build --config ${{matrix.build_type}} + - name: Build Debug + run: cd ${{github.workspace}}/libcyphal_demo && cmake --build --preset Demo-Linux-Debug + + - name: Build Release + run: cd ${{github.workspace}}/libcyphal_demo && cmake --build --preset Demo-Linux-Release build_libudpard_demo: name: Build LibUDPard demo @@ -46,10 +42,6 @@ jobs: with: submodules: recursive - - name: get nunavut - run: > - pip install git+https://github.com/OpenCyphal/nunavut.git@3.0.preview - - name: Configure CMake run: cmake -B ${{github.workspace}}/libudpard_demo/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{github.workspace}}/libudpard_demo @@ -65,10 +57,6 @@ jobs: with: submodules: recursive - - name: get nunavut - run: > - pip install git+https://github.com/OpenCyphal/nunavut.git@3.0.preview - - name: Configure CMake run: cmake -B ${{github.workspace}}/differential_pressure_sensor/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{github.workspace}}/differential_pressure_sensor @@ -85,10 +73,6 @@ jobs: with: submodules: recursive - - name: get nunavut - run: > - pip install git+https://github.com/OpenCyphal/nunavut.git@3.0.preview - - name: Configure CMake run: cmake -B ${{github.workspace}}/udral_servo/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} ${{github.workspace}}/udral_servo From b35f52839de03c3f2078f3286b39c895e04d9822 Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 18 Feb 2025 22:43:07 +0200 Subject: [PATCH 7/9] try fix CI --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 67a670c..4d8ee94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,9 @@ jobs: with: submodules: recursive + - name: Install Ninja + run: apt-get install ninja-build + - name: Configure CMake run: cd ${{github.workspace}}/libcyphal_demo && cmake --preset Demo-Linux From 5b5de86aa34c97261053f029cc8b94c5a15b262d Mon Sep 17 00:00:00 2001 From: Sergei Date: Tue, 18 Feb 2025 22:43:51 +0200 Subject: [PATCH 8/9] try fix CI --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d8ee94..498c65e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: submodules: recursive - name: Install Ninja - run: apt-get install ninja-build + run: sudo apt-get install ninja-build - name: Configure CMake run: cd ${{github.workspace}}/libcyphal_demo && cmake --preset Demo-Linux From 656ce3c8059f52c79cc3a5444b2ad8f952b2618f Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Mon, 3 Mar 2025 14:11:53 +0200 Subject: [PATCH 9/9] revert back to Nunavut 3.0.preview --- .gitmodules | 2 +- submodules/nunavut | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index d1f3572..08d93c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -18,4 +18,4 @@ [submodule "submodules/nunavut"] path = submodules/nunavut url = https://github.com/OpenCyphal/nunavut.git - branch = sshirokov/rpi32 + branch = 3.0.preview diff --git a/submodules/nunavut b/submodules/nunavut index cd5f563..948b75d 160000 --- a/submodules/nunavut +++ b/submodules/nunavut @@ -1 +1 @@ -Subproject commit cd5f563bd1587990cfa59fa1a7932c42be7f0dca +Subproject commit 948b75de19145eb8de01b9485aa2cd23a1494029