Add LibCarla module
This commit is contained in:
parent
860dbe908e
commit
084fe6c0f6
|
@ -0,0 +1,19 @@
|
||||||
|
cmake_minimum_required(VERSION 3.9.0)
|
||||||
|
project(libcarla)
|
||||||
|
|
||||||
|
message(STATUS "Building ${PROJECT_NAME} version ${CARLA_VERSION}")
|
||||||
|
|
||||||
|
set(libcarla_source_path "${PROJECT_SOURCE_DIR}/../source")
|
||||||
|
|
||||||
|
include_directories(${libcarla_source_path})
|
||||||
|
|
||||||
|
configure_file(${libcarla_source_path}/carla/Version.h.in ${libcarla_source_path}/carla/Version.h)
|
||||||
|
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Client")
|
||||||
|
add_subdirectory("client")
|
||||||
|
elseif (CMAKE_BUILD_TYPE STREQUAL "Server")
|
||||||
|
add_subdirectory("server")
|
||||||
|
add_subdirectory("test")
|
||||||
|
else ()
|
||||||
|
message(FATAL_ERROR "Unknown build type '${CMAKE_BUILD_TYPE}'")
|
||||||
|
endif ()
|
|
@ -0,0 +1,42 @@
|
||||||
|
cmake_minimum_required(VERSION 3.9.0)
|
||||||
|
project(libcarla-client)
|
||||||
|
|
||||||
|
# Install rpclib.
|
||||||
|
install(DIRECTORY "${RPCLIB_INCLUDE_PATH}/rpc" DESTINATION include)
|
||||||
|
install(FILES "${RPCLIB_LIB_PATH}/librpc.a" DESTINATION lib)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE libcarla_client_sources
|
||||||
|
"${libcarla_source_path}/carla/client/*.h"
|
||||||
|
"${libcarla_source_path}/carla/client/*.cpp")
|
||||||
|
|
||||||
|
# Create targets for debug and release in the same build type.
|
||||||
|
foreach(target carla_client_debug carla_client)
|
||||||
|
add_library(${target} STATIC ${libcarla_client_sources})
|
||||||
|
|
||||||
|
target_include_directories(${target} PRIVATE
|
||||||
|
"${BOOST_INCLUDE_PATH}"
|
||||||
|
"${RPCLIB_INCLUDE_PATH}")
|
||||||
|
|
||||||
|
install(TARGETS ${target} DESTINATION lib)
|
||||||
|
endforeach(target)
|
||||||
|
|
||||||
|
# Specific options for debug.
|
||||||
|
set_target_properties(carla_client_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
|
||||||
|
target_compile_definitions(carla_client_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||||
|
|
||||||
|
# Specific options for release.
|
||||||
|
set_target_properties(carla_client PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
|
||||||
|
|
||||||
|
# Install headers.
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_headers "${libcarla_source_path}/carla/*.h")
|
||||||
|
install(FILES ${libcarla_carla_headers} DESTINATION include/carla)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_client_headers "${libcarla_source_path}/carla/client/*.h")
|
||||||
|
install(FILES ${libcarla_carla_client_headers} DESTINATION include/carla/client)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_rpc_headers "${libcarla_source_path}/carla/rpc/*.h")
|
||||||
|
install(FILES ${libcarla_carla_rpc_headers} DESTINATION include/carla/rpc)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_streaming_headers "${libcarla_source_path}/carla/streaming/*.h")
|
||||||
|
install(FILES ${libcarla_carla_streaming_headers} DESTINATION include/carla/streaming)
|
|
@ -0,0 +1,25 @@
|
||||||
|
cmake_minimum_required(VERSION 3.9.0)
|
||||||
|
project(libcarla-server)
|
||||||
|
|
||||||
|
# Install libc++ shared libraries.
|
||||||
|
file(GLOB LibCXX_Libraries "${LLVM_LIB_PATH}/libc++*")
|
||||||
|
install(FILES ${LibCXX_Libraries} DESTINATION lib)
|
||||||
|
|
||||||
|
# Install rpclib.
|
||||||
|
install(DIRECTORY "${RPCLIB_INCLUDE_PATH}/rpc" DESTINATION include)
|
||||||
|
install(FILES "${RPCLIB_LIB_PATH}/librpc.a" DESTINATION lib)
|
||||||
|
|
||||||
|
# Install headers.
|
||||||
|
|
||||||
|
install(DIRECTORY "${libcarla_source_path}/compiler" DESTINATION include)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_headers "${libcarla_source_path}/carla/*.h")
|
||||||
|
install(FILES ${libcarla_carla_headers} DESTINATION include/carla)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_rpc_headers "${libcarla_source_path}/carla/rpc/*.h")
|
||||||
|
install(FILES ${libcarla_carla_rpc_headers} DESTINATION include/carla/rpc)
|
||||||
|
|
||||||
|
file(GLOB libcarla_carla_streaming_headers "${libcarla_source_path}/carla/streaming/*.h")
|
||||||
|
install(FILES ${libcarla_carla_streaming_headers} DESTINATION include/carla/streaming)
|
||||||
|
|
||||||
|
install(DIRECTORY "${BOOST_INCLUDE_PATH}/boost" DESTINATION include)
|
|
@ -0,0 +1,36 @@
|
||||||
|
cmake_minimum_required(VERSION 3.9.0)
|
||||||
|
project(libcarla-unit-tests)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE libcarla_test_sources
|
||||||
|
"${libcarla_source_path}/carla/profiler/*.h"
|
||||||
|
"${libcarla_source_path}/carla/profiler/*.cpp"
|
||||||
|
"${libcarla_source_path}/test/*.h"
|
||||||
|
"${libcarla_source_path}/test/*.cpp")
|
||||||
|
|
||||||
|
link_directories(
|
||||||
|
${RPCLIB_LIB_PATH}
|
||||||
|
${GTEST_LIB_PATH})
|
||||||
|
|
||||||
|
# Create targets for debug and release in the same build type.
|
||||||
|
foreach(target libcarla_test_debug libcarla_test_release)
|
||||||
|
add_executable(${target} ${libcarla_test_sources})
|
||||||
|
|
||||||
|
target_compile_definitions(${target} PUBLIC
|
||||||
|
-DLIBCARLA_ENABLE_PROFILER
|
||||||
|
-DLIBCARLA_WITH_GTEST)
|
||||||
|
|
||||||
|
target_include_directories(${target} PRIVATE
|
||||||
|
"${BOOST_INCLUDE_PATH}"
|
||||||
|
"${RPCLIB_INCLUDE_PATH}"
|
||||||
|
"${GTEST_INCLUDE_PATH}")
|
||||||
|
target_link_libraries(${target} "-lrpc -lgtest_main -lgtest")
|
||||||
|
|
||||||
|
install(TARGETS ${target} DESTINATION test)
|
||||||
|
endforeach(target)
|
||||||
|
|
||||||
|
# Specific options for debug.
|
||||||
|
set_target_properties(libcarla_test_debug PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_DEBUG})
|
||||||
|
target_compile_definitions(libcarla_test_debug PUBLIC -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING)
|
||||||
|
|
||||||
|
# Specific options for release.
|
||||||
|
set_target_properties(libcarla_test_release PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
|
|
@ -0,0 +1 @@
|
||||||
|
Version.h
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
# include <cassert>
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# define DEBUG_ONLY(code)
|
||||||
|
#else
|
||||||
|
# define DEBUG_ONLY(code) code
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
#define DEBUG_ASSERT(predicate) DEBUG_ONLY(assert(predicate));
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_WITH_GTEST
|
||||||
|
# include <gtest/gtest.h>
|
||||||
|
|
||||||
|
# define DEBUG_ASSERT_EQ(lhs, rhs) DEBUG_ONLY(EXPECT_EQ(lhs, rhs));DEBUG_ASSERT(lhs == rhs);
|
||||||
|
# define DEBUG_ASSERT_NE(lhs, rhs) DEBUG_ONLY(EXPECT_NE(lhs, rhs));DEBUG_ASSERT(lhs != rhs);
|
||||||
|
#else
|
||||||
|
# define DEBUG_ASSERT_EQ(lhs, rhs) DEBUG_ASSERT((lhs) == (rhs))
|
||||||
|
# define DEBUG_ASSERT_NE(lhs, rhs) DEBUG_ASSERT((lhs) != (rhs))
|
||||||
|
#endif // LIBCARLA_WITH_GTEST
|
|
@ -0,0 +1,148 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define LIBCARLA_LOG_LEVEL_DEBUG 10
|
||||||
|
#define LIBCARLA_LOG_LEVEL_INFO 20
|
||||||
|
#define LIBCARLA_LOG_LEVEL_WARNING 30
|
||||||
|
#define LIBCARLA_LOG_LEVEL_ERROR 40
|
||||||
|
#define LIBCARLA_LOG_LEVEL_CRITICAL 50
|
||||||
|
#define LIBCARLA_LOG_LEVEL_NONE 100
|
||||||
|
|
||||||
|
#ifndef LIBCARLA_LOG_LEVEL
|
||||||
|
# ifdef NDEBUG
|
||||||
|
# define LIBCARLA_LOG_LEVEL LIBCARLA_LOG_LEVEL_WARNING
|
||||||
|
# else
|
||||||
|
# define LIBCARLA_LOG_LEVEL LIBCARLA_LOG_LEVEL_INFO
|
||||||
|
# endif // NDEBUG
|
||||||
|
#endif // LIBCARLA_LOG_LEVEL
|
||||||
|
|
||||||
|
// The following log functions are available, they are only active if
|
||||||
|
// LIBCARLA_LOG_LEVEL is greater equal the function's log level.
|
||||||
|
//
|
||||||
|
// * log_debug
|
||||||
|
// * log_info
|
||||||
|
// * log_error
|
||||||
|
// * log_critical
|
||||||
|
//
|
||||||
|
// And macros
|
||||||
|
//
|
||||||
|
// * LOG_DEBUG_ONLY(/* code here */)
|
||||||
|
// * LOG_INFO_ONLY(/* code here */)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// -- Implementation of log functions ------------------------------------------
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
|
||||||
|
namespace logging {
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/27375675
|
||||||
|
template <typename Arg, typename ... Args>
|
||||||
|
static void write_to_stream(std::ostream &out, Arg &&arg, Args && ... args) {
|
||||||
|
out << std::boolalpha << std::forward<Arg>(arg);
|
||||||
|
using expander = int[];
|
||||||
|
(void) expander{0, (void(out << ' ' << std::forward<Args>(args)), 0) ...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cout, std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace logging
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_debug(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cout, "DEBUG:", std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_debug(Args && ...) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_INFO
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_info(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cout, "INFO: ", std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_info(Args && ...) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_WARNING
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_warning(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cerr, "WARNING:", std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_warning(Args && ...) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_ERROR
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_error(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cerr, "ERROR:", std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_error(Args && ...) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_CRITICAL
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_critical(Args && ... args) {
|
||||||
|
logging::write_to_stream(std::cerr, "CRITICAL:", std::forward<Args>(args) ..., '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
static inline void log_critical(Args && ...) {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace carla
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// -- Implementation of macros -------------------------------------------------
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_DEBUG
|
||||||
|
# define LOG_DEBUG_ONLY(code) code
|
||||||
|
#else
|
||||||
|
# define LOG_DEBUG_ONLY(code)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LIBCARLA_LOG_LEVEL <= LIBCARLA_LOG_LEVEL_INFO
|
||||||
|
# define LOG_INFO_ONLY(code) code
|
||||||
|
#else
|
||||||
|
# define LOG_INFO_ONLY(code)
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
|
||||||
|
class NonCopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
NonCopyable() = default;
|
||||||
|
|
||||||
|
NonCopyable(const NonCopyable &) = delete;
|
||||||
|
|
||||||
|
void operator=(const NonCopyable &x) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
|
||||||
|
template <typename CLOCK>
|
||||||
|
class StopWatchTmpl {
|
||||||
|
static_assert(CLOCK::is_steady, "The StopWatch's clock must be steady");
|
||||||
|
public:
|
||||||
|
|
||||||
|
using clock = CLOCK;
|
||||||
|
|
||||||
|
StopWatchTmpl() : _start(clock::now()), _end(), _is_running(true) {}
|
||||||
|
|
||||||
|
void Restart() {
|
||||||
|
_is_running = true;
|
||||||
|
_start = clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
_end = clock::now();
|
||||||
|
_is_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename clock::duration GetDuration() const {
|
||||||
|
return _is_running ? clock::now() - _start : _end - _start;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RESOLUTION=std::chrono::milliseconds>
|
||||||
|
typename RESOLUTION::rep GetElapsedTime() const {
|
||||||
|
return std::chrono::duration_cast<RESOLUTION>(GetDuration()).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning() const {
|
||||||
|
return _is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typename clock::time_point _start;
|
||||||
|
|
||||||
|
typename clock::time_point _end;
|
||||||
|
|
||||||
|
bool _is_running;
|
||||||
|
};
|
||||||
|
|
||||||
|
using StopWatch = StopWatchTmpl<std::chrono::steady_clock>;
|
||||||
|
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
|
||||||
|
class ThreadGroup {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ThreadGroup() {}
|
||||||
|
|
||||||
|
ThreadGroup(const ThreadGroup &) = delete;
|
||||||
|
ThreadGroup &operator=(const ThreadGroup &) = delete;
|
||||||
|
|
||||||
|
~ThreadGroup() {
|
||||||
|
JoinAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void CreateThread(F &&functor) {
|
||||||
|
_threads.emplace_back(std::forward<F>(functor));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void CreateThreads(size_t count, F functor) {
|
||||||
|
_threads.reserve(_threads.size() + count);
|
||||||
|
for (size_t i = 0u; i < count; ++i) {
|
||||||
|
CreateThread(functor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JoinAll() {
|
||||||
|
for (auto &thread : _threads) {
|
||||||
|
if (thread.joinable()) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<std::thread> _threads;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
|
||||||
|
constexpr const char *version() {
|
||||||
|
return "${CARLA_VERSION}";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/NonCopyable.h"
|
||||||
|
#include "carla/client/Control.h"
|
||||||
|
#include "carla/client/Memory.h"
|
||||||
|
#include "carla/client/World.h"
|
||||||
|
#include "carla/rpc/Actor.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
class Actor
|
||||||
|
: public EnableSharedFromThis<Actor>,
|
||||||
|
private NonCopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Actor(Actor &&) = default;
|
||||||
|
Actor &operator=(Actor &&) = default;
|
||||||
|
|
||||||
|
auto GetId() const {
|
||||||
|
return _actor.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &GetTypeId() const {
|
||||||
|
return _actor.blueprint.type_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ActorBlueprint GetBlueprint() const {
|
||||||
|
return _actor.blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<World> GetWorld() const {
|
||||||
|
return _world;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyControl(const VehicleControl &control) {
|
||||||
|
_world->ApplyControlToActor(*this, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &Serialize() const {
|
||||||
|
return _actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Client;
|
||||||
|
|
||||||
|
Actor(carla::rpc::Actor actor, SharedPtr<World> world)
|
||||||
|
: _actor(actor),
|
||||||
|
_world(std::move(world)) {
|
||||||
|
DEBUG_ASSERT(_world != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
carla::rpc::Actor _actor;
|
||||||
|
|
||||||
|
SharedPtr<World> _world;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "carla/client/ActorBlueprint.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <shlwapi.h>
|
||||||
|
#else
|
||||||
|
# include <fnmatch.h>
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
static bool MatchWildcards(const std::string &str, const std::string &test) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return PathMatchSpecA(str.c_str(), test.c_str());
|
||||||
|
#else
|
||||||
|
return 0 == fnmatch(test.c_str(), str.c_str(), 0);
|
||||||
|
#endif // _WIN32
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActorBlueprint::StartsWith(const std::string &test) const {
|
||||||
|
return boost::starts_with(GetTypeId(), test);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActorBlueprint::MatchWildcards(const std::string &test) const {
|
||||||
|
return ::carla::client::MatchWildcards(GetTypeId(), test);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/rpc/ActorBlueprint.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class ActorBlueprint {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ActorBlueprint(carla::rpc::ActorBlueprint blueprint)
|
||||||
|
: _blueprint(std::move(blueprint)) {}
|
||||||
|
|
||||||
|
ActorBlueprint(const ActorBlueprint &) = default;
|
||||||
|
ActorBlueprint(ActorBlueprint &&) = default;
|
||||||
|
ActorBlueprint &operator=(const ActorBlueprint &) = default;
|
||||||
|
ActorBlueprint &operator=(ActorBlueprint &&) = default;
|
||||||
|
|
||||||
|
const std::string &GetTypeId() const {
|
||||||
|
return _blueprint.type_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StartsWith(const std::string &test) const;
|
||||||
|
|
||||||
|
bool MatchWildcards(const std::string &test) const;
|
||||||
|
|
||||||
|
const auto &Serialize() const {
|
||||||
|
return _blueprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
carla::rpc::ActorBlueprint _blueprint;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/NonCopyable.h"
|
||||||
|
#include "carla/client/ActorBlueprint.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
class BlueprintLibrary /*: private NonCopyable*/ {
|
||||||
|
using list_type = std::vector<ActorBlueprint>;
|
||||||
|
public:
|
||||||
|
|
||||||
|
// BlueprintLibrary() = default;
|
||||||
|
|
||||||
|
// BlueprintLibrary(BlueprintLibrary &&) = default;
|
||||||
|
// BlueprintLibrary &operator=(BlueprintLibrary &&) = default;
|
||||||
|
|
||||||
|
using value_type = list_type::value_type;
|
||||||
|
using size_type = list_type::size_type;
|
||||||
|
using const_iterator = list_type::const_iterator;
|
||||||
|
using const_reference = list_type::const_reference;
|
||||||
|
|
||||||
|
BlueprintLibrary Filter(const std::string &wildcard_pattern) const {
|
||||||
|
list_type result;
|
||||||
|
std::copy_if(begin(), end(), std::back_inserter(result), [&](const auto &x) {
|
||||||
|
return x.MatchWildcards(wildcard_pattern);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference operator[](size_type pos) const {
|
||||||
|
return _blueprints[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const /*noexcept*/ {
|
||||||
|
return _blueprints.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const /*noexcept*/ {
|
||||||
|
return _blueprints.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const /*noexcept*/ {
|
||||||
|
return _blueprints.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const /*noexcept*/ {
|
||||||
|
return _blueprints.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Client;
|
||||||
|
|
||||||
|
BlueprintLibrary(list_type blueprints)
|
||||||
|
: _blueprints(std::move(blueprints)) {}
|
||||||
|
|
||||||
|
BlueprintLibrary(const std::vector<carla::rpc::ActorBlueprint> &blueprints)
|
||||||
|
: _blueprints(blueprints.begin(), blueprints.end()) {}
|
||||||
|
|
||||||
|
list_type _blueprints;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "carla/client/Client.h"
|
||||||
|
|
||||||
|
#include "carla/client/Actor.h"
|
||||||
|
#include "carla/client/Control.h"
|
||||||
|
#include "carla/client/World.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
SharedPtr<World> Client::GetWorld() {
|
||||||
|
if (_active_world == nullptr) {
|
||||||
|
_active_world.reset(new World(shared_from_this()));
|
||||||
|
}
|
||||||
|
return _active_world;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Actor> Client::SpawnActor(
|
||||||
|
const ActorBlueprint &blueprint,
|
||||||
|
const Transform &transform) {
|
||||||
|
auto actor = Call<carla::rpc::Actor>("spawn_actor", blueprint.Serialize(), transform);
|
||||||
|
return SharedPtr<Actor>(new Actor{actor, GetWorld()});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::ApplyControlToActor(
|
||||||
|
const Actor &actor,
|
||||||
|
const VehicleControl &control) {
|
||||||
|
_client.call("apply_control_to_actor", actor.Serialize(), control);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/NonCopyable.h"
|
||||||
|
#include "carla/Version.h"
|
||||||
|
#include "carla/client/BlueprintLibrary.h"
|
||||||
|
#include "carla/client/Control.h"
|
||||||
|
#include "carla/client/Memory.h"
|
||||||
|
#include "carla/client/Transform.h"
|
||||||
|
#include "carla/rpc/Client.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Actor;
|
||||||
|
class ActorBlueprint;
|
||||||
|
class World;
|
||||||
|
|
||||||
|
class Client
|
||||||
|
: public EnableSharedFromThis<Client>,
|
||||||
|
private NonCopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
explicit Client(Args && ... args)
|
||||||
|
: _client(std::forward<Args>(args) ...) {
|
||||||
|
SetTimeout(10'000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTimeout(int64_t milliseconds) {
|
||||||
|
_client.set_timeout(milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename ... Args>
|
||||||
|
T Call(const std::string &function, Args && ... args) {
|
||||||
|
return _client.call(function, std::forward<Args>(args) ...).template as<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetClientVersion() const {
|
||||||
|
return ::carla::version();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetServerVersion() {
|
||||||
|
return Call<std::string>("version");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ping() {
|
||||||
|
return Call<bool>("ping");
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<World> GetWorld();
|
||||||
|
|
||||||
|
BlueprintLibrary GetBlueprintLibrary() {
|
||||||
|
return Call<std::vector<carla::rpc::ActorBlueprint>>("get_blueprints");
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Actor> SpawnActor(const ActorBlueprint &blueprint, const Transform &transform);
|
||||||
|
|
||||||
|
void ApplyControlToActor(
|
||||||
|
const Actor &actor,
|
||||||
|
const VehicleControl &control);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
carla::rpc::Client _client;
|
||||||
|
|
||||||
|
SharedPtr<World> _active_world;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/VehicleControl.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
using VehicleControl = carla::rpc::VehicleControl;
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// In this namespace, we use boost::shared_ptr for now to make it compatible
|
||||||
|
// with boost::python, but it would be nice to make an adaptor for
|
||||||
|
// std::shared_ptr.
|
||||||
|
#include <boost/enable_shared_from_this.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using EnableSharedFromThis = boost::enable_shared_from_this<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using SharedPtr = boost::shared_ptr<T>;
|
||||||
|
|
||||||
|
template <typename T, typename ... Args>
|
||||||
|
auto MakeShared(Args && ... args) {
|
||||||
|
return boost::make_shared<T>(std::forward<Args>(args) ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/Transform.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
using Location = carla::rpc::Location;
|
||||||
|
using Rotation = carla::rpc::Rotation;
|
||||||
|
using Transform = carla::rpc::Transform;
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "carla/client/World.h"
|
||||||
|
|
||||||
|
#include "carla/Logging.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
SharedPtr<Actor> World::TrySpawnActor(
|
||||||
|
const ActorBlueprint &blueprint,
|
||||||
|
const Transform &transform) {
|
||||||
|
try {
|
||||||
|
return SpawnActor(blueprint, transform);
|
||||||
|
} catch (const std::exception & DEBUG_ONLY(e)) {
|
||||||
|
DEBUG_ONLY(log_debug("TrySpawnActor: failed with:", e.what()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/NonCopyable.h"
|
||||||
|
#include "carla/client/Client.h"
|
||||||
|
#include "carla/client/Memory.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace client {
|
||||||
|
|
||||||
|
class Actor;
|
||||||
|
|
||||||
|
class World
|
||||||
|
: public EnableSharedFromThis<World>,
|
||||||
|
private NonCopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
World(World &&) = default;
|
||||||
|
World &operator=(World &&) = default;
|
||||||
|
|
||||||
|
BlueprintLibrary GetBlueprintLibrary() const {
|
||||||
|
return _parent->GetBlueprintLibrary();
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Actor> TrySpawnActor(
|
||||||
|
const ActorBlueprint &blueprint,
|
||||||
|
const Transform &transform);
|
||||||
|
|
||||||
|
SharedPtr<Actor> SpawnActor(
|
||||||
|
const ActorBlueprint &blueprint,
|
||||||
|
const Transform &transform) {
|
||||||
|
return _parent->SpawnActor(blueprint, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ControlType>
|
||||||
|
void ApplyControlToActor(const Actor &actor, const ControlType &control) {
|
||||||
|
_parent->ApplyControlToActor(actor, control);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Client;
|
||||||
|
|
||||||
|
explicit World(SharedPtr<Client> parent) : _parent(std::move(parent)) {
|
||||||
|
DEBUG_ASSERT(parent != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<Client> _parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace client
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#ifndef LIBCARLA_ENABLE_PROFILER
|
||||||
|
# define LIBCARLA_ENABLE_PROFILER
|
||||||
|
#endif // LIBCARLA_ENABLE_PROFILER
|
||||||
|
|
||||||
|
#include "carla/Logging.h"
|
||||||
|
#include "carla/Version.h"
|
||||||
|
#include "carla/profiler/Profiler.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace profiler {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename Arg, typename ... Args>
|
||||||
|
static void write_csv_to_stream(std::ostream &out, Arg &&arg, Args &&... args) {
|
||||||
|
out << std::boolalpha
|
||||||
|
<< std::left << std::setw(44)
|
||||||
|
<< std::forward<Arg>(arg)
|
||||||
|
<< std::right
|
||||||
|
<< std::fixed << std::setprecision(2);
|
||||||
|
using expander = int[];
|
||||||
|
(void)expander{0, (void(out << ", " << std::setw(10) << std::forward<Args>(args)),0)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
class StaticProfiler {
|
||||||
|
public:
|
||||||
|
|
||||||
|
StaticProfiler(std::string filename)
|
||||||
|
: _filename(std::move(filename)) {
|
||||||
|
logging::log("PROFILER: writing profiling data to", _filename);
|
||||||
|
std::string header = "# LibCarla Profiler ";
|
||||||
|
header += carla::version();
|
||||||
|
#ifdef NDEBUG
|
||||||
|
header += " (release)";
|
||||||
|
#else
|
||||||
|
header += " (debug)";
|
||||||
|
#endif // NDEBUG
|
||||||
|
write_to_file(std::ios_base::out, header);
|
||||||
|
write_line("# context", "average", "maximum", "minimum", "units", "times");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
void write_line(Args &&... args) {
|
||||||
|
write_to_file(std::ios_base::app|std::ios_base::out, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
void write_to_file(std::ios_base::openmode mode, Args &&... args) {
|
||||||
|
if (!_filename.empty()) {
|
||||||
|
static std::mutex MUTEX;
|
||||||
|
std::lock_guard<std::mutex> guard(MUTEX);
|
||||||
|
std::ofstream file(_filename, mode);
|
||||||
|
write_csv_to_stream(file, std::forward<Args>(args)...);
|
||||||
|
file << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string _filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProfilerData::~ProfilerData() {
|
||||||
|
static StaticProfiler PROFILER{"profiler.csv"};
|
||||||
|
if (_count > 0u) {
|
||||||
|
if (_print_fps) {
|
||||||
|
PROFILER.write_line(_name, fps(average()), fps(minimum()), fps(maximum()), "FPS", _count);
|
||||||
|
} else {
|
||||||
|
PROFILER.write_line(_name, average(), maximum(), minimum(), "ms", _count);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("profiler", _name, " was never run!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace profiler
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef LIBCARLA_ENABLE_PROFILER
|
||||||
|
# define CARLA_PROFILE_SCOPE(context, profiler_name)
|
||||||
|
# define CARLA_PROFILE_FPS(context, profiler_name)
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "carla/StopWatch.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace profiler {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class ProfilerData {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit ProfilerData(std::string name, bool print_fps = false)
|
||||||
|
: _name(std::move(name)),
|
||||||
|
_print_fps(print_fps) {}
|
||||||
|
|
||||||
|
~ProfilerData();
|
||||||
|
|
||||||
|
void Annotate(const StopWatch &stop_watch) {
|
||||||
|
const size_t elapsed_microseconds =
|
||||||
|
stop_watch.GetElapsedTime<std::chrono::microseconds>();
|
||||||
|
++_count;
|
||||||
|
_total_microseconds += elapsed_microseconds;
|
||||||
|
_max_elapsed = std::max(elapsed_microseconds, _max_elapsed);
|
||||||
|
_min_elapsed = std::min(elapsed_microseconds, _min_elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
float average() const {
|
||||||
|
return ms(_total_microseconds) / static_cast<float>(_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
float maximum() const {
|
||||||
|
return ms(_max_elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
float minimum() const {
|
||||||
|
return ms(_min_elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static inline float ms(size_t microseconds) {
|
||||||
|
return 1e-3f * static_cast<float>(microseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float fps(float milliseconds) {
|
||||||
|
return 1e3f / milliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string _name;
|
||||||
|
|
||||||
|
const bool _print_fps;
|
||||||
|
|
||||||
|
size_t _count = 0u;
|
||||||
|
|
||||||
|
size_t _total_microseconds = 0u;
|
||||||
|
|
||||||
|
size_t _max_elapsed = 0u;
|
||||||
|
|
||||||
|
size_t _min_elapsed = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedProfiler {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit ScopedProfiler(ProfilerData &parent) : _profiler(parent) {}
|
||||||
|
|
||||||
|
~ScopedProfiler() {
|
||||||
|
_stop_watch.Stop();
|
||||||
|
_profiler.Annotate(_stop_watch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ProfilerData &_profiler;
|
||||||
|
|
||||||
|
StopWatch _stop_watch;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace profiler
|
||||||
|
} // namespace carla
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_WITH_GTEST
|
||||||
|
# define LIBCARLA_GTEST_GET_TEST_NAME() std::string(::testing::UnitTest::GetInstance()->current_test_info()->name())
|
||||||
|
#else
|
||||||
|
# define LIBCARLA_GTEST_GET_TEST_NAME() std::string("")
|
||||||
|
#endif // LIBCARLA_WITH_GTEST
|
||||||
|
|
||||||
|
#define CARLA_PROFILE_SCOPE(context, profiler_name) \
|
||||||
|
static thread_local ::carla::profiler::detail::ProfilerData carla_profiler_ ## context ## _ ## profiler_name ## _data( \
|
||||||
|
LIBCARLA_GTEST_GET_TEST_NAME() + "." #context "." #profiler_name); \
|
||||||
|
::carla::profiler::detail::ScopedProfiler carla_profiler_ ## context ## _ ## profiler_name ## _scoped_profiler( \
|
||||||
|
carla_profiler_ ## context ## _ ## profiler_name ## _data);
|
||||||
|
|
||||||
|
#define CARLA_PROFILE_FPS(context, profiler_name) \
|
||||||
|
{ \
|
||||||
|
static thread_local ::carla::StopWatch stop_watch; \
|
||||||
|
stop_watch.Stop(); \
|
||||||
|
static thread_local bool first_time = true; \
|
||||||
|
if (!first_time) { \
|
||||||
|
static thread_local ::carla::profiler::detail::ProfilerData profiler_data( \
|
||||||
|
LIBCARLA_GTEST_GET_TEST_NAME() + "." #context "." #profiler_name, true); \
|
||||||
|
profiler_data.Annotate(stop_watch); \
|
||||||
|
} else { \
|
||||||
|
first_time = false; \
|
||||||
|
} \
|
||||||
|
stop_watch.Restart(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIBCARLA_ENABLE_PROFILER
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/ActorBlueprint.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
class Actor {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using id_type = uint32_t;
|
||||||
|
|
||||||
|
id_type id;
|
||||||
|
|
||||||
|
ActorBlueprint blueprint;
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(id, blueprint);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/MsgPack.h"
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
# include "UnrealString.h"
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
class ActorBlueprint {
|
||||||
|
public:
|
||||||
|
|
||||||
|
ActorBlueprint(std::string type_id)
|
||||||
|
: type_id(std::move(type_id)) {}
|
||||||
|
|
||||||
|
ActorBlueprint() = default;
|
||||||
|
ActorBlueprint(const ActorBlueprint &) = default;
|
||||||
|
ActorBlueprint(ActorBlueprint &&) = default;
|
||||||
|
ActorBlueprint &operator=(const ActorBlueprint &) = default;
|
||||||
|
ActorBlueprint &operator=(ActorBlueprint &&) = default;
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
// ActorBlueprint(const FString &Type)
|
||||||
|
// : type_id(TCHAR_TO_UTF8(*Type)) {}
|
||||||
|
|
||||||
|
FString GetTypeIdAsFString() const {
|
||||||
|
return FString(type_id.size(), UTF8_TO_TCHAR(type_id.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
std::string type_id;
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(type_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <rpc/client.h>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
using Client = ::rpc::client;
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <rpc/msgpack.hpp>
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
|
||||||
|
#include <rpc/server.h>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/// Function traits based on http://rpclib.net implementation.
|
||||||
|
/// MIT Licensed, Copyright (c) 2015-2017, Tamás Szelei
|
||||||
|
template <typename T>
|
||||||
|
struct wrapper_function_traits : wrapper_function_traits<decltype(&T::operator())> {};
|
||||||
|
|
||||||
|
template <typename C, typename R, typename... Args>
|
||||||
|
struct wrapper_function_traits<R (C::*)(Args...)> : wrapper_function_traits<R (*)(Args...)> {};
|
||||||
|
|
||||||
|
template <typename C, typename R, typename... Args>
|
||||||
|
struct wrapper_function_traits<R (C::*)(Args...) const> : wrapper_function_traits<R (*)(Args...)> {};
|
||||||
|
|
||||||
|
template <typename R, typename... Args> struct wrapper_function_traits<R (*)(Args...)> {
|
||||||
|
using result_type = R;
|
||||||
|
using function_type = std::function<R(Args...)>;
|
||||||
|
using packaged_task_type = std::packaged_task<R()>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Wraps @a functor into a function type with equivalent signature. The wrap
|
||||||
|
/// function returned, when called, posts @a functor into the io_service and
|
||||||
|
/// waits for it to finish.
|
||||||
|
///
|
||||||
|
/// This way, no matter from which thread the wrap function is called, the
|
||||||
|
/// @a functor provided is always called from the context of the io_service.
|
||||||
|
///
|
||||||
|
/// @warning The wrap function blocks until @a functor is executed by the
|
||||||
|
/// io_service.
|
||||||
|
template <typename F>
|
||||||
|
inline auto WrapSyncCall(boost::asio::io_service &io, F functor) {
|
||||||
|
using func_t = typename wrapper_function_traits<F>::function_type;
|
||||||
|
using task_t = typename wrapper_function_traits<F>::packaged_task_type;
|
||||||
|
|
||||||
|
return func_t([&io, functor=std::move(functor)](auto && ... args) {
|
||||||
|
// We can pass arguments by ref to the lambda because the task will be
|
||||||
|
// executed before this function exits.
|
||||||
|
task_t task([functor=std::move(functor), &args...]() {
|
||||||
|
return functor(std::forward<decltype(args)>(args)...);
|
||||||
|
});
|
||||||
|
auto result = task.get_future();
|
||||||
|
io.post([&]() mutable { task(); });
|
||||||
|
return result.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// An RPC server in which functions can be bind to run synchronously or
|
||||||
|
/// asynchronously.
|
||||||
|
///
|
||||||
|
/// Use `AsyncRun` to start the worker threads, and use `SyncRunFor` to
|
||||||
|
/// run a slice of work in the caller's thread.
|
||||||
|
///
|
||||||
|
/// Functions that are bind using `BindAsync` will run asynchronously in the
|
||||||
|
/// worker threads. Functions that are bind using `BindSync` will run within
|
||||||
|
/// `SyncRunFor` function.
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename ... Args>
|
||||||
|
explicit Server(Args && ... args)
|
||||||
|
: _server(std::forward<Args>(args) ...) {
|
||||||
|
_server.suppress_exceptions(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void BindAsync(const std::string &name, Functor &&functor) {
|
||||||
|
_server.bind(name, std::forward<Functor>(functor));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void BindSync(const std::string &name, Functor functor) {
|
||||||
|
_server.bind(
|
||||||
|
name,
|
||||||
|
detail::WrapSyncCall(_sync_io_service, std::move(functor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncRun(size_t worker_threads) {
|
||||||
|
_server.async_run(worker_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Duration>
|
||||||
|
void SyncRunFor(Duration duration) {
|
||||||
|
_sync_io_service.reset();
|
||||||
|
_sync_io_service.run_for(duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @warning does not stop the game thread.
|
||||||
|
void Stop() {
|
||||||
|
_server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
boost::asio::io_service _sync_io_service;
|
||||||
|
|
||||||
|
::rpc::server _server;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/MsgPack.h"
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
# include "Transform.h"
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
class Location {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Location() = default;
|
||||||
|
|
||||||
|
Location(float ix, float iy, float iz)
|
||||||
|
: x(ix),
|
||||||
|
y(iy),
|
||||||
|
z(iz) {}
|
||||||
|
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
Location(const FVector &vector) // from centimeters to meters.
|
||||||
|
: Location(1e-2f * vector.X, 1e-2f * vector.Y, 1e-2f * vector.Z) {}
|
||||||
|
|
||||||
|
operator FVector() const {
|
||||||
|
return FVector{1e2f * x, 1e2f * y, 1e2f * z}; // from meters to centimeters.
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(x, y, z);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Rotation {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Rotation() = default;
|
||||||
|
|
||||||
|
Rotation(float p, float y, float r)
|
||||||
|
: pitch(p),
|
||||||
|
yaw(y),
|
||||||
|
roll(r) {}
|
||||||
|
|
||||||
|
float pitch = 0.0f;
|
||||||
|
float yaw = 0.0f;
|
||||||
|
float roll = 0.0f;
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
Rotation(const FRotator &rotator)
|
||||||
|
: Rotation(rotator.Pitch, rotator.Yaw, rotator.Roll) {}
|
||||||
|
|
||||||
|
operator FRotator() const {
|
||||||
|
return FRotator{pitch, yaw, roll};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(pitch, yaw, roll);
|
||||||
|
};
|
||||||
|
|
||||||
|
class Transform {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Transform() = default;
|
||||||
|
|
||||||
|
Transform(const Location &in_location, const Rotation &in_rotation)
|
||||||
|
: location(in_location),
|
||||||
|
rotation(in_rotation) {}
|
||||||
|
|
||||||
|
Location location;
|
||||||
|
Rotation rotation;
|
||||||
|
|
||||||
|
#ifdef LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
Transform(const FTransform &transform)
|
||||||
|
: Transform(Location(transform.GetLocation()), Rotation(transform.Rotator())) {}
|
||||||
|
|
||||||
|
operator FTransform() const {
|
||||||
|
const FVector scale{1.0f, 1.0f, 1.0f};
|
||||||
|
return FTransform{FRotator(rotation), FVector(location), scale};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(location, rotation);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/rpc/MsgPack.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace rpc {
|
||||||
|
|
||||||
|
class VehicleControl {
|
||||||
|
public:
|
||||||
|
|
||||||
|
VehicleControl() = default;
|
||||||
|
|
||||||
|
VehicleControl(
|
||||||
|
float in_throttle,
|
||||||
|
float in_steer,
|
||||||
|
float in_brake,
|
||||||
|
bool in_hand_brake,
|
||||||
|
bool in_reverse)
|
||||||
|
: throttle(in_throttle),
|
||||||
|
steer(in_steer),
|
||||||
|
brake(in_brake),
|
||||||
|
hand_brake(in_hand_brake),
|
||||||
|
reverse(in_reverse) {}
|
||||||
|
|
||||||
|
float throttle = 0.0f;
|
||||||
|
float steer = 0.0f;
|
||||||
|
float brake = 0.0f;
|
||||||
|
bool hand_brake = false;
|
||||||
|
bool reverse = false;
|
||||||
|
|
||||||
|
MSGPACK_DEFINE_ARRAY(
|
||||||
|
throttle,
|
||||||
|
steer,
|
||||||
|
brake,
|
||||||
|
hand_brake,
|
||||||
|
reverse);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace rpc
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/ThreadGroup.h"
|
||||||
|
#include "carla/streaming/low_level/Client.h"
|
||||||
|
#include "carla/streaming/low_level/tcp/Client.h"
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
|
||||||
|
using stream_token = low_level::token_type;
|
||||||
|
|
||||||
|
/// With this client you can subscribe to multiple streams.
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
|
||||||
|
~Client() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void Subscribe(const stream_token &token, Functor &&callback) {
|
||||||
|
_client.Subscribe(_io_service, token, std::forward<Functor>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Run() {
|
||||||
|
_io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncRun(size_t worker_threads) {
|
||||||
|
_workers.CreateThreads(worker_threads, [this]() { Run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
_io_service.stop();
|
||||||
|
_workers.JoinAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
using underlying_client = low_level::Client<low_level::tcp::Client>;
|
||||||
|
|
||||||
|
boost::asio::io_service _io_service;
|
||||||
|
|
||||||
|
ThreadGroup _workers;
|
||||||
|
|
||||||
|
underlying_client _client;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <exception>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
|
||||||
|
namespace low_level {
|
||||||
|
namespace tcp {
|
||||||
|
|
||||||
|
class Client; /// @todo
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace tcp
|
||||||
|
|
||||||
|
/// A message owns a buffer with raw data.
|
||||||
|
class Message {
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// -- Typedefs -------------------------------------------------------------
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using value_type = unsigned char;
|
||||||
|
|
||||||
|
using size_type = uint32_t;
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// -- Construction and assignment ------------------------------------------
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Message() = default;
|
||||||
|
|
||||||
|
explicit Message(size_type size)
|
||||||
|
: _size(size),
|
||||||
|
_data(std::make_unique<value_type[]>(_size)) {}
|
||||||
|
|
||||||
|
explicit Message(uint64_t size)
|
||||||
|
: Message([size]() {
|
||||||
|
if (size > std::numeric_limits<size_type>::max()) {
|
||||||
|
throw std::invalid_argument("message size too big");
|
||||||
|
}
|
||||||
|
return static_cast<size_type>(size);
|
||||||
|
} ()) {}
|
||||||
|
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
explicit Message(ConstBufferSequence source)
|
||||||
|
: Message(boost::asio::buffer_size(source)) {
|
||||||
|
DEBUG_ONLY(auto bytes_copied = )
|
||||||
|
boost::asio::buffer_copy(buffer(), source);
|
||||||
|
DEBUG_ASSERT(bytes_copied == size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Message(const Message &) = delete;
|
||||||
|
Message &operator=(const Message &) = delete;
|
||||||
|
|
||||||
|
Message(Message &&rhs) noexcept
|
||||||
|
: _size(rhs._size),
|
||||||
|
_data(std::move(rhs._data)) {
|
||||||
|
rhs._size = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message &operator=(Message &&rhs) noexcept {
|
||||||
|
_size = rhs._size;
|
||||||
|
_data = std::move(rhs._data);
|
||||||
|
rhs._size = 0u;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// -- Data access ----------------------------------------------------------
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return _size == 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type *data() const {
|
||||||
|
return _data.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type *data() {
|
||||||
|
return _data.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// -- Conversions ----------------------------------------------------------
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
boost::asio::const_buffer buffer() const {
|
||||||
|
return {data(), size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::mutable_buffer buffer() {
|
||||||
|
return {data(), size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<boost::asio::const_buffer, 2u> encode() const {
|
||||||
|
DEBUG_ASSERT(!empty());
|
||||||
|
return {boost::asio::buffer(&_size, sizeof(_size)), buffer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// -- Private members ------------------------------------------------------
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class low_level::tcp::Client; /// @todo
|
||||||
|
|
||||||
|
size_type _size = 0u;
|
||||||
|
|
||||||
|
std::unique_ptr<value_type[]> _data = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/ThreadGroup.h"
|
||||||
|
#include "carla/streaming/low_level/Server.h"
|
||||||
|
#include "carla/streaming/low_level/tcp/Server.h"
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
using underlying_server = low_level::Server<low_level::tcp::Server>;
|
||||||
|
public:
|
||||||
|
|
||||||
|
using duration_type = underlying_server::duration_type;
|
||||||
|
|
||||||
|
explicit Server(uint16_t port)
|
||||||
|
: _server(_io_service, port) {}
|
||||||
|
|
||||||
|
explicit Server(const std::string &address, uint16_t port)
|
||||||
|
: _server(_io_service, address, port) {}
|
||||||
|
|
||||||
|
~Server() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_timeout(duration_type timeout) {
|
||||||
|
_server.set_timeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream MakeStream() {
|
||||||
|
return _server.MakeStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Run() {
|
||||||
|
_io_service.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncRun(size_t worker_threads) {
|
||||||
|
_workers.CreateThreads(worker_threads, [this](){ Run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
_io_service.stop();
|
||||||
|
_workers.JoinAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
boost::asio::io_service _io_service;
|
||||||
|
|
||||||
|
underlying_server _server;
|
||||||
|
|
||||||
|
ThreadGroup _workers;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/streaming/Message.h"
|
||||||
|
#include "carla/streaming/low_level/StreamState.h"
|
||||||
|
#include "carla/streaming/low_level/Token.h"
|
||||||
|
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
class Dispatcher;
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
|
||||||
|
using stream_token = low_level::token_type;
|
||||||
|
|
||||||
|
class Stream {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Stream() = delete;
|
||||||
|
|
||||||
|
Stream(const Stream &) = default;
|
||||||
|
Stream(Stream &&) = default;
|
||||||
|
|
||||||
|
Stream &operator=(const Stream &) = default;
|
||||||
|
Stream &operator=(Stream &&) = default;
|
||||||
|
|
||||||
|
stream_token token() const {
|
||||||
|
return _shared_state->token();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ConstBufferSequence>
|
||||||
|
void Write(ConstBufferSequence buffer) {
|
||||||
|
_shared_state->Write(std::make_shared<Message>(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Stream &operator<<(const T &rhs) {
|
||||||
|
Write(boost::asio::buffer(rhs));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class low_level::Dispatcher;
|
||||||
|
|
||||||
|
Stream(std::shared_ptr<low_level::StreamState> state)
|
||||||
|
: _shared_state(std::move(state)) {
|
||||||
|
DEBUG_ASSERT(_shared_state != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<low_level::StreamState> _shared_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/low_level/Token.h"
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
/// Wrapper around low level clients. You can subscribe to multiple streams.
|
||||||
|
///
|
||||||
|
/// @warning The client should not be destroyed before the @a io_service is
|
||||||
|
/// stopped.
|
||||||
|
/// @pre T has to be hashable.
|
||||||
|
template <typename T>
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using underlying_client = T;
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void Subscribe(
|
||||||
|
boost::asio::io_service &io_service,
|
||||||
|
const token_type &token,
|
||||||
|
Functor &&callback) {
|
||||||
|
if (!token.protocol_is_tcp()) {
|
||||||
|
throw std::invalid_argument("invalid token, only TCP tokens supported");
|
||||||
|
}
|
||||||
|
_clients.emplace(
|
||||||
|
io_service,
|
||||||
|
token.to_tcp_endpoint(),
|
||||||
|
token.get_stream_id(),
|
||||||
|
std::forward<Functor>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unordered_set<underlying_client> _clients;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/Stream.h"
|
||||||
|
#include "carla/streaming/low_level/Session.h"
|
||||||
|
#include "carla/streaming/low_level/StreamState.h"
|
||||||
|
#include "carla/streaming/low_level/Token.h"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
class Dispatcher {
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
explicit Dispatcher(const boost::asio::ip::basic_endpoint<P> &ep)
|
||||||
|
: _cached_token(0u, ep) {}
|
||||||
|
|
||||||
|
Stream MakeStream() {
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
++_cached_token._token.stream_id; // id zero only happens in overflow.
|
||||||
|
auto ptr = std::make_shared<StreamState>(_cached_token);
|
||||||
|
auto result = _stream_map.emplace(std::make_pair(_cached_token.get_stream_id(), ptr));
|
||||||
|
if (!result.second) {
|
||||||
|
throw std::runtime_error("failed to create stream!");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterSession(std::shared_ptr<Session> session) {
|
||||||
|
DEBUG_ASSERT(session != nullptr);
|
||||||
|
std::lock_guard<std::mutex> lock(_mutex);
|
||||||
|
auto search = _stream_map.find(session->get_stream_id());
|
||||||
|
if (search != _stream_map.end()) {
|
||||||
|
DEBUG_ASSERT(search->second != nullptr);
|
||||||
|
search->second->set_session(std::move(session));
|
||||||
|
} else {
|
||||||
|
log_error("Invalid session: no stream available with id", session->get_stream_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// We use a mutex here, but we assume that sessions and streams won't be
|
||||||
|
// created too often.
|
||||||
|
std::mutex _mutex;
|
||||||
|
|
||||||
|
token_type _cached_token;
|
||||||
|
|
||||||
|
std::unordered_map<
|
||||||
|
stream_id_type,
|
||||||
|
std::shared_ptr<StreamState>> _stream_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/low_level/Dispatcher.h"
|
||||||
|
#include "carla/streaming/Stream.h"
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
/// Wrapper around low level servers.
|
||||||
|
template <typename T>
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using underlying_server = T;
|
||||||
|
|
||||||
|
using duration_type = typename underlying_server::duration_type;
|
||||||
|
using endpoint = typename underlying_server::endpoint;
|
||||||
|
using protocol_type = typename underlying_server::protocol_type;
|
||||||
|
|
||||||
|
explicit Server(boost::asio::io_service &io_service, const endpoint &ep)
|
||||||
|
: _server(io_service, ep),
|
||||||
|
_dispatcher(ep) {
|
||||||
|
_server.Listen([this](auto session){
|
||||||
|
_dispatcher.RegisterSession(session);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Server(boost::asio::io_service &io_service, uint16_t port)
|
||||||
|
: Server(io_service, endpoint(protocol_type::v4(), port)) {}
|
||||||
|
|
||||||
|
explicit Server(boost::asio::io_service &io_service, const std::string &address, uint16_t port)
|
||||||
|
: Server(io_service, endpoint(boost::asio::ip::address::from_string(address), port)) {}
|
||||||
|
|
||||||
|
void set_timeout(duration_type timeout) {
|
||||||
|
_server.set_timeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream MakeStream() {
|
||||||
|
return _dispatcher.MakeStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
underlying_server _server;
|
||||||
|
|
||||||
|
Dispatcher _dispatcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/low_level/tcp/ServerSession.h"
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
using Session = tcp::ServerSession;
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/Message.h"
|
||||||
|
#include "carla/streaming/low_level/Session.h"
|
||||||
|
#include "carla/streaming/low_level/Token.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/// Handles the synchronization of the shared session.
|
||||||
|
class SessionHolder {
|
||||||
|
public:
|
||||||
|
|
||||||
|
void set_session(std::shared_ptr<Session> session) {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
|
_session = std::move(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
std::shared_ptr<Session> get_session() const {
|
||||||
|
std::lock_guard<std::mutex> guard(_mutex);
|
||||||
|
return _session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
mutable std::mutex _mutex; /// @todo it can be atomic
|
||||||
|
|
||||||
|
std::shared_ptr<Session> _session;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Shared state among all the copies of a stream. Provides access to the
|
||||||
|
/// underlying UDP session if active.
|
||||||
|
class StreamState
|
||||||
|
: public detail::SessionHolder,
|
||||||
|
private boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit StreamState(const token_type &token) : _token(token) {}
|
||||||
|
|
||||||
|
const token_type &token() const {
|
||||||
|
return _token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(std::shared_ptr<const Message> message) {
|
||||||
|
auto session = get_session();
|
||||||
|
if (session != nullptr) {
|
||||||
|
session->Write(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const token_type _token;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/streaming/low_level/Types.h"
|
||||||
|
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/ip/udp.hpp>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
struct token {
|
||||||
|
stream_id_type stream_id;
|
||||||
|
|
||||||
|
uint16_t port;
|
||||||
|
|
||||||
|
enum class protocol : uint8_t {
|
||||||
|
not_set,
|
||||||
|
tcp,
|
||||||
|
udp
|
||||||
|
} protocol = protocol::not_set;
|
||||||
|
|
||||||
|
enum class address : uint8_t {
|
||||||
|
not_set,
|
||||||
|
ip_v4,
|
||||||
|
ip_v6
|
||||||
|
} address_type = address::not_set;
|
||||||
|
|
||||||
|
union {
|
||||||
|
boost::asio::ip::address_v4::bytes_type v4;
|
||||||
|
boost::asio::ip::address_v6::bytes_type v6;
|
||||||
|
} address;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
sizeof(token) == 24u,
|
||||||
|
"Size shouldn't be more than"
|
||||||
|
" v6 address : 128"
|
||||||
|
" + state : 16"
|
||||||
|
" + port : 16"
|
||||||
|
" + stream id : 32"
|
||||||
|
" -----------------"
|
||||||
|
" 192");
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// Serializes a stream endpoint. Contains all the necessary information for a
|
||||||
|
/// client to subscribe to a stream.
|
||||||
|
class token_type {
|
||||||
|
private:
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static constexpr auto get_protocol() {
|
||||||
|
return std::is_same<P, boost::asio::ip::tcp>::value ?
|
||||||
|
detail::token::protocol::tcp :
|
||||||
|
detail::token::protocol::udp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_address(const boost::asio::ip::address &addr) {
|
||||||
|
if (addr.is_v4()) {
|
||||||
|
_token.address_type = detail::token::address::ip_v4;
|
||||||
|
_token.address.v4 = addr.to_v4().to_bytes();
|
||||||
|
} else if (addr.is_v6()) {
|
||||||
|
_token.address_type = detail::token::address::ip_v6;
|
||||||
|
_token.address.v6 = addr.to_v6().to_bytes();
|
||||||
|
} else {
|
||||||
|
throw std::invalid_argument("invalid ip address!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::address get_address() const {
|
||||||
|
if (_token.address_type == detail::token::address::ip_v4) {
|
||||||
|
return boost::asio::ip::address_v4(_token.address.v4);
|
||||||
|
}
|
||||||
|
return boost::asio::ip::address_v6(_token.address.v6);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
boost::asio::ip::basic_endpoint<P> get_endpoint() const {
|
||||||
|
DEBUG_ASSERT(is_valid());
|
||||||
|
DEBUG_ASSERT(get_protocol<P>() == _token.protocol);
|
||||||
|
return {get_address(), _token.port};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
explicit token_type(
|
||||||
|
stream_id_type stream_id,
|
||||||
|
const boost::asio::ip::basic_endpoint<P> &ep) {
|
||||||
|
_token.stream_id = stream_id;
|
||||||
|
_token.port = ep.port();
|
||||||
|
_token.protocol = get_protocol<P>();
|
||||||
|
set_address(ep.address());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
token_type() = default;
|
||||||
|
token_type(const token_type &) = default;
|
||||||
|
|
||||||
|
auto get_stream_id() const {
|
||||||
|
return _token.stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_port() const {
|
||||||
|
return _token.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid() const {
|
||||||
|
return ((_token.protocol != detail::token::protocol::not_set) &&
|
||||||
|
(_token.address_type != detail::token::address::not_set));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool address_is_v4() const {
|
||||||
|
return _token.address_type == detail::token::address::ip_v4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool address_is_v6() const {
|
||||||
|
return _token.address_type == detail::token::address::ip_v6;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool protocol_is_udp() const {
|
||||||
|
return _token.protocol == detail::token::protocol::udp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool protocol_is_tcp() const {
|
||||||
|
return _token.protocol == detail::token::protocol::tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::udp::endpoint to_udp_endpoint() const {
|
||||||
|
return get_endpoint<boost::asio::ip::udp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::endpoint to_tcp_endpoint() const {
|
||||||
|
return get_endpoint<boost::asio::ip::tcp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::const_buffer as_buffer() const {
|
||||||
|
return boost::asio::buffer(&_token, sizeof(_token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Dispatcher;
|
||||||
|
|
||||||
|
detail::token _token;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
|
||||||
|
using stream_id_type = uint32_t;
|
||||||
|
|
||||||
|
using message_size_type = uint32_t;
|
||||||
|
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,249 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/Logging.h"
|
||||||
|
#include "carla/streaming/Message.h"
|
||||||
|
#include "carla/streaming/low_level/Types.h"
|
||||||
|
#include "carla/streaming/low_level/tcp/Timeout.h"
|
||||||
|
|
||||||
|
#include <boost/asio/connect.hpp>
|
||||||
|
#include <boost/asio/deadline_timer.hpp>
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/read.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
|
#include <boost/asio/write.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
namespace tcp {
|
||||||
|
|
||||||
|
class Encoder {
|
||||||
|
public:
|
||||||
|
|
||||||
|
boost::asio::mutable_buffer header() {
|
||||||
|
return boost::asio::buffer(reinterpret_cast<unsigned char *>(&_size), sizeof(_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::mutable_buffer body() {
|
||||||
|
DEBUG_ASSERT(_size > 0u);
|
||||||
|
DEBUG_ASSERT(_message == nullptr);
|
||||||
|
_message = std::make_shared<Message>(_size);
|
||||||
|
return _message->buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size() const {
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pop() {
|
||||||
|
return std::move(_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
message_size_type _size = 0u;
|
||||||
|
|
||||||
|
std::shared_ptr<Message> _message;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @warning The client should not be destroyed before the @a io_service is
|
||||||
|
/// stopped.
|
||||||
|
class Client : private boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using endpoint = boost::asio::ip::tcp::endpoint;
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
Client(
|
||||||
|
boost::asio::io_service &io_service,
|
||||||
|
endpoint ep,
|
||||||
|
stream_id_type stream_id,
|
||||||
|
Functor &&callback)
|
||||||
|
: _endpoint(std::move(ep)),
|
||||||
|
_stream_id(stream_id),
|
||||||
|
_callback(std::forward<Functor>(callback)),
|
||||||
|
_socket(io_service),
|
||||||
|
_strand(io_service),
|
||||||
|
_connection_timer(io_service) {
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Client() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_id_type get_id() const {
|
||||||
|
return _stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Client &rhs) const {
|
||||||
|
return get_id() == rhs.get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
_connection_timer.cancel();
|
||||||
|
_strand.post([this]() {
|
||||||
|
_done = true;
|
||||||
|
if (_socket.is_open()) {
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @todo Stop inlining and make cpp files.
|
||||||
|
|
||||||
|
inline void Connect();
|
||||||
|
|
||||||
|
inline void Reconnect() {
|
||||||
|
_connection_timer.expires_from_now(timeout_type::seconds(1u));
|
||||||
|
_connection_timer.async_wait([this](boost::system::error_code ec) {
|
||||||
|
if (!ec) {
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ReadData();
|
||||||
|
|
||||||
|
const endpoint _endpoint;
|
||||||
|
|
||||||
|
const stream_id_type _stream_id;
|
||||||
|
|
||||||
|
std::function<void(std::shared_ptr<Message>)> _callback;
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::socket _socket;
|
||||||
|
|
||||||
|
boost::asio::io_service::strand _strand;
|
||||||
|
|
||||||
|
boost::asio::deadline_timer _connection_timer;
|
||||||
|
|
||||||
|
bool _done = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Client::Connect() {
|
||||||
|
_strand.post([this]() {
|
||||||
|
if (_done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using boost::system::error_code;
|
||||||
|
|
||||||
|
if (_socket.is_open()) {
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handle_connect = [=](error_code ec) {
|
||||||
|
if (!ec) {
|
||||||
|
// Send the stream id to subscribe to the stream.
|
||||||
|
log_debug("streaming client: sending stream id", _stream_id);
|
||||||
|
boost::asio::async_write(
|
||||||
|
_socket,
|
||||||
|
boost::asio::buffer(&_stream_id, sizeof(_stream_id)),
|
||||||
|
_strand.wrap([=](error_code ec, size_t DEBUG_ONLY(bytes)) {
|
||||||
|
if (!ec) {
|
||||||
|
DEBUG_ASSERT_EQ(bytes, sizeof(_stream_id));
|
||||||
|
// If succeeded start reading data.
|
||||||
|
ReadData();
|
||||||
|
} else {
|
||||||
|
// Else try again.
|
||||||
|
log_debug("streaming client: failed to send stream id:", ec.message());
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
log_debug("streaming client: connection failed:", ec.message());
|
||||||
|
Reconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log_debug("streaming client: connecting to", _endpoint);
|
||||||
|
_socket.async_connect(_endpoint, _strand.wrap(handle_connect));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::ReadData() {
|
||||||
|
_strand.post([this]() {
|
||||||
|
if (_done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("streaming client: Client::ReadData");
|
||||||
|
|
||||||
|
auto encoder = std::make_shared<Encoder>();
|
||||||
|
|
||||||
|
auto handle_read_data = [=](boost::system::error_code ec, size_t DEBUG_ONLY(bytes)) {
|
||||||
|
DEBUG_ONLY(log_debug("streaming client: Client::ReadData.handle_read_data", bytes, "bytes"));
|
||||||
|
if (!ec) {
|
||||||
|
DEBUG_ASSERT_EQ(bytes, encoder->size());
|
||||||
|
DEBUG_ASSERT_NE(bytes, 0u);
|
||||||
|
// Move the buffer to the callback function and start reading the next
|
||||||
|
// piece of data.
|
||||||
|
log_debug("streaming client: success reading data, calling the callback");
|
||||||
|
_socket.get_io_service().post([this, encoder]() { _callback(encoder->pop()); });
|
||||||
|
ReadData();
|
||||||
|
} else {
|
||||||
|
// As usual, if anything fails start over from the very top.
|
||||||
|
log_debug("streaming client: failed to read data:", ec.message());
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto handle_read_header = [=](boost::system::error_code ec, size_t DEBUG_ONLY(bytes)) {
|
||||||
|
DEBUG_ONLY(log_debug("streaming client: Client::ReadData.handle_read_header", bytes, "bytes"));
|
||||||
|
if (!ec && (encoder->size() > 0u)) {
|
||||||
|
DEBUG_ASSERT_EQ(bytes, sizeof(message_size_type));
|
||||||
|
// Now that we know the size of the coming buffer, we can allocate
|
||||||
|
// our buffer and start putting data into it.
|
||||||
|
boost::asio::async_read(
|
||||||
|
_socket,
|
||||||
|
encoder->body(),
|
||||||
|
_strand.wrap(handle_read_data));
|
||||||
|
} else {
|
||||||
|
log_debug("streaming client: failed to read header:", ec.message());
|
||||||
|
DEBUG_ONLY(log_debug("size = ", encoder->size()));
|
||||||
|
DEBUG_ONLY(log_debug("bytes = ", bytes));
|
||||||
|
Connect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the size of the buffer that is coming.
|
||||||
|
boost::asio::async_read(
|
||||||
|
_socket,
|
||||||
|
encoder->header(),
|
||||||
|
_strand.wrap(handle_read_header));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tcp
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
// Injecting a hash function for our clients into std namespace so we can
|
||||||
|
// directly insert them into std::unordered_set.
|
||||||
|
template <>
|
||||||
|
struct hash<carla::streaming::low_level::tcp::Client> {
|
||||||
|
using argument_type = carla::streaming::low_level::tcp::Client;
|
||||||
|
using result_type = std::size_t;
|
||||||
|
result_type operator()(const argument_type &client) const noexcept {
|
||||||
|
return std::hash<carla::streaming::low_level::stream_id_type>()(client.get_id());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/streaming/low_level/tcp/ServerSession.h"
|
||||||
|
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
namespace tcp {
|
||||||
|
|
||||||
|
class Server : private boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using endpoint = boost::asio::ip::tcp::endpoint;
|
||||||
|
using protocol_type = endpoint::protocol_type;
|
||||||
|
using duration_type = ServerSession::duration_type;
|
||||||
|
|
||||||
|
explicit Server(boost::asio::io_service &io_service, endpoint ep)
|
||||||
|
: _acceptor(io_service, std::move(ep)),
|
||||||
|
_timeout(duration_type::seconds(10u)) {}
|
||||||
|
|
||||||
|
/// Set session time-out. Applies only to newly created sessions.
|
||||||
|
void set_timeout(duration_type timeout) {
|
||||||
|
_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void Listen(Functor callback) {
|
||||||
|
log_info("starting streaming server at port", _acceptor.local_endpoint().port());
|
||||||
|
_acceptor.get_io_service().post([=]() { OpenSession(_timeout, callback); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
void OpenSession(duration_type timeout, Functor callback) {
|
||||||
|
using boost::system::error_code;
|
||||||
|
|
||||||
|
auto session = std::make_shared<ServerSession>(_acceptor.get_io_service(), timeout);
|
||||||
|
|
||||||
|
auto handle_query = [=](const error_code &ec) {
|
||||||
|
if (!ec) {
|
||||||
|
session->Open(callback);
|
||||||
|
} else {
|
||||||
|
log_error("tcp accept error:", ec.message());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_acceptor.async_accept(session->_socket, [=](error_code ec) {
|
||||||
|
// Handle query and open a new session immediately.
|
||||||
|
_acceptor.get_io_service().post([=]() { handle_query(ec); });
|
||||||
|
OpenSession(timeout, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::asio::ip::tcp::acceptor _acceptor;
|
||||||
|
|
||||||
|
std::atomic<timeout_type> _timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tcp
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,173 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "carla/Debug.h"
|
||||||
|
#include "carla/Logging.h"
|
||||||
|
#include "carla/streaming/Message.h"
|
||||||
|
#include "carla/streaming/low_level/Types.h"
|
||||||
|
#include "carla/streaming/low_level/tcp/Timeout.h"
|
||||||
|
|
||||||
|
#include <boost/asio/deadline_timer.hpp>
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/asio/read.hpp>
|
||||||
|
#include <boost/asio/strand.hpp>
|
||||||
|
#include <boost/asio/write.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
namespace tcp {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
static std::atomic_size_t SESSION_COUNTER{0u};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// A TCP server session. When a session opens, it reads from the socket a
|
||||||
|
/// stream id object and passes itself to the callback functor. The session
|
||||||
|
/// closes itself after @a timeout of inactivity is met.
|
||||||
|
class ServerSession
|
||||||
|
: public std::enable_shared_from_this<ServerSession>,
|
||||||
|
private boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
using duration_type = timeout_type;
|
||||||
|
|
||||||
|
explicit ServerSession(boost::asio::io_service &io_service, duration_type timeout)
|
||||||
|
: _session_id(detail::SESSION_COUNTER++),
|
||||||
|
_socket(io_service),
|
||||||
|
_timeout(timeout),
|
||||||
|
_deadline(io_service),
|
||||||
|
_strand(io_service) {}
|
||||||
|
|
||||||
|
~ServerSession() {
|
||||||
|
_deadline.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the session and calls @a callback after successfully reading the
|
||||||
|
/// stream id.
|
||||||
|
///
|
||||||
|
/// @pre Callback function signature:
|
||||||
|
/// `void(std::shared_ptr<ServerSession>)`.
|
||||||
|
template <typename Functor>
|
||||||
|
void Open(Functor callback) {
|
||||||
|
StartTimer();
|
||||||
|
auto self = shared_from_this(); // To keep myself alive.
|
||||||
|
_strand.post([=]() {
|
||||||
|
|
||||||
|
auto handle_query = [this, self, callback](
|
||||||
|
const boost::system::error_code &ec,
|
||||||
|
size_t DEBUG_ONLY(bytes_received)) {
|
||||||
|
DEBUG_ASSERT_EQ(bytes_received, sizeof(_stream_id));
|
||||||
|
if (!ec) {
|
||||||
|
log_debug("session", _session_id, "for stream", _stream_id, " started");
|
||||||
|
_socket.get_io_service().post([=]() { callback(self); });
|
||||||
|
} else {
|
||||||
|
log_error("session", _session_id, ": error retrieving stream id :", ec.message());
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read the stream id.
|
||||||
|
_deadline.expires_from_now(_timeout);
|
||||||
|
boost::asio::async_read(
|
||||||
|
_socket,
|
||||||
|
boost::asio::buffer(&_stream_id, sizeof(_stream_id)),
|
||||||
|
_strand.wrap(handle_query));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_id_type get_stream_id() const {
|
||||||
|
// Note that the stream id isn't synchronized. This function should only be
|
||||||
|
// called from the @a callback function, and after that point the stream_id
|
||||||
|
// can't change.
|
||||||
|
return _stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes some data to the socket.
|
||||||
|
void Write(std::shared_ptr<const Message> message) {
|
||||||
|
auto self = shared_from_this();
|
||||||
|
_strand.post([=]() {
|
||||||
|
|
||||||
|
/// @todo has to be a better way of doing this...
|
||||||
|
if (_is_writing) {
|
||||||
|
// Repost and return;
|
||||||
|
Write(std::move(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_is_writing = true;
|
||||||
|
|
||||||
|
auto handle_sent = [this, self, message](const boost::system::error_code &ec, size_t DEBUG_ONLY(bytes)) {
|
||||||
|
_is_writing = false;
|
||||||
|
if (ec) {
|
||||||
|
log_error("session", _session_id, ": error sending data :", ec.message());
|
||||||
|
} else {
|
||||||
|
DEBUG_ONLY(log_debug("session", _session_id, ": successfully sent", bytes, "bytes"));
|
||||||
|
DEBUG_ASSERT_EQ(bytes, sizeof(message_size_type) + message->size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log_debug("session", _session_id, ": sending message of", message->size(), "bytes");
|
||||||
|
|
||||||
|
_deadline.expires_from_now(_timeout);
|
||||||
|
boost::asio::async_write(
|
||||||
|
_socket,
|
||||||
|
message->encode(),
|
||||||
|
_strand.wrap(handle_sent));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() {
|
||||||
|
_strand.post([this, self = shared_from_this()]() {
|
||||||
|
if (_socket.is_open()) {
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
|
log_debug("session", _session_id, "closed");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void StartTimer() {
|
||||||
|
if (_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) {
|
||||||
|
log_debug("session", _session_id, "timed out");
|
||||||
|
Close();
|
||||||
|
} else {
|
||||||
|
_deadline.async_wait([self = shared_from_this()](boost::system::error_code) {
|
||||||
|
self->StartTimer();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class Server;
|
||||||
|
|
||||||
|
const size_t _session_id;
|
||||||
|
|
||||||
|
stream_id_type _stream_id = 0u;
|
||||||
|
|
||||||
|
socket_type _socket;
|
||||||
|
|
||||||
|
duration_type _timeout;
|
||||||
|
|
||||||
|
boost::asio::deadline_timer _deadline;
|
||||||
|
|
||||||
|
boost::asio::io_service::strand _strand;
|
||||||
|
|
||||||
|
bool _is_writing = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tcp
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,62 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
namespace low_level {
|
||||||
|
namespace tcp {
|
||||||
|
|
||||||
|
/// Positive time-out up to milliseconds resolution.
|
||||||
|
class timeout_type {
|
||||||
|
public:
|
||||||
|
|
||||||
|
static inline timeout_type seconds(size_t timeout) {
|
||||||
|
return std::chrono::seconds(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline timeout_type milliseconds(size_t timeout) {
|
||||||
|
return std::chrono::milliseconds(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr timeout_type() : _milliseconds(0u) {}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period>
|
||||||
|
timeout_type(std::chrono::duration<Rep, Period> duration)
|
||||||
|
: _milliseconds(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count()) {}
|
||||||
|
|
||||||
|
timeout_type(boost::posix_time::time_duration timeout)
|
||||||
|
: timeout_type(std::chrono::milliseconds(timeout.total_milliseconds())) {}
|
||||||
|
|
||||||
|
timeout_type(const timeout_type &) = default;
|
||||||
|
timeout_type &operator=(const timeout_type &) = default;
|
||||||
|
|
||||||
|
boost::posix_time::time_duration to_posix_time() const {
|
||||||
|
return boost::posix_time::milliseconds(_milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto to_chrono() const {
|
||||||
|
return std::chrono::milliseconds(_milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator boost::posix_time::time_duration() const {
|
||||||
|
return to_posix_time();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
size_t _milliseconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace tcp
|
||||||
|
} // namespace low_level
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma push_macro("check")
|
||||||
|
#undef check
|
||||||
|
|
||||||
|
#define LIBCARLA_INCLUDED_FROM_UE4
|
||||||
|
|
||||||
|
#ifndef BOOST_ERROR_CODE_HEADER_ONLY
|
||||||
|
# define BOOST_ERROR_CODE_HEADER_ONLY
|
||||||
|
#endif // BOOST_ERROR_CODE_HEADER_ONLY
|
||||||
|
|
||||||
|
#ifndef BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||||
|
# define BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
||||||
|
#endif // BOOST_COROUTINES_NO_DEPRECATION_WARNING
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma pop_macro("check")
|
||||||
|
|
||||||
|
#undef LIBCARLA_INCLUDED_FROM_UE4
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
# define LIBCARLA_LOG_LEVEL LIBCARLA_LOG_LEVEL_INFO
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
|
#include "test/util/Message.h"
|
||||||
|
|
||||||
|
#include <carla/Logging.h>
|
||||||
|
#include <carla/profiler/Profiler.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
constexpr uint16_t TESTING_PORT = 8080u;
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
|
@ -0,0 +1,137 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/streaming/Client.h>
|
||||||
|
#include <carla/streaming/Server.h>
|
||||||
|
|
||||||
|
using namespace carla::streaming;
|
||||||
|
|
||||||
|
static auto make_special_message(size_t size) {
|
||||||
|
std::vector<uint32_t> v(size/sizeof(uint32_t), 42u);
|
||||||
|
Message msg(boost::asio::buffer(v));
|
||||||
|
EXPECT_EQ(msg.size(), size);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Benchmark {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Benchmark(uint16_t port, size_t message_size)
|
||||||
|
: _server(port),
|
||||||
|
_client(),
|
||||||
|
_message(make_special_message(message_size)),
|
||||||
|
_client_callback(),
|
||||||
|
_work_to_do(_client_callback) {}
|
||||||
|
|
||||||
|
void AddStream() {
|
||||||
|
Stream stream = _server.MakeStream();
|
||||||
|
|
||||||
|
_client.Subscribe(stream.token(), [this](std::shared_ptr<Message> msg) {
|
||||||
|
DEBUG_ASSERT_EQ(msg->size(), _message.size());
|
||||||
|
DEBUG_ASSERT(*msg == _message);
|
||||||
|
_client_callback.post([this]() {
|
||||||
|
CARLA_PROFILE_FPS(client, listen_callback);
|
||||||
|
++_number_of_messages_received;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
_streams.push_back(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddStreams(size_t count) {
|
||||||
|
for (auto i = 0u; i < count; ++i) {
|
||||||
|
AddStream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Run(size_t number_of_messages) {
|
||||||
|
_threads.CreateThread([this]() { _client_callback.run(); });
|
||||||
|
_server.AsyncRun(_streams.size());
|
||||||
|
_client.AsyncRun(_streams.size());
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s); // the client needs to be ready so we make
|
||||||
|
// sure we get all the messages.
|
||||||
|
|
||||||
|
for (auto &&stream : _streams) {
|
||||||
|
_threads.CreateThread([=]() mutable {
|
||||||
|
for (auto i = 0u; i < number_of_messages; ++i) {
|
||||||
|
CARLA_PROFILE_SCOPE(game, write_to_stream);
|
||||||
|
stream << _message.buffer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < 30; ++i) {
|
||||||
|
if (_number_of_messages_received >= (_streams.size() * number_of_messages)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::cout << "received only " << _number_of_messages_received
|
||||||
|
<< " messages, waiting..." << std::endl;
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
}
|
||||||
|
|
||||||
|
_client_callback.stop();
|
||||||
|
_threads.JoinAll();
|
||||||
|
|
||||||
|
std::cout << _number_of_messages_received << " messages received; done.\n";
|
||||||
|
|
||||||
|
_client.Stop();
|
||||||
|
_server.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
carla::ThreadGroup _threads;
|
||||||
|
|
||||||
|
Server _server;
|
||||||
|
|
||||||
|
Client _client;
|
||||||
|
|
||||||
|
const Message _message;
|
||||||
|
|
||||||
|
boost::asio::io_service _client_callback;
|
||||||
|
|
||||||
|
boost::asio::io_service::work _work_to_do;
|
||||||
|
|
||||||
|
std::vector<Stream> _streams;
|
||||||
|
|
||||||
|
std::atomic_size_t _number_of_messages_received{0u};
|
||||||
|
};
|
||||||
|
|
||||||
|
static void benchmark_image(
|
||||||
|
const size_t dimensions,
|
||||||
|
const size_t number_of_streams = 1u) {
|
||||||
|
constexpr auto number_of_messages = 100u;
|
||||||
|
Benchmark benchmark(TESTING_PORT, 4u * dimensions);
|
||||||
|
benchmark.AddStreams(number_of_streams);
|
||||||
|
benchmark.Run(number_of_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_200x200) {
|
||||||
|
benchmark_image(200u * 200u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_800x600) {
|
||||||
|
benchmark_image(800u * 600u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_1920x1080) {
|
||||||
|
benchmark_image(1920u * 1080u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_200x200_mt) {
|
||||||
|
benchmark_image(200u * 200u, 9u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_800x600_mt) {
|
||||||
|
benchmark_image(800u * 600u, 9u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(benchmark_streaming, image_1920x1080_mt) {
|
||||||
|
benchmark_image(1920u * 1080u, 9u);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/Version.h>
|
||||||
|
|
||||||
|
TEST(miscellaneous, version) {
|
||||||
|
std::cout << "LibCarla " << carla::version() << std::endl;
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/ThreadGroup.h>
|
||||||
|
#include <carla/rpc/Client.h>
|
||||||
|
#include <carla/rpc/Server.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace carla::rpc;
|
||||||
|
|
||||||
|
TEST(rpc, compilation_tests) {
|
||||||
|
Server server(TESTING_PORT);
|
||||||
|
server.BindSync("bind00", []() { return 2.0f; });
|
||||||
|
server.BindSync("bind01", [](int x) { return x; });
|
||||||
|
server.BindSync("bind02", [](int, float) { return 0.0; });
|
||||||
|
server.BindSync("bind03", [](int, float, double, char) {});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(rpc, server_bind_sync_run_on_game_thread) {
|
||||||
|
const auto main_thread_id = std::this_thread::get_id();
|
||||||
|
|
||||||
|
Server server(TESTING_PORT);
|
||||||
|
|
||||||
|
server.BindSync("do_the_thing", [=](int x, int y) -> int {
|
||||||
|
EXPECT_EQ(std::this_thread::get_id(), main_thread_id);
|
||||||
|
return x + y;
|
||||||
|
});
|
||||||
|
|
||||||
|
server.AsyncRun(1u);
|
||||||
|
|
||||||
|
std::atomic_bool done{false};
|
||||||
|
|
||||||
|
carla::ThreadGroup threads;
|
||||||
|
threads.CreateThread([&]() {
|
||||||
|
Client client("localhost", TESTING_PORT);
|
||||||
|
for (auto i = 0u; i < 300u; ++i) {
|
||||||
|
auto result = client.call("do_the_thing", i, 1).as<int>();
|
||||||
|
EXPECT_EQ(result, i + 1);
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto i = 0u;
|
||||||
|
for (; i < 1'000'000u; ++i) {
|
||||||
|
server.SyncRunFor(2ms);
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "game thread: run " << i << " slices.\n";
|
||||||
|
ASSERT_TRUE(done);
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/ThreadGroup.h>
|
||||||
|
#include <carla/streaming/low_level/Client.h>
|
||||||
|
#include <carla/streaming/low_level/Server.h>
|
||||||
|
#include <carla/streaming/low_level/tcp/Client.h>
|
||||||
|
#include <carla/streaming/low_level/tcp/Server.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
TEST(streaming_low_level, sending_strings) {
|
||||||
|
using namespace util::message;
|
||||||
|
using namespace carla::streaming::low_level;
|
||||||
|
|
||||||
|
constexpr auto number_of_messages = 5'000u;
|
||||||
|
const std::string message_text = "Hello client!";
|
||||||
|
|
||||||
|
std::atomic_size_t message_count{0u};
|
||||||
|
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
|
||||||
|
Server<tcp::Server> srv(io_service, TESTING_PORT);
|
||||||
|
srv.set_timeout(1s);
|
||||||
|
|
||||||
|
auto stream = srv.MakeStream();
|
||||||
|
|
||||||
|
Client<tcp::Client> c;
|
||||||
|
c.Subscribe(io_service, stream.token(), [&](auto message) {
|
||||||
|
++message_count;
|
||||||
|
ASSERT_NE(message, nullptr);
|
||||||
|
ASSERT_EQ(message->size(), message_text.size());
|
||||||
|
const std::string msg = as_string(*message);
|
||||||
|
ASSERT_EQ(msg, message_text);
|
||||||
|
});
|
||||||
|
|
||||||
|
carla::ThreadGroup threads;
|
||||||
|
threads.CreateThreads(
|
||||||
|
std::max(2u, std::thread::hardware_concurrency()),
|
||||||
|
[&]() { io_service.run(); });
|
||||||
|
|
||||||
|
for (auto i = 0u; i < number_of_messages; ++i) {
|
||||||
|
stream << message_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(1s);
|
||||||
|
io_service.stop();
|
||||||
|
|
||||||
|
std::cout << "client received " << message_count << " messages\n";
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/ThreadGroup.h>
|
||||||
|
#include <carla/streaming/low_level/tcp/Client.h>
|
||||||
|
#include <carla/streaming/low_level/tcp/Server.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
TEST(streaming_low_level_tcp, small_message) {
|
||||||
|
using namespace util::message;
|
||||||
|
using namespace carla::streaming::low_level;
|
||||||
|
using shared_session = std::shared_ptr<tcp::ServerSession>;
|
||||||
|
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
tcp::Server::endpoint ep(boost::asio::ip::tcp::v4(), TESTING_PORT);
|
||||||
|
|
||||||
|
tcp::Server srv(io_service, ep);
|
||||||
|
srv.set_timeout(1s);
|
||||||
|
std::atomic_bool done{false};
|
||||||
|
std::atomic_size_t message_count{0u};
|
||||||
|
|
||||||
|
srv.Listen([&](std::shared_ptr<tcp::ServerSession> session) {
|
||||||
|
ASSERT_EQ(session->get_stream_id(), 42u);
|
||||||
|
const std::string msg = "Hola!";
|
||||||
|
auto message = std::make_shared<Message>(boost::asio::buffer(msg));
|
||||||
|
while (!done) {
|
||||||
|
session->Write(message);
|
||||||
|
std::this_thread::sleep_for(1ns);
|
||||||
|
}
|
||||||
|
std::cout << "done!\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
tcp::Client c(io_service, ep, 42u, [&](std::shared_ptr<Message> message) {
|
||||||
|
++message_count;
|
||||||
|
ASSERT_NE(message, nullptr);
|
||||||
|
ASSERT_EQ(message->size(), 5u);
|
||||||
|
const std::string msg = as_string(*message);
|
||||||
|
ASSERT_EQ(msg, std::string("Hola!"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// We need at least two threads because this server loop consumes one.
|
||||||
|
carla::ThreadGroup threads;
|
||||||
|
threads.CreateThreads(
|
||||||
|
std::max(2u, std::thread::hardware_concurrency()),
|
||||||
|
[&]() { io_service.run(); });
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(2s);
|
||||||
|
io_service.stop();
|
||||||
|
done = true;
|
||||||
|
std::cout << "client received " << message_count << " messages\n";
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#include <carla/streaming/Message.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace util::message;
|
||||||
|
|
||||||
|
TEST(streaming_message, to_from_string) {
|
||||||
|
const std::string str = "The quick brown fox jumps over the lazy dog";
|
||||||
|
Message msg(boost::asio::buffer(str));
|
||||||
|
ASSERT_EQ(msg.size(), str.size());
|
||||||
|
const std::string result = as_string(msg);
|
||||||
|
ASSERT_EQ(result, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(streaming_message, to_from_vector) {
|
||||||
|
constexpr auto size = 1000u;
|
||||||
|
using T = size_t;
|
||||||
|
std::vector<T> v;
|
||||||
|
v.reserve(size);
|
||||||
|
for (auto i = 0u; i < size; ++i) {
|
||||||
|
v.emplace_back(i);
|
||||||
|
}
|
||||||
|
Message msg(boost::asio::buffer(v));
|
||||||
|
ASSERT_EQ(msg.size(), sizeof(T) * size);
|
||||||
|
auto begin = reinterpret_cast<const T *>(msg.data());
|
||||||
|
std::vector<T> result(begin, begin + size);
|
||||||
|
ASSERT_EQ(result, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(streaming_message, memcpy) {
|
||||||
|
const std::string str = "The quick brown fox jumps over the lazy dog";
|
||||||
|
Message msg(str.size());
|
||||||
|
ASSERT_EQ(msg.size(), str.size());
|
||||||
|
auto buffer = msg.buffer();
|
||||||
|
std::memcpy(buffer.data(), str.data(), buffer.size());
|
||||||
|
const std::string result = as_string(msg);
|
||||||
|
ASSERT_EQ(result, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(streaming_message, message_too_big) {
|
||||||
|
ASSERT_THROW(Message(4294967296ul), std::invalid_argument);
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#include "Message.h"
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
namespace message {
|
||||||
|
|
||||||
|
shared_message make_empty(size_t size) {
|
||||||
|
return size == 0u ?
|
||||||
|
std::make_shared<Message>() :
|
||||||
|
std::make_shared<Message>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_message make_random(size_t size) {
|
||||||
|
if (size == 0u)
|
||||||
|
return make_empty();
|
||||||
|
using random_bytes_engine = std::independent_bits_engine<
|
||||||
|
std::random_device,
|
||||||
|
CHAR_BIT,
|
||||||
|
unsigned char>;
|
||||||
|
random_bytes_engine rbe;
|
||||||
|
auto message = make_empty(size);
|
||||||
|
std::generate(begin(*message), end(*message), std::ref(rbe));
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_hex_string(const Message &msg, size_t length) {
|
||||||
|
length = std::min(static_cast<size_t>(msg.size()), length);
|
||||||
|
auto buffer = std::make_unique<char[]>(2u * length + 1u);
|
||||||
|
for (auto i = 0u; i < length; ++i)
|
||||||
|
sprintf(&buffer[2u * i], "%02x", msg.data()[i]);
|
||||||
|
if (length < msg.size())
|
||||||
|
return std::string(buffer.get()) + std::string("...");
|
||||||
|
return std::string(buffer.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace message
|
||||||
|
} // namespace util
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||||
|
// de Barcelona (UAB).
|
||||||
|
//
|
||||||
|
// This work is licensed under the terms of the MIT license.
|
||||||
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <carla/streaming/Message.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
namespace message {
|
||||||
|
|
||||||
|
using carla::streaming::Message;
|
||||||
|
|
||||||
|
using shared_message = std::shared_ptr<Message>;
|
||||||
|
using const_shared_message = std::shared_ptr<const Message>;
|
||||||
|
|
||||||
|
static inline shared_message make_empty() {
|
||||||
|
return std::make_shared<Message>();
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_message make_empty(size_t size);
|
||||||
|
|
||||||
|
shared_message make_random(size_t size);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline shared_message make(const T &buffer) {
|
||||||
|
return std::make_shared<Message>(boost::asio::buffer(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string as_string(const Message &msg) {
|
||||||
|
return {reinterpret_cast<const char *>(msg.data()), msg.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string to_hex_string(const Message &msg, size_t length = 16u);
|
||||||
|
|
||||||
|
} // namespace message
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
namespace carla {
|
||||||
|
namespace streaming {
|
||||||
|
|
||||||
|
static inline unsigned char *begin(Message &msg) {
|
||||||
|
return msg.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const unsigned char *begin(const Message &msg) {
|
||||||
|
return msg.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned char *end(Message &msg) {
|
||||||
|
return msg.data() + msg.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const unsigned char *end(const Message &msg) {
|
||||||
|
return msg.data() + msg.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::ostream &operator<<(std::ostream &out, const Message &msg) {
|
||||||
|
out << "[" << msg.size() << " bytes] " << util::message::to_hex_string(msg);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool operator==(const Message &lhs, const Message &rhs) {
|
||||||
|
return
|
||||||
|
(lhs.size() == rhs.size()) &&
|
||||||
|
std::equal(begin(lhs), end(lhs), begin(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool operator!=(const Message &lhs, const Message &rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace streaming
|
||||||
|
} // namespace carla
|
Loading…
Reference in New Issue