├── .editorconfig ├── .github └── workflows │ └── cmake-multi-platform.yml ├── .gitignore ├── .vscode ├── c_cpp_properties.json └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── bash └── source_env.sh ├── cmake ├── DeclarePlugin.cmake ├── FindAllTargets.cmake └── Unit.cmake ├── cpp ├── .clang-format ├── CMakeLists.txt ├── arguments │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ ├── arguments.h │ │ │ └── arguments │ │ │ ├── argument_types.h │ │ │ └── command_line.h │ └── src │ │ └── argument_types.cpp ├── cli │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ └── cli_logger.h │ ├── src │ │ ├── cli.cpp │ │ ├── cli_launch.h │ │ ├── cli_logger.cpp │ │ ├── cli_schema.h │ │ ├── cli_subcommand.h │ │ ├── cli_topic.h │ │ └── fetch_schema.h │ └── test │ │ ├── example.yaml │ │ ├── test.yaml │ │ └── test.yaml.j2 ├── core │ ├── CMakeLists.txt │ ├── containers │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ └── containers │ │ │ │ ├── simple_mpsc.h │ │ │ │ └── subscriber_callback_queue.h │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── test_containers.cpp │ ├── coordinator │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ ├── coordinator.h │ │ │ │ ├── coordinator_connector.h │ │ │ │ └── coordinator_default_port.h │ │ ├── src │ │ │ ├── coordinator.cpp │ │ │ └── coordinator_main.cpp │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── test_coordinator.cpp │ ├── logging │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ ├── logging.h │ │ │ │ └── logging │ │ │ │ └── macros.h │ │ └── src │ │ │ └── logging.cpp │ ├── networking │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ └── networking │ │ │ │ ├── logger.h │ │ │ │ └── socket.h │ │ ├── src │ │ │ ├── logger.cpp │ │ │ └── socket.cpp │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── test_tcp.cpp │ ├── serialization │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ ├── serialization.h │ │ │ │ └── serialization │ │ │ │ └── message_type_info.h │ │ └── src │ │ │ └── serialization.cpp │ ├── threading │ │ ├── CMakeLists.txt │ │ └── include │ │ │ └── basis │ │ │ └── core │ │ │ └── threading │ │ │ └── thread_pool.h │ ├── time │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── basis │ │ │ │ └── core │ │ │ │ ├── time.h │ │ │ │ └── time │ │ │ │ └── chrono_adapter.h │ │ ├── src │ │ │ └── time.cpp │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── test_time.cpp │ └── transport │ │ ├── CMakeLists.txt │ │ ├── include │ │ └── basis │ │ │ └── core │ │ │ └── transport │ │ │ ├── convertable_inproc.h │ │ │ ├── inproc.h │ │ │ ├── logger.h │ │ │ ├── message_event.h │ │ │ ├── message_packet.h │ │ │ ├── publisher.h │ │ │ ├── publisher_info.h │ │ │ ├── subscriber.h │ │ │ ├── thread_pool_manager.h │ │ │ ├── transport.h │ │ │ └── transport_manager.h │ │ ├── src │ │ ├── inproc.cpp │ │ ├── logger.cpp │ │ ├── publisher.cpp │ │ └── subscriber.cpp │ │ └── test │ │ ├── CMakeLists.txt │ │ └── test_basic_transport.cpp ├── examples │ ├── CMakeLists.txt │ └── src │ │ └── basis_example.cpp ├── launch │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ ├── launch.h │ │ │ └── launch │ │ │ ├── launch_definition.h │ │ │ ├── mermaid_formatter.h │ │ │ └── unit_loader.h │ ├── src │ │ ├── launch.cpp │ │ ├── launch_definition.cpp │ │ ├── mermaid_formatter.cpp │ │ ├── process_manager.cpp │ │ ├── process_manager.h │ │ └── unit_loader.cpp │ └── test │ │ ├── CMakeLists.txt │ │ ├── foxglove.launch.yaml │ │ ├── test_include.launch.yaml │ │ └── test_launch_definition.cpp ├── plugins │ ├── bridges │ │ └── foxglove │ │ │ ├── CMakeLists.txt │ │ │ ├── include │ │ │ └── basis │ │ │ │ └── plugins │ │ │ │ └── bridges │ │ │ │ └── foxglove_bridge.h │ │ │ └── src │ │ │ ├── foxglove_bridge.cpp │ │ │ └── foxglove_dummy_main.cpp │ ├── serialization │ │ ├── protobuf │ │ │ ├── CMakeLists.txt │ │ │ ├── include │ │ │ │ └── basis │ │ │ │ │ └── plugins │ │ │ │ │ └── serialization │ │ │ │ │ └── protobuf.h │ │ │ ├── src │ │ │ │ └── protobuf.cpp │ │ │ └── test │ │ │ │ ├── CMakeLists.txt │ │ │ │ └── test_protobuf.cpp │ │ └── rosmsg │ │ │ ├── CMakeLists.txt │ │ │ ├── include │ │ │ └── basis │ │ │ │ └── plugins │ │ │ │ └── serialization │ │ │ │ └── rosmsg.h │ │ │ ├── src │ │ │ └── rosmsg.cpp │ │ │ └── test │ │ │ ├── CMakeLists.txt │ │ │ └── test_rosmsg.cpp │ └── transport │ │ └── tcp │ │ ├── CMakeLists.txt │ │ ├── include │ │ └── basis │ │ │ └── plugins │ │ │ └── transport │ │ │ ├── epoll.h │ │ │ ├── logger.h │ │ │ ├── tcp.h │ │ │ ├── tcp_connection.h │ │ │ ├── tcp_subscriber.h │ │ │ └── tcp_transport_name.h │ │ ├── src │ │ ├── epoll.cpp │ │ ├── tcp.cpp │ │ ├── tcp_connection.cpp │ │ └── tcp_subscriber.cpp │ │ └── test │ │ ├── CMakeLists.txt │ │ └── test_tcp_transport.cpp ├── recorder │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ ├── recorder.h │ │ │ └── recorder │ │ │ ├── glob.h │ │ │ └── protobuf_log.h │ ├── src │ │ └── recorder.cpp │ └── test │ │ ├── CMakeLists.txt │ │ └── test_recorder.cpp ├── replayer │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ ├── replayer.h │ │ │ └── replayer │ │ │ ├── config.h │ │ │ ├── logger.h │ │ │ └── replay_args.h │ └── src │ │ ├── replay.cpp │ │ ├── replay_args.cpp │ │ └── replayer.cpp ├── synchronizers │ ├── CMakeLists.txt │ ├── include │ │ └── basis │ │ │ └── synchronizers │ │ │ ├── all.h │ │ │ ├── field.h │ │ │ └── synchronizer_base.h │ └── test │ │ ├── CMakeLists.txt │ │ └── test_synchronizers.cpp ├── third_party │ ├── CMakeLists.txt │ └── spdlog │ │ ├── CMakeLists.txt │ │ ├── INSTALL │ │ ├── LICENSE │ │ ├── README.md │ │ ├── appveyor.yml │ │ ├── bench │ │ ├── CMakeLists.txt │ │ ├── async_bench.cpp │ │ ├── bench.cpp │ │ ├── formatter-bench.cpp │ │ ├── latency.cpp │ │ └── utils.h │ │ ├── cmake │ │ ├── ide.cmake │ │ ├── pch.h.in │ │ ├── spdlog.pc.in │ │ ├── spdlogCPack.cmake │ │ ├── spdlogConfig.cmake.in │ │ ├── utils.cmake │ │ └── version.rc.in │ │ ├── example │ │ ├── CMakeLists.txt │ │ └── example.cpp │ │ ├── include │ │ └── spdlog │ │ │ ├── async.h │ │ │ ├── async_logger-inl.h │ │ │ ├── async_logger.h │ │ │ ├── cfg │ │ │ ├── argv.h │ │ │ ├── env.h │ │ │ ├── helpers-inl.h │ │ │ └── helpers.h │ │ │ ├── common-inl.h │ │ │ ├── common.h │ │ │ ├── details │ │ │ ├── backtracer-inl.h │ │ │ ├── backtracer.h │ │ │ ├── circular_q.h │ │ │ ├── console_globals.h │ │ │ ├── file_helper-inl.h │ │ │ ├── file_helper.h │ │ │ ├── fmt_helper.h │ │ │ ├── log_msg-inl.h │ │ │ ├── log_msg.h │ │ │ ├── log_msg_buffer-inl.h │ │ │ ├── log_msg_buffer.h │ │ │ ├── mpmc_blocking_q.h │ │ │ ├── null_mutex.h │ │ │ ├── os-inl.h │ │ │ ├── os.h │ │ │ ├── periodic_worker-inl.h │ │ │ ├── periodic_worker.h │ │ │ ├── registry-inl.h │ │ │ ├── registry.h │ │ │ ├── synchronous_factory.h │ │ │ ├── tcp_client-windows.h │ │ │ ├── tcp_client.h │ │ │ ├── thread_pool-inl.h │ │ │ ├── thread_pool.h │ │ │ ├── udp_client-windows.h │ │ │ ├── udp_client.h │ │ │ └── windows_include.h │ │ │ ├── fmt │ │ │ ├── bin_to_hex.h │ │ │ ├── bundled │ │ │ │ ├── args.h │ │ │ │ ├── chrono.h │ │ │ │ ├── color.h │ │ │ │ ├── compile.h │ │ │ │ ├── core.h │ │ │ │ ├── fmt.license.rst │ │ │ │ ├── format-inl.h │ │ │ │ ├── format.h │ │ │ │ ├── locale.h │ │ │ │ ├── os.h │ │ │ │ ├── ostream.h │ │ │ │ ├── printf.h │ │ │ │ ├── ranges.h │ │ │ │ ├── std.h │ │ │ │ └── xchar.h │ │ │ ├── chrono.h │ │ │ ├── compile.h │ │ │ ├── fmt.h │ │ │ ├── ostr.h │ │ │ ├── ranges.h │ │ │ ├── std.h │ │ │ └── xchar.h │ │ │ ├── formatter.h │ │ │ ├── fwd.h │ │ │ ├── logger-inl.h │ │ │ ├── logger.h │ │ │ ├── mdc.h │ │ │ ├── pattern_formatter-inl.h │ │ │ ├── pattern_formatter.h │ │ │ ├── sinks │ │ │ ├── android_sink.h │ │ │ ├── ansicolor_sink-inl.h │ │ │ ├── ansicolor_sink.h │ │ │ ├── base_sink-inl.h │ │ │ ├── base_sink.h │ │ │ ├── basic_file_sink-inl.h │ │ │ ├── basic_file_sink.h │ │ │ ├── callback_sink.h │ │ │ ├── daily_file_sink.h │ │ │ ├── dist_sink.h │ │ │ ├── dup_filter_sink.h │ │ │ ├── hourly_file_sink.h │ │ │ ├── kafka_sink.h │ │ │ ├── mongo_sink.h │ │ │ ├── msvc_sink.h │ │ │ ├── null_sink.h │ │ │ ├── ostream_sink.h │ │ │ ├── qt_sinks.h │ │ │ ├── ringbuffer_sink.h │ │ │ ├── rotating_file_sink-inl.h │ │ │ ├── rotating_file_sink.h │ │ │ ├── sink-inl.h │ │ │ ├── sink.h │ │ │ ├── stdout_color_sinks-inl.h │ │ │ ├── stdout_color_sinks.h │ │ │ ├── stdout_sinks-inl.h │ │ │ ├── stdout_sinks.h │ │ │ ├── syslog_sink.h │ │ │ ├── systemd_sink.h │ │ │ ├── tcp_sink.h │ │ │ ├── udp_sink.h │ │ │ ├── win_eventlog_sink.h │ │ │ ├── wincolor_sink-inl.h │ │ │ └── wincolor_sink.h │ │ │ ├── spdlog-inl.h │ │ │ ├── spdlog.h │ │ │ ├── stopwatch.h │ │ │ ├── tweakme.h │ │ │ └── version.h │ │ ├── logos │ │ ├── jetbrains-variant-4.svg │ │ └── spdlog.png │ │ ├── scripts │ │ ├── ci_setup_clang.sh │ │ ├── extract_version.py │ │ └── format.sh │ │ ├── src │ │ ├── async.cpp │ │ ├── bundled_fmtlib_format.cpp │ │ ├── cfg.cpp │ │ ├── color_sinks.cpp │ │ ├── file_sinks.cpp │ │ ├── spdlog.cpp │ │ └── stdout_sinks.cpp │ │ └── tests │ │ ├── CMakeLists.txt │ │ ├── includes.h │ │ ├── main.cpp │ │ ├── test_async.cpp │ │ ├── test_backtrace.cpp │ │ ├── test_bin_to_hex.cpp │ │ ├── test_cfg.cpp │ │ ├── test_circular_q.cpp │ │ ├── test_create_dir.cpp │ │ ├── test_custom_callbacks.cpp │ │ ├── test_daily_logger.cpp │ │ ├── test_dup_filter.cpp │ │ ├── test_errors.cpp │ │ ├── test_eventlog.cpp │ │ ├── test_file_helper.cpp │ │ ├── test_file_logging.cpp │ │ ├── test_fmt_helper.cpp │ │ ├── test_macros.cpp │ │ ├── test_misc.cpp │ │ ├── test_mpmc_q.cpp │ │ ├── test_pattern_formatter.cpp │ │ ├── test_registry.cpp │ │ ├── test_sink.h │ │ ├── test_stdout_api.cpp │ │ ├── test_stopwatch.cpp │ │ ├── test_systemd.cpp │ │ ├── test_time_point.cpp │ │ ├── utils.cpp │ │ └── utils.h └── unit │ ├── CMakeLists.txt │ ├── include │ └── basis │ │ ├── unit.h │ │ └── unit │ │ ├── args_template.h │ │ ├── create_unit.h │ │ └── create_unit_impl.h │ └── src │ ├── args_template.cpp │ ├── unit.cpp │ └── unit_main.cpp ├── docker ├── Dockerfile ├── build-env.sh └── run-env.sh ├── docs └── bug light.svg ├── proto ├── CMakeLists.txt ├── basis_example.proto ├── test.proto ├── time.proto └── transport.proto ├── python └── unit │ ├── generate_unit.py │ ├── templates │ ├── create_unit.cpp.j2 │ ├── handler.h.j2 │ ├── unit.cpp.j2 │ ├── unit.h.j2 │ ├── unit_base.cpp.j2 │ └── unit_base.h.j2 │ └── test_validate_unit.py └── unit ├── CMakeLists.txt ├── example ├── CMakeLists.txt └── example.unit.yaml ├── schema.yaml ├── test ├── CMakeLists.txt ├── include │ ├── test_unit.h │ └── time_test_inproc.h ├── src │ └── test_unit.cpp ├── template │ ├── test_unit.example.cpp │ └── test_unit.example.h ├── test │ ├── CMakeLists.txt │ └── test_unit_generation.cpp └── test_unit.unit.yaml └── wip ├── CMakeLists.txt ├── include └── wip.h ├── src └── wip.cpp ├── template ├── wip.example.cpp └── wip.example.h └── wip.unit.yaml /.github/workflows/cmake-multi-platform.yml: -------------------------------------------------------------------------------- 1 | name: Build basis 2 | permissions: 3 | packages: write 4 | contents: read 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build-container: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: 'Login to GitHub Container Registry' 20 | uses: docker/login-action@v3 21 | with: 22 | registry: ghcr.io 23 | username: ${{github.actor}} 24 | password: ${{secrets.GITHUB_TOKEN}} 25 | 26 | - name: Set up Docker Buildx 27 | uses: docker/setup-buildx-action@v3 28 | 29 | # TODO: use proper tags 30 | - name: Build and push base image 31 | uses: docker/build-push-action@v5 32 | with: 33 | context: . 34 | file: ./docker/Dockerfile 35 | push: true 36 | target: basis-env 37 | tags: ghcr.io/basis-robotics/basis-env:${{ github.sha }} 38 | # cache-from: type=gha 39 | # cache-to: type=gha,mode=max 40 | - name: Build and push ROS image 41 | uses: docker/build-push-action@v5 42 | with: 43 | context: . 44 | file: ./docker/Dockerfile 45 | push: true 46 | target: basis-env-ros 47 | tags: ghcr.io/basis-robotics/basis-env-ros:${{ github.sha }} 48 | # cache-from: type=gha 49 | # cache-to: type=gha,mode=max 50 | 51 | build-test: 52 | runs-on: ubuntu-latest 53 | needs: build-container 54 | strategy: 55 | matrix: 56 | configuration: 57 | - image: basis-env 58 | - image: basis-env-ros 59 | cmake-options: -DBASIS_ENABLE_ROS=1 60 | 61 | container: 62 | image: ghcr.io/basis-robotics/${{ matrix.configuration.image }}:${{ github.sha }} 63 | options: --user root 64 | 65 | steps: 66 | - uses: actions/checkout@v4 67 | 68 | - name: Configure CMake 69 | run: > 70 | cmake -B ./build 71 | -DCMAKE_CXX_COMPILER=clang++-18 72 | ${{matrix.configuration.cmake-options}} 73 | -S . 74 | 75 | - name: Build 76 | run: cmake --build ./build -j 77 | 78 | - name: Test 79 | working-directory: ./build 80 | run: ctest 81 | 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | __pycache__ 3 | .DS_Store 4 | proto/foxglove 5 | .cache -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/clang++", 10 | "cppStandard": "c++20" 11 | } 12 | ], 13 | "version": 4 14 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25.1) 2 | project(basis) 3 | 4 | # Allow include() on our cmake directory 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 6 | 7 | set(BASIS_SOURCE_ROOT ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL STRING) 8 | message(STATUS "BASIS_SOURCE_ROOT: ${BASIS_SOURCE_ROOT}") 9 | 10 | ################################################################################# 11 | # Options 12 | set(BASIS_INSTALL_DIR /opt/basis CACHE STRING "Directory to install basis to") 13 | option(BASIS_ENABLE_ROS "Enable basis support for ROS" OFF) 14 | option(BASIS_ENABLE_TESTING "Build tests" ${PROJECT_IS_TOP_LEVEL}) 15 | 16 | set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) 17 | 18 | # Set variables based on options 19 | set(CMAKE_INSTALL_PREFIX ${BASIS_INSTALL_DIR} CACHE INTERNAL STRING) 20 | 21 | if(${BASIS_ENABLE_TESTING}) 22 | enable_testing() 23 | endif() 24 | 25 | if(BASIS_ENABLE_ROS) 26 | set(BASIS_ROS_ROOT /opt/ros/noetic CACHE STRING "ros root to use") 27 | add_compile_definitions(BASIS_ENABLE_ROS=1) 28 | endif() 29 | ################################################################################# 30 | 31 | # Set global settings 32 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 33 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 34 | 35 | # Add all of our supported languages 36 | add_subdirectory(cpp) 37 | add_subdirectory(proto) 38 | 39 | # Add the units 40 | add_subdirectory(unit) 41 | 42 | include(FindAllTargets) 43 | find_all_targets(ALL_TARGETS) 44 | 45 | # For each binary and shared object, install 46 | # TODO: we probably want https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#exporting-targets-from-the-build-tree 47 | # TODO: headers aren't installed this way 48 | foreach(TARGET IN LISTS ALL_TARGETS) 49 | get_target_property(TARGET_TYPE ${TARGET} TYPE) 50 | if(${TARGET_TYPE} STREQUAL "EXECUTABLE") 51 | install(TARGETS ${TARGET}) 52 | elseif(${TARGET_TYPE} STREQUAL "SHARED_LIBRARY") 53 | get_target_property(PLUGIN_TYPE ${TARGET} BASIS_PLUGIN_TYPE) 54 | 55 | # TODO: plugins are special 56 | if("${PLUGIN_TYPE}" STREQUAL "PLUGIN_TYPE-NOTFOUND") 57 | install(TARGETS ${TARGET}) 58 | endif() 59 | endif() 60 | endforeach() 61 | -------------------------------------------------------------------------------- /bash/source_env.sh: -------------------------------------------------------------------------------- 1 | # TODO generate this with the install step 2 | export BASIS_ROOT=/opt/basis/ 3 | export PATH=${PATH}:${BASIS_ROOT}/bin 4 | export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${BASIS_ROOT}/lib:${BASIS_ROOT}/plugins/serialization:${BASIS_ROOT}/plugins/transport:${BASIS_ROOT}/unit -------------------------------------------------------------------------------- /cmake/DeclarePlugin.cmake: -------------------------------------------------------------------------------- 1 | function(add_plugin TARGET_NAME) 2 | get_filename_component(PARENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY) 3 | get_filename_component(PLUGIN_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | get_filename_component(PLUGIN_TYPE ${PARENT_DIR} NAME) 5 | 6 | # Remove known arguments from the list 7 | list(REMOVE_AT ARGV 0) 8 | 9 | # Declare the library 10 | add_library(${TARGET_NAME} SHARED ${ARGV}) 11 | 12 | set_target_properties(${TARGET_NAME} PROPERTIES 13 | # 14 | BASIS_PLUGIN_TYPE ${PLUGIN_TYPE} 15 | # Rename it to "plugin_name.so" 16 | PREFIX "" 17 | OUTPUT_NAME ${PLUGIN_NAME}) 18 | 19 | # install to the proper plugins dir 20 | install(TARGETS ${TARGET_NAME} DESTINATION plugins/${PLUGIN_TYPE}) 21 | 22 | endfunction() -------------------------------------------------------------------------------- /cmake/FindAllTargets.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Utilities to get all targets of our build 3 | function(find_all_targets var) 4 | set(targets) 5 | find_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) 6 | set(${var} ${targets} PARENT_SCOPE) 7 | endfunction() 8 | 9 | macro(find_all_targets_recursive targets dir) 10 | get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) 11 | foreach(subdir ${subdirectories}) 12 | if(NOT ${dir} STREQUAL ${subdir}) 13 | find_all_targets_recursive(${targets} ${subdir}) 14 | endif() 15 | endforeach() 16 | 17 | get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS) 18 | list(APPEND ${targets} ${current_targets}) 19 | endmacro() -------------------------------------------------------------------------------- /cpp/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | ColumnLimit: '120' 3 | 4 | ... 5 | -------------------------------------------------------------------------------- /cpp/arguments/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # basis::arguments - Library to add common argparse interface for units and launch files 2 | add_library(basis_arguments SHARED src/argument_types.cpp) 3 | 4 | target_include_directories(basis_arguments PUBLIC include) 5 | target_link_libraries(basis_arguments PUBLIC argparse expected-lite nlohmann_json spdlog::spdlog) 6 | 7 | add_library(basis::arguments ALIAS basis_arguments) -------------------------------------------------------------------------------- /cpp/arguments/include/basis/arguments/command_line.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file command_line.h 4 | * 5 | * Only contains CommandLineTypes, to avoid pulling in argparse to the main unit headers 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace basis::arguments { 13 | /** 14 | * Wrapper to allow specifying argc+argv, key value pairs, or a vector of strings for arguments. 15 | */ 16 | using CommandLineTypes = std::variant>, // key value 17 | std::vector, 18 | std::pair>; // argc+argv - NOTE: includes program name 19 | 20 | } // namespace basis::arguments -------------------------------------------------------------------------------- /cpp/arguments/src/argument_types.cpp: -------------------------------------------------------------------------------- 1 | #include "argparse/argparse.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace basis::arguments::types { 9 | using namespace std; 10 | template 11 | void ArgumentToJson(const argparse::ArgumentParser &arg_parser, std::string_view name, bool is_optional, 12 | nlohmann::json &out) { 13 | std::string key = "--" + std::string(name); 14 | 15 | if (is_optional) { 16 | std::optional maybe_value = arg_parser.present(key); 17 | if (maybe_value) { 18 | out = *maybe_value; 19 | } 20 | } else { 21 | out = arg_parser.get(key); 22 | } 23 | } 24 | 25 | bool StringToBool(std::string_view name, std::string_view s) { 26 | std::string lowered(s); 27 | std::transform(s.begin(), s.end(), lowered.begin(), [](unsigned char c) { return std::tolower(c); }); 28 | if (lowered == "true" or lowered == "1") { 29 | return true; 30 | } 31 | if (lowered == "false" or lowered == "0") { 32 | return false; 33 | } 34 | throw std::runtime_error(fmt::format( 35 | "[--{} {}] can't be converted to bool, must be '0', '1', 'true', or 'false' (case insensitive)", name, s)); 36 | } 37 | 38 | template auto ArgParseAction(std::string_view name) { 39 | if constexpr (std::is_same_v) { 40 | return [name = std::string(name)](const std::string &s) { return StringToBool(name, s); }; 41 | } else if constexpr (std::is_floating_point_v) { 42 | // arg.template scan<'g', T_ARGUMENT_TYPE>(); 43 | return argparse::details::parse_number(); 44 | } else if constexpr (std::is_arithmetic_v) { 45 | // arg.template scan<'i', T_ARGUMENT_TYPE>(); 46 | return argparse::details::parse_number(); 47 | } else if constexpr (std::is_same_v) { 48 | // identity 49 | return [](const std::string &s) { return s; }; 50 | } 51 | } 52 | 53 | template 54 | void ArgumentSetDefaultValue(argparse::Argument &arg, std::string_view name, const std::string &default_value) { 55 | arg.default_value(ArgParseAction(name)(default_value)); 56 | } 57 | 58 | template void ArgumentTypeValidator(argparse::Argument &arg, std::string_view name) { 59 | arg.action(ArgParseAction(name)); 60 | } 61 | 62 | } // namespace basis::arguments::types -------------------------------------------------------------------------------- /cpp/cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_cli_logger 2 | src/cli_logger.cpp) 3 | 4 | target_include_directories(basis_cli_logger PUBLIC include) 5 | 6 | target_link_libraries(basis_cli_logger basis::core::logging) 7 | add_library(basis::cli_logger ALIAS basis_cli_logger) 8 | 9 | add_executable( 10 | basis 11 | src/cli.cpp 12 | ) 13 | 14 | target_link_libraries( 15 | basis 16 | basis::launch 17 | basis::plugins::transport::tcp 18 | basis::core::coordinator 19 | argparse 20 | ) 21 | -------------------------------------------------------------------------------- /cpp/cli/include/basis/cli_logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis::cli { 6 | 7 | extern std::shared_ptr basis_logger; 8 | inline const std::shared_ptr &AUTO_LOGGER(basis_logger); 9 | 10 | } // namespace basis::cli -------------------------------------------------------------------------------- /cpp/cli/src/cli_logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace basis::cli { 4 | 5 | std::shared_ptr basis_logger = basis::core::logging::CreateLogger("basis"); 6 | // TODO: set up a special CLI logger that prints to stderr 7 | 8 | } -------------------------------------------------------------------------------- /cpp/cli/src/cli_schema.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "cli_subcommand.h" 3 | #include "fetch_schema.h" 4 | #include 5 | 6 | namespace basis::cli { 7 | 8 | bool PrintSchema(const std::string &schema_name, basis::core::transport::CoordinatorConnector *connector) { 9 | std::optional maybe_schema = FetchSchema(schema_name, connector, 5); 10 | if (maybe_schema) { 11 | std::cout << maybe_schema->serializer() << ":" << maybe_schema->name() << std::endl; 12 | std::cout << maybe_schema->schema() << std::endl; 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | class SchemaPrintCommand : public CLISubcommand { 19 | public: 20 | SchemaPrintCommand(argparse::ArgumentParser &parent_parser) : CLISubcommand("print", parent_parser) { 21 | parser.add_description("print a schema"); 22 | parser.add_argument("schema"); 23 | Commit(); 24 | } 25 | bool HandleArgs(basis::core::transport::CoordinatorConnector *connector) { 26 | return PrintSchema(parser.get("schema"), connector); 27 | } 28 | }; 29 | class SchemaCommand : public CLISubcommand { 30 | public: 31 | SchemaCommand(argparse::ArgumentParser &parent_parser) 32 | : CLISubcommand("schema", parent_parser), schema_print_command(parser) { 33 | parser.add_description("Schema information"); 34 | Commit(); 35 | }; 36 | 37 | bool HandleArgs(uint16_t port) { 38 | auto connector = CreateCoordinatorConnector(port); 39 | if(!connector) { 40 | return false; 41 | } 42 | if (schema_print_command.IsInUse()) { 43 | return schema_print_command.HandleArgs(connector.get()); 44 | } 45 | return false; 46 | } 47 | 48 | protected: 49 | // basis schema ls 50 | // todo 51 | // basis schema print 52 | SchemaPrintCommand schema_print_command; 53 | }; 54 | } // namespace basis::cli -------------------------------------------------------------------------------- /cpp/cli/src/cli_subcommand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace basis::core::transport { 7 | class CoordinatorConnector; 8 | } 9 | 10 | namespace basis::cli { 11 | class CLISubcommand { 12 | protected: 13 | CLISubcommand(const std::string &name, argparse::ArgumentParser &parent_parser) 14 | : name(name), parent_parser(parent_parser), parser(name) {} 15 | 16 | public: 17 | bool IsInUse() const { return parent_parser.is_subcommand_used(name); } 18 | 19 | void Commit() { parent_parser.add_subparser(parser); } 20 | 21 | protected: 22 | const std::string name; 23 | 24 | std::unique_ptr CreateCoordinatorConnector(uint16_t port); 25 | 26 | private: 27 | argparse::ArgumentParser &parent_parser; 28 | 29 | protected: 30 | argparse::ArgumentParser parser; 31 | }; 32 | } // namespace basis::cli -------------------------------------------------------------------------------- /cpp/cli/src/fetch_schema.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | std::optional 7 | FetchSchema(const std::string &schema_id, basis::core::transport::CoordinatorConnector *connector, int timeout_s) { 8 | connector->RequestSchemas({&schema_id, 1}); 9 | 10 | auto end = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); 11 | while (std::chrono::steady_clock::now() < end) { 12 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 13 | connector->Update(); 14 | auto schema_ptr = connector->TryGetSchema(schema_id); 15 | if (schema_ptr) { 16 | return *schema_ptr; 17 | } 18 | 19 | if (connector->errors_from_coordinator.size()) { 20 | for (const std::string &error : connector->errors_from_coordinator) { 21 | std::cerr << "Errors when fetching schema:" << std::endl; 22 | std::cerr << error << std::endl; 23 | } 24 | return {}; 25 | } 26 | } 27 | 28 | std::cerr << "Timed out while fetching schema [" << schema_id << "]" << std::endl; 29 | 30 | return {}; 31 | } 32 | -------------------------------------------------------------------------------- /cpp/cli/test/example.yaml: -------------------------------------------------------------------------------- 1 | # Example of future syntax definitions for the launch system 2 | kv_store: 3 | key: value 4 | other_key: other_value 5 | processes: 6 | navigation: 7 | # Specify the units used by the process 8 | units: 9 | # Unit pathfinder, named pathfinder 10 | pathfinder: {} 11 | # named alt_pathfinder 12 | alt_pathfinder: 13 | # Including the same unit twice in one process requires disambigution on the name 14 | unit: pathfinder 15 | args: 16 | use_experimental_algo: 1 17 | env: 18 | MAP_DIR: /home/basis/maps/oakland 19 | lidar: 20 | units: 21 | lidar_driver: 22 | unit_path: /custom/basis/lidar_driver.so 23 | lidar_segmentation: {} -------------------------------------------------------------------------------- /cpp/cli/test/test.yaml: -------------------------------------------------------------------------------- 1 | processes: 2 | test: 3 | units: 4 | wip: {} 5 | wip2: 6 | unit: wip -------------------------------------------------------------------------------- /cpp/cli/test/test.yaml.j2: -------------------------------------------------------------------------------- 1 | # Example future use of jinja 2 | 3 | kv_store: 4 | key: value 5 | other_key: other_value 6 | 7 | processes: 8 | navigation: 9 | {# Specify the units used by the process #} 10 | units: 11 | {# Unit pathfinder, named pathfinder #} 12 | pathfinder: {} 13 | {# named alt_pathfinder #} 14 | alt_pathfinder: 15 | {# Including the same unit twice in one process requires disambigution on the name #} 16 | unit: pathfinder 17 | env: 18 | MAP_DIR: /home/basis/maps/oakland 19 | args: 20 | - use_experimental_algo=1 21 | lidar: 22 | units: 23 | lidar_driver: {} 24 | lidar_segmentation: {} 25 | cameras: 26 | units: 27 | {# could also be passed in #} 28 | {% set cameras = ['left', 'right'] %} 29 | {% for camera in cameras %} 30 | {# this could also be done as an inline block #} 31 | {% include 'camera.yaml' %} 32 | {% endwith %} 33 | stereo_match: 34 | cameras: {{cameras}} {# or something like that #} 35 | 36 | --- camera.yaml --- 37 | 38 | driver_camera_{{camera}}: 39 | namespace: camera_{{camera}} 40 | unit: camera_driver 41 | compress_camera_{{camera}}: 42 | namespace: camera_{{camera}} 43 | unit: jpeg_compression 44 | 45 | 46 | 47 | processes: 48 | robot: 49 | units: 50 | camera_driver: 51 | args: 52 | - path=/dev/camera0 53 | detector: {} 54 | 55 | 56 | processes: 57 | example: 58 | units: 59 | chatter_pub: {} 60 | chatter_sub: {} 61 | args: 62 | - path=/dev/camera0 63 | detector: {} 64 | 65 | -------------------------------------------------------------------------------- /cpp/core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core) 2 | 3 | # basis::core 4 | # Intended for libraries that go into basis::unit 5 | # Not generally intended for end user use, but there are no restrictions against its usage 6 | 7 | add_subdirectory(coordinator) 8 | add_subdirectory(containers) 9 | add_subdirectory(logging) 10 | add_subdirectory(networking) 11 | add_subdirectory(serialization) 12 | add_subdirectory(threading) 13 | add_subdirectory(time) 14 | add_subdirectory(transport) 15 | -------------------------------------------------------------------------------- /cpp/core/containers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_core_containers INTERFACE) 2 | 3 | target_include_directories(basis_core_containers INTERFACE include) 4 | 5 | add_library(basis::core::containers ALIAS basis_core_containers) 6 | 7 | target_link_libraries( 8 | basis_core_containers INTERFACE 9 | basis::core::time 10 | ) 11 | 12 | if(${BASIS_ENABLE_TESTING}) 13 | add_subdirectory(test) 14 | endif() 15 | -------------------------------------------------------------------------------- /cpp/core/containers/include/basis/core/containers/simple_mpsc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | namespace basis::core::containers { 10 | 11 | /** 12 | * Simple thread safe Multi-producer Single Consumer Queue 13 | * Used as a placeholder for now. 14 | * In theory, safe for MPMC as well - needs testing. 15 | * 16 | * TODO: consider using spdlog::details::mpmc_blocking_q 17 | */ 18 | template class SimpleMPSCQueue { 19 | public: 20 | SimpleMPSCQueue() = default; 21 | /** 22 | * Adds item to the queue 23 | */ 24 | void Emplace(T &&item) { 25 | { 26 | std::lock_guard lock(queue_mutex); 27 | queue.emplace(std::move(item)); 28 | } 29 | queue_cv.notify_one(); 30 | } 31 | 32 | size_t Size() { 33 | std::lock_guard lock(queue_mutex); 34 | return queue.size(); 35 | } 36 | 37 | /** 38 | * Removes an item out of the queue - or nothing if there's a timeout. 39 | * @todo I don't see why this can't be used as MPMC 40 | * @todo It would be great if the mspc queue could take an std::atomic bool for when to stop 41 | */ 42 | std::optional Pop(const Duration &sleep = basis::core::Duration::FromSecondsNanoseconds(0, 0)) { 43 | std::unique_lock lock(queue_mutex); 44 | if (queue.empty()) { 45 | queue_cv.wait_for(lock, std::chrono::duration(sleep.ToSeconds()), [this] { return !queue.empty(); }); 46 | } 47 | std::optional ret; 48 | if (!queue.empty()) { 49 | ret = std::move(queue.front()); 50 | queue.pop(); 51 | } 52 | return ret; 53 | } 54 | 55 | // todo: add PopAll() 56 | 57 | std::mutex queue_mutex; 58 | std::condition_variable queue_cv; 59 | std::queue queue; 60 | }; 61 | 62 | } // namespace basis::core::containers -------------------------------------------------------------------------------- /cpp/core/containers/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_containers 3 | test_containers.cpp 4 | ) 5 | target_link_libraries( 6 | test_containers 7 | GTest::gtest_main 8 | ) 9 | 10 | target_link_libraries( 11 | test_containers 12 | basis::core::containers 13 | ) 14 | 15 | include(GoogleTest REQUIRED) 16 | gtest_discover_tests(test_containers) 17 | -------------------------------------------------------------------------------- /cpp/core/coordinator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_libcoordinator SHARED src/coordinator.cpp) 2 | target_include_directories(basis_libcoordinator PUBLIC include) 3 | target_link_libraries( 4 | basis_libcoordinator 5 | basis::plugins::transport::tcp 6 | basis::plugins::serialization::protobuf 7 | basis_proto 8 | ) 9 | 10 | add_executable( 11 | coordinator 12 | src/coordinator_main.cpp 13 | ) 14 | target_link_libraries( 15 | coordinator 16 | basis_libcoordinator 17 | ) 18 | 19 | add_library(basis::core::coordinator ALIAS basis_libcoordinator) 20 | 21 | if(${BASIS_ENABLE_TESTING}) 22 | add_subdirectory(test) 23 | endif() -------------------------------------------------------------------------------- /cpp/core/coordinator/include/basis/core/coordinator_default_port.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr uint16_t BASIS_PUBLISH_INFO_PORT = 1492; -------------------------------------------------------------------------------- /cpp/core/coordinator/src/coordinator_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * Standalone Coordinator binary. 6 | */ 7 | 8 | using namespace basis::core::transport; 9 | int main() { 10 | basis::core::logging::InitializeLoggingSystem(); 11 | 12 | std::optional coordinator = Coordinator::Create(); 13 | if (!coordinator) { 14 | BASIS_LOG_ERROR_NS(coordinator, "Unable to create coordinator."); 15 | return 1; 16 | } 17 | auto next_sleep = std::chrono::steady_clock::now(); 18 | while (true) { 19 | BASIS_LOG_TRACE_NS(coordinator, "Coordinator::Update()"); 20 | next_sleep += std::chrono::milliseconds(50); 21 | coordinator->Update(); 22 | std::this_thread::sleep_until(next_sleep); 23 | } 24 | return 0; 25 | } -------------------------------------------------------------------------------- /cpp/core/coordinator/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_coordinator 3 | test_coordinator.cpp 4 | ) 5 | target_link_libraries( 6 | test_coordinator 7 | GTest::gtest_main 8 | basis::core::coordinator 9 | ) 10 | 11 | include(GoogleTest REQUIRED) 12 | gtest_discover_tests(test_coordinator) 13 | -------------------------------------------------------------------------------- /cpp/core/logging/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_core_logging SHARED src/logging.cpp) 2 | 3 | target_include_directories(basis_core_logging PUBLIC include) 4 | target_link_libraries(basis_core_logging 5 | basis::core::time 6 | spdlog) 7 | add_library(basis::core::logging ALIAS basis_core_logging) 8 | -------------------------------------------------------------------------------- /cpp/core/logging/include/basis/core/logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "spdlog/sinks/stdout_color_sinks.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | namespace basis::core::logging { 14 | namespace internal { 15 | 16 | class RecorderSink : public spdlog::sinks::base_sink { 17 | protected: 18 | virtual void sink_it_(const spdlog::details::log_msg &msg) override; 19 | 20 | virtual void flush_() override {} 21 | 22 | // todo: buffer here, dump when callback set 23 | }; 24 | } // namespace internal 25 | 26 | class LogHandler { 27 | public: 28 | virtual void HandleLog(const MonotonicTime &time, const spdlog::details::log_msg &log_msg, 29 | std::string &&formatted_message) = 0; 30 | }; 31 | 32 | void InitializeLoggingSystem(); 33 | 34 | void SetLogHandler(std::shared_ptr log_handler); 35 | 36 | std::shared_ptr CreateLogger(std::string &&logger_name); 37 | 38 | } // namespace basis::core::logging -------------------------------------------------------------------------------- /cpp/core/logging/src/logging.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "basis/core/time.h" 4 | #include "spdlog/fmt/ostr.h" // support for user defined types 5 | #include 6 | #include // support for loading levels from the environment variable 7 | 8 | namespace basis::core::logging { 9 | 10 | std::shared_ptr global_logging_handler; 11 | // Lock, mainly to prevent issues with shutting down while logging 12 | // this may later go away - we may write the log handler in such a way as to allow for writing to disk all the way up to 13 | // shutdown This will mainly require the recorder interface to be carried as a shared pointer 14 | std::mutex global_logging_handler_mutex; 15 | 16 | namespace internal { 17 | void RecorderSink::sink_it_(const spdlog::details::log_msg &msg) { 18 | // todo: if no global_logging_handler, buffer 19 | // if global_logging_handler, loop over previous items, write 20 | 21 | spdlog::memory_buf_t formatted; 22 | spdlog::sinks::base_sink::formatter_->format(msg, formatted); 23 | // Take a reference here 24 | 25 | std::unique_lock lock(global_logging_handler_mutex); 26 | if (global_logging_handler) { 27 | auto now = basis::core::MonotonicTime::FromNanoseconds(std::chrono::time_point_cast(msg.time).time_since_epoch().count()); 28 | global_logging_handler->HandleLog(now, msg, fmt::to_string(formatted)); 29 | } 30 | } 31 | } // namespace internal 32 | 33 | std::mutex create_log_mutex; 34 | 35 | // TODO: we could instead enforce that the logger has the recorder pointer pushed in 36 | // but then it would be fairly useless for system level functions 37 | std::shared_ptr CreateLogger(std::string &&logger_name) { 38 | std::unique_lock lock(create_log_mutex); 39 | auto logger = spdlog::get(logger_name); 40 | 41 | if (logger) { 42 | return logger; 43 | } 44 | logger = spdlog::create_async_nb(std::move(logger_name)); 45 | 46 | logger->sinks().push_back(std::make_shared()); 47 | 48 | return logger; 49 | } 50 | 51 | void InitializeLoggingSystem() { 52 | // https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/cfg/env.h 53 | spdlog::cfg::load_env_levels(); 54 | spdlog::set_pattern("[%E.%F] [%n] [%^%l%$] %v"); 55 | } 56 | 57 | void SetLogHandler(std::shared_ptr handler) { 58 | std::unique_lock lock(global_logging_handler_mutex); 59 | global_logging_handler = handler; 60 | } 61 | 62 | } // namespace basis::core::logging -------------------------------------------------------------------------------- /cpp/core/networking/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core_networking) 2 | 3 | add_library(basis_core_networking SHARED src/logger.cpp src/socket.cpp) 4 | target_link_libraries(basis_core_networking basis::core::logging expected-lite) 5 | target_include_directories(basis_core_networking PUBLIC include) 6 | 7 | add_library(basis::core::networking ALIAS basis_core_networking) 8 | 9 | if(${BASIS_ENABLE_TESTING}) 10 | add_subdirectory(test) 11 | endif() 12 | -------------------------------------------------------------------------------- /cpp/core/networking/include/basis/core/networking/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | DEFINE_AUTO_LOGGER_NS(basis::core::networking) -------------------------------------------------------------------------------- /cpp/core/networking/src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | DECLARE_AUTO_LOGGER_NS(basis::core::networking) -------------------------------------------------------------------------------- /cpp/core/networking/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_tcp 3 | test_tcp.cpp 4 | ) 5 | target_link_libraries( 6 | test_tcp 7 | GTest::gtest_main 8 | basis::core::networking 9 | ) 10 | 11 | include(GoogleTest REQUIRED) 12 | gtest_discover_tests(test_tcp) 13 | -------------------------------------------------------------------------------- /cpp/core/networking/test/test_tcp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using namespace basis::core::networking; 7 | 8 | TEST(TcpListenSocket, TestAcceptTimeout) { 9 | auto maybe_listen_socket = TcpListenSocket::Create(4242); 10 | ASSERT_TRUE(maybe_listen_socket.has_value()); 11 | TcpListenSocket socket{std::move(maybe_listen_socket.value())}; 12 | ASSERT_NE(socket.GetFd(), -1); 13 | auto maybe_client = socket.Accept(1); 14 | ASSERT_FALSE(maybe_client.has_value()); 15 | auto error = maybe_client.error(); 16 | ASSERT_EQ(error.first, Socket::ErrorSource::TIMEOUT); 17 | } 18 | 19 | TEST(TcpListenSocket, TestAcceptSuccess) { 20 | auto maybe_listen_socket = TcpListenSocket::Create(4242); 21 | ASSERT_TRUE(maybe_listen_socket.has_value()); 22 | TcpListenSocket socket{std::move(maybe_listen_socket.value())}; 23 | ASSERT_NE(socket.GetFd(), -1); 24 | 25 | auto maybe_client_socket = TcpSocket::Connect("127.0.0.1", 4242); 26 | ASSERT_TRUE(maybe_client_socket.has_value()); 27 | TcpSocket client_socket{std::move(maybe_client_socket.value())}; 28 | 29 | auto maybe_server_socket = socket.Accept(1); 30 | ASSERT_TRUE(maybe_server_socket.has_value()); 31 | TcpSocket server_socket{std::move(maybe_server_socket.value())}; 32 | 33 | const std::string message = "Hello, World!"; 34 | 35 | client_socket.Send((std::byte *)message.c_str(), message.size() + 1); 36 | 37 | char buffer[1024]; 38 | 39 | server_socket.RecvInto(buffer, 1024); 40 | 41 | ASSERT_STREQ(buffer, message.c_str()); 42 | 43 | const std::string response = "Goodbye, World!"; 44 | 45 | memset(buffer, 0, 1024); 46 | 47 | server_socket.Send((std::byte *)response.c_str(), response.size() + 1); 48 | 49 | client_socket.RecvInto(buffer, 1024); 50 | 51 | ASSERT_STREQ(buffer, response.c_str()); 52 | } 53 | -------------------------------------------------------------------------------- /cpp/core/serialization/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core_serialization) 2 | 3 | add_library(basis_core_serialization SHARED src/serialization.cpp) 4 | target_include_directories(basis_core_serialization PUBLIC include) 5 | add_library(basis::core::serialization ALIAS basis_core_serialization) -------------------------------------------------------------------------------- /cpp/core/serialization/include/basis/core/serialization/message_type_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis::core::serialization { 6 | 7 | struct MessageTypeInfo { 8 | std::string serializer; 9 | std::string name; 10 | 11 | std::string mcap_message_encoding; 12 | std::string mcap_schema_encoding; 13 | 14 | std::string SchemaId() const { return serializer + ":" + name; } 15 | // size_t type_size = 0; // Required for raw types, to help ensure safety 16 | }; 17 | 18 | // In all likelihood, a schema should container a message type info 19 | struct MessageSchema { 20 | std::string serializer; 21 | std::string name; 22 | std::string schema; 23 | std::string 24 | schema_efficient; // Optional - some serializers may have a more efficient representation wanted by the recorder 25 | std::string hash_id; 26 | }; 27 | 28 | constexpr char MCAP_CHANNEL_METADATA_SERIALIZER[] = "basis_serializer"; 29 | constexpr char MCAP_CHANNEL_METADATA_READABLE_SCHEMA[] = "basis_human_readable_schema"; 30 | constexpr char MCAP_CHANNEL_METADATA_HASH_ID[] = "basis_hash_id"; 31 | 32 | } // namespace basis::core::serialization -------------------------------------------------------------------------------- /cpp/core/serialization/src/serialization.cpp: -------------------------------------------------------------------------------- 1 | // TODO -------------------------------------------------------------------------------- /cpp/core/threading/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core_threading) 2 | 3 | add_library(basis_core_threading INTERFACE) 4 | target_include_directories(basis_core_threading INTERFACE include) 5 | 6 | add_library(basis::core::threading ALIAS basis_core_threading) 7 | -------------------------------------------------------------------------------- /cpp/core/time/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core_time) 2 | 3 | add_library(basis_core_time STATIC src/time.cpp) 4 | target_include_directories(basis_core_time PUBLIC include) 5 | 6 | add_library(basis::core::time ALIAS basis_core_time) 7 | 8 | if(${BASIS_ENABLE_TESTING}) 9 | add_subdirectory(test) 10 | endif() -------------------------------------------------------------------------------- /cpp/core/time/include/basis/core/time/chrono_adapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace basis { 6 | namespace time { 7 | struct ChronoClock { 8 | typedef std::chrono::nanoseconds duration; 9 | typedef duration::rep rep; 10 | typedef duration::period period; 11 | typedef std::chrono::time_point time_point; 12 | static const bool is_steady = true; 13 | 14 | static time_point now() noexcept { 15 | using namespace std::chrono; 16 | return time_point(std::chrono::nanoseconds(basis::core::MonotonicTime::Now().nsecs)); 17 | } 18 | 19 | static std::time_t to_time_t(const time_point &t) noexcept { 20 | return std::chrono::time_point_cast(t).time_since_epoch().count(); 21 | } 22 | }; 23 | } // namespace time 24 | } // namespace basis -------------------------------------------------------------------------------- /cpp/core/time/src/time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | namespace basis::core { 10 | namespace { 11 | static std::atomic simulated_time_ns = time::INVALID_NSECS; 12 | static std::atomic current_run_token = 0; 13 | } // namespace 14 | MonotonicTime MonotonicTime::FromNanoseconds(int64_t ns) { 15 | return {ns}; 16 | } 17 | 18 | MonotonicTime MonotonicTime::FromSeconds(double seconds) { return {TimeBase::SecondsToNanoseconds(seconds)}; } 19 | MonotonicTime MonotonicTime::FromSecondsNanoseconds(int64_t seconds, int64_t nanoseconds) { 20 | return {TimeBase::SecondsToNanoseconds(seconds) + nanoseconds}; 21 | } 22 | MonotonicTime MonotonicTime::Now(bool ignore_simulated_time) { 23 | if (!ignore_simulated_time && UsingSimulatedTime()) { 24 | return {simulated_time_ns}; 25 | } 26 | timespec ts; 27 | // not CLOCK_MONOTONIC_RAW because it's unsupported by clock_nanosleep 28 | clock_gettime(CLOCK_MONOTONIC, &ts); 29 | 30 | return {TimeBase::SecondsToNanoseconds(ts.tv_sec) + ts.tv_nsec}; 31 | } 32 | 33 | void MonotonicTime::SleepUntil(uint64_t run_token) const { 34 | // TODO: these sleeps are both inaccurate and not performant if under sim time. Use a condition variable! 35 | if (UsingSimulatedTime()) { 36 | while(Now().nsecs < nsecs && run_token == current_run_token) { 37 | // Spin until time catches up 38 | timespec ts; 39 | ts.tv_sec = 0; 40 | ts.tv_nsec = 100000; // .1 ms 41 | clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, nullptr); 42 | } 43 | } 44 | else { 45 | timespec ts = ToTimespec(); 46 | 47 | clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr); 48 | } 49 | } 50 | 51 | void MonotonicTime::SetSimulatedTime(int64_t nanoseconds, uint64_t run_token) { 52 | simulated_time_ns = nanoseconds; 53 | current_run_token = run_token; 54 | } 55 | 56 | bool MonotonicTime::UsingSimulatedTime() { 57 | return simulated_time_ns != time::INVALID_NSECS; 58 | } 59 | 60 | uint64_t MonotonicTime::GetRunToken() { 61 | return current_run_token; 62 | } 63 | 64 | } // namespace basis::core -------------------------------------------------------------------------------- /cpp/core/time/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_time 3 | test_time.cpp 4 | ) 5 | target_link_libraries( 6 | test_time 7 | GTest::gtest_main 8 | basis::core::time 9 | ) 10 | 11 | include(GoogleTest REQUIRED) 12 | gtest_discover_tests(test_time) 13 | -------------------------------------------------------------------------------- /cpp/core/time/test/test_time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace basis::core { 6 | 7 | struct TestTime : public MonotonicTime {}; 8 | 9 | TEST(TestTime, TestConversions) { 10 | RealTimeDuration invalid; 11 | ASSERT_FALSE(invalid.IsValid()); 12 | 13 | RealTimeDuration one_sec = RealTimeDuration::FromSeconds(1.0); 14 | ASSERT_TRUE(one_sec.IsValid()); 15 | ASSERT_EQ(one_sec.nsecs, 1000000000); 16 | ASSERT_EQ(one_sec.ToSeconds(), 1.0); 17 | 18 | RealTimeDuration minus_one_sec = RealTimeDuration::FromSeconds(-1.0); 19 | ASSERT_TRUE(minus_one_sec.IsValid()); 20 | ASSERT_EQ(minus_one_sec.nsecs, -1000000000); 21 | ASSERT_EQ(minus_one_sec.ToSeconds(), -1.0); 22 | 23 | RealTimeDuration minus_10_25 = RealTimeDuration::FromSeconds(-10.25); 24 | ASSERT_TRUE(minus_10_25.IsValid()); 25 | ASSERT_EQ(minus_10_25.nsecs, -10250000000); 26 | ASSERT_EQ(minus_10_25.ToSeconds(), -10.25); 27 | 28 | // Test a bunch of numbers here 29 | for (int i = -10000000; i < 10000001; i++) { 30 | RealTimeDuration d = RealTimeDuration::FromSeconds(i); 31 | ASSERT_TRUE(d.IsValid()) << i; 32 | // Within this range, we should still get perfect conversions on integers 33 | ASSERT_EQ(d.ToSeconds(), i) << i; 34 | if (i != 0) { 35 | RealTimeDuration d_frac = RealTimeDuration::FromSeconds(i + (double)1 / i); 36 | ASSERT_TRUE(d_frac.IsValid()) << i; 37 | // No promises on precision here 38 | ASSERT_FLOAT_EQ(d_frac.ToSeconds(), i + (double)1 / i) << i; 39 | } 40 | } 41 | 42 | for (int i = -1; i <= 1; i++) { 43 | 44 | TestTime right_now; 45 | // clang-format off 46 | int64_t now_total_nsecs = i * 1715386762'545773252; 47 | double now_double = i * 1715386762.545773252; 48 | int64_t now_secs = i * 1715386762; 49 | int64_t now_nsecs = i * 545773252; 50 | // clang-format on 51 | 52 | right_now.nsecs = now_total_nsecs; 53 | ASSERT_FLOAT_EQ(right_now.ToSeconds(), now_double); 54 | std::pair now_ros(now_secs, now_nsecs); 55 | ASSERT_EQ(right_now.ToRosTime(), now_ros); 56 | 57 | timeval now_tv = {.tv_sec = now_secs, .tv_usec = now_nsecs * (std::nano::den / std::micro::den)}; 58 | 59 | timeval converted_timeval = right_now.ToTimeval(); 60 | ASSERT_EQ(converted_timeval.tv_sec, now_tv.tv_sec); 61 | ASSERT_EQ(converted_timeval.tv_usec, now_tv.tv_usec); 62 | } 63 | } 64 | 65 | } // namespace basis::core -------------------------------------------------------------------------------- /cpp/core/transport/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_core_transport SHARED 2 | src/inproc.cpp 3 | src/logger.cpp 4 | src/publisher.cpp 5 | src/subscriber.cpp) 6 | target_link_libraries(basis_core_transport basis::core::serialization basis::core::time basis::core::threading basis::core::containers basis::recorder spdlog uuid basis_proto) 7 | target_include_directories(basis_core_transport PUBLIC include) 8 | 9 | add_library(basis::core::transport ALIAS basis_core_transport) 10 | 11 | if(${BASIS_ENABLE_TESTING}) 12 | add_subdirectory(test) 13 | endif() -------------------------------------------------------------------------------- /cpp/core/transport/include/basis/core/transport/convertable_inproc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis { 6 | 7 | namespace MessageVariant { 8 | enum { 9 | NO_MESSAGE, 10 | TYPE_MESSAGE, 11 | INPROC_TYPE_MESSAGE, 12 | }; 13 | } 14 | 15 | 16 | // Default conversion from struct to message, override if T_CONVERTABLE_INPROC isn't modifyable 17 | template 18 | std::shared_ptr ConvertToMessage(const std::shared_ptr& in) { 19 | return in->ToMessage(); 20 | } 21 | namespace core::transport { 22 | 23 | 24 | class NoAdditionalInproc { 25 | 26 | }; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /cpp/core/transport/include/basis/core/transport/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | DEFINE_AUTO_LOGGER_NS(basis::core::transport) -------------------------------------------------------------------------------- /cpp/core/transport/include/basis/core/transport/message_event.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace basis::core::transport { 7 | 8 | struct TopicInfo { 9 | // TODO: a bunch of allocations here 10 | std::string topic; 11 | // std::string type; 12 | // std::string publisher_unit; 13 | // std::string publisher_host; 14 | }; 15 | 16 | template struct MessageEvent { 17 | MonotonicTime time; 18 | TopicInfo topic_info; 19 | 20 | std::shared_ptr message; 21 | }; 22 | 23 | } // namespace basis::core::transport -------------------------------------------------------------------------------- /cpp/core/transport/include/basis/core/transport/publisher_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #pragma clang diagnostic push 7 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 8 | #include 9 | #pragma clang diagnostic pop 10 | 11 | namespace basis::core::transport { 12 | 13 | /** 14 | * Contains information needed to subscribe to a publisher 15 | * 16 | * @todo: should we remove this and deal in raw proto? Would be faster. 17 | */ 18 | struct PublisherInfo { 19 | /** 20 | * Randomly generated, non colliding publisher id 21 | */ 22 | union { 23 | __uint128_t publisher_id; 24 | uint64_t publisher_id_64s[2]; 25 | }; 26 | /** 27 | * The topic associated with the publisher 28 | */ 29 | std::string topic; 30 | /** 31 | * The schema ID associated with the messages sent over the topic. 32 | * ie "protobuf:Pose" 33 | */ 34 | std::string schema_id; 35 | /** 36 | * Possible transports 37 | */ 38 | std::unordered_map transport_info; 39 | 40 | /** 41 | * Converts to a proto::PublisherInfo 42 | */ 43 | proto::PublisherInfo ToProto() { 44 | proto::PublisherInfo out; 45 | out.set_publisher_id_high(publisher_id_64s[0]); 46 | out.set_publisher_id_low(publisher_id_64s[1]); 47 | out.set_topic(topic); 48 | out.set_schema_id(schema_id); 49 | 50 | for (auto &p : transport_info) { 51 | out.mutable_transport_info()->insert({p.first, p.second}); 52 | } 53 | return out; 54 | } 55 | 56 | /** 57 | * Converts from a proto::PublisherInfo 58 | */ 59 | static PublisherInfo FromProto(const proto::PublisherInfo &proto) { 60 | PublisherInfo out; 61 | out.publisher_id_64s[0] = proto.publisher_id_high(); 62 | out.publisher_id_64s[1] = proto.publisher_id_low(); 63 | out.topic = proto.topic(); 64 | out.schema_id = proto.schema_id(); 65 | for (auto &[topic, endpoint] : proto.transport_info()) { 66 | out.transport_info[topic] = endpoint; 67 | } 68 | return out; 69 | } 70 | }; 71 | 72 | } // namespace basis::core::transport -------------------------------------------------------------------------------- /cpp/core/transport/include/basis/core/transport/thread_pool_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis::core::transport { 6 | /** 7 | * Simple class to manage thread pools across publishers. 8 | * Later this will be used to also manage named thread pools. 9 | */ 10 | class ThreadPoolManager { 11 | public: 12 | ThreadPoolManager() = default; 13 | 14 | // TODO: do we actually need to share ownership of the pool or can we pass out raw pointers and enforce destruction 15 | // order? 16 | std::shared_ptr GetDefaultThreadPool() { return default_thread_pool; } 17 | 18 | private: 19 | std::shared_ptr default_thread_pool = std::make_shared(4); 20 | }; 21 | 22 | } // namespace basis::core::transport -------------------------------------------------------------------------------- /cpp/core/transport/src/inproc.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basis-robotics/basis/ec8e3e9f86215546d8179317e2c3080760d87a14/cpp/core/transport/src/inproc.cpp -------------------------------------------------------------------------------- /cpp/core/transport/src/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | DECLARE_AUTO_LOGGER_NS(basis::core::transport) -------------------------------------------------------------------------------- /cpp/core/transport/src/publisher.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | namespace basis::core::transport { 7 | 8 | std::atomic publisher_id_counter; 9 | 10 | __uint128_t CreatePublisherId() { 11 | __uint128_t out; 12 | static_assert(sizeof(__uint128_t) == sizeof(char[16])); 13 | [[maybe_unused]] const int retval = uuid_generate_time_safe(reinterpret_cast(&out)); 14 | /// @todo BASIS-16 not everything is mounted in the basis-env container that needs to be 15 | // Guard against uuid collisions 16 | // assert(retval == 0); 17 | return out; 18 | } 19 | 20 | PublisherInfo PublisherBase::GetPublisherInfo() { 21 | PublisherInfo out; 22 | out.publisher_id = publisher_id; 23 | out.topic = topic; 24 | out.schema_id = type_info.SchemaId(); 25 | 26 | if (has_inproc) { 27 | out.transport_info["inproc"] = std::to_string(getpid()); 28 | } 29 | for (auto &pub : transport_publishers) { 30 | out.transport_info[pub->GetTransportName()] = pub->GetConnectionInformation(); 31 | } 32 | 33 | return out; 34 | } 35 | } // namespace basis::core::transport -------------------------------------------------------------------------------- /cpp/core/transport/src/subscriber.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace basis::core::transport { 9 | void SubscriberBase::HandlePublisherInfo(const std::vector &info) { 10 | for (const PublisherInfo &publisher_info : info) { 11 | if (publisher_info.topic != topic) { 12 | BASIS_LOG_ERROR("Skipping publisher info because no topic"); 13 | continue; 14 | } 15 | const __uint128_t &publisher_id = publisher_info.publisher_id; 16 | auto it = publisher_id_to_transport_sub.find(publisher_id); 17 | 18 | if (it != publisher_id_to_transport_sub.end()) { 19 | if (it->second == nullptr) { 20 | // Inproc is always valid 21 | continue; 22 | } 23 | 24 | /// @todo BASIS-11: need to be able to handle disconnects 25 | #if 0 26 | if(it->second->IsConnectedToPublisher(publisher_id)) { 27 | continue; 28 | } 29 | #else 30 | continue; 31 | #endif 32 | } 33 | 34 | // todo check if inproc pid is ours 35 | if (has_inproc) { 36 | auto it = publisher_info.transport_info.find("inproc"); 37 | if (it != publisher_info.transport_info.end() && it->second == std::to_string(getpid())) { 38 | // No need to do anything 39 | publisher_id_to_transport_sub.emplace(publisher_id, nullptr); 40 | continue; 41 | } 42 | } 43 | 44 | /// @todo BASIS-12: this will arbitrarily pick a transport once we add another type 45 | for (auto &transport_subscriber : transport_subscribers) { 46 | for (const auto &[pub_transport_name, pub_transport_endpoint] : publisher_info.transport_info) { 47 | if (pub_transport_name == transport_subscriber->GetTransportName()) { 48 | if (transport_subscriber->Connect("127.0.0.1", pub_transport_endpoint, publisher_id)) { 49 | publisher_id_to_transport_sub.emplace(publisher_id, transport_subscriber.get()); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | size_t SubscriberBase::GetPublisherCount() { 58 | size_t count = 0; 59 | for (auto &ts : transport_subscribers) { 60 | count += ts->GetPublisherCount(); 61 | } 62 | return count; 63 | } 64 | } // namespace basis::core::transport -------------------------------------------------------------------------------- /cpp/core/transport/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_basic_transport 3 | test_basic_transport.cpp 4 | ) 5 | target_link_libraries( 6 | test_basic_transport 7 | GTest::gtest_main 8 | basis::core::transport 9 | ) 10 | 11 | include(GoogleTest REQUIRED) 12 | gtest_discover_tests(test_basic_transport) 13 | -------------------------------------------------------------------------------- /cpp/core/transport/test/test_basic_transport.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | using namespace basis::core::transport; 8 | 9 | TEST(Inproc, PubSub) { 10 | // Create a Coordinator 11 | InprocConnector coordinator; 12 | // Create a publisher 13 | auto publisher = coordinator.Advertise("topic", nullptr); 14 | 15 | std::atomic num_recv = 0; 16 | 17 | auto subscriber = coordinator.Subscribe( 18 | "topic", [&num_recv](const MessageEvent &message) { GTEST_ASSERT_EQ(*message.message, num_recv++); }, nullptr); 19 | 20 | for (int i = 0; i < 10; i++) { 21 | publisher->Publish(std::make_shared(i)); 22 | } 23 | 24 | GTEST_ASSERT_EQ(num_recv, 10); 25 | } 26 | 27 | TEST(Inproc, PubSubWait) { 28 | // Create a Coordinator 29 | InprocConnector coordinator; 30 | // Create a publisher 31 | auto publisher = coordinator.Advertise("topic", nullptr); 32 | 33 | std::atomic num_recv = 0; 34 | 35 | auto subscriber = coordinator.Subscribe( 36 | "topic", [&num_recv](const MessageEvent &message) { ASSERT_EQ(*message.message, num_recv++); }, nullptr); 37 | 38 | std::thread pub_thread([&]() { 39 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 40 | for (int i = 0; i < 10; i++) { 41 | publisher->Publish(std::make_shared(i)); 42 | } 43 | }); 44 | 45 | pub_thread.join(); 46 | 47 | GTEST_ASSERT_EQ(num_recv, 10); 48 | } 49 | 50 | struct TestStruct { 51 | uint32_t foo = 3; 52 | float bar = 8.5; 53 | std::string baz = "baz"; 54 | }; 55 | 56 | TEST(TransportManager, Basic) { 57 | 58 | TransportManager transport_manager(std::make_unique()); 59 | 60 | auto publisher = 61 | transport_manager.Advertise("InprocTransport"); 62 | 63 | basis::core::threading::ThreadPool work_thread_pool(4); 64 | 65 | std::atomic num_recv = 0; 66 | auto subscriber = transport_manager.Subscribe( 67 | "InprocTransport", [&num_recv](std::shared_ptr) { num_recv++; }, &work_thread_pool); 68 | 69 | publisher->Publish(std::make_shared()); 70 | ASSERT_EQ(num_recv, 1); 71 | } -------------------------------------------------------------------------------- /cpp/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable( 3 | basis_example 4 | src/basis_example.cpp 5 | ) 6 | target_link_libraries( 7 | basis_example 8 | basis::unit 9 | basis::plugins::transport::tcp 10 | basis::plugins::serialization::protobuf 11 | basis::core::coordinator 12 | basis::synchronizers 13 | argparse 14 | ) 15 | 16 | if(BASIS_ENABLE_ROS) 17 | 18 | find_package(std_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/std_msgs/cmake) 19 | find_package(geometry_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/geometry_msgs/cmake) 20 | find_package(sensor_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/sensor_msgs/cmake) 21 | 22 | target_link_libraries( 23 | basis_example 24 | rostime 25 | basis::plugins::serialization::rosmsg) 26 | 27 | endif() -------------------------------------------------------------------------------- /cpp/launch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_launch 2 | src/launch.cpp 3 | src/launch_definition.cpp 4 | src/mermaid_formatter.cpp 5 | src/unit_loader.cpp 6 | src/process_manager.cpp) 7 | 8 | target_include_directories(basis_launch PUBLIC include) 9 | 10 | target_link_libraries(basis_launch 11 | basis::unit 12 | basis::cli_logger 13 | Backward::Interface 14 | yaml-cpp 15 | dl 16 | ) 17 | 18 | add_library(basis::launch ALIAS basis_launch) 19 | 20 | if(${BASIS_ENABLE_TESTING}) 21 | add_subdirectory(test) 22 | endif() 23 | -------------------------------------------------------------------------------- /cpp/launch/include/basis/launch.h: -------------------------------------------------------------------------------- 1 | #include "basis/launch/launch_definition.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | DEFINE_AUTO_LOGGER_NS(basis::launch) 9 | 10 | namespace basis::launch { 11 | 12 | /** 13 | * Search for a unit in well known directories given a unit name 14 | * @param unit_name 15 | * @return std::optional 16 | */ 17 | std::optional FindUnit(std::string_view unit_name); 18 | 19 | /** 20 | * Launch a set of processes, given a definition 21 | */ 22 | void LaunchYamlDefinition(const LaunchDefinition &launch, const LaunchContext &context); 23 | 24 | } // namespace basis::launch -------------------------------------------------------------------------------- /cpp/launch/include/basis/launch/mermaid_formatter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "launch_definition.h" 4 | 5 | namespace basis::launch { 6 | struct LaunchDefinitionMermaidFormatter : public LaunchDefinitionDebugFormatter { 7 | virtual std::string HandleStart() override { 8 | return R"""(--- 9 | config: 10 | flowchart: 11 | nodeSpacing: 5 12 | subGraphTitleMargin: 13 | bottom: 10 14 | defaultRenderer: elk 15 | elk: 16 | mergeEdges: True 17 | --- 18 | graph LR 19 | %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%% 20 | )""";} 21 | 22 | virtual std::string HandleEnd() override; 23 | virtual std::string FormatUnit(std::string_view unit_name, const UnitDefinition& unit) override; 24 | virtual std::string FormatProcess(std::string_view process_name, std::vector unit_cmds) override; 25 | 26 | 27 | std::unordered_map> handlers_with_input; 28 | std::unordered_map> handlers_with_output; 29 | 30 | }; 31 | } -------------------------------------------------------------------------------- /cpp/launch/include/basis/launch/unit_loader.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /** 7 | * Creates a unit given a path to a shared object. 8 | * Will load the shared object, inspect it for required functionality, and return an initialized Unit. 9 | * 10 | * @param path the path to the shared object 11 | * @return std::unique_ptr 12 | */ 13 | std::unique_ptr CreateUnitWithLoader(const std::filesystem::path &path, std::string_view unit_name, 14 | const basis::arguments::CommandLineTypes &command_line); 15 | -------------------------------------------------------------------------------- /cpp/launch/src/process_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "process_manager.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace basis::cli { 11 | 12 | bool Process::Wait(int timeout_s) { 13 | // Don't wait if we're empty or already waited 14 | if (pid == -1) { 15 | return true; 16 | } 17 | 18 | const auto end = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_s); 19 | 20 | int wait_ret = 21 | waitpid(pid, nullptr, timeout_s >= 0 ? /* has timeout, just check */ WNOHANG : /* no timeout, wait forever*/ 0); 22 | 23 | // Loop until we have a successful wait or time out 24 | while (wait_ret <= 0 && end > std::chrono::steady_clock::now()) { 25 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 26 | wait_ret = waitpid(pid, nullptr, WNOHANG); 27 | } 28 | 29 | // We hit an error, bail 30 | if (wait_ret < 0) { 31 | BASIS_LOG_ERROR("waitpid({}): {}", pid, strerror(errno)); 32 | } 33 | if (wait_ret > 0) { 34 | pid = -1; 35 | } 36 | 37 | return pid == -1; 38 | } 39 | 40 | void Process::Kill(int sig) { 41 | if (pid == -1) { 42 | return; 43 | } 44 | 45 | kill(pid, sig); 46 | } 47 | 48 | void Process::KillAndWait() { 49 | if (pid == -1) { 50 | return; 51 | } 52 | // todo BASIS-21: Need to work from SIGINT SIGKILL etc 53 | Kill(SIGHUP); 54 | 55 | Wait(5); 56 | } 57 | 58 | } // namespace basis::cli -------------------------------------------------------------------------------- /cpp/launch/src/process_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis::cli { 6 | 7 | struct ProcessConfig { 8 | /** 9 | * If set, will kill and wait for process death on shutdown 10 | */ 11 | bool kill_on_shutdown = true; 12 | }; 13 | 14 | /** 15 | * Class to manage an external process, including killing on death. 16 | */ 17 | class Process { 18 | public: 19 | Process(int pid, ProcessConfig config = {}) : pid(pid), config(config) {} 20 | // No copying 21 | Process(const Process &) = delete; 22 | Process &operator=(const Process &) = delete; 23 | // Moving is fine 24 | Process(Process &&other) { *this = std::move(other); } 25 | Process &operator=(Process &&other) { 26 | if (this != &other) { 27 | // When we do move, if we have a process, kill it. 28 | Shutdown(); 29 | // Copy the other pid and config over 30 | pid = other.pid; 31 | config = other.config; 32 | // And clear from the donor 33 | other.pid = -1; 34 | } 35 | return *this; 36 | } 37 | 38 | ~Process() { Shutdown(); } 39 | 40 | int GetPid() const { return pid; } 41 | 42 | void Shutdown() { 43 | if (config.kill_on_shutdown) { 44 | KillAndWait(); 45 | } 46 | } 47 | 48 | bool Wait(int timeout_s = -1); 49 | 50 | void Kill(int sig); 51 | 52 | void KillAndWait(); 53 | 54 | private: 55 | int pid = -1; 56 | ProcessConfig config; 57 | }; 58 | 59 | } // namespace basis::cli -------------------------------------------------------------------------------- /cpp/launch/src/unit_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | struct DlClose { 7 | void operator()(void *handle) { dlclose(handle); } 8 | }; 9 | 10 | using ManagedSharedObject = std::unique_ptr; 11 | 12 | using CreateUnitCallback = decltype(CreateUnit) *; 13 | 14 | struct UnitLoader { 15 | ManagedSharedObject handle; 16 | CreateUnitCallback create_unit; 17 | }; 18 | 19 | std::unordered_map unit_loaders; 20 | 21 | std::unique_ptr CreateUnitWithLoader(const std::filesystem::path &path, std::string_view unit_name, 22 | const basis::arguments::CommandLineTypes &command_line) { 23 | std::string string_path = path.string(); 24 | 25 | auto maybe_unit_loader = unit_loaders.find(string_path); 26 | if (maybe_unit_loader == unit_loaders.end()) { 27 | // For now - need to use RTLD_GLOBAL to allow different inproc transports to communicate 28 | // This is the opposite of how the protobuf needs things - but no crashes on shutdown (yet?) 29 | void *handle = dlopen(path.c_str(), RTLD_NOW | RTLD_GLOBAL); 30 | if (!handle) { 31 | std::cerr << "Failed to dlopen " << path << std::endl; 32 | std::cerr << "dlerror: " << dlerror() << std::endl; 33 | return nullptr; 34 | } 35 | 36 | ManagedSharedObject managed_handle(handle); 37 | 38 | dlerror(); 39 | auto load_unit = reinterpret_cast(dlsym(handle, "CreateUnit")); 40 | if (!load_unit) { 41 | std::cerr << "Failed to find unit interface CreateUnit in " << path << std::endl; 42 | std::cerr << "dlerror: " << dlerror() << std::endl; 43 | return nullptr; 44 | } 45 | maybe_unit_loader = unit_loaders.emplace(path.string(), UnitLoader{std::move(managed_handle), load_unit}).first; 46 | } 47 | 48 | return std::unique_ptr(maybe_unit_loader->second.create_unit( 49 | std::string(unit_name), command_line, [](const char *msg) { std::cerr << msg << std::endl; })); 50 | } 51 | -------------------------------------------------------------------------------- /cpp/launch/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_launch_definition 3 | test_launch_definition.cpp 4 | ) 5 | 6 | target_link_libraries( 7 | test_launch_definition 8 | basis::launch 9 | GTest::gtest_main 10 | ) 11 | 12 | include(GoogleTest REQUIRED) 13 | message(${CMAKE_CURRENT_BINARY_DIR}) 14 | gtest_discover_tests(test_launch_definition WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 15 | 16 | macro(add_test_file FILE_NAME) 17 | add_custom_target( 18 | test_launch_definition_${FILE_NAME} 19 | COMMAND ${CMAKE_COMMAND} -E copy 20 | ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_NAME} 21 | ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}) 22 | add_dependencies(test_launch_definition test_launch_definition_${FILE_NAME}) 23 | endmacro() 24 | 25 | add_test_file(test_include.launch.yaml) 26 | add_test_file(foxglove.launch.yaml) 27 | -------------------------------------------------------------------------------- /cpp/launch/test/foxglove.launch.yaml: -------------------------------------------------------------------------------- 1 | args: 2 | split_process: 3 | type: bool 4 | default: False 5 | --- 6 | groups: 7 | foxglove_inner: 8 | process: {{args.split_process}} 9 | units: 10 | foxglove: {} -------------------------------------------------------------------------------- /cpp/launch/test/test_include.launch.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | args: 3 | include_foxglove_type: 4 | type: int32_t 5 | help: test to run 6 | default: 0 7 | --- 8 | recording: 9 | directory: /tmp/ 10 | # TODO: we should allow multiple recorders with filtering 11 | name: camera_pipeline 12 | topics: 13 | - /log 14 | 15 | 16 | {% if args.include_foxglove_type == 1 %} 17 | # The default syntax that most will reach for 18 | include: 19 | foxglove.launch.yaml: {} 20 | {% else if args.include_foxglove_type == 2 %} 21 | include: 22 | - foxglove.launch.yaml: {} 23 | {% endif %} 24 | groups: 25 | foxglove: 26 | {% if args.include_foxglove_type in [3, 4] %} 27 | include: 28 | foxglove.launch.yaml: 29 | split_process: {{ args.include_foxglove_type == 4 }} 30 | {% endif %} 31 | webcam: 32 | units: 33 | v4l2_camera_driver: 34 | args: 35 | device: /dev/video0 36 | topic_namespace: /camera 37 | -------------------------------------------------------------------------------- /cpp/plugins/bridges/foxglove/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(foxglove_bridge) 2 | include(FetchContent) 3 | 4 | add_plugin(basis_plugins_bridges_foxglove src/foxglove_bridge.cpp) 5 | 6 | FetchContent_Declare( 7 | foxglove_websocket 8 | PATCH_COMMAND sed -i -e "s/CMAKE_SOURCE_DIR/PROJECT_SOURCE_DIR/g" cpp/foxglove-websocket/CMakeLists.txt 9 | URL https://github.com/foxglove/ws-protocol/archive/refs/tags/releases/cpp/v1.3.0.zip 10 | ) 11 | FetchContent_MakeAvailable(foxglove_websocket) 12 | 13 | add_subdirectory( 14 | ${foxglove_websocket_SOURCE_DIR}/cpp/foxglove-websocket 15 | ${CMAKE_BINARY_DIR}/foxglove-websocket 16 | ) 17 | set_target_properties(foxglove_websocket PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON) 18 | 19 | find_package(OpenSSL) 20 | 21 | target_compile_definitions(basis_plugins_bridges_foxglove PRIVATE ASIO_STANDALONE) 22 | target_compile_definitions(foxglove_websocket PUBLIC ASIO_STANDALONE) 23 | 24 | target_include_directories(basis_plugins_bridges_foxglove PUBLIC 25 | ${foxglove_websocket_SOURCE_DIR}/cpp/foxglove-websocket/include 26 | ) 27 | 28 | target_link_libraries(basis_plugins_bridges_foxglove 29 | foxglove_websocket 30 | basis::core::logging 31 | basis::core::threading 32 | basis::plugins::transport::tcp 33 | basis::unit 34 | OpenSSL::SSL 35 | ) 36 | target_include_directories(basis_plugins_bridges_foxglove PUBLIC include) 37 | 38 | set_target_properties(basis_plugins_bridges_foxglove PROPERTIES OUTPUT_NAME "foxglove.unit") 39 | set_target_properties(basis_plugins_bridges_foxglove PROPERTIES PREFIX "") 40 | 41 | install(TARGETS basis_plugins_bridges_foxglove DESTINATION unit/) 42 | 43 | add_library(basis::plugins::bridges::foxglove ALIAS basis_plugins_bridges_foxglove) 44 | 45 | add_executable(foxglove_bridge src/foxglove_dummy_main.cpp) 46 | target_link_libraries(foxglove_bridge basis_plugins_bridges_foxglove basis::unit::main) 47 | install(TARGETS foxglove_bridge DESTINATION bin/) -------------------------------------------------------------------------------- /cpp/plugins/bridges/foxglove/include/basis/plugins/bridges/foxglove_bridge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | DEFINE_AUTO_LOGGER_PLUGIN(bridges, foxglove) 10 | 11 | namespace basis::plugins::bridges::foxglove { 12 | class FoxgloveBridge : public basis::SingleThreadedUnit { 13 | using ConnectionHandle = websocketpp::connection_hdl; 14 | using ClientPublications = 15 | std::unordered_map<::foxglove::ClientChannelId, std::shared_ptr>; 16 | using PublicationsByClient = std::map>; 17 | using SubscriptionsByClient = 18 | std::map, std::owner_less<>>; 19 | 20 | public: 21 | FoxgloveBridge(const std::optional& unit_name); 22 | 23 | ~FoxgloveBridge(); 24 | 25 | void Initialize(const UnitInitializeOptions &options) override; 26 | 27 | void Update(std::atomic *stop_token, const basis::core::Duration &max_sleep_duration) override; 28 | 29 | private: 30 | struct TopicAndDatatype { 31 | std::string topic; 32 | std::string schema_serializer; 33 | std::string schema; 34 | }; 35 | 36 | void init(const std::string &address = "0.0.0.0", int port = 8765); 37 | 38 | void logHandler(::foxglove::WebSocketLogLevel level, [[maybe_unused]] char const *msg); 39 | 40 | void subscribe(::foxglove::ChannelId channelId, ConnectionHandle clientHandle); 41 | 42 | void unsubscribe(::foxglove::ChannelId channelId, ConnectionHandle clientHandle); 43 | 44 | void clientAdvertise(const ::foxglove::ClientAdvertisement &channel, ConnectionHandle clientHandle); 45 | 46 | void clientUnadvertise(::foxglove::ClientChannelId channelId, ConnectionHandle clientHandle); 47 | 48 | void clientMessage(const ::foxglove::ClientMessage &clientMsg, ConnectionHandle clientHandle); 49 | 50 | void updateAdvertisedTopics(); 51 | 52 | std::unique_ptr<::foxglove::ServerInterface> server; 53 | bool useSimTime = false; 54 | 55 | std::mutex subscriptionsMutex; 56 | std::shared_mutex publicationsMutex; 57 | 58 | std::unordered_map<::foxglove::ChannelId, ::foxglove::ChannelWithoutId> advertisedTopics; 59 | std::unordered_map<::foxglove::ChannelId, SubscriptionsByClient> subscriptions; 60 | 61 | PublicationsByClient clientAdvertisedTopics; 62 | 63 | std::vector topicWhitelistPatterns = {std::regex(".*")}; 64 | }; 65 | 66 | } // namespace basis::plugins::bridges::foxglove -------------------------------------------------------------------------------- /cpp/plugins/bridges/foxglove/src/foxglove_dummy_main.cpp: -------------------------------------------------------------------------------- 1 | // This file should be left empty - it's needed to give CMake a source file, 2 | // which is needed if you have a binary target that is solely the result of linking other targets together -------------------------------------------------------------------------------- /cpp/plugins/serialization/protobuf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_plugins_serialization_protobuf) 2 | include(FindProtobuf) 3 | include(DeclarePlugin) 4 | find_package(Protobuf REQUIRED) 5 | 6 | add_plugin(basis_plugins_serialization_protobuf src/protobuf.cpp) 7 | target_include_directories(basis_plugins_serialization_protobuf PUBLIC include) 8 | target_link_libraries(basis_plugins_serialization_protobuf 9 | basis::core::serialization 10 | basis::core::logging 11 | protobuf::libprotobuf 12 | ) 13 | 14 | add_library(basis::plugins::serialization::protobuf ALIAS basis_plugins_serialization_protobuf) 15 | 16 | if(${BASIS_ENABLE_TESTING}) 17 | add_subdirectory(test) 18 | endif() -------------------------------------------------------------------------------- /cpp/plugins/serialization/protobuf/src/protobuf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | DECLARE_AUTO_LOGGER_PLUGIN(serialization, protobuf) 4 | 5 | namespace basis::plugins::serialization::protobuf { 6 | google::protobuf::SimpleDescriptorDatabase ProtobufSerializer::protoDb; 7 | google::protobuf::DescriptorPool ProtobufSerializer::protoPool{&ProtobufSerializer::protoDb}; 8 | google::protobuf::DynamicMessageFactory ProtobufSerializer::protoFactory(&ProtobufSerializer::protoPool); 9 | std::unordered_set ProtobufSerializer::known_schemas; 10 | 11 | } // namespace basis::plugins::serialization::protobuf 12 | 13 | extern "C" { 14 | 15 | basis::core::serialization::SerializationPlugin *LoadPlugin() { 16 | return new basis::plugins::serialization::protobuf::ProtobufPlugin(); 17 | } 18 | } -------------------------------------------------------------------------------- /cpp/plugins/serialization/protobuf/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_protobuf 3 | test_protobuf.cpp 4 | ) 5 | target_link_libraries( 6 | test_protobuf 7 | GTest::gtest_main 8 | basis::plugins::serialization::protobuf basis_proto 9 | spdlog 10 | ) 11 | 12 | include(GoogleTest REQUIRED) 13 | gtest_discover_tests(test_protobuf) -------------------------------------------------------------------------------- /cpp/plugins/serialization/rosmsg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_plugins_serialization_rosmsg) 2 | 3 | # Manually find required ros libraries 4 | # This should be moved out to a helper library if/when ros connections are support 5 | # Ideally we stay far away from catkin here 6 | # Two options for cleaning this up: 7 | # 1. Attempt to figure out how to use find_package with ROS - probably via CMAKE_PREFIX_PATH 8 | # 2. fork and modify https://github.com/ros/roscpp_core/blob/noetic-devel/doc/roscpp.rst for a smaller footprint 9 | 10 | message("using BASIS_ROS_ROOT=${BASIS_ROS_ROOT}") 11 | 12 | add_plugin(basis_plugins_serialization_rosmsg src/rosmsg.cpp) 13 | 14 | target_include_directories(basis_plugins_serialization_rosmsg PUBLIC include) 15 | target_include_directories(basis_plugins_serialization_rosmsg PUBLIC ${BASIS_ROS_ROOT}/include/) 16 | 17 | target_link_directories(basis_plugins_serialization_rosmsg PUBLIC ${BASIS_ROS_ROOT}/lib) 18 | 19 | target_link_libraries(basis_plugins_serialization_rosmsg PUBLIC basis_core_serialization roscpp_serialization rosx_introspection) 20 | 21 | add_library(basis::plugins::serialization::rosmsg ALIAS basis_plugins_serialization_rosmsg) 22 | 23 | if(${BASIS_ENABLE_TESTING}) 24 | add_subdirectory(test) 25 | endif() -------------------------------------------------------------------------------- /cpp/plugins/serialization/rosmsg/src/rosmsg.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace basis::plugins::serialization { 4 | RosMsgParser::ParsersCollection RosmsgSerializer::parser_collection; 5 | } 6 | 7 | extern "C" { 8 | 9 | basis::core::serialization::SerializationPlugin *LoadPlugin() { 10 | return new basis::plugins::serialization::RosMsgPlugin(); 11 | } 12 | } -------------------------------------------------------------------------------- /cpp/plugins/serialization/rosmsg/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(geometry_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/geometry_msgs/cmake) 2 | find_package(sensor_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/sensor_msgs/cmake) 3 | 4 | add_executable( 5 | test_rosmsg 6 | test_rosmsg.cpp 7 | ) 8 | 9 | target_link_libraries( 10 | test_rosmsg 11 | GTest::gtest_main 12 | basis::plugins::serialization::rosmsg spdlog 13 | ) 14 | 15 | include(GoogleTest REQUIRED) 16 | gtest_discover_tests(test_rosmsg) -------------------------------------------------------------------------------- /cpp/plugins/transport/tcp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_plugins_transport_tcp) 2 | 3 | add_plugin(basis_plugins_transport_tcp src/tcp.cpp src/tcp_subscriber.cpp src/epoll.cpp src/tcp_connection.cpp) 4 | target_link_libraries(basis_plugins_transport_tcp basis::core::time basis::core::transport basis::core::networking basis::core::threading) 5 | target_include_directories(basis_plugins_transport_tcp PUBLIC include) 6 | 7 | add_library(basis::plugins::transport::tcp ALIAS basis_plugins_transport_tcp) 8 | 9 | if(${BASIS_ENABLE_TESTING}) 10 | add_subdirectory(test) 11 | endif() -------------------------------------------------------------------------------- /cpp/plugins/transport/tcp/include/basis/plugins/transport/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | DEFINE_AUTO_LOGGER_PLUGIN(transport, tcp) -------------------------------------------------------------------------------- /cpp/plugins/transport/tcp/include/basis/plugins/transport/tcp_transport_name.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace basis::plugins::transport { 3 | 4 | constexpr char TCP_TRANSPORT_NAME[] = "net_tcp"; 5 | 6 | } -------------------------------------------------------------------------------- /cpp/plugins/transport/tcp/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_tcp_transport 3 | test_tcp_transport.cpp 4 | ) 5 | target_link_libraries( 6 | test_tcp_transport 7 | GTest::gtest_main 8 | basis::plugins::transport::tcp 9 | basis::plugins::serialization::protobuf 10 | basis_proto 11 | ) 12 | 13 | include(GoogleTest REQUIRED) 14 | gtest_discover_tests(test_tcp_transport) 15 | -------------------------------------------------------------------------------- /cpp/recorder/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_recorder SHARED src/recorder.cpp) 2 | 3 | target_include_directories(basis_recorder PUBLIC include) 4 | target_link_libraries(basis_recorder 5 | basis::core::containers 6 | basis::core::logging 7 | basis::core::serialization 8 | basis::core::time 9 | foxglove_schemas_protobuf 10 | mcap) 11 | add_library(basis::recorder ALIAS basis_recorder) 12 | 13 | if(${BASIS_ENABLE_TESTING}) 14 | add_subdirectory(test) 15 | endif() -------------------------------------------------------------------------------- /cpp/recorder/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GoogleTest REQUIRED) 2 | 3 | add_executable( 4 | test_recorder 5 | test_recorder.cpp 6 | ) 7 | 8 | target_link_libraries( 9 | test_recorder 10 | basis::recorder 11 | basis_proto 12 | GTest::gtest_main 13 | $<$:basis::plugins::serialization::rosmsg> 14 | ) 15 | 16 | gtest_discover_tests(test_recorder) -------------------------------------------------------------------------------- /cpp/replayer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_replayer SHARED src/replayer.cpp) 2 | 3 | target_include_directories(basis_replayer PUBLIC include) 4 | target_link_libraries(basis_replayer 5 | basis::core::coordinator 6 | basis::core::containers 7 | basis::core::logging 8 | basis::core::serialization 9 | basis::core::time 10 | basis::unit 11 | mcap) 12 | 13 | add_library(basis::replayer ALIAS basis_replayer) 14 | 15 | add_library(basis_replayer_args src/replay_args.cpp) 16 | target_link_libraries(basis_replayer_args basis::replayer argparse) 17 | add_library(basis::replayer::args ALIAS basis_replayer_args) 18 | 19 | 20 | add_executable(replay src/replay.cpp) 21 | 22 | target_link_libraries(replay basis::replayer basis::replayer::args) 23 | # if(${BASIS_ENABLE_TESTING}) 24 | # add_subdirectory(test) 25 | # endif() -------------------------------------------------------------------------------- /cpp/replayer/include/basis/replayer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "basis/core/coordinator_connector.h" 3 | #include "basis/core/transport/publisher.h" 4 | #include "basis/core/transport/transport_manager.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "replayer/logger.h" 14 | 15 | namespace basis { 16 | namespace core::transport { 17 | class TransportManager; 18 | } 19 | using namespace replayer; 20 | 21 | class Replayer { 22 | public: 23 | Replayer(Config config, 24 | basis::core::transport::TransportManager &transport_manager, 25 | basis::core::transport::CoordinatorConnector& coordinator_connector) 26 | : config(std::move(config)), transport_manager(transport_manager), coordinator_connector(coordinator_connector) { 27 | } 28 | 29 | virtual bool Run(); 30 | 31 | basis::core::MonotonicTime StartTime() { 32 | return basis::core::MonotonicTime::FromNanoseconds(mcap_reader.statistics()->messageStartTime); 33 | } 34 | basis::core::MonotonicTime EndTime() { 35 | return basis::core::MonotonicTime::FromNanoseconds(mcap_reader.statistics()->messageEndTime); 36 | } 37 | protected: 38 | bool LoadRecording(std::filesystem::path recording_path); 39 | 40 | virtual bool OnRecordLoaded(); 41 | 42 | const Config config; 43 | 44 | basis::core::transport::TransportManager &transport_manager; 45 | basis::core::transport::CoordinatorConnector& coordinator_connector; 46 | mcap::McapReader mcap_reader; 47 | std::unordered_map> publishers; 48 | std::shared_ptr> time_publisher; 49 | }; 50 | 51 | } // namespace basis -------------------------------------------------------------------------------- /cpp/replayer/include/basis/replayer/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace basis::replayer { 6 | struct Config { 7 | bool loop = false; 8 | std::filesystem::path input; 9 | }; 10 | } -------------------------------------------------------------------------------- /cpp/replayer/include/basis/replayer/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | DEFINE_AUTO_LOGGER_NS(basis::replayer) -------------------------------------------------------------------------------- /cpp/replayer/include/basis/replayer/replay_args.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace basis::replayer { 5 | 6 | constexpr char LOOP_ARG[] = "--loop"; 7 | constexpr char RECORDING_ARG[] = "recording"; 8 | 9 | std::unique_ptr CreateArgumentParser(); 10 | 11 | } // namespace basis::replayer -------------------------------------------------------------------------------- /cpp/replayer/src/replay.cpp: -------------------------------------------------------------------------------- 1 | #include "basis/replayer/config.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) { 11 | using namespace basis::replayer; 12 | 13 | basis::core::logging::InitializeLoggingSystem(); 14 | 15 | std::unique_ptr parser = CreateArgumentParser(); 16 | 17 | try { 18 | parser->parse_args(argc, argv); 19 | } catch (const std::exception &err) { 20 | std::cerr << err.what() << std::endl; 21 | std::cerr << parser; 22 | return 1; 23 | } 24 | 25 | std::unique_ptr coordinator_connector = basis::core::transport::WaitForCoordinator(); 26 | 27 | basis::core::transport::TransportManager transport_manager(nullptr); 28 | transport_manager.RegisterTransport("net_tcp", std::make_unique()); 29 | 30 | Config config; 31 | config.loop = parser->get(LOOP_ARG); 32 | config.input = parser->get(RECORDING_ARG); 33 | 34 | basis::Replayer replayer(std::move(config), transport_manager, *coordinator_connector); 35 | 36 | replayer.Run(); 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /cpp/replayer/src/replay_args.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace basis::replayer { 4 | 5 | std::unique_ptr CreateArgumentParser() { 6 | auto parser = std::make_unique("replay"); 7 | 8 | 9 | parser->add_argument(LOOP_ARG).help("Whether or not to loop the playback.").default_value(false).implicit_value(true); 10 | // TODO: allow multiple mcap files, directory support 11 | parser->add_argument(RECORDING_ARG).help("The MCAP file to replay."); 12 | 13 | return parser; 14 | } 15 | 16 | } // namespace basis::replayer -------------------------------------------------------------------------------- /cpp/synchronizers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(basis_synchronizers INTERFACE) 2 | 3 | target_include_directories(basis_synchronizers INTERFACE include) 4 | 5 | add_library(basis::synchronizers ALIAS basis_synchronizers) 6 | 7 | if(${BASIS_ENABLE_TESTING}) 8 | add_subdirectory(test) 9 | endif() -------------------------------------------------------------------------------- /cpp/synchronizers/include/basis/synchronizers/all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "synchronizer_base.h" 4 | 5 | namespace basis::synchronizers { 6 | 7 | template class All : public SynchronizerBase { 8 | public: 9 | using Base = SynchronizerBase; 10 | using Base::Base; 11 | 12 | protected: 13 | virtual bool IsReadyNoLock() override { return Base::AreAllNonOptionalFieldsFilledNoLock(); } 14 | }; 15 | 16 | } // namespace basis::synchronizers 17 | -------------------------------------------------------------------------------- /cpp/synchronizers/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable( 2 | test_synchronizers 3 | test_synchronizers.cpp 4 | ) 5 | target_link_libraries( 6 | test_synchronizers 7 | GTest::gtest_main 8 | basis::synchronizers 9 | basis_proto 10 | 11 | ) 12 | 13 | include(GoogleTest REQUIRED) 14 | gtest_discover_tests(test_synchronizers) 15 | -------------------------------------------------------------------------------- /cpp/third_party/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Forks of third party libraries 2 | 3 | add_subdirectory(spdlog) 4 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/INSTALL: -------------------------------------------------------------------------------- 1 | Header Only Version 2 | ================================================================== 3 | Just copy the files to your build tree and use a C++11 compiler. 4 | Or use CMake: 5 | ``` 6 | add_executable(example_header_only example.cpp) 7 | target_link_libraries(example_header_only spdlog::spdlog_header_only) 8 | ``` 9 | 10 | Compiled Library Version 11 | ================================================================== 12 | CMake: 13 | ``` 14 | add_executable(example example.cpp) 15 | target_link_libraries(example spdlog::spdlog) 16 | ``` 17 | 18 | Or copy files src/*.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler. 19 | 20 | Important Information for Compilation: 21 | ================================================================== 22 | * If you encounter compilation errors with gcc 4.8.x, please note that gcc 4.8.x does not fully support C++11. In such cases, consider upgrading your compiler or using a different version that fully supports C++11 standards 23 | 24 | Tested on: 25 | gcc 4.8.1 and above 26 | clang 3.5 27 | Visual Studio 2013 -------------------------------------------------------------------------------- /cpp/third_party/spdlog/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gabi Melman. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -- NOTE: Third party dependency used by this software -- 24 | This software depends on the fmt lib (MIT License), 25 | and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE 26 | 27 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) 2 | 3 | cmake_minimum_required(VERSION 3.11) 4 | project(spdlog_bench CXX) 5 | 6 | if(NOT TARGET spdlog) 7 | # Stand-alone build 8 | find_package(spdlog CONFIG REQUIRED) 9 | endif() 10 | 11 | find_package(Threads REQUIRED) 12 | find_package(benchmark CONFIG) 13 | if(NOT benchmark_FOUND) 14 | message(STATUS "Using CMake Version ${CMAKE_VERSION}") 15 | # User can fetch googlebenchmark 16 | message(STATUS "Downloading GoogleBenchmark") 17 | include(FetchContent) 18 | 19 | # disable tests 20 | set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") 21 | # Do not build and run googlebenchmark tests 22 | FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) 23 | FetchContent_MakeAvailable(googlebenchmark) 24 | endif() 25 | 26 | add_executable(bench bench.cpp) 27 | spdlog_enable_warnings(bench) 28 | target_link_libraries(bench PRIVATE spdlog::spdlog) 29 | 30 | add_executable(async_bench async_bench.cpp) 31 | target_link_libraries(async_bench PRIVATE spdlog::spdlog) 32 | 33 | add_executable(latency latency.cpp) 34 | target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) 35 | 36 | add_executable(formatter-bench formatter-bench.cpp) 37 | target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) 38 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/bench/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace utils { 13 | 14 | template 15 | inline std::string format(const T &value) { 16 | static std::locale loc(""); 17 | std::stringstream ss; 18 | ss.imbue(loc); 19 | ss << value; 20 | return ss.str(); 21 | } 22 | 23 | template <> 24 | inline std::string format(const double &value) { 25 | static std::locale loc(""); 26 | std::stringstream ss; 27 | ss.imbue(loc); 28 | ss << std::fixed << std::setprecision(1) << value; 29 | return ss.str(); 30 | } 31 | 32 | } // namespace utils 33 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/cmake/ide.cmake: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------- 2 | # IDE support for headers 3 | # --------------------------------------------------------------------------------------- 4 | set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include") 5 | 6 | file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h") 7 | file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h") 8 | file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h") 9 | file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h") 10 | file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h") 11 | set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} 12 | ${SPDLOG_FMT_BUNDELED_HEADERS}) 13 | 14 | source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS}) 15 | source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS}) 16 | source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS}) 17 | source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS}) 18 | source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS}) 19 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/cmake/spdlog.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | includedir=@PKG_CONFIG_INCLUDEDIR@ 4 | libdir=@PKG_CONFIG_LIBDIR@ 5 | 6 | Name: lib@PROJECT_NAME@ 7 | Description: Fast C++ logging library. 8 | URL: https://github.com/gabime/@PROJECT_NAME@ 9 | Version: @SPDLOG_VERSION@ 10 | CFlags: -I${includedir} @PKG_CONFIG_DEFINES@ 11 | Libs: -L${libdir} -lspdlog -pthread 12 | Requires: @PKG_CONFIG_REQUIRES@ 13 | 14 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/cmake/spdlogConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors 2 | # Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | @PACKAGE_INIT@ 5 | 6 | find_package(Threads REQUIRED) 7 | 8 | set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) 9 | set(SPDLOG_FMT_EXTERNAL_HO @SPDLOG_FMT_EXTERNAL_HO@) 10 | set(config_targets_file @config_targets_file@) 11 | 12 | if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) 13 | include(CMakeFindDependencyMacro) 14 | find_dependency(fmt CONFIG) 15 | endif() 16 | 17 | 18 | include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") 19 | 20 | check_required_components(spdlog) 21 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/cmake/version.rc.in: -------------------------------------------------------------------------------- 1 | #define APSTUDIO_READONLY_SYMBOLS 2 | #include 3 | #undef APSTUDIO_READONLY_SYMBOLS 4 | 5 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 6 | 7 | 8 | VS_VERSION_INFO VERSIONINFO 9 | FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 10 | PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 11 | FILEFLAGSMASK 0x3fL 12 | #ifdef _DEBUG 13 | FILEFLAGS 0x1L 14 | #else 15 | FILEFLAGS 0x0L 16 | #endif 17 | FILEOS 0x40004L 18 | FILETYPE 0x2L 19 | FILESUBTYPE 0x0L 20 | BEGIN 21 | BLOCK "StringFileInfo" 22 | BEGIN 23 | BLOCK "040904b0" 24 | BEGIN 25 | VALUE "FileDescription", "spdlog dll\0" 26 | VALUE "FileVersion", "@SPDLOG_VERSION@.0\0" 27 | VALUE "InternalName", "spdlog.dll\0" 28 | VALUE "LegalCopyright", "Copyright (C) spdlog\0" 29 | VALUE "ProductName", "spdlog\0" 30 | VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0" 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) 2 | 3 | cmake_minimum_required(VERSION 3.11) 4 | project(spdlog_examples CXX) 5 | 6 | if(NOT TARGET spdlog) 7 | # Stand-alone build 8 | find_package(spdlog REQUIRED) 9 | endif() 10 | 11 | # --------------------------------------------------------------------------------------- 12 | # Example of using pre-compiled library 13 | # --------------------------------------------------------------------------------------- 14 | add_executable(example example.cpp) 15 | target_link_libraries(example PRIVATE spdlog::spdlog $<$:ws2_32>) 16 | 17 | # --------------------------------------------------------------------------------------- 18 | # Example of using header-only library 19 | # --------------------------------------------------------------------------------------- 20 | if(SPDLOG_BUILD_EXAMPLE_HO) 21 | add_executable(example_header_only example.cpp) 22 | target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only) 23 | endif() 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/cfg/argv.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | // 9 | // Init log levels using each argv entry that starts with "SPDLOG_LEVEL=" 10 | // 11 | // set all loggers to debug level: 12 | // example.exe "SPDLOG_LEVEL=debug" 13 | 14 | // set logger1 to trace level 15 | // example.exe "SPDLOG_LEVEL=logger1=trace" 16 | 17 | // turn off all logging except for logger1 and logger2: 18 | // example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info" 19 | 20 | namespace spdlog { 21 | namespace cfg { 22 | 23 | // search for SPDLOG_LEVEL= in the args and use it to init the levels 24 | inline void load_argv_levels(int argc, const char **argv) { 25 | const std::string spdlog_level_prefix = "SPDLOG_LEVEL="; 26 | for (int i = 1; i < argc; i++) { 27 | std::string arg = argv[i]; 28 | if (arg.find(spdlog_level_prefix) == 0) { 29 | auto levels_string = arg.substr(spdlog_level_prefix.size()); 30 | helpers::load_levels(levels_string); 31 | } 32 | } 33 | } 34 | 35 | inline void load_argv_levels(int argc, char **argv) { 36 | load_argv_levels(argc, const_cast(argv)); 37 | } 38 | 39 | } // namespace cfg 40 | } // namespace spdlog 41 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/cfg/env.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | 9 | // 10 | // Init levels and patterns from env variables SPDLOG_LEVEL 11 | // Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger). 12 | // Note - fallback to "info" level on unrecognized levels 13 | // 14 | // Examples: 15 | // 16 | // set global level to debug: 17 | // export SPDLOG_LEVEL=debug 18 | // 19 | // turn off all logging except for logger1: 20 | // export SPDLOG_LEVEL="*=off,logger1=debug" 21 | // 22 | 23 | // turn off all logging except for logger1 and logger2: 24 | // export SPDLOG_LEVEL="off,logger1=debug,logger2=info" 25 | 26 | namespace spdlog { 27 | namespace cfg { 28 | inline void load_env_levels() { 29 | auto env_val = details::os::getenv("SPDLOG_LEVEL"); 30 | if (!env_val.empty()) { 31 | helpers::load_levels(env_val); 32 | } 33 | } 34 | 35 | } // namespace cfg 36 | } // namespace spdlog 37 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/cfg/helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace cfg { 11 | namespace helpers { 12 | // 13 | // Init levels from given string 14 | // 15 | // Examples: 16 | // 17 | // set global level to debug: "debug" 18 | // turn off all logging except for logger1: "off,logger1=debug" 19 | // turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info" 20 | // 21 | SPDLOG_API void load_levels(const std::string &txt); 22 | } // namespace helpers 23 | 24 | } // namespace cfg 25 | } // namespace spdlog 26 | 27 | #ifdef SPDLOG_HEADER_ONLY 28 | #include "helpers-inl.h" 29 | #endif // SPDLOG_HEADER_ONLY 30 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/common-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | namespace level { 15 | 16 | #if __cplusplus >= 201703L 17 | constexpr 18 | #endif 19 | static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; 20 | 21 | static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; 22 | 23 | SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT { 24 | return level_string_views[l]; 25 | } 26 | 27 | SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT { 28 | return short_level_names[l]; 29 | } 30 | 31 | SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT { 32 | auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name); 33 | if (it != std::end(level_string_views)) 34 | return static_cast(std::distance(std::begin(level_string_views), it)); 35 | 36 | // check also for "warn" and "err" before giving up.. 37 | if (name == "warn") { 38 | return level::warn; 39 | } 40 | if (name == "err") { 41 | return level::err; 42 | } 43 | return level::off; 44 | } 45 | } // namespace level 46 | 47 | SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) 48 | : msg_(std::move(msg)) {} 49 | 50 | SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) { 51 | #ifdef SPDLOG_USE_STD_FORMAT 52 | msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what(); 53 | #else 54 | memory_buf_t outbuf; 55 | fmt::format_system_error(outbuf, last_errno, msg.c_str()); 56 | msg_ = fmt::to_string(outbuf); 57 | #endif 58 | } 59 | 60 | SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT { return msg_.c_str(); } 61 | 62 | SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno) { 63 | SPDLOG_THROW(spdlog_ex(msg, last_errno)); 64 | } 65 | 66 | SPDLOG_INLINE void throw_spdlog_ex(std::string msg) { SPDLOG_THROW(spdlog_ex(std::move(msg))); } 67 | 68 | } // namespace spdlog 69 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/backtracer-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | namespace spdlog { 10 | namespace details { 11 | SPDLOG_INLINE backtracer::backtracer(const backtracer &other) { 12 | std::lock_guard lock(other.mutex_); 13 | enabled_ = other.enabled(); 14 | messages_ = other.messages_; 15 | } 16 | 17 | SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT { 18 | std::lock_guard lock(other.mutex_); 19 | enabled_ = other.enabled(); 20 | messages_ = std::move(other.messages_); 21 | } 22 | 23 | SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) { 24 | std::lock_guard lock(mutex_); 25 | enabled_ = other.enabled(); 26 | messages_ = std::move(other.messages_); 27 | return *this; 28 | } 29 | 30 | SPDLOG_INLINE void backtracer::enable(size_t size) { 31 | std::lock_guard lock{mutex_}; 32 | enabled_.store(true, std::memory_order_relaxed); 33 | messages_ = circular_q{size}; 34 | } 35 | 36 | SPDLOG_INLINE void backtracer::disable() { 37 | std::lock_guard lock{mutex_}; 38 | enabled_.store(false, std::memory_order_relaxed); 39 | } 40 | 41 | SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); } 42 | 43 | SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) { 44 | std::lock_guard lock{mutex_}; 45 | messages_.push_back(log_msg_buffer{msg}); 46 | } 47 | 48 | SPDLOG_INLINE bool backtracer::empty() const { 49 | std::lock_guard lock{mutex_}; 50 | return messages_.empty(); 51 | } 52 | 53 | // pop all items in the q and apply the given fun on each of them. 54 | SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) { 55 | std::lock_guard lock{mutex_}; 56 | while (!messages_.empty()) { 57 | auto &front_msg = messages_.front(); 58 | fun(front_msg); 59 | messages_.pop_front(); 60 | } 61 | } 62 | } // namespace details 63 | } // namespace spdlog 64 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/backtracer.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // Store log messages in circular buffer. 14 | // Useful for storing debug data in case of error/warning happens. 15 | 16 | namespace spdlog { 17 | namespace details { 18 | class SPDLOG_API backtracer { 19 | mutable std::mutex mutex_; 20 | std::atomic enabled_{false}; 21 | circular_q messages_; 22 | 23 | public: 24 | backtracer() = default; 25 | backtracer(const backtracer &other); 26 | 27 | backtracer(backtracer &&other) SPDLOG_NOEXCEPT; 28 | backtracer &operator=(backtracer other); 29 | 30 | void enable(size_t size); 31 | void disable(); 32 | bool enabled() const; 33 | void push_back(const log_msg &msg); 34 | bool empty() const; 35 | 36 | // pop all items in the q and apply the given fun on each of them. 37 | void foreach_pop(std::function fun); 38 | }; 39 | 40 | } // namespace details 41 | } // namespace spdlog 42 | 43 | #ifdef SPDLOG_HEADER_ONLY 44 | #include "backtracer-inl.h" 45 | #endif 46 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/console_globals.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | 12 | struct console_mutex { 13 | using mutex_t = std::mutex; 14 | static mutex_t &mutex() { 15 | static mutex_t s_mutex; 16 | return s_mutex; 17 | } 18 | }; 19 | 20 | struct console_nullmutex { 21 | using mutex_t = null_mutex; 22 | static mutex_t &mutex() { 23 | static mutex_t s_mutex; 24 | return s_mutex; 25 | } 26 | }; 27 | } // namespace details 28 | } // namespace spdlog 29 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/file_helper.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | 12 | // Helper class for file sinks. 13 | // When failing to open a file, retry several times(5) with a delay interval(10 ms). 14 | // Throw spdlog_ex exception on errors. 15 | 16 | class SPDLOG_API file_helper { 17 | public: 18 | file_helper() = default; 19 | explicit file_helper(const file_event_handlers &event_handlers); 20 | 21 | file_helper(const file_helper &) = delete; 22 | file_helper &operator=(const file_helper &) = delete; 23 | ~file_helper(); 24 | 25 | void open(const filename_t &fname, bool truncate = false); 26 | void reopen(bool truncate); 27 | void flush(); 28 | void sync(); 29 | void close(); 30 | void write(const memory_buf_t &buf); 31 | size_t size() const; 32 | const filename_t &filename() const; 33 | 34 | // 35 | // return file path and its extension: 36 | // 37 | // "mylog.txt" => ("mylog", ".txt") 38 | // "mylog" => ("mylog", "") 39 | // "mylog." => ("mylog.", "") 40 | // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") 41 | // 42 | // the starting dot in filenames is ignored (hidden files): 43 | // 44 | // ".mylog" => (".mylog". "") 45 | // "my_folder/.mylog" => ("my_folder/.mylog", "") 46 | // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") 47 | static std::tuple split_by_extension(const filename_t &fname); 48 | 49 | private: 50 | const int open_tries_ = 5; 51 | const unsigned int open_interval_ = 10; 52 | std::FILE *fd_{nullptr}; 53 | filename_t filename_; 54 | file_event_handlers event_handlers_; 55 | }; 56 | } // namespace details 57 | } // namespace spdlog 58 | 59 | #ifdef SPDLOG_HEADER_ONLY 60 | #include "file_helper-inl.h" 61 | #endif 62 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/log_msg-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | namespace spdlog { 13 | namespace details { 14 | 15 | SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, 16 | spdlog::source_loc loc, 17 | string_view_t a_logger_name, 18 | spdlog::level::level_enum lvl, 19 | spdlog::string_view_t msg) 20 | : logger_name(a_logger_name), 21 | level(lvl), 22 | time(log_time) 23 | #ifndef SPDLOG_NO_THREAD_ID 24 | , 25 | thread_id(os::thread_id()) 26 | #endif 27 | , 28 | source(loc), 29 | payload(msg) { 30 | } 31 | 32 | SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, 33 | string_view_t a_logger_name, 34 | spdlog::level::level_enum lvl, 35 | spdlog::string_view_t msg) 36 | : log_msg(os::now(), loc, a_logger_name, lvl, msg) {} 37 | 38 | SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, 39 | spdlog::level::level_enum lvl, 40 | spdlog::string_view_t msg) 41 | : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg) {} 42 | 43 | } // namespace details 44 | } // namespace spdlog 45 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/log_msg.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | struct SPDLOG_API log_msg { 12 | log_msg() = default; 13 | log_msg(log_clock::time_point log_time, 14 | source_loc loc, 15 | string_view_t logger_name, 16 | level::level_enum lvl, 17 | string_view_t msg); 18 | log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg); 19 | log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg); 20 | log_msg(const log_msg &other) = default; 21 | log_msg &operator=(const log_msg &other) = default; 22 | 23 | string_view_t logger_name; 24 | level::level_enum level{level::off}; 25 | log_clock::time_point time; 26 | size_t thread_id{0}; 27 | 28 | // wrapping the formatted text with color (updated by pattern_formatter). 29 | mutable size_t color_range_start{0}; 30 | mutable size_t color_range_end{0}; 31 | 32 | source_loc source; 33 | string_view_t payload; 34 | }; 35 | } // namespace details 36 | } // namespace spdlog 37 | 38 | #ifdef SPDLOG_HEADER_ONLY 39 | #include "log_msg-inl.h" 40 | #endif 41 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/log_msg_buffer-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | namespace spdlog { 11 | namespace details { 12 | 13 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) 14 | : log_msg{orig_msg} { 15 | buffer.append(logger_name.begin(), logger_name.end()); 16 | buffer.append(payload.begin(), payload.end()); 17 | update_string_views(); 18 | } 19 | 20 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) 21 | : log_msg{other} { 22 | buffer.append(logger_name.begin(), logger_name.end()); 23 | buffer.append(payload.begin(), payload.end()); 24 | update_string_views(); 25 | } 26 | 27 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT 28 | : log_msg{other}, 29 | buffer{std::move(other.buffer)} { 30 | update_string_views(); 31 | } 32 | 33 | SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) { 34 | log_msg::operator=(other); 35 | buffer.clear(); 36 | buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size()); 37 | update_string_views(); 38 | return *this; 39 | } 40 | 41 | SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT { 42 | log_msg::operator=(other); 43 | buffer = std::move(other.buffer); 44 | update_string_views(); 45 | return *this; 46 | } 47 | 48 | SPDLOG_INLINE void log_msg_buffer::update_string_views() { 49 | logger_name = string_view_t{buffer.data(), logger_name.size()}; 50 | payload = string_view_t{buffer.data() + logger_name.size(), payload.size()}; 51 | } 52 | 53 | } // namespace details 54 | } // namespace spdlog 55 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/log_msg_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace spdlog { 9 | namespace details { 10 | 11 | // Extend log_msg with internal buffer to store its payload. 12 | // This is needed since log_msg holds string_views that points to stack data. 13 | 14 | class SPDLOG_API log_msg_buffer : public log_msg { 15 | memory_buf_t buffer; 16 | void update_string_views(); 17 | 18 | public: 19 | log_msg_buffer() = default; 20 | explicit log_msg_buffer(const log_msg &orig_msg); 21 | log_msg_buffer(const log_msg_buffer &other); 22 | log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT; 23 | log_msg_buffer &operator=(const log_msg_buffer &other); 24 | log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT; 25 | }; 26 | 27 | } // namespace details 28 | } // namespace spdlog 29 | 30 | #ifdef SPDLOG_HEADER_ONLY 31 | #include "log_msg_buffer-inl.h" 32 | #endif 33 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/null_mutex.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | // null, no cost dummy "mutex" and dummy "atomic" int 9 | 10 | namespace spdlog { 11 | namespace details { 12 | struct null_mutex { 13 | void lock() const {} 14 | void unlock() const {} 15 | }; 16 | 17 | struct null_atomic_int { 18 | int value; 19 | null_atomic_int() = default; 20 | 21 | explicit null_atomic_int(int new_value) 22 | : value(new_value) {} 23 | 24 | int load(std::memory_order = std::memory_order_relaxed) const { return value; } 25 | 26 | void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; } 27 | 28 | int exchange(int new_value, std::memory_order = std::memory_order_relaxed) { 29 | std::swap(new_value, value); 30 | return new_value; // return value before the call 31 | } 32 | }; 33 | 34 | } // namespace details 35 | } // namespace spdlog 36 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/periodic_worker-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | namespace spdlog { 11 | namespace details { 12 | 13 | // stop the worker thread and join it 14 | SPDLOG_INLINE periodic_worker::~periodic_worker() { 15 | if (worker_thread_.joinable()) { 16 | { 17 | std::lock_guard lock(mutex_); 18 | active_ = false; 19 | } 20 | cv_.notify_one(); 21 | worker_thread_.join(); 22 | } 23 | } 24 | 25 | } // namespace details 26 | } // namespace spdlog 27 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/periodic_worker.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // periodic worker thread - periodically executes the given callback function. 7 | // 8 | // RAII over the owned thread: 9 | // creates the thread on construction. 10 | // stops and joins the thread on destruction (if the thread is executing a callback, wait for it 11 | // to finish first). 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | namespace spdlog { 19 | namespace details { 20 | 21 | class SPDLOG_API periodic_worker { 22 | public: 23 | template 24 | periodic_worker(const std::function &callback_fun, 25 | std::chrono::duration interval) { 26 | active_ = (interval > std::chrono::duration::zero()); 27 | if (!active_) { 28 | return; 29 | } 30 | 31 | worker_thread_ = std::thread([this, callback_fun, interval]() { 32 | for (;;) { 33 | std::unique_lock lock(this->mutex_); 34 | if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) { 35 | return; // active_ == false, so exit this thread 36 | } 37 | callback_fun(); 38 | } 39 | }); 40 | } 41 | std::thread &get_thread() { return worker_thread_; } 42 | periodic_worker(const periodic_worker &) = delete; 43 | periodic_worker &operator=(const periodic_worker &) = delete; 44 | // stop the worker thread and join it 45 | ~periodic_worker(); 46 | 47 | private: 48 | bool active_; 49 | std::thread worker_thread_; 50 | std::mutex mutex_; 51 | std::condition_variable cv_; 52 | }; 53 | } // namespace details 54 | } // namespace spdlog 55 | 56 | #ifdef SPDLOG_HEADER_ONLY 57 | #include "periodic_worker-inl.h" 58 | #endif 59 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/synchronous_factory.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "registry.h" 7 | 8 | namespace spdlog { 9 | 10 | // Default logger factory- creates synchronous loggers 11 | class logger; 12 | 13 | struct synchronous_factory { 14 | template 15 | static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { 16 | auto sink = std::make_shared(std::forward(args)...); 17 | auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); 18 | details::registry::instance().initialize_logger(new_logger); 19 | return new_logger; 20 | } 21 | }; 22 | } // namespace spdlog 23 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/udp_client.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // Helper RAII over unix udp client socket. 7 | // Will throw on construction if the socket creation failed. 8 | 9 | #ifdef _WIN32 10 | #error "include udp_client-windows.h instead" 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | namespace spdlog { 26 | namespace details { 27 | 28 | class udp_client { 29 | static constexpr int TX_BUFFER_SIZE = 1024 * 10; 30 | int socket_ = -1; 31 | struct sockaddr_in sockAddr_; 32 | 33 | void cleanup_() { 34 | if (socket_ != -1) { 35 | ::close(socket_); 36 | socket_ = -1; 37 | } 38 | } 39 | 40 | public: 41 | udp_client(const std::string &host, uint16_t port) { 42 | socket_ = ::socket(PF_INET, SOCK_DGRAM, 0); 43 | if (socket_ < 0) { 44 | throw_spdlog_ex("error: Create Socket Failed!"); 45 | } 46 | 47 | int option_value = TX_BUFFER_SIZE; 48 | if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, 49 | reinterpret_cast(&option_value), sizeof(option_value)) < 0) { 50 | cleanup_(); 51 | throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!"); 52 | } 53 | 54 | sockAddr_.sin_family = AF_INET; 55 | sockAddr_.sin_port = htons(port); 56 | 57 | if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) { 58 | cleanup_(); 59 | throw_spdlog_ex("error: Invalid address!"); 60 | } 61 | 62 | ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero)); 63 | } 64 | 65 | ~udp_client() { cleanup_(); } 66 | 67 | int fd() const { return socket_; } 68 | 69 | // Send exactly n_bytes of the given data. 70 | // On error close the connection and throw. 71 | void send(const char *data, size_t n_bytes) { 72 | ssize_t toslen = 0; 73 | socklen_t tolen = sizeof(struct sockaddr); 74 | if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == 75 | -1) { 76 | throw_spdlog_ex("sendto(2) failed", errno); 77 | } 78 | } 79 | }; 80 | } // namespace details 81 | } // namespace spdlog 82 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/details/windows_include.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NOMINMAX 4 | #define NOMINMAX // prevent windows redefining min/max 5 | #endif 6 | 7 | #ifndef WIN32_LEAN_AND_MEAN 8 | #define WIN32_LEAN_AND_MEAN 9 | #endif 10 | 11 | #include 12 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/bundled/fmt.license.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/bundled/locale.h: -------------------------------------------------------------------------------- 1 | #include "xchar.h" 2 | #warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead 3 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/chrono.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's chrono support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's compile-time support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/fmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016-2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Include a bundled header-only copy of fmtlib or an external one. 10 | // By default spdlog include its own copy. 11 | // 12 | #include 13 | 14 | #if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format 15 | #include 16 | #elif !defined(SPDLOG_FMT_EXTERNAL) 17 | #if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY) 18 | #define FMT_HEADER_ONLY 19 | #endif 20 | #ifndef FMT_USE_WINDOWS_H 21 | #define FMT_USE_WINDOWS_H 0 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib 28 | #include 29 | #include 30 | #endif 31 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/ostr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ostream support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ranges support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/std.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's std support (for formatting e.g. 9 | // std::filesystem::path, std::thread::id, std::monostate, std::variant, ...) 10 | // 11 | #include 12 | 13 | #if !defined(SPDLOG_USE_STD_FORMAT) 14 | #if !defined(SPDLOG_FMT_EXTERNAL) 15 | #ifdef SPDLOG_HEADER_ONLY 16 | #ifndef FMT_HEADER_ONLY 17 | #define FMT_HEADER_ONLY 18 | #endif 19 | #endif 20 | #include 21 | #else 22 | #include 23 | #endif 24 | #endif 25 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fmt/xchar.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's xchar support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/formatter.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | 11 | class formatter { 12 | public: 13 | virtual ~formatter() = default; 14 | virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0; 15 | virtual std::unique_ptr clone() const = 0; 16 | }; 17 | } // namespace spdlog 18 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/fwd.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | namespace spdlog { 7 | class logger; 8 | class formatter; 9 | 10 | namespace sinks { 11 | class sink; 12 | } 13 | 14 | namespace level { 15 | enum level_enum : int; 16 | } 17 | 18 | } // namespace spdlog 19 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/mdc.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | // MDC is a simple map of key->string values stored in thread local storage whose content will be printed by the loggers. 12 | // Note: Not supported in async mode (thread local storage - so the async thread pool have different copy). 13 | // 14 | // Usage example: 15 | // spdlog::mdc::put("mdc_key_1", "mdc_value_1"); 16 | // spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] [mdc_key_1:mdc_value_1] Hello, World! 17 | 18 | namespace spdlog { 19 | class SPDLOG_API mdc { 20 | public: 21 | using mdc_map_t = std::map; 22 | 23 | static void put(const std::string &key, const std::string &value) { 24 | get_context()[key] = value; 25 | } 26 | 27 | static std::string get(const std::string &key) { 28 | auto &context = get_context(); 29 | auto it = context.find(key); 30 | if (it != context.end()) { 31 | return it->second; 32 | } 33 | return ""; 34 | } 35 | 36 | static void remove(const std::string &key) { get_context().erase(key); } 37 | 38 | static void clear() { get_context().clear(); } 39 | 40 | static mdc_map_t &get_context() { 41 | static thread_local mdc_map_t context; 42 | return context; 43 | } 44 | }; 45 | 46 | } // namespace spdlog 47 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/base_sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | template 17 | SPDLOG_INLINE spdlog::sinks::base_sink::base_sink() 18 | : formatter_{details::make_unique()} {} 19 | 20 | template 21 | SPDLOG_INLINE spdlog::sinks::base_sink::base_sink( 22 | std::unique_ptr formatter) 23 | : formatter_{std::move(formatter)} {} 24 | 25 | template 26 | void SPDLOG_INLINE spdlog::sinks::base_sink::log(const details::log_msg &msg) { 27 | std::lock_guard lock(mutex_); 28 | sink_it_(msg); 29 | } 30 | 31 | template 32 | void SPDLOG_INLINE spdlog::sinks::base_sink::flush() { 33 | std::lock_guard lock(mutex_); 34 | flush_(); 35 | } 36 | 37 | template 38 | void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern(const std::string &pattern) { 39 | std::lock_guard lock(mutex_); 40 | set_pattern_(pattern); 41 | } 42 | 43 | template 44 | void SPDLOG_INLINE 45 | spdlog::sinks::base_sink::set_formatter(std::unique_ptr sink_formatter) { 46 | std::lock_guard lock(mutex_); 47 | set_formatter_(std::move(sink_formatter)); 48 | } 49 | 50 | template 51 | void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern_(const std::string &pattern) { 52 | set_formatter_(details::make_unique(pattern)); 53 | } 54 | 55 | template 56 | void SPDLOG_INLINE 57 | spdlog::sinks::base_sink::set_formatter_(std::unique_ptr sink_formatter) { 58 | formatter_ = std::move(sink_formatter); 59 | } 60 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/base_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | // 6 | // base sink templated over a mutex (either dummy or real) 7 | // concrete implementation should override the sink_it_() and flush_() methods. 8 | // locking is taken care of in this class - no locking needed by the 9 | // implementers.. 10 | // 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace spdlog { 17 | namespace sinks { 18 | template 19 | class SPDLOG_API base_sink : public sink { 20 | public: 21 | base_sink(); 22 | explicit base_sink(std::unique_ptr formatter); 23 | ~base_sink() override = default; 24 | 25 | base_sink(const base_sink &) = delete; 26 | base_sink(base_sink &&) = delete; 27 | 28 | base_sink &operator=(const base_sink &) = delete; 29 | base_sink &operator=(base_sink &&) = delete; 30 | 31 | void log(const details::log_msg &msg) final; 32 | void flush() final; 33 | void set_pattern(const std::string &pattern) final; 34 | void set_formatter(std::unique_ptr sink_formatter) final; 35 | 36 | protected: 37 | // sink formatter 38 | std::unique_ptr formatter_; 39 | Mutex mutex_; 40 | 41 | virtual void sink_it_(const details::log_msg &msg) = 0; 42 | virtual void flush_() = 0; 43 | virtual void set_pattern_(const std::string &pattern); 44 | virtual void set_formatter_(std::unique_ptr sink_formatter); 45 | }; 46 | } // namespace sinks 47 | } // namespace spdlog 48 | 49 | #ifdef SPDLOG_HEADER_ONLY 50 | #include "base_sink-inl.h" 51 | #endif 52 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/basic_file_sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | namespace sinks { 15 | 16 | template 17 | SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, 18 | bool truncate, 19 | const file_event_handlers &event_handlers) 20 | : file_helper_{event_handlers} { 21 | file_helper_.open(filename, truncate); 22 | } 23 | 24 | template 25 | SPDLOG_INLINE const filename_t &basic_file_sink::filename() const { 26 | return file_helper_.filename(); 27 | } 28 | 29 | template 30 | SPDLOG_INLINE void basic_file_sink::sink_it_(const details::log_msg &msg) { 31 | memory_buf_t formatted; 32 | base_sink::formatter_->format(msg, formatted); 33 | file_helper_.write(formatted); 34 | } 35 | 36 | template 37 | SPDLOG_INLINE void basic_file_sink::flush_() { 38 | file_helper_.flush(); 39 | } 40 | 41 | } // namespace sinks 42 | } // namespace spdlog 43 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/basic_file_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | /* 17 | * Trivial file sink with single file as target 18 | */ 19 | template 20 | class basic_file_sink final : public base_sink { 21 | public: 22 | explicit basic_file_sink(const filename_t &filename, 23 | bool truncate = false, 24 | const file_event_handlers &event_handlers = {}); 25 | const filename_t &filename() const; 26 | 27 | protected: 28 | void sink_it_(const details::log_msg &msg) override; 29 | void flush_() override; 30 | 31 | private: 32 | details::file_helper file_helper_; 33 | }; 34 | 35 | using basic_file_sink_mt = basic_file_sink; 36 | using basic_file_sink_st = basic_file_sink; 37 | 38 | } // namespace sinks 39 | 40 | // 41 | // factory functions 42 | // 43 | template 44 | inline std::shared_ptr basic_logger_mt(const std::string &logger_name, 45 | const filename_t &filename, 46 | bool truncate = false, 47 | const file_event_handlers &event_handlers = {}) { 48 | return Factory::template create(logger_name, filename, truncate, 49 | event_handlers); 50 | } 51 | 52 | template 53 | inline std::shared_ptr basic_logger_st(const std::string &logger_name, 54 | const filename_t &filename, 55 | bool truncate = false, 56 | const file_event_handlers &event_handlers = {}) { 57 | return Factory::template create(logger_name, filename, truncate, 58 | event_handlers); 59 | } 60 | 61 | } // namespace spdlog 62 | 63 | #ifdef SPDLOG_HEADER_ONLY 64 | #include "basic_file_sink-inl.h" 65 | #endif 66 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/callback_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | 15 | // callbacks type 16 | typedef std::function custom_log_callback; 17 | 18 | namespace sinks { 19 | /* 20 | * Trivial callback sink, gets a callback function and calls it on each log 21 | */ 22 | template 23 | class callback_sink final : public base_sink { 24 | public: 25 | explicit callback_sink(const custom_log_callback &callback) 26 | : callback_{callback} {} 27 | 28 | protected: 29 | void sink_it_(const details::log_msg &msg) override { callback_(msg); } 30 | void flush_() override{}; 31 | 32 | private: 33 | custom_log_callback callback_; 34 | }; 35 | 36 | using callback_sink_mt = callback_sink; 37 | using callback_sink_st = callback_sink; 38 | 39 | } // namespace sinks 40 | 41 | // 42 | // factory functions 43 | // 44 | template 45 | inline std::shared_ptr callback_logger_mt(const std::string &logger_name, 46 | const custom_log_callback &callback) { 47 | return Factory::template create(logger_name, callback); 48 | } 49 | 50 | template 51 | inline std::shared_ptr callback_logger_st(const std::string &logger_name, 52 | const custom_log_callback &callback) { 53 | return Factory::template create(logger_name, callback); 54 | } 55 | 56 | } // namespace spdlog 57 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/msvc_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2016 Alexander Dalshov & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #if defined(_WIN32) 7 | 8 | #include 9 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 10 | #include 11 | #endif 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | // Avoid including windows.h (https://stackoverflow.com/a/30741042) 18 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 19 | extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString); 20 | #else 21 | extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString); 22 | #endif 23 | extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 24 | 25 | namespace spdlog { 26 | namespace sinks { 27 | /* 28 | * MSVC sink (logging using OutputDebugStringA) 29 | */ 30 | template 31 | class msvc_sink : public base_sink { 32 | public: 33 | msvc_sink() = default; 34 | msvc_sink(bool check_debugger_present) 35 | : check_debugger_present_{check_debugger_present} {}; 36 | 37 | protected: 38 | void sink_it_(const details::log_msg &msg) override { 39 | if (check_debugger_present_ && !IsDebuggerPresent()) { 40 | return; 41 | } 42 | memory_buf_t formatted; 43 | base_sink::formatter_->format(msg, formatted); 44 | formatted.push_back('\0'); // add a null terminator for OutputDebugString 45 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 46 | wmemory_buf_t wformatted; 47 | details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted); 48 | OutputDebugStringW(wformatted.data()); 49 | #else 50 | OutputDebugStringA(formatted.data()); 51 | #endif 52 | } 53 | 54 | void flush_() override {} 55 | 56 | bool check_debugger_present_ = true; 57 | }; 58 | 59 | using msvc_sink_mt = msvc_sink; 60 | using msvc_sink_st = msvc_sink; 61 | 62 | using windebug_sink_mt = msvc_sink_mt; 63 | using windebug_sink_st = msvc_sink_st; 64 | 65 | } // namespace sinks 66 | } // namespace spdlog 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/null_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | 15 | template 16 | class null_sink : public base_sink { 17 | protected: 18 | void sink_it_(const details::log_msg &) override {} 19 | void flush_() override {} 20 | }; 21 | 22 | using null_sink_mt = null_sink; 23 | using null_sink_st = null_sink; 24 | 25 | } // namespace sinks 26 | 27 | template 28 | inline std::shared_ptr null_logger_mt(const std::string &logger_name) { 29 | auto null_logger = Factory::template create(logger_name); 30 | null_logger->set_level(level::off); 31 | return null_logger; 32 | } 33 | 34 | template 35 | inline std::shared_ptr null_logger_st(const std::string &logger_name) { 36 | auto null_logger = Factory::template create(logger_name); 37 | null_logger->set_level(level::off); 38 | return null_logger; 39 | } 40 | 41 | } // namespace spdlog 42 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/ostream_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | template 15 | class ostream_sink final : public base_sink { 16 | public: 17 | explicit ostream_sink(std::ostream &os, bool force_flush = false) 18 | : ostream_(os), 19 | force_flush_(force_flush) {} 20 | ostream_sink(const ostream_sink &) = delete; 21 | ostream_sink &operator=(const ostream_sink &) = delete; 22 | 23 | protected: 24 | void sink_it_(const details::log_msg &msg) override { 25 | memory_buf_t formatted; 26 | base_sink::formatter_->format(msg, formatted); 27 | ostream_.write(formatted.data(), static_cast(formatted.size())); 28 | if (force_flush_) { 29 | ostream_.flush(); 30 | } 31 | } 32 | 33 | void flush_() override { ostream_.flush(); } 34 | 35 | std::ostream &ostream_; 36 | bool force_flush_; 37 | }; 38 | 39 | using ostream_sink_mt = ostream_sink; 40 | using ostream_sink_st = ostream_sink; 41 | 42 | } // namespace sinks 43 | } // namespace spdlog 44 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/ringbuffer_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "spdlog/details/circular_q.h" 7 | #include "spdlog/details/log_msg_buffer.h" 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | /* 18 | * Ring buffer sink 19 | */ 20 | template 21 | class ringbuffer_sink final : public base_sink { 22 | public: 23 | explicit ringbuffer_sink(size_t n_items) 24 | : q_{n_items} {} 25 | 26 | std::vector last_raw(size_t lim = 0) { 27 | std::lock_guard lock(base_sink::mutex_); 28 | auto items_available = q_.size(); 29 | auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available; 30 | std::vector ret; 31 | ret.reserve(n_items); 32 | for (size_t i = (items_available - n_items); i < items_available; i++) { 33 | ret.push_back(q_.at(i)); 34 | } 35 | return ret; 36 | } 37 | 38 | std::vector last_formatted(size_t lim = 0) { 39 | std::lock_guard lock(base_sink::mutex_); 40 | auto items_available = q_.size(); 41 | auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available; 42 | std::vector ret; 43 | ret.reserve(n_items); 44 | for (size_t i = (items_available - n_items); i < items_available; i++) { 45 | memory_buf_t formatted; 46 | base_sink::formatter_->format(q_.at(i), formatted); 47 | ret.push_back(SPDLOG_BUF_TO_STRING(formatted)); 48 | } 49 | return ret; 50 | } 51 | 52 | protected: 53 | void sink_it_(const details::log_msg &msg) override { 54 | q_.push_back(details::log_msg_buffer{msg}); 55 | } 56 | void flush_() override {} 57 | 58 | private: 59 | details::circular_q q_; 60 | }; 61 | 62 | using ringbuffer_sink_mt = ringbuffer_sink; 63 | using ringbuffer_sink_st = ringbuffer_sink; 64 | 65 | } // namespace sinks 66 | 67 | } // namespace spdlog 68 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const { 13 | return msg_level >= level_.load(std::memory_order_relaxed); 14 | } 15 | 16 | SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level) { 17 | level_.store(log_level, std::memory_order_relaxed); 18 | } 19 | 20 | SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const { 21 | return static_cast(level_.load(std::memory_order_relaxed)); 22 | } 23 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | 11 | namespace sinks { 12 | class SPDLOG_API sink { 13 | public: 14 | virtual ~sink() = default; 15 | virtual void log(const details::log_msg &msg) = 0; 16 | virtual void flush() = 0; 17 | virtual void set_pattern(const std::string &pattern) = 0; 18 | virtual void set_formatter(std::unique_ptr sink_formatter) = 0; 19 | 20 | void set_level(level::level_enum log_level); 21 | level::level_enum level() const; 22 | bool should_log(level::level_enum msg_level) const; 23 | 24 | protected: 25 | // sink log level - default is all 26 | level_t level_{level::trace}; 27 | }; 28 | 29 | } // namespace sinks 30 | } // namespace spdlog 31 | 32 | #ifdef SPDLOG_HEADER_ONLY 33 | #include "sink-inl.h" 34 | #endif 35 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/stdout_color_sinks-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | 15 | template 16 | SPDLOG_INLINE std::shared_ptr stdout_color_mt(const std::string &logger_name, 17 | color_mode mode) { 18 | return Factory::template create(logger_name, mode); 19 | } 20 | 21 | template 22 | SPDLOG_INLINE std::shared_ptr stdout_color_st(const std::string &logger_name, 23 | color_mode mode) { 24 | return Factory::template create(logger_name, mode); 25 | } 26 | 27 | template 28 | SPDLOG_INLINE std::shared_ptr stderr_color_mt(const std::string &logger_name, 29 | color_mode mode) { 30 | return Factory::template create(logger_name, mode); 31 | } 32 | 33 | template 34 | SPDLOG_INLINE std::shared_ptr stderr_color_st(const std::string &logger_name, 35 | color_mode mode) { 36 | return Factory::template create(logger_name, mode); 37 | } 38 | } // namespace spdlog 39 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/stdout_color_sinks.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifdef _WIN32 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | #ifdef _WIN32 17 | using stdout_color_sink_mt = wincolor_stdout_sink_mt; 18 | using stdout_color_sink_st = wincolor_stdout_sink_st; 19 | using stderr_color_sink_mt = wincolor_stderr_sink_mt; 20 | using stderr_color_sink_st = wincolor_stderr_sink_st; 21 | #else 22 | using stdout_color_sink_mt = ansicolor_stdout_sink_mt; 23 | using stdout_color_sink_st = ansicolor_stdout_sink_st; 24 | using stderr_color_sink_mt = ansicolor_stderr_sink_mt; 25 | using stderr_color_sink_st = ansicolor_stderr_sink_st; 26 | #endif 27 | } // namespace sinks 28 | 29 | template 30 | std::shared_ptr stdout_color_mt(const std::string &logger_name, 31 | color_mode mode = color_mode::automatic); 32 | 33 | template 34 | std::shared_ptr stdout_color_st(const std::string &logger_name, 35 | color_mode mode = color_mode::automatic); 36 | 37 | template 38 | std::shared_ptr stderr_color_mt(const std::string &logger_name, 39 | color_mode mode = color_mode::automatic); 40 | 41 | template 42 | std::shared_ptr stderr_color_st(const std::string &logger_name, 43 | color_mode mode = color_mode::automatic); 44 | 45 | } // namespace spdlog 46 | 47 | #ifdef SPDLOG_HEADER_ONLY 48 | #include "stdout_color_sinks-inl.h" 49 | #endif 50 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/tcp_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #pragma once 21 | 22 | // Simple tcp client sink 23 | // Connects to remote address and send the formatted log. 24 | // Will attempt to reconnect if connection drops. 25 | // If more complicated behaviour is needed (i.e get responses), you can inherit it and override the 26 | // sink_it_ method. 27 | 28 | namespace spdlog { 29 | namespace sinks { 30 | 31 | struct tcp_sink_config { 32 | std::string server_host; 33 | int server_port; 34 | bool lazy_connect = false; // if true connect on first log call instead of on construction 35 | 36 | tcp_sink_config(std::string host, int port) 37 | : server_host{std::move(host)}, 38 | server_port{port} {} 39 | }; 40 | 41 | template 42 | class tcp_sink : public spdlog::sinks::base_sink { 43 | public: 44 | // connect to tcp host/port or throw if failed 45 | // host can be hostname or ip address 46 | 47 | explicit tcp_sink(tcp_sink_config sink_config) 48 | : config_{std::move(sink_config)} { 49 | if (!config_.lazy_connect) { 50 | this->client_.connect(config_.server_host, config_.server_port); 51 | } 52 | } 53 | 54 | ~tcp_sink() override = default; 55 | 56 | protected: 57 | void sink_it_(const spdlog::details::log_msg &msg) override { 58 | spdlog::memory_buf_t formatted; 59 | spdlog::sinks::base_sink::formatter_->format(msg, formatted); 60 | if (!client_.is_connected()) { 61 | client_.connect(config_.server_host, config_.server_port); 62 | } 63 | client_.send(formatted.data(), formatted.size()); 64 | } 65 | 66 | void flush_() override {} 67 | tcp_sink_config config_; 68 | details::tcp_client client_; 69 | }; 70 | 71 | using tcp_sink_mt = tcp_sink; 72 | using tcp_sink_st = tcp_sink; 73 | 74 | } // namespace sinks 75 | } // namespace spdlog 76 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/sinks/udp_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // Simple udp client sink 21 | // Sends formatted log via udp 22 | 23 | namespace spdlog { 24 | namespace sinks { 25 | 26 | struct udp_sink_config { 27 | std::string server_host; 28 | uint16_t server_port; 29 | 30 | udp_sink_config(std::string host, uint16_t port) 31 | : server_host{std::move(host)}, 32 | server_port{port} {} 33 | }; 34 | 35 | template 36 | class udp_sink : public spdlog::sinks::base_sink { 37 | public: 38 | // host can be hostname or ip address 39 | explicit udp_sink(udp_sink_config sink_config) 40 | : client_{sink_config.server_host, sink_config.server_port} {} 41 | 42 | ~udp_sink() override = default; 43 | 44 | protected: 45 | void sink_it_(const spdlog::details::log_msg &msg) override { 46 | spdlog::memory_buf_t formatted; 47 | spdlog::sinks::base_sink::formatter_->format(msg, formatted); 48 | client_.send(formatted.data(), formatted.size()); 49 | } 50 | 51 | void flush_() override {} 52 | details::udp_client client_; 53 | }; 54 | 55 | using udp_sink_mt = udp_sink; 56 | using udp_sink_st = udp_sink; 57 | 58 | } // namespace sinks 59 | 60 | // 61 | // factory functions 62 | // 63 | template 64 | inline std::shared_ptr udp_logger_mt(const std::string &logger_name, 65 | sinks::udp_sink_config skin_config) { 66 | return Factory::template create(logger_name, skin_config); 67 | } 68 | 69 | } // namespace spdlog 70 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/stopwatch.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | // Stopwatch support for spdlog (using std::chrono::steady_clock). 10 | // Displays elapsed seconds since construction as double. 11 | // 12 | // Usage: 13 | // 14 | // spdlog::stopwatch sw; 15 | // ... 16 | // spdlog::debug("Elapsed: {} seconds", sw); => "Elapsed 0.005116733 seconds" 17 | // spdlog::info("Elapsed: {:.6} seconds", sw); => "Elapsed 0.005163 seconds" 18 | // 19 | // 20 | // If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use 21 | // "duration_cast<..>(sw.elapsed())": 22 | // 23 | // #include 24 | //.. 25 | // using std::chrono::duration_cast; 26 | // using std::chrono::milliseconds; 27 | // spdlog::info("Elapsed {}", duration_cast(sw.elapsed())); => "Elapsed 5ms" 28 | 29 | namespace spdlog { 30 | class stopwatch { 31 | using clock = std::chrono::steady_clock; 32 | std::chrono::time_point start_tp_; 33 | 34 | public: 35 | stopwatch() 36 | : start_tp_{clock::now()} {} 37 | 38 | std::chrono::duration elapsed() const { 39 | return std::chrono::duration(clock::now() - start_tp_); 40 | } 41 | 42 | std::chrono::milliseconds elapsed_ms() const { 43 | return std::chrono::duration_cast(clock::now() - start_tp_); 44 | } 45 | 46 | void reset() { start_tp_ = clock::now(); } 47 | }; 48 | } // namespace spdlog 49 | 50 | // Support for fmt formatting (e.g. "{:012.9}" or just "{}") 51 | namespace 52 | #ifdef SPDLOG_USE_STD_FORMAT 53 | std 54 | #else 55 | fmt 56 | #endif 57 | { 58 | 59 | template <> 60 | struct formatter : formatter { 61 | template 62 | auto format(const spdlog::stopwatch &sw, FormatContext &ctx) const -> decltype(ctx.out()) { 63 | return formatter::format(sw.elapsed().count(), ctx); 64 | } 65 | }; 66 | } // namespace std 67 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/include/spdlog/version.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #define SPDLOG_VER_MAJOR 1 7 | #define SPDLOG_VER_MINOR 14 8 | #define SPDLOG_VER_PATCH 1 9 | 10 | #define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch) 11 | #define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH) 12 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/logos/spdlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basis-robotics/basis/ec8e3e9f86215546d8179317e2c3080760d87a14/cpp/third_party/spdlog/logos/spdlog.png -------------------------------------------------------------------------------- /cpp/third_party/spdlog/scripts/ci_setup_clang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | VERSION=$1 6 | 7 | apt-get update 8 | apt-get install -y libc++-${VERSION}-dev libc++abi-${VERSION}-dev 9 | 10 | if [[ "${VERSION}" -ge 12 ]]; then 11 | apt-get install -y --no-install-recommends libunwind-${VERSION}-dev 12 | fi 13 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/scripts/extract_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | 6 | base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 7 | config_h = os.path.join(base_path, 'include', 'spdlog', 'version.h') 8 | data = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0} 9 | reg = re.compile(r'^\s*#define\s+SPDLOG_VER_([A-Z]+)\s+([0-9]+).*$') 10 | 11 | with open(config_h, 'r') as fp: 12 | for l in fp: 13 | m = reg.match(l) 14 | if m: 15 | data[m.group(1)] = int(m.group(2)) 16 | 17 | print(f"{data['MAJOR']}.{data['MINOR']}.{data['PATCH']}") 18 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")"/.. 4 | pwd 5 | find_sources="find include src tests example bench -not ( -path include/spdlog/fmt/bundled -prune ) -type f -name *\.h -o -name *\.cpp" 6 | echo -n "Running dos2unix " 7 | $find_sources | xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'" 8 | echo 9 | echo -n "Running clang-format " 10 | 11 | $find_sources | xargs -I {} sh -c "clang-format -i {}; echo -n '.'" 12 | 13 | echo 14 | echo -n "Running cmake-format " 15 | find . -type f -name "CMakeLists.txt" -o -name "*\.cmake"|grep -v bundled|grep -v build|xargs -I {} sh -c "cmake-format --line-width 120 --tab-size 4 --max-subgroups-hwrap 4 -i {}; echo -n '.'" 16 | echo 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/async.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/bundled_fmtlib_format.cpp: -------------------------------------------------------------------------------- 1 | // Slightly modified version of fmt lib's format.cc source file. 2 | // Copyright (c) 2012 - 2016, Victor Zverovich 3 | // All rights reserved. 4 | 5 | #ifndef SPDLOG_COMPILED_LIB 6 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 7 | #endif 8 | 9 | #if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT) 10 | 11 | #include 12 | 13 | FMT_BEGIN_NAMESPACE 14 | namespace detail { 15 | 16 | template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; 17 | template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; 18 | 19 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 20 | template FMT_API locale_ref::locale_ref(const std::locale& loc); 21 | template FMT_API auto locale_ref::get() const -> std::locale; 22 | #endif 23 | 24 | // Explicit instantiations for char. 25 | 26 | template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; 27 | template FMT_API auto decimal_point_impl(locale_ref) -> char; 28 | 29 | template FMT_API void buffer::append(const char*, const char*); 30 | 31 | template FMT_API void vformat_to(buffer&, 32 | string_view, 33 | typename vformat_args<>::type, 34 | locale_ref); 35 | 36 | // Explicit instantiations for wchar_t. 37 | 38 | template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; 39 | template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; 40 | 41 | template FMT_API void buffer::append(const wchar_t*, const wchar_t*); 42 | 43 | } // namespace detail 44 | FMT_END_NAMESPACE 45 | 46 | #endif // !SPDLOG_FMT_EXTERNAL 47 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/cfg.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/file_sinks.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | template class SPDLOG_API spdlog::sinks::basic_file_sink; 16 | template class SPDLOG_API spdlog::sinks::basic_file_sink; 17 | 18 | #include 19 | template class SPDLOG_API spdlog::sinks::rotating_file_sink; 20 | template class SPDLOG_API spdlog::sinks::rotating_file_sink; 21 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/spdlog.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | // template instantiate logger constructor with sinks init list 24 | template SPDLOG_API spdlog::logger::logger(std::string name, 25 | sinks_init_list::iterator begin, 26 | sinks_init_list::iterator end); 27 | template class SPDLOG_API spdlog::sinks::base_sink; 28 | template class SPDLOG_API spdlog::sinks::base_sink; 29 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/src/stdout_sinks.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | template class SPDLOG_API spdlog::sinks::stdout_sink_base; 15 | template class SPDLOG_API spdlog::sinks::stdout_sink_base; 16 | template class SPDLOG_API spdlog::sinks::stdout_sink; 17 | template class SPDLOG_API spdlog::sinks::stdout_sink; 18 | template class SPDLOG_API spdlog::sinks::stderr_sink; 19 | template class SPDLOG_API spdlog::sinks::stderr_sink; 20 | 21 | template SPDLOG_API std::shared_ptr 22 | spdlog::stdout_logger_mt(const std::string &logger_name); 23 | template SPDLOG_API std::shared_ptr 24 | spdlog::stdout_logger_st(const std::string &logger_name); 25 | template SPDLOG_API std::shared_ptr 26 | spdlog::stderr_logger_mt(const std::string &logger_name); 27 | template SPDLOG_API std::shared_ptr 28 | spdlog::stderr_logger_st(const std::string &logger_name); 29 | 30 | template SPDLOG_API std::shared_ptr spdlog::stdout_logger_mt( 31 | const std::string &logger_name); 32 | template SPDLOG_API std::shared_ptr spdlog::stdout_logger_st( 33 | const std::string &logger_name); 34 | template SPDLOG_API std::shared_ptr spdlog::stderr_logger_mt( 35 | const std::string &logger_name); 36 | template SPDLOG_API std::shared_ptr spdlog::stderr_logger_st( 37 | const std::string &logger_name); 38 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__GNUC__) && __GNUC__ == 12 4 | #pragma GCC diagnostic push 5 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 6 | #endif 7 | #include 8 | #if defined(__GNUC__) && __GNUC__ == 12 9 | #pragma GCC diagnostic pop 10 | #endif 11 | 12 | #include "utils.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG 25 | 26 | #include "spdlog/spdlog.h" 27 | #include "spdlog/async.h" 28 | #include "spdlog/details/fmt_helper.h" 29 | #include "spdlog/mdc.h" 30 | #include "spdlog/sinks/basic_file_sink.h" 31 | #include "spdlog/sinks/daily_file_sink.h" 32 | #include "spdlog/sinks/null_sink.h" 33 | #include "spdlog/sinks/ostream_sink.h" 34 | #include "spdlog/sinks/rotating_file_sink.h" 35 | #include "spdlog/sinks/stdout_color_sinks.h" 36 | #include "spdlog/sinks/msvc_sink.h" 37 | #include "spdlog/pattern_formatter.h" 38 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/main.cpp: -------------------------------------------------------------------------------- 1 | #if defined(__GNUC__) && __GNUC__ == 12 2 | #pragma GCC diagnostic push 3 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 4 | #endif 5 | 6 | #include 7 | 8 | #if defined(__GNUC__) && __GNUC__ == 12 9 | #pragma GCC diagnostic pop 10 | #endif 11 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_circular_q.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "spdlog/details/circular_q.h" 3 | 4 | using q_type = spdlog::details::circular_q; 5 | TEST_CASE("test_size", "[circular_q]") { 6 | const size_t q_size = 4; 7 | q_type q(q_size); 8 | REQUIRE(q.size() == 0); 9 | REQUIRE(q.empty() == true); 10 | for (size_t i = 0; i < q_size; i++) { 11 | q.push_back(std::move(i)); 12 | } 13 | REQUIRE(q.size() == q_size); 14 | q.push_back(999); 15 | REQUIRE(q.size() == q_size); 16 | } 17 | 18 | TEST_CASE("test_rolling", "[circular_q]") { 19 | const size_t q_size = 4; 20 | q_type q(q_size); 21 | 22 | for (size_t i = 0; i < q_size + 2; i++) { 23 | q.push_back(std::move(i)); 24 | } 25 | 26 | REQUIRE(q.size() == q_size); 27 | 28 | REQUIRE(q.front() == 2); 29 | q.pop_front(); 30 | 31 | REQUIRE(q.front() == 3); 32 | q.pop_front(); 33 | 34 | REQUIRE(q.front() == 4); 35 | q.pop_front(); 36 | 37 | REQUIRE(q.front() == 5); 38 | q.pop_front(); 39 | 40 | REQUIRE(q.empty()); 41 | 42 | q.push_back(6); 43 | REQUIRE(q.front() == 6); 44 | } 45 | 46 | TEST_CASE("test_empty", "[circular_q]") { 47 | q_type q(0); 48 | q.push_back(1); 49 | REQUIRE(q.empty()); 50 | } -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_custom_callbacks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is released under the MIT License as specified in 3 | * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE 4 | */ 5 | #include "includes.h" 6 | #include "test_sink.h" 7 | #include "spdlog/sinks/callback_sink.h" 8 | #include "spdlog/async.h" 9 | #include "spdlog/common.h" 10 | 11 | TEST_CASE("custom_callback_logger", "[custom_callback_logger]") { 12 | std::vector lines; 13 | spdlog::pattern_formatter formatter; 14 | auto callback_logger = 15 | std::make_shared([&](const spdlog::details::log_msg &msg) { 16 | spdlog::memory_buf_t formatted; 17 | formatter.format(msg, formatted); 18 | auto eol_len = strlen(spdlog::details::os::default_eol); 19 | lines.emplace_back(formatted.begin(), formatted.end() - eol_len); 20 | }); 21 | std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); 22 | 23 | spdlog::logger logger("test-callback", {callback_logger, test_sink}); 24 | 25 | logger.info("test message 1"); 26 | logger.info("test message 2"); 27 | logger.info("test message 3"); 28 | 29 | std::vector ref_lines = test_sink->lines(); 30 | 31 | REQUIRE(lines[0] == ref_lines[0]); 32 | REQUIRE(lines[1] == ref_lines[1]); 33 | REQUIRE(lines[2] == ref_lines[2]); 34 | spdlog::drop_all(); 35 | } 36 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_fmt_helper.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "includes.h" 3 | 4 | using spdlog::memory_buf_t; 5 | using spdlog::details::to_string_view; 6 | 7 | void test_pad2(int n, const char *expected) { 8 | memory_buf_t buf; 9 | spdlog::details::fmt_helper::pad2(n, buf); 10 | 11 | REQUIRE(to_string_view(buf) == expected); 12 | } 13 | 14 | void test_pad3(uint32_t n, const char *expected) { 15 | memory_buf_t buf; 16 | spdlog::details::fmt_helper::pad3(n, buf); 17 | 18 | REQUIRE(to_string_view(buf) == expected); 19 | } 20 | 21 | void test_pad6(std::size_t n, const char *expected) { 22 | memory_buf_t buf; 23 | spdlog::details::fmt_helper::pad6(n, buf); 24 | 25 | REQUIRE(to_string_view(buf) == expected); 26 | } 27 | 28 | void test_pad9(std::size_t n, const char *expected) { 29 | memory_buf_t buf; 30 | spdlog::details::fmt_helper::pad9(n, buf); 31 | 32 | REQUIRE(to_string_view(buf) == expected); 33 | } 34 | 35 | TEST_CASE("pad2", "[fmt_helper]") { 36 | test_pad2(0, "00"); 37 | test_pad2(3, "03"); 38 | test_pad2(10, "10"); 39 | test_pad2(23, "23"); 40 | test_pad2(99, "99"); 41 | test_pad2(100, "100"); 42 | test_pad2(123, "123"); 43 | test_pad2(1234, "1234"); 44 | test_pad2(-5, "-5"); 45 | } 46 | 47 | TEST_CASE("pad3", "[fmt_helper]") { 48 | test_pad3(0, "000"); 49 | test_pad3(3, "003"); 50 | test_pad3(10, "010"); 51 | test_pad3(23, "023"); 52 | test_pad3(99, "099"); 53 | test_pad3(100, "100"); 54 | test_pad3(123, "123"); 55 | test_pad3(999, "999"); 56 | test_pad3(1000, "1000"); 57 | test_pad3(1234, "1234"); 58 | } 59 | 60 | TEST_CASE("pad6", "[fmt_helper]") { 61 | test_pad6(0, "000000"); 62 | test_pad6(3, "000003"); 63 | test_pad6(23, "000023"); 64 | test_pad6(123, "000123"); 65 | test_pad6(1234, "001234"); 66 | test_pad6(12345, "012345"); 67 | test_pad6(123456, "123456"); 68 | } 69 | 70 | TEST_CASE("pad9", "[fmt_helper]") { 71 | test_pad9(0, "000000000"); 72 | test_pad9(3, "000000003"); 73 | test_pad9(23, "000000023"); 74 | test_pad9(123, "000000123"); 75 | test_pad9(1234, "000001234"); 76 | test_pad9(12345, "000012345"); 77 | test_pad9(123456, "000123456"); 78 | test_pad9(1234567, "001234567"); 79 | test_pad9(12345678, "012345678"); 80 | test_pad9(123456789, "123456789"); 81 | test_pad9(1234567891, "1234567891"); 82 | } 83 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_macros.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is released under the MIT License as specified in 3 | * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE 4 | */ 5 | 6 | #include "includes.h" 7 | 8 | #if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG 9 | #error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG" 10 | #endif 11 | 12 | #define TEST_FILENAME "test_logs/simple_log" 13 | 14 | TEST_CASE("debug and trace w/o format string", "[macros]") { 15 | prepare_logdir(); 16 | spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); 17 | 18 | auto logger = spdlog::create("logger", filename); 19 | logger->set_pattern("%v"); 20 | logger->set_level(spdlog::level::trace); 21 | 22 | SPDLOG_LOGGER_TRACE(logger, "Test message 1"); 23 | SPDLOG_LOGGER_DEBUG(logger, "Test message 2"); 24 | logger->flush(); 25 | 26 | using spdlog::details::os::default_eol; 27 | REQUIRE(ends_with(file_contents(TEST_FILENAME), 28 | spdlog::fmt_lib::format("Test message 2{}", default_eol))); 29 | REQUIRE(count_lines(TEST_FILENAME) == 1); 30 | 31 | auto orig_default_logger = spdlog::default_logger(); 32 | spdlog::set_default_logger(logger); 33 | 34 | SPDLOG_TRACE("Test message 3"); 35 | SPDLOG_DEBUG("Test message {}", 4); 36 | logger->flush(); 37 | 38 | require_message_count(TEST_FILENAME, 2); 39 | REQUIRE(ends_with(file_contents(TEST_FILENAME), 40 | spdlog::fmt_lib::format("Test message 4{}", default_eol))); 41 | spdlog::set_default_logger(std::move(orig_default_logger)); 42 | } 43 | 44 | TEST_CASE("disable param evaluation", "[macros]") { 45 | SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated")); 46 | } 47 | 48 | TEST_CASE("pass logger pointer", "[macros]") { 49 | auto logger = spdlog::create("refmacro"); 50 | auto &ref = *logger; 51 | SPDLOG_LOGGER_TRACE(&ref, "Test message 1"); 52 | SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); 53 | } 54 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | #include "spdlog/fmt/fmt.h" 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | 18 | template 19 | class test_sink : public base_sink { 20 | const size_t lines_to_save = 100; 21 | 22 | public: 23 | size_t msg_counter() { 24 | std::lock_guard lock(base_sink::mutex_); 25 | return msg_counter_; 26 | } 27 | 28 | size_t flush_counter() { 29 | std::lock_guard lock(base_sink::mutex_); 30 | return flush_counter_; 31 | } 32 | 33 | void set_delay(std::chrono::milliseconds delay) { 34 | std::lock_guard lock(base_sink::mutex_); 35 | delay_ = delay; 36 | } 37 | 38 | // return last output without the eol 39 | std::vector lines() { 40 | std::lock_guard lock(base_sink::mutex_); 41 | return lines_; 42 | } 43 | 44 | protected: 45 | void sink_it_(const details::log_msg &msg) override { 46 | memory_buf_t formatted; 47 | base_sink::formatter_->format(msg, formatted); 48 | // save the line without the eol 49 | auto eol_len = strlen(details::os::default_eol); 50 | if (lines_.size() < lines_to_save) { 51 | lines_.emplace_back(formatted.begin(), formatted.end() - eol_len); 52 | } 53 | msg_counter_++; 54 | std::this_thread::sleep_for(delay_); 55 | } 56 | 57 | void flush_() override { flush_counter_++; } 58 | 59 | size_t msg_counter_{0}; 60 | size_t flush_counter_{0}; 61 | std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()}; 62 | std::vector lines_; 63 | }; 64 | 65 | using test_sink_mt = test_sink; 66 | using test_sink_st = test_sink; 67 | 68 | } // namespace sinks 69 | } // namespace spdlog 70 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_stopwatch.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/stopwatch.h" 4 | 5 | TEST_CASE("stopwatch1", "[stopwatch]") { 6 | using std::chrono::milliseconds; 7 | using clock = std::chrono::steady_clock; 8 | milliseconds wait_ms(200); 9 | milliseconds tolerance_ms(250); 10 | auto start = clock::now(); 11 | spdlog::stopwatch sw; 12 | std::this_thread::sleep_for(wait_ms); 13 | auto stop = clock::now(); 14 | auto diff_ms = std::chrono::duration_cast(stop - start); 15 | REQUIRE(sw.elapsed() >= diff_ms); 16 | REQUIRE(sw.elapsed() <= diff_ms + tolerance_ms); 17 | } 18 | 19 | TEST_CASE("stopwatch2", "[stopwatch]") { 20 | using spdlog::sinks::test_sink_st; 21 | using std::chrono::duration_cast; 22 | using std::chrono::milliseconds; 23 | using clock = std::chrono::steady_clock; 24 | 25 | clock::duration wait_duration(milliseconds(200)); 26 | clock::duration tolerance_duration(milliseconds(250)); 27 | 28 | auto test_sink = std::make_shared(); 29 | 30 | auto start = clock::now(); 31 | spdlog::stopwatch sw; 32 | spdlog::logger logger("test-stopwatch", test_sink); 33 | logger.set_pattern("%v"); 34 | std::this_thread::sleep_for(wait_duration); 35 | auto stop = clock::now(); 36 | logger.info("{}", sw); 37 | auto val = std::stod(test_sink->lines()[0]); 38 | auto diff_duration = duration_cast>(stop - start); 39 | 40 | REQUIRE(val >= (diff_duration).count() - 0.001); 41 | REQUIRE(val <= (diff_duration + tolerance_duration).count()); 42 | } 43 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_systemd.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "spdlog/sinks/systemd_sink.h" 3 | 4 | TEST_CASE("systemd", "[all]") { 5 | auto systemd_sink = std::make_shared(); 6 | spdlog::logger logger("spdlog_systemd_test", systemd_sink); 7 | logger.set_level(spdlog::level::trace); 8 | logger.trace("test spdlog trace"); 9 | logger.debug("test spdlog debug"); 10 | SPDLOG_LOGGER_INFO((&logger), "test spdlog info"); 11 | SPDLOG_LOGGER_WARN((&logger), "test spdlog warn"); 12 | SPDLOG_LOGGER_ERROR((&logger), "test spdlog error"); 13 | SPDLOG_LOGGER_CRITICAL((&logger), "test spdlog critical"); 14 | } 15 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/test_time_point.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/async.h" 4 | 5 | TEST_CASE("time_point1", "[time_point log_msg]") { 6 | std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); 7 | spdlog::logger logger("test-time_point", test_sink); 8 | 9 | spdlog::source_loc source{}; 10 | std::chrono::system_clock::time_point tp{std::chrono::system_clock::now()}; 11 | test_sink->set_pattern("%T.%F"); // interested in the time_point 12 | 13 | // all the following should have the same time 14 | test_sink->set_delay(std::chrono::milliseconds(10)); 15 | for (int i = 0; i < 5; i++) { 16 | spdlog::details::log_msg msg{tp, source, "test_logger", spdlog::level::info, "message"}; 17 | test_sink->log(msg); 18 | } 19 | 20 | logger.log(tp, source, spdlog::level::info, "formatted message"); 21 | logger.log(tp, source, spdlog::level::info, "formatted message"); 22 | logger.log(tp, source, spdlog::level::info, "formatted message"); 23 | logger.log(tp, source, spdlog::level::info, "formatted message"); 24 | logger.log(source, spdlog::level::info, 25 | "formatted message"); // last line has different time_point 26 | 27 | // now the real test... that the times are the same. 28 | std::vector lines = test_sink->lines(); 29 | REQUIRE(lines[0] == lines[1]); 30 | REQUIRE(lines[2] == lines[3]); 31 | REQUIRE(lines[4] == lines[5]); 32 | REQUIRE(lines[6] == lines[7]); 33 | REQUIRE(lines[8] != lines[9]); 34 | spdlog::drop_all(); 35 | } 36 | -------------------------------------------------------------------------------- /cpp/third_party/spdlog/tests/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::size_t count_files(const std::string &folder); 7 | 8 | void prepare_logdir(); 9 | 10 | std::string file_contents(const std::string &filename); 11 | 12 | std::size_t count_lines(const std::string &filename); 13 | 14 | void require_message_count(const std::string &filename, const std::size_t messages); 15 | 16 | std::size_t get_filesize(const std::string &filename); 17 | 18 | bool ends_with(std::string const &value, std::string const &ending); -------------------------------------------------------------------------------- /cpp/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_core_unit) 2 | 3 | add_library(basis_unit SHARED src/unit.cpp src/args_template.cpp) 4 | target_include_directories(basis_unit PUBLIC include) 5 | target_link_libraries(basis_unit 6 | argparse 7 | inja 8 | basis::arguments 9 | basis::core::coordinator 10 | basis::core::logging 11 | basis::core::threading 12 | basis::synchronizers 13 | basis::plugins::transport::tcp 14 | ) 15 | 16 | add_library(basis::unit ALIAS basis_unit) 17 | 18 | add_library(basis_unit_main SHARED src/unit_main.cpp) 19 | target_link_libraries(basis_unit_main basis::unit) 20 | 21 | add_library(basis::unit::main ALIAS basis_unit_main) 22 | -------------------------------------------------------------------------------- /cpp/unit/include/basis/unit/args_template.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file args_template.h 4 | * 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace basis::unit { 12 | /** 13 | * Converts from from topics like {{args.camera_name}}/rgb -> /webcam/rgb using inja 14 | * @param args key value pairs of the arguments used for context in the template engine 15 | * @param topics the list of possibly templated topics to convert 16 | * @return nonstd::expected, std::string> the rendered topics 17 | */ 18 | nonstd::expected, std::string> 19 | RenderTemplatedTopics(const basis::arguments::ArgumentsBase &args, const std::vector &topics); 20 | 21 | } // namespace basis::unit -------------------------------------------------------------------------------- /cpp/unit/include/basis/unit/create_unit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file create_unit.h 4 | * 5 | * Contains the CreateUnit definition. Please use this to avoid drift between declaration and usage of CreateUnit. 6 | * Functions linked as extern "C" can't do interface type checking when used via dlsym! 7 | */ 8 | #include 9 | #include 10 | #include 11 | 12 | namespace basis { 13 | class Unit; 14 | /** 15 | * Logger interface mainly for error logs. Dead simple, only accepts char*. 16 | */ 17 | using CreateUnitLoggerInterface = void (*)(const char *); 18 | } // namespace basis 19 | 20 | extern "C" { 21 | /** 22 | * Forward declaration of CreateUnit - declared once in each unit library to provide an easy interface to create the 23 | * contained unit without prior type knowledge. Basically - the entrypoint into a unit "plugin" 24 | * 25 | * @param unit_name_override Optionally override the name of the unit. 26 | * @param command_line The arguments given to this unit, can be one of several types - see command_line.h 27 | * @param error_logger The logger to use when there's an error creating the unit (we don't assume that basis logging 28 | * system is initialized). 29 | * @return basis::Unit* The created unit, or nullptr if there was an error. 30 | */ 31 | basis::Unit *CreateUnit(const std::optional &unit_name_override, 32 | const basis::arguments::CommandLineTypes &command_line, 33 | basis::CreateUnitLoggerInterface error_logger); 34 | } -------------------------------------------------------------------------------- /cpp/unit/include/basis/unit/create_unit_impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** 3 | * @file create_unit_impl.h 4 | * 5 | */ 6 | #include "args_template.h" 7 | #include "create_unit.h" 8 | 9 | namespace basis::unit { 10 | template 11 | basis::Unit *CreateUnit(const std::optional &unit_name_override, 12 | const basis::arguments::CommandLineTypes &command_line, 13 | basis::CreateUnitLoggerInterface error_logger, const std::string_view unit_type_name) { 14 | auto args = T_UNIT::Args::ParseArgumentsVariant(command_line); 15 | 16 | if (!args.has_value()) { 17 | error_logger(fmt::format("Failed to launch {} ({}), bad arguments:\n{}", 18 | unit_name_override.value_or(unit_type_name), unit_type_name, args.error()) 19 | .c_str()); 20 | return nullptr; 21 | } 22 | 23 | auto maybe_templated_topics = basis::unit::RenderTemplatedTopics(*args, T_UNIT::all_templated_topics); 24 | if (!maybe_templated_topics) { 25 | error_logger(fmt::format("Failed to parse templates for {} ({}):\t\n{}", 26 | unit_name_override.value_or(unit_type_name), unit_type_name, 27 | maybe_templated_topics.error()) 28 | .c_str()); 29 | return nullptr; 30 | } 31 | 32 | return new T_UNIT(*args, unit_name_override); 33 | } 34 | } // namespace basis::unit -------------------------------------------------------------------------------- /cpp/unit/src/args_template.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace basis::unit { 7 | 8 | nonstd::expected, std::string> 9 | RenderTemplatedTopics(const basis::arguments::ArgumentsBase &args, const std::vector &topics) { 10 | inja::Environment inja_env; 11 | inja_env.set_throw_at_missing_includes(true); 12 | nlohmann::json template_data; 13 | template_data["args"] = args.GetArgumentMapping(); 14 | std::unordered_map out; 15 | for (const std::string &topic : topics) { 16 | if (!out.contains(topic)) { 17 | try { 18 | auto rendered = inja_env.render(topic, template_data); 19 | out.emplace(topic, rendered); 20 | 21 | // Do some basic validation to protect against common issues 22 | // TODO: hoist this out, use in transport manager 23 | if (rendered.empty()) { 24 | return nonstd::make_unexpected(fmt::format("{}: topic resolved to empty string", topic)); 25 | } 26 | if (rendered[0] != '/') { 27 | return nonstd::make_unexpected(fmt::format("{} -> {}: topic must start with /", topic, rendered)); 28 | } 29 | if (rendered[rendered.size() - 1] == '/') { 30 | return nonstd::make_unexpected(fmt::format("{} -> {}: topic must not end with /", topic, rendered)); 31 | } 32 | for (char c : rendered) { 33 | if (!(isalpha(c) || isdigit(c) || c == '_' || '/')) { 34 | return nonstd::make_unexpected(fmt::format("{} -> {}: topic must not end with /", topic, rendered)); 35 | } 36 | } 37 | } catch (const std::exception &err) { 38 | return nonstd::make_unexpected(fmt::format("{}: {}", topic, err.what())); 39 | } 40 | } 41 | } 42 | return out; 43 | } 44 | } // namespace basis::unit -------------------------------------------------------------------------------- /cpp/unit/src/unit.cpp: -------------------------------------------------------------------------------- 1 | #include "basis/unit.h" 2 | 3 | namespace basis { 4 | std::unique_ptr 5 | CreateStandardTransportManager(basis::RecorderInterface *recorder) { 6 | auto transport_manager = std::make_unique( 7 | std::make_unique()); 8 | 9 | if (recorder) { 10 | transport_manager->SetRecorder(recorder); 11 | } 12 | 13 | transport_manager->RegisterTransport(basis::plugins::transport::TCP_TRANSPORT_NAME, 14 | std::make_unique()); 15 | 16 | return transport_manager; 17 | } 18 | 19 | // TODO: this is purely concerned with transport concepts, we should consider moving this to transport 20 | void StandardUpdate(basis::core::transport::TransportManager *transport_manager, 21 | basis::core::transport::CoordinatorConnector *coordinator_connector) { 22 | transport_manager->Update(); 23 | // send it off to the coordinator 24 | if (coordinator_connector) { 25 | std::vector new_schemas = 26 | transport_manager->GetSchemaManager().ConsumeSchemasToSend(); 27 | if (new_schemas.size()) { 28 | coordinator_connector->SendSchemas(new_schemas); 29 | } 30 | coordinator_connector->SendTransportManagerInfo(transport_manager->GetTransportManagerInfo()); 31 | coordinator_connector->Update(); 32 | 33 | if (coordinator_connector->GetLastNetworkInfo()) { 34 | transport_manager->HandleNetworkInfo(*coordinator_connector->GetLastNetworkInfo()); 35 | } 36 | } 37 | } 38 | 39 | } // namespace basis -------------------------------------------------------------------------------- /cpp/unit/src/unit_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | basis::core::logging::InitializeLoggingSystem(); 10 | 11 | std::unique_ptr unit(CreateUnit({}, std::pair{argc, argv}, [](const char* msg) { 12 | std::cerr << msg << std::endl; 13 | })); 14 | 15 | if(!unit) { 16 | return 1; 17 | } 18 | 19 | unit->WaitForCoordinatorConnection(); 20 | auto* transport_manager = unit->CreateTransportManager(); 21 | basis::CreateLogHandler(*transport_manager); 22 | 23 | unit->Initialize(); 24 | 25 | while (true) { 26 | unit->Update(nullptr, basis::core::Duration::FromSecondsNanoseconds(1, 0)); 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /docker/build-env.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | SCRIPT_DIR=$(cd $(dirname $0); pwd) 3 | BASIS_ROOT=$SCRIPT_DIR/.. 4 | 5 | 6 | BASIS_ENABLE_ROS=${BASIS_ENABLE_ROS:-1} 7 | echo "BASIS_ENABLE_ROS: ${BASIS_ENABLE_ROS}" 8 | 9 | docker build --tag basis-env --target basis-env -f $BASIS_ROOT/docker/Dockerfile $@ $BASIS_ROOT 10 | 11 | if [[ "${BASIS_ENABLE_ROS}" = 1 ]]; then 12 | docker build --tag basis-env-ros --target basis-env-ros -f $BASIS_ROOT/docker/Dockerfile $@ $BASIS_ROOT 13 | fi -------------------------------------------------------------------------------- /docker/run-env.sh: -------------------------------------------------------------------------------- 1 | SCRIPT_DIR=$(cd $(dirname $0); pwd) 2 | BASIS_ROOT=$SCRIPT_DIR/.. 3 | 4 | if [ "$(docker ps -a -q -f name=^/basis$)" ]; then 5 | docker exec -it basis /bin/bash $@ 6 | else 7 | # Note: this relies on macos specific user mapping magic to mount with the proper permissions 8 | docker run $BASIS_DOCKER_ADDITIONAL_ARGS -v $BASIS_ROOT:/basis -v $BASIS_ROOT/../deterministic_replay:/deterministic_replay --privileged --name basis --rm -it basis-env-ros /bin/bash $@ 9 | fi -------------------------------------------------------------------------------- /docs/bug light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /proto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(basis_proto) 2 | include(FindProtobuf) 3 | find_package(Protobuf REQUIRED) 4 | 5 | add_library(basis_proto SHARED 6 | transport.proto 7 | time.proto 8 | test.proto 9 | basis_example.proto) 10 | target_link_libraries(basis_proto basis::plugins::serialization::protobuf) 11 | 12 | set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") 13 | 14 | set(BASIS_GENERATED_DIR "${GENERATED_DIR}/basis_proto") 15 | make_directory(${BASIS_GENERATED_DIR}) 16 | target_include_directories(basis_proto SYSTEM PUBLIC "$") 17 | 18 | protobuf_generate( 19 | LANGUAGE cpp 20 | TARGET basis_proto 21 | PROTOC_OUT_DIR "${BASIS_GENERATED_DIR}" 22 | ) 23 | 24 | # Import the foxglove protobuf files 25 | FetchContent_Declare(foxglove_schemas 26 | GIT_REPOSITORY https://github.com/foxglove/schemas 27 | GIT_TAG 446d4f0c7450c0b9fb1c5b3889c2ea640ee045e6 28 | CONFIGURE_COMMAND "" 29 | BUILD_COMMAND "" 30 | ) 31 | 32 | FetchContent_MakeAvailable(foxglove_schemas) 33 | 34 | set(FOXGLOVE_GENERATED_DIR "${GENERATED_DIR}") 35 | make_directory(${FOXGLOVE_GENERATED_DIR}) 36 | 37 | 38 | set(FOXGLOVE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 39 | # Need to copy in the foxglove protobufs to get protoc to be happy about paths - everything breaks if the protos aren't in a directory relative to the current one 40 | file(COPY ${foxglove_schemas_SOURCE_DIR}/schemas/proto/foxglove DESTINATION ${FOXGLOVE_SOURCE_DIR}) 41 | file(GLOB FOXGLOVE_PROTOS ${FOXGLOVE_SOURCE_DIR}/foxglove/*.proto) 42 | 43 | add_library(foxglove_schemas_protobuf SHARED ${FOXGLOVE_PROTOS}) 44 | 45 | protobuf_generate( 46 | LANGUAGE cpp 47 | TARGET foxglove_schemas_protobuf 48 | OUT_VAR FOOBAR 49 | PROTOC_OUT_DIR ${FOXGLOVE_GENERATED_DIR} 50 | IMPORT_DIRS ${FOXGLOVE_SOURCE_DIR} 51 | ) 52 | 53 | target_include_directories(foxglove_schemas_protobuf SYSTEM PUBLIC "$") -------------------------------------------------------------------------------- /proto/basis_example.proto: -------------------------------------------------------------------------------- 1 | syntax= "proto3"; 2 | 3 | message ExampleVector { 4 | double x = 1; 5 | double y = 2; 6 | double z = 3; 7 | } 8 | 9 | message ExampleStampedVector { 10 | double time = 1; 11 | ExampleVector pos = 2; 12 | } 13 | 14 | message TimeTest { 15 | double time = 1; 16 | } 17 | 18 | message SyncedVectorLidarEvent { 19 | double time_vector = 1; 20 | double time_lidar = 2; 21 | } -------------------------------------------------------------------------------- /proto/test.proto: -------------------------------------------------------------------------------- 1 | syntax= "proto3"; 2 | 3 | message TestEmptyEvent { 4 | 5 | } 6 | 7 | message TestExampleMessage { 8 | string name = 1; 9 | int32 id = 2; 10 | string email = 3; 11 | } 12 | 13 | message TestProtoStruct { 14 | uint32 foo = 1; 15 | float bar = 2; 16 | string baz = 3; 17 | }; 18 | 19 | message ReferencedMessage { 20 | string data = 1; 21 | } 22 | 23 | message InnerSyncTestStruct { 24 | uint32 stamp = 1; 25 | } 26 | 27 | message OuterSyncTestStruct { 28 | InnerSyncTestStruct header = 1; 29 | } 30 | 31 | enum EnumTest { 32 | CORPUS_UNSPECIFIED = 0; 33 | CORPUS_UNIVERSAL = 1; 34 | CORPUS_WEB = 2; 35 | CORPUS_IMAGES = 3; 36 | CORPUS_LOCAL = 4; 37 | CORPUS_NEWS = 5; 38 | CORPUS_PRODUCTS = 6; 39 | CORPUS_VIDEO = 7; 40 | } 41 | 42 | message SchemaTestMessage { 43 | message EmbeddedMessage { 44 | string url = 1; 45 | string title = 2; 46 | repeated string snippets = 3; 47 | } 48 | 49 | ReferencedMessage referenced = 1; 50 | repeated uint32 repeated_u32 = 2; 51 | EnumTest an_enum = 3; 52 | EmbeddedMessage embedded = 4; 53 | string some_string = 5; 54 | } -------------------------------------------------------------------------------- /proto/time.proto: -------------------------------------------------------------------------------- 1 | syntax= "proto3"; 2 | 3 | package basis.core.transport.proto; 4 | 5 | message Time { 6 | int64 nsecs = 1; // The current simulated time 7 | uint64 run_token = 2; // Changes every time a replay or simulation starts 8 | } -------------------------------------------------------------------------------- /proto/transport.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package basis.core.transport.proto; 4 | 5 | // Common 6 | 7 | message MessageSchema { 8 | string serializer = 1; 9 | string name = 2; 10 | string schema = 3; 11 | string hash_id = 4; 12 | bytes schema_efficient = 5; 13 | } 14 | 15 | message PublisherInfo { 16 | uint64 publisher_id_high = 1; 17 | uint64 publisher_id_low = 2; 18 | // TODO: type 19 | // TODO: this results in a lot of duplication of topic names, but its probably ok - topic names are mostly unique 20 | string topic = 3; 21 | string schema_id = 4; 22 | map transport_info = 5; 23 | } 24 | 25 | // TransportManager -> Coordinator 26 | message TransportManagerInfo { 27 | // todo we should consider letting these also be mapped, to simplify things 28 | repeated PublisherInfo publishers = 1; 29 | } 30 | 31 | // Coordinator -> TransportManager 32 | message RepeatedPublisherInfo { 33 | repeated PublisherInfo publishers = 1; 34 | } 35 | 36 | /* 37 | // todo not implemented on either side 38 | message PublishersDelta { 39 | map added_publishers = 1; 40 | map removed_publishers = 2; 41 | } 42 | */ 43 | message NetworkInfo { 44 | map publishers_by_topic = 1; 45 | } 46 | 47 | message MessageSchemas { 48 | repeated MessageSchema schemas = 1; 49 | } 50 | 51 | message RequestSchemas { 52 | repeated string schema_ids = 1; 53 | } 54 | 55 | message ClientToCoordinatorMessage { 56 | oneof PossibleMessages { 57 | //string error = 1; 58 | TransportManagerInfo transport_manager_info = 2; 59 | MessageSchemas schemas = 3; 60 | RequestSchemas request_schemas = 4; 61 | } 62 | } 63 | message CoordinatorMessage { 64 | oneof PossibleMessages { 65 | string error = 1; 66 | NetworkInfo network_info = 2; 67 | MessageSchemas schemas = 3; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /python/unit/templates/create_unit.cpp.j2: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include <{{unit_name}}.h> 4 | 5 | #include 6 | 7 | extern "C" { 8 | 9 | basis::Unit* CreateUnit(const std::optional& unit_name_override, const basis::arguments::CommandLineTypes& command_line, basis::CreateUnitLoggerInterface error_logger) { 10 | return basis::unit::CreateUnit<{{unit_name}}>(unit_name_override, command_line, error_logger, "{{unit_name}}"); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /python/unit/templates/unit.cpp.j2: -------------------------------------------------------------------------------- 1 | /* 2 | {% if in_template_dir %} 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | {% else %} 7 | This is the starting point for your Unit. Edit this directly and implement the missing methods! 8 | {% endif %} 9 | */ 10 | 11 | #include <{{unit_name}}.h> 12 | 13 | using namespace unit::{{unit_name}}; 14 | 15 | {% for handler_name in handlers %} 16 | {{handler_name}}::Output {{unit_name}}::{{handler_name}}(const {{handler_name}}::Input& input) { 17 | static_assert(false, "Implement me"); 18 | } 19 | {% endfor %} -------------------------------------------------------------------------------- /python/unit/templates/unit.h.j2: -------------------------------------------------------------------------------- 1 | /* 2 | {% if in_template_dir %} 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | {% else %} 7 | This is the starting point for your Unit. Edit this directly and implement the missing methods! 8 | {% endif %} 9 | */ 10 | #include 11 | 12 | class {{unit_name}} : public unit::{{unit_name}}::Base { 13 | public: 14 | {{unit_name}}(const Args& args, const std::optional& name_override = {}) 15 | : unit::{{unit_name}}::Base(args, name_override) 16 | {} 17 | 18 | {% for handler_name in handlers %} 19 | virtual unit::{{unit_name}}::{{handler_name}}::Output 20 | {{handler_name}}(const unit::{{unit_name}}::{{handler_name}}::Input &input) override; 21 | {% endfor %} 22 | }; -------------------------------------------------------------------------------- /python/unit/test_validate_unit.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | import jsonschema 3 | 4 | SCHEMA_PATH = "/basis/unit/schema.yaml" 5 | UNIT_PATH = "/basis/unit/example/example.yaml" 6 | 7 | 8 | with open(SCHEMA_PATH) as f: 9 | schema = yaml.safe_load(f) 10 | 11 | with open(UNIT_PATH) as f: 12 | unit = yaml.safe_load(f) 13 | jsonschema.validate(instance=unit, schema=schema) 14 | 15 | 16 | -------------------------------------------------------------------------------- /unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB V_GLOB LIST_DIRECTORIES true "*") 2 | foreach(item ${V_GLOB}) 3 | if(IS_DIRECTORY ${item}) 4 | add_subdirectory(${item}) 5 | endif() 6 | endforeach() 7 | -------------------------------------------------------------------------------- /unit/example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Unit for enumerating all planned features - will likely not compile 2 | 3 | include(Unit) 4 | 5 | # generate_unit(example) -------------------------------------------------------------------------------- /unit/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # WIP unit for adding new features 2 | if(${BASIS_ENABLE_ROS} AND ${BASIS_ENABLE_TESTING}) 3 | include(Unit) 4 | 5 | find_package(sensor_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/sensor_msgs/cmake) 6 | 7 | generate_unit(test_unit) 8 | 9 | # TODO: query the yaml for the types 10 | target_link_libraries(unit_test_unit basis::plugins::serialization::rosmsg rostime) 11 | 12 | add_subdirectory(test) 13 | endif() -------------------------------------------------------------------------------- /unit/test/include/time_test_inproc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "basis_example.pb.h" 3 | 4 | struct TimeTestInproc { 5 | basis::core::MonotonicTime time; 6 | 7 | mutable bool ran_conversion_function = false; 8 | 9 | std::shared_ptr ToMessage() const { 10 | ran_conversion_function = true; 11 | return std::make_shared(); 12 | } 13 | }; -------------------------------------------------------------------------------- /unit/test/template/test_unit.example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | 7 | */ 8 | 9 | #include 10 | 11 | using namespace unit::test_unit; 12 | 13 | 14 | StereoMatch::Output test_unit::StereoMatch(const StereoMatch::Input& input) { 15 | static_assert(false, "Implement me"); 16 | } 17 | 18 | AllTest::Output test_unit::AllTest(const AllTest::Input& input) { 19 | static_assert(false, "Implement me"); 20 | } 21 | 22 | ApproxTest::Output test_unit::ApproxTest(const ApproxTest::Input& input) { 23 | static_assert(false, "Implement me"); 24 | } 25 | 26 | TestEqualOptions::Output test_unit::TestEqualOptions(const TestEqualOptions::Input& input) { 27 | static_assert(false, "Implement me"); 28 | } 29 | 30 | TestInprocTypePub::Output test_unit::TestInprocTypePub(const TestInprocTypePub::Input& input) { 31 | static_assert(false, "Implement me"); 32 | } 33 | 34 | TestInprocTypeSubEither::Output test_unit::TestInprocTypeSubEither(const TestInprocTypeSubEither::Input& input) { 35 | static_assert(false, "Implement me"); 36 | } 37 | 38 | TestInprocTypeSubOnlyMessage::Output test_unit::TestInprocTypeSubOnlyMessage(const TestInprocTypeSubOnlyMessage::Input& input) { 39 | static_assert(false, "Implement me"); 40 | } 41 | 42 | TestInprocTypeSubOnlyInproc::Output test_unit::TestInprocTypeSubOnlyInproc(const TestInprocTypeSubOnlyInproc::Input& input) { 43 | static_assert(false, "Implement me"); 44 | } 45 | 46 | TestInprocTypeSubAccumulate::Output test_unit::TestInprocTypeSubAccumulate(const TestInprocTypeSubAccumulate::Input& input) { 47 | static_assert(false, "Implement me"); 48 | } 49 | 50 | TestInprocTypeSubAvoidPointlessConversion::Output test_unit::TestInprocTypeSubAvoidPointlessConversion(const TestInprocTypeSubAvoidPointlessConversion::Input& input) { 51 | static_assert(false, "Implement me"); 52 | } 53 | -------------------------------------------------------------------------------- /unit/test/template/test_unit.example.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | 7 | */ 8 | #include 9 | 10 | class test_unit : public unit::test_unit::Base { 11 | public: 12 | test_unit(const Args& args, const std::optional& name_override = {}) 13 | : unit::test_unit::Base(name_override) 14 | {} 15 | 16 | 17 | virtual unit::test_unit::StereoMatch::Output 18 | StereoMatch(const unit::test_unit::StereoMatch::Input &input) override; 19 | 20 | virtual unit::test_unit::AllTest::Output 21 | AllTest(const unit::test_unit::AllTest::Input &input) override; 22 | 23 | virtual unit::test_unit::ApproxTest::Output 24 | ApproxTest(const unit::test_unit::ApproxTest::Input &input) override; 25 | 26 | virtual unit::test_unit::TestEqualOptions::Output 27 | TestEqualOptions(const unit::test_unit::TestEqualOptions::Input &input) override; 28 | 29 | virtual unit::test_unit::TestInprocTypePub::Output 30 | TestInprocTypePub(const unit::test_unit::TestInprocTypePub::Input &input) override; 31 | 32 | virtual unit::test_unit::TestInprocTypeSubEither::Output 33 | TestInprocTypeSubEither(const unit::test_unit::TestInprocTypeSubEither::Input &input) override; 34 | 35 | virtual unit::test_unit::TestInprocTypeSubOnlyMessage::Output 36 | TestInprocTypeSubOnlyMessage(const unit::test_unit::TestInprocTypeSubOnlyMessage::Input &input) override; 37 | 38 | virtual unit::test_unit::TestInprocTypeSubOnlyInproc::Output 39 | TestInprocTypeSubOnlyInproc(const unit::test_unit::TestInprocTypeSubOnlyInproc::Input &input) override; 40 | 41 | virtual unit::test_unit::TestInprocTypeSubAccumulate::Output 42 | TestInprocTypeSubAccumulate(const unit::test_unit::TestInprocTypeSubAccumulate::Input &input) override; 43 | 44 | virtual unit::test_unit::TestInprocTypeSubAvoidPointlessConversion::Output 45 | TestInprocTypeSubAvoidPointlessConversion(const unit::test_unit::TestInprocTypeSubAvoidPointlessConversion::Input &input) override; 46 | 47 | }; -------------------------------------------------------------------------------- /unit/test/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable( 3 | test_unit_generation 4 | test_unit_generation.cpp 5 | ) 6 | 7 | target_link_libraries( 8 | test_unit_generation 9 | unit::test_unit 10 | GTest::gtest_main 11 | ) 12 | 13 | include(GoogleTest REQUIRED) 14 | gtest_discover_tests(test_unit_generation) -------------------------------------------------------------------------------- /unit/wip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # WIP unit for adding new features 2 | if(${BASIS_ENABLE_ROS}) 3 | include(Unit) 4 | 5 | find_package(sensor_msgs REQUIRED PATHS ${BASIS_ROS_ROOT}/share/sensor_msgs/cmake) 6 | 7 | generate_unit(wip) 8 | 9 | # TODO: query the yaml for the types 10 | target_link_libraries(unit_wip basis::plugins::serialization::rosmsg rostime) 11 | endif() -------------------------------------------------------------------------------- /unit/wip/include/wip.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is the starting point for your Unit. Edit this directly and implement the 4 | missing methods! 5 | 6 | */ 7 | #include 8 | 9 | class wip : public unit::wip::Base { 10 | public: 11 | wip(const unit::wip::Args &args, 12 | const std::optional &unit_name_override = {}) 13 | : unit::wip::Base(args, unit_name_override) {} 14 | 15 | virtual unit::wip::StereoMatch::Output 16 | StereoMatch(const unit::wip::StereoMatch::Input &input) override; 17 | 18 | virtual unit::wip::TimeTest::Output 19 | TimeTest(const unit::wip::TimeTest::Input &input) override; 20 | 21 | virtual unit::wip::ApproxTest::Output 22 | ApproxTest(const unit::wip::ApproxTest::Input &input) override; 23 | }; -------------------------------------------------------------------------------- /unit/wip/src/wip.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This is the starting point for your Unit. Edit this directly and implement the 4 | missing methods! 5 | 6 | */ 7 | 8 | #include 9 | 10 | using namespace unit::wip; 11 | 12 | StereoMatch::Output wip::StereoMatch(const StereoMatch::Input &input) { 13 | StereoMatch::Output out; 14 | return out; 15 | } 16 | 17 | unit::wip::TimeTest::Output 18 | wip::TimeTest(const unit::wip::TimeTest::Input &input) { 19 | unit::wip::TimeTest::Output out; 20 | BASIS_LOG_INFO("Got timetest"); 21 | out.time_test_forwarded_2 = input.time_test_forwarded; 22 | return out; 23 | } 24 | 25 | ApproxTest::Output wip::ApproxTest(const ApproxTest::Input &input) { 26 | BASIS_LOG_INFO("Got approximate output"); 27 | return {}; 28 | } 29 | -------------------------------------------------------------------------------- /unit/wip/template/wip.example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | 7 | */ 8 | 9 | #include 10 | 11 | using namespace unit::wip; 12 | 13 | 14 | StereoMatch::Output wip::StereoMatch(const StereoMatch::Input& input) { 15 | static_assert(false, "Implement me"); 16 | } 17 | 18 | TimeTest::Output wip::TimeTest(const TimeTest::Input& input) { 19 | static_assert(false, "Implement me"); 20 | } 21 | 22 | ApproxTest::Output wip::ApproxTest(const ApproxTest::Input& input) { 23 | static_assert(false, "Implement me"); 24 | } 25 | -------------------------------------------------------------------------------- /unit/wip/template/wip.example.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | DO NOT EDIT THIS FILE 4 | 5 | This is a template for use with your Unit, to use as a base, provided as an example. 6 | 7 | */ 8 | #include 9 | 10 | class wip : public unit::wip::Base { 11 | public: 12 | wip(const Args& args, const std::optional& name_override = {}) 13 | : unit::wip::Base(name_override) 14 | {} 15 | 16 | 17 | virtual unit::wip::StereoMatch::Output 18 | StereoMatch(const unit::wip::StereoMatch::Input &input) override; 19 | 20 | virtual unit::wip::TimeTest::Output 21 | TimeTest(const unit::wip::TimeTest::Input &input) override; 22 | 23 | virtual unit::wip::ApproxTest::Output 24 | ApproxTest(const unit::wip::ApproxTest::Input &input) override; 25 | 26 | }; -------------------------------------------------------------------------------- /unit/wip/wip.unit.yaml: -------------------------------------------------------------------------------- 1 | # TODO: don't do this, use the file name? 2 | # the default name of the unit 3 | # name: example 4 | 5 | # The ways handlers are allowed to interact with eachother 6 | threading_model: 7 | # single - by default all handlers run mutually exclusive from eachother 8 | # multi - by default all handlers run in parallel 9 | single 10 | cpp_includes: 11 | - sensor_msgs/Image.h 12 | - sensor_msgs/PointCloud2.h 13 | - test.pb.h 14 | - basis_example.pb.h 15 | 16 | handlers: 17 | # This handler looks for two image messages coming in with the same timestamp and outputs one stereo image 18 | StereoMatch: 19 | sync: 20 | # require exact field matching 21 | type: equal 22 | # buffer at most 2 messages on each channel before dropping 23 | buffer_size: 2 24 | inputs: 25 | /camera_left: 26 | # TODO: we may end up allowing the `rosmsg:` field to be dropped, either by doing a search in the schemas 27 | # or by allowing a default serializer to be specified 28 | type: rosmsg:sensor_msgs::Image 29 | # TODO: We may end up providing default sync_fields for some serializer or message types to reduce boilerplate 30 | sync_field: header 31 | /camera_right: 32 | type: rosmsg:sensor_msgs::Image 33 | sync_field: header 34 | outputs: 35 | /difference_image: 36 | type: rosmsg:sensor_msgs::Image 37 | TimeTest: 38 | sync: 39 | type: all 40 | inputs: 41 | /time_test: 42 | type: "protobuf: ::TimeTest" 43 | /time_test_forwarded: 44 | type: "protobuf: ::TimeTest" 45 | outputs: 46 | /time_test_forwarded_2: 47 | type: "protobuf: ::TimeTest" 48 | ApproxTest: 49 | sync: 50 | type: 51 | approximate: 0.01 52 | inputs: 53 | /stamped_vector: 54 | type: protobuf:ExampleStampedVector 55 | sync_field: ::time 56 | /point_cloud: 57 | type: rosmsg:sensor_msgs::PointCloud2 58 | sync_field: header.stamp.toSec() 59 | 60 | --------------------------------------------------------------------------------