+++ /dev/null
-clone_depth: 5
-
-environment:
- matrix:
- - GENERATOR : "Visual Studio 15 2017 Win64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- PLATFORM: x64
-
-
-configuration:
- - Release
-
-install:
- - set PATH=C:\MinGW\bin;C:\MinGW\msys\1.0;%PATH%
-
-before_build:
- - mkdir build
- - cd build
- - cmake "-G%GENERATOR%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" ..
-
-build_script:
-- cmake --build .
--- /dev/null
+name: CMake Build Matrix
+
+on: [push]
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally
+ # well on Windows or Mac. You can convert this to a matrix build if you need
+ # cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest]
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install Dependencies (Linux)
+ run: sudo apt-get install libboost-dev
+ if: matrix.os == 'ubuntu-latest'
+
+ - name: Create Build Environment
+ # Some projects don't allow in-source building, so create a separate build directory
+ # We'll use this as our working directory for all subsequent commands
+ run: cmake -E make_directory ${{github.workspace}}/build
+
+ - name: Configure CMake
+ # Use a bash shell so we can use the same syntax for environment variable
+ # access regardless of the host operating system
+ shell: bash
+ working-directory: ${{github.workspace}}/build
+ # Note the current convention is to use the -S and -B options here to specify source
+ # and build directories, but this is only available with CMake 3.13 and higher.
+ # The CMake binaries on the Github Actions machines are (as of this writing) 3.12
+ run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
+
+ - name: Build
+ working-directory: ${{github.workspace}}/build
+ shell: bash
+ # Execute the build. You can specify a specific target with "--target <NAME>"
+ run: cmake --build . --config $BUILD_TYPE
+
+ - name: run test (Linux)
+ if: matrix.os == 'ubuntu-latest'
+ working-directory: ${{github.workspace}}/build
+ run: ./bin/behaviortree_cpp_v3_test
+
--- /dev/null
+*~
+/CMakeLists.txt.user
+build*
+site/*
+/.vscode/
+++ /dev/null
-# This config file for Travis CI utilizes ros-industrial/industrial_ci package.
-# For more info for the package, see https://github.com/ros-industrial/industrial_ci/blob/master/README.rst
-
-sudo: required
-dist: xenial
-language: cpp
-
-os:
- - linux
-
-compiler:
- - gcc
-
-conan-linux: &conan-linux
- os: linux
- dist: xenial
- language: python
- python: "3.7"
- services:
- - docker
- before_install:
- - true
- install:
- - ./conan/travis/install.sh
- script:
- - ./conan/travis/build.sh
-
-conan-osx: &conan-osx
- os: osx
- language: generic
- before_install:
- - true
- install:
- - ./conan/travis/install.sh
- script:
- - ./conan/travis/build.sh
-
-matrix:
- include:
- - bare_linux:
- env: ROS_DISTRO="none"
- fast_finish: false
-
-before_install:
- - sudo apt-get update && sudo apt-get --reinstall install -qq build-essential
- - if [ "$ROS_DISTRO" = "none" ]; then sudo apt-get --reinstall install -qq libzmq3-dev libdw-dev; fi
- # GTest: see motivation here https://www.eriksmistad.no/getting-started-with-google-test-on-ubuntu/
- - sudo apt-get --reinstall install -qq libgtest-dev cmake
- - cd /usr/src/gtest
- - sudo cmake CMakeLists.txt
- - sudo make
- - sudo cp *.a /usr/lib
- - cd $TRAVIS_BUILD_DIR
-
-install:
- - if [ "$ROS_DISTRO" != "none" ]; then git clone https://github.com/ros-industrial/industrial_ci.git .ci_config; fi
-
-before_script:
- # Prepare build directory
- - mkdir -p build
-
-script:
- - if [ "$ROS_DISTRO" = "none" ]; then (cd build; cmake .. ; sudo cmake --build . --target install; ./bin/behaviortree_cpp_v3_test); fi
- - if [ "$ROS_DISTRO" != "none" ]; then (.ci_config/travis.sh); fi
-
-
--- /dev/null
+/*
+ Copyright (c) 2016-2017 ZeroMQ community
+ Copyright (c) 2009-2011 250bpm s.r.o.
+ Copyright (c) 2011 Botond Ballo
+ Copyright (c) 2007-2009 iMatix Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+*/
+
+#ifndef __ZMQ_HPP_INCLUDED__
+#define __ZMQ_HPP_INCLUDED__
+
+#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#endif
+
+// included here for _HAS_CXX* macros
+#include <zmq.h>
+
+#if defined(_MSVC_LANG)
+#define CPPZMQ_LANG _MSVC_LANG
+#else
+#define CPPZMQ_LANG __cplusplus
+#endif
+// overwrite if specific language macros indicate higher version
+#if defined(_HAS_CXX14) && _HAS_CXX14 && CPPZMQ_LANG < 201402L
+#undef CPPZMQ_LANG
+#define CPPZMQ_LANG 201402L
+#endif
+#if defined(_HAS_CXX17) && _HAS_CXX17 && CPPZMQ_LANG < 201703L
+#undef CPPZMQ_LANG
+#define CPPZMQ_LANG 201703L
+#endif
+
+// macros defined if has a specific standard or greater
+#if CPPZMQ_LANG >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+#define ZMQ_CPP11
+#endif
+#if CPPZMQ_LANG >= 201402L
+#define ZMQ_CPP14
+#endif
+#if CPPZMQ_LANG >= 201703L
+#define ZMQ_CPP17
+#endif
+
+#if defined(ZMQ_CPP14) && !defined(_MSC_VER)
+#define ZMQ_DEPRECATED(msg) [[deprecated(msg)]]
+#elif defined(_MSC_VER)
+#define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg))
+#elif defined(__GNUC__)
+#define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg)))
+#endif
+
+#if defined(ZMQ_CPP17)
+#define ZMQ_NODISCARD [[nodiscard]]
+#else
+#define ZMQ_NODISCARD
+#endif
+
+#if defined(ZMQ_CPP11)
+#define ZMQ_NOTHROW noexcept
+#define ZMQ_EXPLICIT explicit
+#define ZMQ_OVERRIDE override
+#define ZMQ_NULLPTR nullptr
+#define ZMQ_CONSTEXPR_FN constexpr
+#define ZMQ_CONSTEXPR_VAR constexpr
+#define ZMQ_CPP11_DEPRECATED(msg) ZMQ_DEPRECATED(msg)
+#else
+#define ZMQ_NOTHROW throw()
+#define ZMQ_EXPLICIT
+#define ZMQ_OVERRIDE
+#define ZMQ_NULLPTR 0
+#define ZMQ_CONSTEXPR_FN
+#define ZMQ_CONSTEXPR_VAR const
+#define ZMQ_CPP11_DEPRECATED(msg)
+#endif
+#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900)
+#define ZMQ_EXTENDED_CONSTEXPR
+#endif
+#if defined(ZMQ_CPP17)
+#define ZMQ_INLINE_VAR inline
+#define ZMQ_CONSTEXPR_IF constexpr
+#else
+#define ZMQ_INLINE_VAR
+#define ZMQ_CONSTEXPR_IF
+#endif
+
+#include <cassert>
+#include <cstring>
+
+#include <algorithm>
+#include <exception>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+#ifdef ZMQ_CPP11
+#include <array>
+#include <chrono>
+#include <tuple>
+#include <memory>
+#endif
+
+#if defined(__has_include) && defined(ZMQ_CPP17)
+#define CPPZMQ_HAS_INCLUDE_CPP17(X) __has_include(X)
+#else
+#define CPPZMQ_HAS_INCLUDE_CPP17(X) 0
+#endif
+
+#if CPPZMQ_HAS_INCLUDE_CPP17(<optional>) && !defined(CPPZMQ_HAS_OPTIONAL)
+#define CPPZMQ_HAS_OPTIONAL 1
+#endif
+#ifndef CPPZMQ_HAS_OPTIONAL
+#define CPPZMQ_HAS_OPTIONAL 0
+#elif CPPZMQ_HAS_OPTIONAL
+#include <optional>
+#endif
+
+#if CPPZMQ_HAS_INCLUDE_CPP17(<string_view>) && !defined(CPPZMQ_HAS_STRING_VIEW)
+#define CPPZMQ_HAS_STRING_VIEW 1
+#endif
+#ifndef CPPZMQ_HAS_STRING_VIEW
+#define CPPZMQ_HAS_STRING_VIEW 0
+#elif CPPZMQ_HAS_STRING_VIEW
+#include <string_view>
+#endif
+
+/* Version macros for compile-time API version detection */
+#define CPPZMQ_VERSION_MAJOR 4
+#define CPPZMQ_VERSION_MINOR 8
+#define CPPZMQ_VERSION_PATCH 0
+
+#define CPPZMQ_VERSION \
+ ZMQ_MAKE_VERSION(CPPZMQ_VERSION_MAJOR, CPPZMQ_VERSION_MINOR, \
+ CPPZMQ_VERSION_PATCH)
+
+// Detect whether the compiler supports C++11 rvalue references.
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) \
+ && defined(__GXX_EXPERIMENTAL_CXX0X__))
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION = delete
+#elif defined(__clang__)
+#if __has_feature(cxx_rvalue_references)
+#define ZMQ_HAS_RVALUE_REFS
+#endif
+
+#if __has_feature(cxx_deleted_functions)
+#define ZMQ_DELETED_FUNCTION = delete
+#else
+#define ZMQ_DELETED_FUNCTION
+#endif
+#elif defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION = delete
+#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION
+#else
+#define ZMQ_DELETED_FUNCTION
+#endif
+
+#if defined(ZMQ_CPP11) && !defined(__llvm__) && !defined(__INTEL_COMPILER) \
+ && defined(__GNUC__) && __GNUC__ < 5
+#define ZMQ_CPP11_PARTIAL
+#elif defined(__GLIBCXX__) && __GLIBCXX__ < 20160805
+//the date here is the last date of gcc 4.9.4, which
+// effectively means libstdc++ from gcc 5.5 and higher won't trigger this branch
+#define ZMQ_CPP11_PARTIAL
+#endif
+
+#ifdef ZMQ_CPP11
+#ifdef ZMQ_CPP11_PARTIAL
+#define ZMQ_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
+#else
+#include <type_traits>
+#define ZMQ_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
+#endif
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0)
+#define ZMQ_NEW_MONITOR_EVENT_LAYOUT
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
+#define ZMQ_HAS_PROXY_STEERABLE
+/* Socket event data */
+typedef struct
+{
+ uint16_t event; // id of the event as bitfield
+ int32_t value; // value is either error code, fd or reconnect interval
+} zmq_event_t;
+#endif
+
+// Avoid using deprecated message receive function when possible
+#if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0)
+#define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags)
+#endif
+
+
+// In order to prevent unused variable warnings when building in non-debug
+// mode use this macro to make assertions.
+#ifndef NDEBUG
+#define ZMQ_ASSERT(expression) assert(expression)
+#else
+#define ZMQ_ASSERT(expression) (void) (expression)
+#endif
+
+namespace zmq
+{
+#ifdef ZMQ_CPP11
+namespace detail
+{
+namespace ranges
+{
+using std::begin;
+using std::end;
+template<class T> auto begin(T &&r) -> decltype(begin(std::forward<T>(r)))
+{
+ return begin(std::forward<T>(r));
+}
+template<class T> auto end(T &&r) -> decltype(end(std::forward<T>(r)))
+{
+ return end(std::forward<T>(r));
+}
+} // namespace ranges
+
+template<class T> using void_t = void;
+
+template<class Iter>
+using iter_value_t = typename std::iterator_traits<Iter>::value_type;
+
+template<class Range>
+using range_iter_t = decltype(
+ ranges::begin(std::declval<typename std::remove_reference<Range>::type &>()));
+
+template<class Range> using range_value_t = iter_value_t<range_iter_t<Range>>;
+
+template<class T, class = void> struct is_range : std::false_type
+{
+};
+
+template<class T>
+struct is_range<
+ T,
+ void_t<decltype(
+ ranges::begin(std::declval<typename std::remove_reference<T>::type &>())
+ == ranges::end(std::declval<typename std::remove_reference<T>::type &>()))>>
+ : std::true_type
+{
+};
+
+} // namespace detail
+#endif
+
+typedef zmq_free_fn free_fn;
+typedef zmq_pollitem_t pollitem_t;
+
+// duplicate definition from libzmq 4.3.3
+#if defined _WIN32
+#if defined _WIN64
+typedef unsigned __int64 fd_t;
+#else
+typedef unsigned int fd_t;
+#endif
+#else
+typedef int fd_t;
+#endif
+
+class error_t : public std::exception
+{
+ public:
+ error_t() ZMQ_NOTHROW : errnum(zmq_errno()) {}
+ explicit error_t(int err) ZMQ_NOTHROW : errnum(err) {}
+ virtual const char *what() const ZMQ_NOTHROW ZMQ_OVERRIDE
+ {
+ return zmq_strerror(errnum);
+ }
+ int num() const ZMQ_NOTHROW { return errnum; }
+
+ private:
+ int errnum;
+};
+
+inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1)
+{
+ int rc = zmq_poll(items_, static_cast<int>(nitems_), timeout_);
+ if (rc < 0)
+ throw error_t();
+ return rc;
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(zmq_pollitem_t const *items_, size_t nitems_, long timeout_ = -1)
+{
+ return poll(const_cast<zmq_pollitem_t *>(items_), nitems_, timeout_);
+}
+
+#ifdef ZMQ_CPP11
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int
+poll(zmq_pollitem_t const *items, size_t nitems, std::chrono::milliseconds timeout)
+{
+ return poll(const_cast<zmq_pollitem_t *>(items), nitems,
+ static_cast<long>(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(std::vector<zmq_pollitem_t> const &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(const_cast<zmq_pollitem_t *>(items.data()), items.size(),
+ static_cast<long>(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(std::vector<zmq_pollitem_t> const &items, long timeout_ = -1)
+{
+ return poll(const_cast<zmq_pollitem_t *>(items.data()), items.size(), timeout_);
+}
+
+inline int
+poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout)
+{
+ return poll(items, nitems, static_cast<long>(timeout.count()));
+}
+
+inline int poll(std::vector<zmq_pollitem_t> &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(items.data(), items.size(), static_cast<long>(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono instead of long")
+inline int poll(std::vector<zmq_pollitem_t> &items, long timeout_ = -1)
+{
+ return poll(items.data(), items.size(), timeout_);
+}
+
+template<std::size_t SIZE>
+inline int poll(std::array<zmq_pollitem_t, SIZE> &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(items.data(), items.size(), static_cast<long>(timeout.count()));
+}
+#endif
+
+
+inline void version(int *major_, int *minor_, int *patch_)
+{
+ zmq_version(major_, minor_, patch_);
+}
+
+#ifdef ZMQ_CPP11
+inline std::tuple<int, int, int> version()
+{
+ std::tuple<int, int, int> v;
+ zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v));
+ return v;
+}
+
+#if !defined(ZMQ_CPP11_PARTIAL)
+namespace detail
+{
+template<class T> struct is_char_type
+{
+ // true if character type for string literals in C++11
+ static constexpr bool value =
+ std::is_same<T, char>::value || std::is_same<T, wchar_t>::value
+ || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value;
+};
+}
+#endif
+
+#endif
+
+class message_t
+{
+ public:
+ message_t() ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_init(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ explicit message_t(size_t size_)
+ {
+ int rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ template<class ForwardIter> message_t(ForwardIter first, ForwardIter last)
+ {
+ typedef typename std::iterator_traits<ForwardIter>::value_type value_t;
+
+ assert(std::distance(first, last) >= 0);
+ size_t const size_ =
+ static_cast<size_t>(std::distance(first, last)) * sizeof(value_t);
+ int const rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ std::copy(first, last, data<value_t>());
+ }
+
+ message_t(const void *data_, size_t size_)
+ {
+ int rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ if (size_) {
+ // this constructor allows (nullptr, 0),
+ // memcpy with a null pointer is UB
+ memcpy(data(), data_, size_);
+ }
+ }
+
+ message_t(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
+ {
+ int rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ // overload set of string-like types and generic containers
+#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
+ // NOTE this constructor will include the null terminator
+ // when called with a string literal.
+ // An overload taking const char* can not be added because
+ // it would be preferred over this function and break compatiblity.
+ template<
+ class Char,
+ size_t N,
+ typename = typename std::enable_if<detail::is_char_type<Char>::value>::type>
+ ZMQ_DEPRECATED("from 4.7.0, use constructors taking iterators, (pointer, size) "
+ "or strings instead")
+ explicit message_t(const Char (&data)[N]) :
+ message_t(detail::ranges::begin(data), detail::ranges::end(data))
+ {
+ }
+
+ template<class Range,
+ typename = typename std::enable_if<
+ detail::is_range<Range>::value
+ && ZMQ_IS_TRIVIALLY_COPYABLE(detail::range_value_t<Range>)
+ && !detail::is_char_type<detail::range_value_t<Range>>::value
+ && !std::is_same<Range, message_t>::value>::type>
+ explicit message_t(const Range &rng) :
+ message_t(detail::ranges::begin(rng), detail::ranges::end(rng))
+ {
+ }
+
+ explicit message_t(const std::string &str) : message_t(str.data(), str.size()) {}
+
+#if CPPZMQ_HAS_STRING_VIEW
+ explicit message_t(std::string_view str) : message_t(str.data(), str.size()) {}
+#endif
+
+#endif
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ message_t(message_t &&rhs) ZMQ_NOTHROW : msg(rhs.msg)
+ {
+ int rc = zmq_msg_init(&rhs.msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ message_t &operator=(message_t &&rhs) ZMQ_NOTHROW
+ {
+ std::swap(msg, rhs.msg);
+ return *this;
+ }
+#endif
+
+ ~message_t() ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_close(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ void rebuild()
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ void rebuild(size_t size_)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void rebuild(const void *data_, size_t size_)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ memcpy(data(), data_, size_);
+ }
+
+ void rebuild(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_DEPRECATED("from 4.3.1, use move taking non-const reference instead")
+ void move(message_t const *msg_)
+ {
+ int rc = zmq_msg_move(&msg, const_cast<zmq_msg_t *>(msg_->handle()));
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void move(message_t &msg_)
+ {
+ int rc = zmq_msg_move(&msg, msg_.handle());
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_DEPRECATED("from 4.3.1, use copy taking non-const reference instead")
+ void copy(message_t const *msg_)
+ {
+ int rc = zmq_msg_copy(&msg, const_cast<zmq_msg_t *>(msg_->handle()));
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void copy(message_t &msg_)
+ {
+ int rc = zmq_msg_copy(&msg, msg_.handle());
+ if (rc != 0)
+ throw error_t();
+ }
+
+ bool more() const ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_more(const_cast<zmq_msg_t *>(&msg));
+ return rc != 0;
+ }
+
+ void *data() ZMQ_NOTHROW { return zmq_msg_data(&msg); }
+
+ const void *data() const ZMQ_NOTHROW
+ {
+ return zmq_msg_data(const_cast<zmq_msg_t *>(&msg));
+ }
+
+ size_t size() const ZMQ_NOTHROW
+ {
+ return zmq_msg_size(const_cast<zmq_msg_t *>(&msg));
+ }
+
+ ZMQ_NODISCARD bool empty() const ZMQ_NOTHROW { return size() == 0u; }
+
+ template<typename T> T *data() ZMQ_NOTHROW { return static_cast<T *>(data()); }
+
+ template<typename T> T const *data() const ZMQ_NOTHROW
+ {
+ return static_cast<T const *>(data());
+ }
+
+ ZMQ_DEPRECATED("from 4.3.0, use operator== instead")
+ bool equal(const message_t *other) const ZMQ_NOTHROW { return *this == *other; }
+
+ bool operator==(const message_t &other) const ZMQ_NOTHROW
+ {
+ const size_t my_size = size();
+ return my_size == other.size() && 0 == memcmp(data(), other.data(), my_size);
+ }
+
+ bool operator!=(const message_t &other) const ZMQ_NOTHROW
+ {
+ return !(*this == other);
+ }
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0)
+ int get(int property_)
+ {
+ int value = zmq_msg_get(&msg, property_);
+ if (value == -1)
+ throw error_t();
+ return value;
+ }
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
+ const char *gets(const char *property_)
+ {
+ const char *value = zmq_msg_gets(&msg, property_);
+ if (value == ZMQ_NULLPTR)
+ throw error_t();
+ return value;
+ }
+#endif
+
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ uint32_t routing_id() const
+ {
+ return zmq_msg_routing_id(const_cast<zmq_msg_t *>(&msg));
+ }
+
+ void set_routing_id(uint32_t routing_id)
+ {
+ int rc = zmq_msg_set_routing_id(&msg, routing_id);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ const char *group() const
+ {
+ return zmq_msg_group(const_cast<zmq_msg_t *>(&msg));
+ }
+
+ void set_group(const char *group)
+ {
+ int rc = zmq_msg_set_group(&msg, group);
+ if (rc != 0)
+ throw error_t();
+ }
+#endif
+
+ // interpret message content as a string
+ std::string to_string() const
+ {
+ return std::string(static_cast<const char *>(data()), size());
+ }
+#if CPPZMQ_HAS_STRING_VIEW
+ // interpret message content as a string
+ std::string_view to_string_view() const noexcept
+ {
+ return std::string_view(static_cast<const char *>(data()), size());
+ }
+#endif
+
+ /** Dump content to string for debugging.
+ * Ascii chars are readable, the rest is printed as hex.
+ * Probably ridiculously slow.
+ * Use to_string() or to_string_view() for
+ * interpreting the message as a string.
+ */
+ std::string str() const
+ {
+ // Partly mutuated from the same method in zmq::multipart_t
+ std::stringstream os;
+
+ const unsigned char *msg_data = this->data<unsigned char>();
+ unsigned char byte;
+ size_t size = this->size();
+ int is_ascii[2] = {0, 0};
+
+ os << "zmq::message_t [size " << std::dec << std::setw(3)
+ << std::setfill('0') << size << "] (";
+ // Totally arbitrary
+ if (size >= 1000) {
+ os << "... too big to print)";
+ } else {
+ while (size--) {
+ byte = *msg_data++;
+
+ is_ascii[1] = (byte >= 32 && byte < 127);
+ if (is_ascii[1] != is_ascii[0])
+ os << " "; // Separate text/non text
+
+ if (is_ascii[1]) {
+ os << byte;
+ } else {
+ os << std::hex << std::uppercase << std::setw(2)
+ << std::setfill('0') << static_cast<short>(byte);
+ }
+ is_ascii[0] = is_ascii[1];
+ }
+ os << ")";
+ }
+ return os.str();
+ }
+
+ void swap(message_t &other) ZMQ_NOTHROW
+ {
+ // this assumes zmq::msg_t from libzmq is trivially relocatable
+ std::swap(msg, other.msg);
+ }
+
+ ZMQ_NODISCARD zmq_msg_t *handle() ZMQ_NOTHROW { return &msg; }
+ ZMQ_NODISCARD const zmq_msg_t *handle() const ZMQ_NOTHROW { return &msg; }
+
+ private:
+ // The underlying message
+ zmq_msg_t msg;
+
+ // Disable implicit message copying, so that users won't use shared
+ // messages (less efficient) without being aware of the fact.
+ message_t(const message_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const message_t &) ZMQ_DELETED_FUNCTION;
+};
+
+inline void swap(message_t &a, message_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+#ifdef ZMQ_CPP11
+enum class ctxopt
+{
+#ifdef ZMQ_BLOCKY
+ blocky = ZMQ_BLOCKY,
+#endif
+#ifdef ZMQ_IO_THREADS
+ io_threads = ZMQ_IO_THREADS,
+#endif
+#ifdef ZMQ_THREAD_SCHED_POLICY
+ thread_sched_policy = ZMQ_THREAD_SCHED_POLICY,
+#endif
+#ifdef ZMQ_THREAD_PRIORITY
+ thread_priority = ZMQ_THREAD_PRIORITY,
+#endif
+#ifdef ZMQ_THREAD_AFFINITY_CPU_ADD
+ thread_affinity_cpu_add = ZMQ_THREAD_AFFINITY_CPU_ADD,
+#endif
+#ifdef ZMQ_THREAD_AFFINITY_CPU_REMOVE
+ thread_affinity_cpu_remove = ZMQ_THREAD_AFFINITY_CPU_REMOVE,
+#endif
+#ifdef ZMQ_THREAD_NAME_PREFIX
+ thread_name_prefix = ZMQ_THREAD_NAME_PREFIX,
+#endif
+#ifdef ZMQ_MAX_MSGSZ
+ max_msgsz = ZMQ_MAX_MSGSZ,
+#endif
+#ifdef ZMQ_ZERO_COPY_RECV
+ zero_copy_recv = ZMQ_ZERO_COPY_RECV,
+#endif
+#ifdef ZMQ_MAX_SOCKETS
+ max_sockets = ZMQ_MAX_SOCKETS,
+#endif
+#ifdef ZMQ_SOCKET_LIMIT
+ socket_limit = ZMQ_SOCKET_LIMIT,
+#endif
+#ifdef ZMQ_IPV6
+ ipv6 = ZMQ_IPV6,
+#endif
+#ifdef ZMQ_MSG_T_SIZE
+ msg_t_size = ZMQ_MSG_T_SIZE
+#endif
+};
+#endif
+
+class context_t
+{
+ public:
+ context_t()
+ {
+ ptr = zmq_ctx_new();
+ if (ptr == ZMQ_NULLPTR)
+ throw error_t();
+ }
+
+
+ explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
+ {
+ ptr = zmq_ctx_new();
+ if (ptr == ZMQ_NULLPTR)
+ throw error_t();
+
+ int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_);
+ ZMQ_ASSERT(rc == 0);
+
+ rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ context_t(context_t &&rhs) ZMQ_NOTHROW : ptr(rhs.ptr) { rhs.ptr = ZMQ_NULLPTR; }
+ context_t &operator=(context_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ std::swap(ptr, rhs.ptr);
+ return *this;
+ }
+#endif
+
+ ~context_t() ZMQ_NOTHROW { close(); }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use set taking zmq::ctxopt instead")
+ int setctxopt(int option_, int optval_)
+ {
+ int rc = zmq_ctx_set(ptr, option_, optval_);
+ ZMQ_ASSERT(rc == 0);
+ return rc;
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use get taking zmq::ctxopt instead")
+ int getctxopt(int option_) { return zmq_ctx_get(ptr, option_); }
+
+#ifdef ZMQ_CPP11
+ void set(ctxopt option, int optval)
+ {
+ int rc = zmq_ctx_set(ptr, static_cast<int>(option), optval);
+ if (rc == -1)
+ throw error_t();
+ }
+
+ ZMQ_NODISCARD int get(ctxopt option)
+ {
+ int rc = zmq_ctx_get(ptr, static_cast<int>(option));
+ // some options have a default value of -1
+ // which is unfortunate, and may result in errors
+ // that don't make sense
+ if (rc == -1)
+ throw error_t();
+ return rc;
+ }
+#endif
+
+ // Terminates context (see also shutdown()).
+ void close() ZMQ_NOTHROW
+ {
+ if (ptr == ZMQ_NULLPTR)
+ return;
+
+ int rc;
+ do {
+ rc = zmq_ctx_destroy(ptr);
+ } while (rc == -1 && errno == EINTR);
+
+ ZMQ_ASSERT(rc == 0);
+ ptr = ZMQ_NULLPTR;
+ }
+
+ // Shutdown context in preparation for termination (close()).
+ // Causes all blocking socket operations and any further
+ // socket operations to return with ETERM.
+ void shutdown() ZMQ_NOTHROW
+ {
+ if (ptr == ZMQ_NULLPTR)
+ return;
+ int rc = zmq_ctx_shutdown(ptr);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ // Be careful with this, it's probably only useful for
+ // using the C api together with an existing C++ api.
+ // Normally you should never need to use this.
+ ZMQ_EXPLICIT operator void *() ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_EXPLICIT operator void const *() const ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_DEPRECATED("from 4.7.0, use handle() != nullptr instead")
+ operator bool() const ZMQ_NOTHROW { return ptr != ZMQ_NULLPTR; }
+
+ void swap(context_t &other) ZMQ_NOTHROW { std::swap(ptr, other.ptr); }
+
+ private:
+ void *ptr;
+
+ context_t(const context_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const context_t &) ZMQ_DELETED_FUNCTION;
+};
+
+inline void swap(context_t &a, context_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+#ifdef ZMQ_CPP11
+
+struct recv_buffer_size
+{
+ size_t size; // number of bytes written to buffer
+ size_t untruncated_size; // untruncated message size in bytes
+
+ ZMQ_NODISCARD bool truncated() const noexcept
+ {
+ return size != untruncated_size;
+ }
+};
+
+#if CPPZMQ_HAS_OPTIONAL
+
+using send_result_t = std::optional<size_t>;
+using recv_result_t = std::optional<size_t>;
+using recv_buffer_result_t = std::optional<recv_buffer_size>;
+
+#else
+
+namespace detail
+{
+// A C++11 type emulating the most basic
+// operations of std::optional for trivial types
+template<class T> class trivial_optional
+{
+ public:
+ static_assert(std::is_trivial<T>::value, "T must be trivial");
+ using value_type = T;
+
+ trivial_optional() = default;
+ trivial_optional(T value) noexcept : _value(value), _has_value(true) {}
+
+ const T *operator->() const noexcept
+ {
+ assert(_has_value);
+ return &_value;
+ }
+ T *operator->() noexcept
+ {
+ assert(_has_value);
+ return &_value;
+ }
+
+ const T &operator*() const noexcept
+ {
+ assert(_has_value);
+ return _value;
+ }
+ T &operator*() noexcept
+ {
+ assert(_has_value);
+ return _value;
+ }
+
+ T &value()
+ {
+ if (!_has_value)
+ throw std::exception();
+ return _value;
+ }
+ const T &value() const
+ {
+ if (!_has_value)
+ throw std::exception();
+ return _value;
+ }
+
+ explicit operator bool() const noexcept { return _has_value; }
+ bool has_value() const noexcept { return _has_value; }
+
+ private:
+ T _value{};
+ bool _has_value{false};
+};
+} // namespace detail
+
+using send_result_t = detail::trivial_optional<size_t>;
+using recv_result_t = detail::trivial_optional<size_t>;
+using recv_buffer_result_t = detail::trivial_optional<recv_buffer_size>;
+
+#endif
+
+namespace detail
+{
+template<class T> constexpr T enum_bit_or(T a, T b) noexcept
+{
+ static_assert(std::is_enum<T>::value, "must be enum");
+ using U = typename std::underlying_type<T>::type;
+ return static_cast<T>(static_cast<U>(a) | static_cast<U>(b));
+}
+template<class T> constexpr T enum_bit_and(T a, T b) noexcept
+{
+ static_assert(std::is_enum<T>::value, "must be enum");
+ using U = typename std::underlying_type<T>::type;
+ return static_cast<T>(static_cast<U>(a) & static_cast<U>(b));
+}
+template<class T> constexpr T enum_bit_xor(T a, T b) noexcept
+{
+ static_assert(std::is_enum<T>::value, "must be enum");
+ using U = typename std::underlying_type<T>::type;
+ return static_cast<T>(static_cast<U>(a) ^ static_cast<U>(b));
+}
+template<class T> constexpr T enum_bit_not(T a) noexcept
+{
+ static_assert(std::is_enum<T>::value, "must be enum");
+ using U = typename std::underlying_type<T>::type;
+ return static_cast<T>(~static_cast<U>(a));
+}
+} // namespace detail
+
+// partially satisfies named requirement BitmaskType
+enum class send_flags : int
+{
+ none = 0,
+ dontwait = ZMQ_DONTWAIT,
+ sndmore = ZMQ_SNDMORE
+};
+
+constexpr send_flags operator|(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr send_flags operator&(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr send_flags operator^(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr send_flags operator~(send_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+// partially satisfies named requirement BitmaskType
+enum class recv_flags : int
+{
+ none = 0,
+ dontwait = ZMQ_DONTWAIT
+};
+
+constexpr recv_flags operator|(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr recv_flags operator&(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr recv_flags operator^(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr recv_flags operator~(recv_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+
+// mutable_buffer, const_buffer and buffer are based on
+// the Networking TS specification, draft:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf
+
+class mutable_buffer
+{
+ public:
+ constexpr mutable_buffer() noexcept : _data(nullptr), _size(0) {}
+ constexpr mutable_buffer(void *p, size_t n) noexcept : _data(p), _size(n)
+ {
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(p != nullptr || n == 0);
+#endif
+ }
+
+ constexpr void *data() const noexcept { return _data; }
+ constexpr size_t size() const noexcept { return _size; }
+ mutable_buffer &operator+=(size_t n) noexcept
+ {
+ // (std::min) is a workaround for when a min macro is defined
+ const auto shift = (std::min)(n, _size);
+ _data = static_cast<char *>(_data) + shift;
+ _size -= shift;
+ return *this;
+ }
+
+ private:
+ void *_data;
+ size_t _size;
+};
+
+inline mutable_buffer operator+(const mutable_buffer &mb, size_t n) noexcept
+{
+ return mutable_buffer(static_cast<char *>(mb.data()) + (std::min)(n, mb.size()),
+ mb.size() - (std::min)(n, mb.size()));
+}
+inline mutable_buffer operator+(size_t n, const mutable_buffer &mb) noexcept
+{
+ return mb + n;
+}
+
+class const_buffer
+{
+ public:
+ constexpr const_buffer() noexcept : _data(nullptr), _size(0) {}
+ constexpr const_buffer(const void *p, size_t n) noexcept : _data(p), _size(n)
+ {
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(p != nullptr || n == 0);
+#endif
+ }
+ constexpr const_buffer(const mutable_buffer &mb) noexcept :
+ _data(mb.data()), _size(mb.size())
+ {
+ }
+
+ constexpr const void *data() const noexcept { return _data; }
+ constexpr size_t size() const noexcept { return _size; }
+ const_buffer &operator+=(size_t n) noexcept
+ {
+ const auto shift = (std::min)(n, _size);
+ _data = static_cast<const char *>(_data) + shift;
+ _size -= shift;
+ return *this;
+ }
+
+ private:
+ const void *_data;
+ size_t _size;
+};
+
+inline const_buffer operator+(const const_buffer &cb, size_t n) noexcept
+{
+ return const_buffer(static_cast<const char *>(cb.data())
+ + (std::min)(n, cb.size()),
+ cb.size() - (std::min)(n, cb.size()));
+}
+inline const_buffer operator+(size_t n, const const_buffer &cb) noexcept
+{
+ return cb + n;
+}
+
+// buffer creation
+
+constexpr mutable_buffer buffer(void *p, size_t n) noexcept
+{
+ return mutable_buffer(p, n);
+}
+constexpr const_buffer buffer(const void *p, size_t n) noexcept
+{
+ return const_buffer(p, n);
+}
+constexpr mutable_buffer buffer(const mutable_buffer &mb) noexcept
+{
+ return mb;
+}
+inline mutable_buffer buffer(const mutable_buffer &mb, size_t n) noexcept
+{
+ return mutable_buffer(mb.data(), (std::min)(mb.size(), n));
+}
+constexpr const_buffer buffer(const const_buffer &cb) noexcept
+{
+ return cb;
+}
+inline const_buffer buffer(const const_buffer &cb, size_t n) noexcept
+{
+ return const_buffer(cb.data(), (std::min)(cb.size(), n));
+}
+
+namespace detail
+{
+template<class T> struct is_buffer
+{
+ static constexpr bool value =
+ std::is_same<T, const_buffer>::value || std::is_same<T, mutable_buffer>::value;
+};
+
+template<class T> struct is_pod_like
+{
+ // NOTE: The networking draft N4771 section 16.11 requires
+ // T in the buffer functions below to be
+ // trivially copyable OR standard layout.
+ // Here we decide to be conservative and require both.
+ static constexpr bool value =
+ ZMQ_IS_TRIVIALLY_COPYABLE(T) && std::is_standard_layout<T>::value;
+};
+
+template<class C> constexpr auto seq_size(const C &c) noexcept -> decltype(c.size())
+{
+ return c.size();
+}
+template<class T, size_t N>
+constexpr size_t seq_size(const T (&/*array*/)[N]) noexcept
+{
+ return N;
+}
+
+template<class Seq>
+auto buffer_contiguous_sequence(Seq &&seq) noexcept
+ -> decltype(buffer(std::addressof(*std::begin(seq)), size_t{}))
+{
+ using T = typename std::remove_cv<
+ typename std::remove_reference<decltype(*std::begin(seq))>::type>::type;
+ static_assert(detail::is_pod_like<T>::value, "T must be POD");
+
+ const auto size = seq_size(seq);
+ return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
+ size * sizeof(T));
+}
+template<class Seq>
+auto buffer_contiguous_sequence(Seq &&seq, size_t n_bytes) noexcept
+ -> decltype(buffer_contiguous_sequence(seq))
+{
+ using T = typename std::remove_cv<
+ typename std::remove_reference<decltype(*std::begin(seq))>::type>::type;
+ static_assert(detail::is_pod_like<T>::value, "T must be POD");
+
+ const auto size = seq_size(seq);
+ return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
+ (std::min)(size * sizeof(T), n_bytes));
+}
+
+} // namespace detail
+
+// C array
+template<class T, size_t N> mutable_buffer buffer(T (&data)[N]) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, size_t N>
+mutable_buffer buffer(T (&data)[N], size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template<class T, size_t N> const_buffer buffer(const T (&data)[N]) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, size_t N>
+const_buffer buffer(const T (&data)[N], size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::array
+template<class T, size_t N> mutable_buffer buffer(std::array<T, N> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, size_t N>
+mutable_buffer buffer(std::array<T, N> &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template<class T, size_t N>
+const_buffer buffer(std::array<const T, N> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, size_t N>
+const_buffer buffer(std::array<const T, N> &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template<class T, size_t N>
+const_buffer buffer(const std::array<T, N> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, size_t N>
+const_buffer buffer(const std::array<T, N> &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::vector
+template<class T, class Allocator>
+mutable_buffer buffer(std::vector<T, Allocator> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, class Allocator>
+mutable_buffer buffer(std::vector<T, Allocator> &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template<class T, class Allocator>
+const_buffer buffer(const std::vector<T, Allocator> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, class Allocator>
+const_buffer buffer(const std::vector<T, Allocator> &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::basic_string
+template<class T, class Traits, class Allocator>
+mutable_buffer buffer(std::basic_string<T, Traits, Allocator> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, class Traits, class Allocator>
+mutable_buffer buffer(std::basic_string<T, Traits, Allocator> &data,
+ size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template<class T, class Traits, class Allocator>
+const_buffer buffer(const std::basic_string<T, Traits, Allocator> &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, class Traits, class Allocator>
+const_buffer buffer(const std::basic_string<T, Traits, Allocator> &data,
+ size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+
+#if CPPZMQ_HAS_STRING_VIEW
+// std::basic_string_view
+template<class T, class Traits>
+const_buffer buffer(std::basic_string_view<T, Traits> data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template<class T, class Traits>
+const_buffer buffer(std::basic_string_view<T, Traits> data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+#endif
+
+// Buffer for a string literal (null terminated)
+// where the buffer size excludes the terminating character.
+// Equivalent to zmq::buffer(std::string_view("...")).
+template<class Char, size_t N>
+constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept
+{
+ static_assert(detail::is_pod_like<Char>::value, "Char must be POD");
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(data[N - 1] == Char{0});
+#endif
+ return const_buffer(static_cast<const Char *>(data), (N - 1) * sizeof(Char));
+}
+
+namespace literals
+{
+constexpr const_buffer operator"" _zbuf(const char *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char));
+}
+constexpr const_buffer operator"" _zbuf(const wchar_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(wchar_t));
+}
+constexpr const_buffer operator"" _zbuf(const char16_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char16_t));
+}
+constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char32_t));
+}
+}
+
+namespace sockopt
+{
+// There are two types of options,
+// integral type with known compiler time size (int, bool, int64_t, uint64_t)
+// and arrays with dynamic size (strings, binary data).
+
+// BoolUnit: if true accepts values of type bool (but passed as T into libzmq)
+template<int Opt, class T, bool BoolUnit = false> struct integral_option
+{
+};
+
+// NullTerm:
+// 0: binary data
+// 1: null-terminated string (`getsockopt` size includes null)
+// 2: binary (size 32) or Z85 encoder string of size 41 (null included)
+template<int Opt, int NullTerm = 1> struct array_option
+{
+};
+
+#define ZMQ_DEFINE_INTEGRAL_OPT(OPT, NAME, TYPE) \
+ using NAME##_t = integral_option<OPT, TYPE, false>; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(OPT, NAME, TYPE) \
+ using NAME##_t = integral_option<OPT, TYPE, true>; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT(OPT, NAME) \
+ using NAME##_t = array_option<OPT>; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT_BINARY(OPT, NAME) \
+ using NAME##_t = array_option<OPT, 0>; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(OPT, NAME) \
+ using NAME##_t = array_option<OPT, 2>; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+
+// deprecated, use zmq::fd_t
+using cppzmq_fd_t = ::zmq::fd_t;
+
+#ifdef ZMQ_AFFINITY
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_AFFINITY, affinity, uint64_t);
+#endif
+#ifdef ZMQ_BACKLOG
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int);
+#endif
+#ifdef ZMQ_BINDTODEVICE
+ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice);
+#endif
+#ifdef ZMQ_CONFLATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int);
+#endif
+#ifdef ZMQ_CONNECT_ROUTING_ID
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_CONNECT_ROUTING_ID, connect_routing_id);
+#endif
+#ifdef ZMQ_CONNECT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_CONNECT_TIMEOUT, connect_timeout, int);
+#endif
+#ifdef ZMQ_CURVE_PUBLICKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_PUBLICKEY, curve_publickey);
+#endif
+#ifdef ZMQ_CURVE_SECRETKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SECRETKEY, curve_secretkey);
+#endif
+#ifdef ZMQ_CURVE_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int);
+#endif
+#ifdef ZMQ_CURVE_SERVERKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey);
+#endif
+#ifdef ZMQ_EVENTS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int);
+#endif
+#ifdef ZMQ_FD
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_FD, fd, ::zmq::fd_t);
+#endif
+#ifdef ZMQ_GSSAPI_PLAINTEXT
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_PLAINTEXT, gssapi_plaintext, int);
+#endif
+#ifdef ZMQ_GSSAPI_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_SERVER, gssapi_server, int);
+#endif
+#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL, gssapi_service_principal);
+#endif
+#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE,
+ gssapi_service_principal_nametype,
+ int);
+#endif
+#ifdef ZMQ_GSSAPI_PRINCIPAL
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_PRINCIPAL, gssapi_principal);
+#endif
+#ifdef ZMQ_GSSAPI_PRINCIPAL_NAMETYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_PRINCIPAL_NAMETYPE,
+ gssapi_principal_nametype,
+ int);
+#endif
+#ifdef ZMQ_HANDSHAKE_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HANDSHAKE_IVL, handshake_ivl, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_IVL, heartbeat_ivl, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_TTL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int);
+#endif
+#ifdef ZMQ_IMMEDIATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int);
+#endif
+#ifdef ZMQ_INVERT_MATCHING
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_INVERT_MATCHING, invert_matching, int);
+#endif
+#ifdef ZMQ_IPV6
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IPV6, ipv6, int);
+#endif
+#ifdef ZMQ_LAST_ENDPOINT
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_LAST_ENDPOINT, last_endpoint);
+#endif
+#ifdef ZMQ_LINGER
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_LINGER, linger, int);
+#endif
+#ifdef ZMQ_MAXMSGSIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MAXMSGSIZE, maxmsgsize, int64_t);
+#endif
+#ifdef ZMQ_MECHANISM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MECHANISM, mechanism, int);
+#endif
+#ifdef ZMQ_METADATA
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_METADATA, metadata);
+#endif
+#ifdef ZMQ_MULTICAST_HOPS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_HOPS, multicast_hops, int);
+#endif
+#ifdef ZMQ_MULTICAST_LOOP
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int);
+#endif
+#ifdef ZMQ_MULTICAST_MAXTPDU
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int);
+#endif
+#ifdef ZMQ_PLAIN_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int);
+#endif
+#ifdef ZMQ_PLAIN_PASSWORD
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password);
+#endif
+#ifdef ZMQ_PLAIN_USERNAME
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username);
+#endif
+#ifdef ZMQ_USE_FD
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int);
+#endif
+#ifdef ZMQ_PROBE_ROUTER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PROBE_ROUTER, probe_router, int);
+#endif
+#ifdef ZMQ_RATE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RATE, rate, int);
+#endif
+#ifdef ZMQ_RCVBUF
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVBUF, rcvbuf, int);
+#endif
+#ifdef ZMQ_RCVHWM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVHWM, rcvhwm, int);
+#endif
+#ifdef ZMQ_RCVMORE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_RCVMORE, rcvmore, int);
+#endif
+#ifdef ZMQ_RCVTIMEO
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVTIMEO, rcvtimeo, int);
+#endif
+#ifdef ZMQ_RECONNECT_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int);
+#endif
+#ifdef ZMQ_RECONNECT_IVL_MAX
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int);
+#endif
+#ifdef ZMQ_RECOVERY_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int);
+#endif
+#ifdef ZMQ_REQ_CORRELATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_CORRELATE, req_correlate, int);
+#endif
+#ifdef ZMQ_REQ_RELAXED
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_RELAXED, req_relaxed, int);
+#endif
+#ifdef ZMQ_ROUTER_HANDOVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_HANDOVER, router_handover, int);
+#endif
+#ifdef ZMQ_ROUTER_MANDATORY
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int);
+#endif
+#ifdef ZMQ_ROUTER_NOTIFY
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int);
+#endif
+#ifdef ZMQ_ROUTING_ID
+ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id);
+#endif
+#ifdef ZMQ_SNDBUF
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDBUF, sndbuf, int);
+#endif
+#ifdef ZMQ_SNDHWM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int);
+#endif
+#ifdef ZMQ_SNDTIMEO
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int);
+#endif
+#ifdef ZMQ_SOCKS_PROXY
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy);
+#endif
+#ifdef ZMQ_STREAM_NOTIFY
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int);
+#endif
+#ifdef ZMQ_SUBSCRIBE
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_SUBSCRIBE, subscribe);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE, tcp_keepalive, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_CNT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_CNT, tcp_keepalive_cnt, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_IDLE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_IDLE, tcp_keepalive_idle, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_INTVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_INTVL, tcp_keepalive_intvl, int);
+#endif
+#ifdef ZMQ_TCP_MAXRT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_MAXRT, tcp_maxrt, int);
+#endif
+#ifdef ZMQ_THREAD_SAFE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_THREAD_SAFE, thread_safe, int);
+#endif
+#ifdef ZMQ_TOS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int);
+#endif
+#ifdef ZMQ_TYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int);
+#endif
+#ifdef ZMQ_UNSUBSCRIBE
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_SIZE, vmci_buffer_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MIN_SIZE, vmci_buffer_min_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MAX_SIZE, vmci_buffer_max_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_CONNECT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_CONNECT_TIMEOUT, vmci_connect_timeout, int);
+#endif
+#ifdef ZMQ_XPUB_VERBOSE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSE, xpub_verbose, int);
+#endif
+#ifdef ZMQ_XPUB_VERBOSER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int);
+#endif
+#ifdef ZMQ_XPUB_MANUAL
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int);
+#endif
+#ifdef ZMQ_XPUB_NODROP
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int);
+#endif
+#ifdef ZMQ_XPUB_WELCOME_MSG
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_XPUB_WELCOME_MSG, xpub_welcome_msg);
+#endif
+#ifdef ZMQ_ZAP_ENFORCE_DOMAIN
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ZAP_ENFORCE_DOMAIN, zap_enforce_domain, int);
+#endif
+#ifdef ZMQ_ZAP_DOMAIN
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_ZAP_DOMAIN, zap_domain);
+#endif
+
+} // namespace sockopt
+#endif // ZMQ_CPP11
+
+
+namespace detail
+{
+class socket_base
+{
+ public:
+ socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) {}
+ ZMQ_EXPLICIT socket_base(void *handle) ZMQ_NOTHROW : _handle(handle) {}
+
+ template<typename T>
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
+ void setsockopt(int option_, T const &optval)
+ {
+ setsockopt(option_, &optval, sizeof(T));
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
+ void setsockopt(int option_, const void *optval_, size_t optvallen_)
+ {
+ int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
+ void getsockopt(int option_, void *optval_, size_t *optvallen_) const
+ {
+ int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ template<typename T>
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
+ T getsockopt(int option_) const
+ {
+ T optval;
+ size_t optlen = sizeof(T);
+ getsockopt(option_, &optval, &optlen);
+ return optval;
+ }
+
+#ifdef ZMQ_CPP11
+ // Set integral socket option, e.g.
+ // `socket.set(zmq::sockopt::linger, 0)`
+ template<int Opt, class T, bool BoolUnit>
+ void set(sockopt::integral_option<Opt, T, BoolUnit>, const T &val)
+ {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+ set_option(Opt, &val, sizeof val);
+ }
+
+ // Set integral socket option from boolean, e.g.
+ // `socket.set(zmq::sockopt::immediate, false)`
+ template<int Opt, class T>
+ void set(sockopt::integral_option<Opt, T, true>, bool val)
+ {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+ T rep_val = val;
+ set_option(Opt, &rep_val, sizeof rep_val);
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::plain_username, "foo123")`
+ template<int Opt, int NullTerm>
+ void set(sockopt::array_option<Opt, NullTerm>, const char *buf)
+ {
+ set_option(Opt, buf, std::strlen(buf));
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, zmq::buffer(id))`
+ template<int Opt, int NullTerm>
+ void set(sockopt::array_option<Opt, NullTerm>, const_buffer buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, id_str)`
+ template<int Opt, int NullTerm>
+ void set(sockopt::array_option<Opt, NullTerm>, const std::string &buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+
+#if CPPZMQ_HAS_STRING_VIEW
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, id_str)`
+ template<int Opt, int NullTerm>
+ void set(sockopt::array_option<Opt, NullTerm>, std::string_view buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+#endif
+
+ // Get scalar socket option, e.g.
+ // `auto opt = socket.get(zmq::sockopt::linger)`
+ template<int Opt, class T, bool BoolUnit>
+ ZMQ_NODISCARD T get(sockopt::integral_option<Opt, T, BoolUnit>) const
+ {
+ static_assert(std::is_integral<T>::value, "T must be integral");
+ T val;
+ size_t size = sizeof val;
+ get_option(Opt, &val, &size);
+ assert(size == sizeof val);
+ return val;
+ }
+
+ // Get array socket option, writes to buf, returns option size in bytes, e.g.
+ // `size_t optsize = socket.get(zmq::sockopt::routing_id, zmq::buffer(id))`
+ template<int Opt, int NullTerm>
+ ZMQ_NODISCARD size_t get(sockopt::array_option<Opt, NullTerm>,
+ mutable_buffer buf) const
+ {
+ size_t size = buf.size();
+ get_option(Opt, buf.data(), &size);
+ return size;
+ }
+
+ // Get array socket option as string (initializes the string buffer size to init_size) e.g.
+ // `auto s = socket.get(zmq::sockopt::routing_id)`
+ // Note: removes the null character from null-terminated string options,
+ // i.e. the string size excludes the null character.
+ template<int Opt, int NullTerm>
+ ZMQ_NODISCARD std::string get(sockopt::array_option<Opt, NullTerm>,
+ size_t init_size = 1024) const
+ {
+ if ZMQ_CONSTEXPR_IF (NullTerm == 2) {
+ if (init_size == 1024) {
+ init_size = 41; // get as Z85 string
+ }
+ }
+ std::string str(init_size, '\0');
+ size_t size = get(sockopt::array_option<Opt>{}, buffer(str));
+ if ZMQ_CONSTEXPR_IF (NullTerm == 1) {
+ if (size > 0) {
+ assert(str[size - 1] == '\0');
+ --size;
+ }
+ } else if ZMQ_CONSTEXPR_IF (NullTerm == 2) {
+ assert(size == 32 || size == 41);
+ if (size == 41) {
+ assert(str[size - 1] == '\0');
+ --size;
+ }
+ }
+ str.resize(size);
+ return str;
+ }
+#endif
+
+ void bind(std::string const &addr) { bind(addr.c_str()); }
+
+ void bind(const char *addr_)
+ {
+ int rc = zmq_bind(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void unbind(std::string const &addr) { unbind(addr.c_str()); }
+
+ void unbind(const char *addr_)
+ {
+ int rc = zmq_unbind(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void connect(std::string const &addr) { connect(addr.c_str()); }
+
+ void connect(const char *addr_)
+ {
+ int rc = zmq_connect(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void disconnect(std::string const &addr) { disconnect(addr.c_str()); }
+
+ void disconnect(const char *addr_)
+ {
+ int rc = zmq_disconnect(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_DEPRECATED("from 4.7.1, use handle() != nullptr or operator bool")
+ bool connected() const ZMQ_NOTHROW { return (_handle != ZMQ_NULLPTR); }
+
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking a const_buffer and send_flags")
+ size_t send(const void *buf_, size_t len_, int flags_ = 0)
+ {
+ int nbytes = zmq_send(_handle, buf_, len_, flags_);
+ if (nbytes >= 0)
+ return static_cast<size_t>(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return 0;
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
+ bool send(message_t &msg_,
+ int flags_ = 0) // default until removed
+ {
+ int nbytes = zmq_msg_send(msg_.handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+ template<typename T>
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.4.1, use send taking message_t or buffer (for contiguous "
+ "ranges), and send_flags")
+ bool send(T first, T last, int flags_ = 0)
+ {
+ zmq::message_t msg(first, last);
+ int nbytes = zmq_msg_send(msg.handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
+ bool send(message_t &&msg_,
+ int flags_ = 0) // default until removed
+ {
+#ifdef ZMQ_CPP11
+ return send(msg_, static_cast<send_flags>(flags_)).has_value();
+#else
+ return send(msg_, flags_);
+#endif
+ }
+#endif
+
+#ifdef ZMQ_CPP11
+ send_result_t send(const_buffer buf, send_flags flags = send_flags::none)
+ {
+ const int nbytes =
+ zmq_send(_handle, buf.data(), buf.size(), static_cast<int>(flags));
+ if (nbytes >= 0)
+ return static_cast<size_t>(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ send_result_t send(message_t &msg, send_flags flags)
+ {
+ int nbytes = zmq_msg_send(msg.handle(), _handle, static_cast<int>(flags));
+ if (nbytes >= 0)
+ return static_cast<size_t>(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ send_result_t send(message_t &&msg, send_flags flags)
+ {
+ return send(msg, flags);
+ }
+#endif
+
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.3.1, use recv taking a mutable_buffer and recv_flags")
+ size_t recv(void *buf_, size_t len_, int flags_ = 0)
+ {
+ int nbytes = zmq_recv(_handle, buf_, len_, flags_);
+ if (nbytes >= 0)
+ return static_cast<size_t>(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return 0;
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.3.1, use recv taking a reference to message_t and recv_flags")
+ bool recv(message_t *msg_, int flags_ = 0)
+ {
+ int nbytes = zmq_msg_recv(msg_->handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+#ifdef ZMQ_CPP11
+ ZMQ_NODISCARD
+ recv_buffer_result_t recv(mutable_buffer buf,
+ recv_flags flags = recv_flags::none)
+ {
+ const int nbytes =
+ zmq_recv(_handle, buf.data(), buf.size(), static_cast<int>(flags));
+ if (nbytes >= 0) {
+ return recv_buffer_size{
+ (std::min)(static_cast<size_t>(nbytes), buf.size()),
+ static_cast<size_t>(nbytes)};
+ }
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ ZMQ_NODISCARD
+ recv_result_t recv(message_t &msg, recv_flags flags = recv_flags::none)
+ {
+ const int nbytes =
+ zmq_msg_recv(msg.handle(), _handle, static_cast<int>(flags));
+ if (nbytes >= 0) {
+ assert(msg.size() == static_cast<size_t>(nbytes));
+ return static_cast<size_t>(nbytes);
+ }
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+#endif
+
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ void join(const char *group)
+ {
+ int rc = zmq_join(_handle, group);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void leave(const char *group)
+ {
+ int rc = zmq_leave(_handle, group);
+ if (rc != 0)
+ throw error_t();
+ }
+#endif
+
+ ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return _handle; }
+ ZMQ_NODISCARD const void *handle() const ZMQ_NOTHROW { return _handle; }
+
+ ZMQ_EXPLICIT operator bool() const ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
+ // note: non-const operator bool can be removed once
+ // operator void* is removed from socket_t
+ ZMQ_EXPLICIT operator bool() ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
+
+ protected:
+ void *_handle;
+
+ private:
+ void set_option(int option_, const void *optval_, size_t optvallen_)
+ {
+ int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void get_option(int option_, void *optval_, size_t *optvallen_) const
+ {
+ int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+};
+} // namespace detail
+
+#ifdef ZMQ_CPP11
+enum class socket_type : int
+{
+ req = ZMQ_REQ,
+ rep = ZMQ_REP,
+ dealer = ZMQ_DEALER,
+ router = ZMQ_ROUTER,
+ pub = ZMQ_PUB,
+ sub = ZMQ_SUB,
+ xpub = ZMQ_XPUB,
+ xsub = ZMQ_XSUB,
+ push = ZMQ_PUSH,
+ pull = ZMQ_PULL,
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ server = ZMQ_SERVER,
+ client = ZMQ_CLIENT,
+ radio = ZMQ_RADIO,
+ dish = ZMQ_DISH,
+#endif
+#if ZMQ_VERSION_MAJOR >= 4
+ stream = ZMQ_STREAM,
+#endif
+ pair = ZMQ_PAIR
+};
+#endif
+
+struct from_handle_t
+{
+ struct _private
+ {
+ }; // disabling use other than with from_handle
+ ZMQ_CONSTEXPR_FN ZMQ_EXPLICIT from_handle_t(_private /*p*/) ZMQ_NOTHROW {}
+};
+
+ZMQ_CONSTEXPR_VAR from_handle_t from_handle =
+ from_handle_t(from_handle_t::_private());
+
+// A non-owning nullable reference to a socket.
+// The reference is invalidated on socket close or destruction.
+class socket_ref : public detail::socket_base
+{
+ public:
+ socket_ref() ZMQ_NOTHROW : detail::socket_base() {}
+#ifdef ZMQ_CPP11
+ socket_ref(std::nullptr_t) ZMQ_NOTHROW : detail::socket_base() {}
+#endif
+ socket_ref(from_handle_t /*fh*/, void *handle) ZMQ_NOTHROW
+ : detail::socket_base(handle)
+ {
+ }
+};
+
+#ifdef ZMQ_CPP11
+inline bool operator==(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
+{
+ return sr.handle() == nullptr;
+}
+inline bool operator==(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
+{
+ return sr.handle() == nullptr;
+}
+inline bool operator!=(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
+{
+ return !(sr == nullptr);
+}
+inline bool operator!=(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
+{
+ return !(sr == nullptr);
+}
+#endif
+
+inline bool operator==(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return std::equal_to<const void *>()(a.handle(), b.handle());
+}
+inline bool operator!=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return !(a == b);
+}
+inline bool operator<(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return std::less<const void *>()(a.handle(), b.handle());
+}
+inline bool operator>(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return b < a;
+}
+inline bool operator<=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return !(a > b);
+}
+inline bool operator>=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW
+{
+ return !(a < b);
+}
+
+} // namespace zmq
+
+#ifdef ZMQ_CPP11
+namespace std
+{
+template<> struct hash<zmq::socket_ref>
+{
+ size_t operator()(zmq::socket_ref sr) const ZMQ_NOTHROW
+ {
+ return hash<void *>()(sr.handle());
+ }
+};
+} // namespace std
+#endif
+
+namespace zmq
+{
+class socket_t : public detail::socket_base
+{
+ friend class monitor_t;
+
+ public:
+ socket_t() ZMQ_NOTHROW : detail::socket_base(ZMQ_NULLPTR), ctxptr(ZMQ_NULLPTR) {}
+
+ socket_t(context_t &context_, int type_) :
+ detail::socket_base(zmq_socket(context_.handle(), type_)),
+ ctxptr(context_.handle())
+ {
+ if (_handle == ZMQ_NULLPTR)
+ throw error_t();
+ }
+
+#ifdef ZMQ_CPP11
+ socket_t(context_t &context_, socket_type type_) :
+ socket_t(context_, static_cast<int>(type_))
+ {
+ }
+#endif
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ socket_t(socket_t &&rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle),
+ ctxptr(rhs.ctxptr)
+ {
+ rhs._handle = ZMQ_NULLPTR;
+ rhs.ctxptr = ZMQ_NULLPTR;
+ }
+ socket_t &operator=(socket_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ std::swap(_handle, rhs._handle);
+ std::swap(ctxptr, rhs.ctxptr);
+ return *this;
+ }
+#endif
+
+ ~socket_t() ZMQ_NOTHROW { close(); }
+
+ operator void *() ZMQ_NOTHROW { return _handle; }
+
+ operator void const *() const ZMQ_NOTHROW { return _handle; }
+
+ void close() ZMQ_NOTHROW
+ {
+ if (_handle == ZMQ_NULLPTR)
+ // already closed
+ return;
+ int rc = zmq_close(_handle);
+ ZMQ_ASSERT(rc == 0);
+ _handle = ZMQ_NULLPTR;
+ ctxptr = ZMQ_NULLPTR;
+ }
+
+ void swap(socket_t &other) ZMQ_NOTHROW
+ {
+ std::swap(_handle, other._handle);
+ std::swap(ctxptr, other.ctxptr);
+ }
+
+ operator socket_ref() ZMQ_NOTHROW { return socket_ref(from_handle, _handle); }
+
+ private:
+ void *ctxptr;
+
+ socket_t(const socket_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const socket_t &) ZMQ_DELETED_FUNCTION;
+
+ // used by monitor_t
+ socket_t(void *context_, int type_) :
+ detail::socket_base(zmq_socket(context_, type_)), ctxptr(context_)
+ {
+ if (_handle == ZMQ_NULLPTR)
+ throw error_t();
+ if (ctxptr == ZMQ_NULLPTR)
+ throw error_t();
+ }
+};
+
+inline void swap(socket_t &a, socket_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use proxy taking socket_t objects")
+inline void proxy(void *frontend, void *backend, void *capture)
+{
+ int rc = zmq_proxy(frontend, backend, capture);
+ if (rc != 0)
+ throw error_t();
+}
+
+inline void
+proxy(socket_ref frontend, socket_ref backend, socket_ref capture = socket_ref())
+{
+ int rc = zmq_proxy(frontend.handle(), backend.handle(), capture.handle());
+ if (rc != 0)
+ throw error_t();
+}
+
+#ifdef ZMQ_HAS_PROXY_STEERABLE
+ZMQ_DEPRECATED("from 4.3.1, use proxy_steerable taking socket_t objects")
+inline void
+proxy_steerable(void *frontend, void *backend, void *capture, void *control)
+{
+ int rc = zmq_proxy_steerable(frontend, backend, capture, control);
+ if (rc != 0)
+ throw error_t();
+}
+
+inline void proxy_steerable(socket_ref frontend,
+ socket_ref backend,
+ socket_ref capture,
+ socket_ref control)
+{
+ int rc = zmq_proxy_steerable(frontend.handle(), backend.handle(),
+ capture.handle(), control.handle());
+ if (rc != 0)
+ throw error_t();
+}
+#endif
+
+class monitor_t
+{
+ public:
+ monitor_t() : _socket(), _monitor_socket() {}
+
+ virtual ~monitor_t() { close(); }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ monitor_t(monitor_t &&rhs) ZMQ_NOTHROW : _socket(), _monitor_socket()
+ {
+ std::swap(_socket, rhs._socket);
+ std::swap(_monitor_socket, rhs._monitor_socket);
+ }
+
+ monitor_t &operator=(monitor_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ _socket = socket_ref();
+ std::swap(_socket, rhs._socket);
+ std::swap(_monitor_socket, rhs._monitor_socket);
+ return *this;
+ }
+#endif
+
+
+ void
+ monitor(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
+ {
+ monitor(socket, addr.c_str(), events);
+ }
+
+ void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
+ {
+ init(socket, addr_, events);
+ while (true) {
+ check_event(-1);
+ }
+ }
+
+ void init(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
+ {
+ init(socket, addr.c_str(), events);
+ }
+
+ void init(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
+ {
+ int rc = zmq_socket_monitor(socket.handle(), addr_, events);
+ if (rc != 0)
+ throw error_t();
+
+ _socket = socket;
+ _monitor_socket = socket_t(socket.ctxptr, ZMQ_PAIR);
+ _monitor_socket.connect(addr_);
+
+ on_monitor_started();
+ }
+
+ bool check_event(int timeout = 0)
+ {
+ assert(_monitor_socket);
+
+ zmq::message_t eventMsg;
+
+ zmq::pollitem_t items[] = {
+ {_monitor_socket.handle(), 0, ZMQ_POLLIN, 0},
+ };
+
+ zmq::poll(&items[0], 1, timeout);
+
+ if (items[0].revents & ZMQ_POLLIN) {
+ int rc = zmq_msg_recv(eventMsg.handle(), _monitor_socket.handle(), 0);
+ if (rc == -1 && zmq_errno() == ETERM)
+ return false;
+ assert(rc != -1);
+
+ } else {
+ return false;
+ }
+
+#if ZMQ_VERSION_MAJOR >= 4
+ const char *data = static_cast<const char *>(eventMsg.data());
+ zmq_event_t msgEvent;
+ memcpy(&msgEvent.event, data, sizeof(uint16_t));
+ data += sizeof(uint16_t);
+ memcpy(&msgEvent.value, data, sizeof(int32_t));
+ zmq_event_t *event = &msgEvent;
+#else
+ zmq_event_t *event = static_cast<zmq_event_t *>(eventMsg.data());
+#endif
+
+#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT
+ zmq::message_t addrMsg;
+ int rc = zmq_msg_recv(addrMsg.handle(), _monitor_socket.handle(), 0);
+ if (rc == -1 && zmq_errno() == ETERM) {
+ return false;
+ }
+
+ assert(rc != -1);
+ std::string address = addrMsg.to_string();
+#else
+ // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types.
+ std::string address = event->data.connected.addr;
+#endif
+
+#ifdef ZMQ_EVENT_MONITOR_STOPPED
+ if (event->event == ZMQ_EVENT_MONITOR_STOPPED) {
+ return false;
+ }
+
+#endif
+
+ switch (event->event) {
+ case ZMQ_EVENT_CONNECTED:
+ on_event_connected(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CONNECT_DELAYED:
+ on_event_connect_delayed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CONNECT_RETRIED:
+ on_event_connect_retried(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_LISTENING:
+ on_event_listening(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_BIND_FAILED:
+ on_event_bind_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_ACCEPTED:
+ on_event_accepted(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_ACCEPT_FAILED:
+ on_event_accept_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CLOSED:
+ on_event_closed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CLOSE_FAILED:
+ on_event_close_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_DISCONNECTED:
+ on_event_disconnected(*event, address.c_str());
+ break;
+#ifdef ZMQ_BUILD_DRAFT_API
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL:
+ on_event_handshake_failed_no_detail(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL:
+ on_event_handshake_failed_protocol(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH:
+ on_event_handshake_failed_auth(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_SUCCEEDED:
+ on_event_handshake_succeeded(*event, address.c_str());
+ break;
+#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
+ case ZMQ_EVENT_HANDSHAKE_FAILED:
+ on_event_handshake_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_SUCCEED:
+ on_event_handshake_succeed(*event, address.c_str());
+ break;
+#endif
+#endif
+ default:
+ on_event_unknown(*event, address.c_str());
+ break;
+ }
+
+ return true;
+ }
+
+#ifdef ZMQ_EVENT_MONITOR_STOPPED
+ void abort()
+ {
+ if (_socket)
+ zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
+
+ _socket = socket_ref();
+ }
+#endif
+ virtual void on_monitor_started() {}
+ virtual void on_event_connected(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_connect_delayed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_connect_retried(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_listening(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_bind_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_accepted(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_accept_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_closed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_close_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_disconnected(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ virtual void on_event_handshake_failed_no_detail(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_failed_protocol(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_failed_auth(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_succeeded(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
+ virtual void on_event_handshake_failed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_succeed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#endif
+ virtual void on_event_unknown(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+
+ private:
+ monitor_t(const monitor_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const monitor_t &) ZMQ_DELETED_FUNCTION;
+
+ socket_ref _socket;
+ socket_t _monitor_socket;
+
+ void close() ZMQ_NOTHROW
+ {
+ if (_socket)
+ zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
+ _monitor_socket.close();
+ }
+};
+
+#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
+
+// polling events
+enum class event_flags : short
+{
+ none = 0,
+ pollin = ZMQ_POLLIN,
+ pollout = ZMQ_POLLOUT,
+ pollerr = ZMQ_POLLERR,
+ pollpri = ZMQ_POLLPRI
+};
+
+constexpr event_flags operator|(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr event_flags operator&(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr event_flags operator^(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr event_flags operator~(event_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+struct no_user_data;
+
+// layout compatible with zmq_poller_event_t
+template<class T = no_user_data> struct poller_event
+{
+ socket_ref socket;
+ ::zmq::fd_t fd;
+ T *user_data;
+ event_flags events;
+};
+
+template<typename T = no_user_data> class poller_t
+{
+ public:
+ using event_type = poller_event<T>;
+
+ poller_t() : poller_ptr(zmq_poller_new())
+ {
+ if (!poller_ptr)
+ throw error_t();
+ }
+
+ template<
+ typename Dummy = void,
+ typename =
+ typename std::enable_if<!std::is_same<T, no_user_data>::value, Dummy>::type>
+ void add(zmq::socket_ref socket, event_flags events, T *user_data)
+ {
+ add_impl(socket, events, user_data);
+ }
+
+ void add(zmq::socket_ref socket, event_flags events)
+ {
+ add_impl(socket, events, nullptr);
+ }
+
+ void remove(zmq::socket_ref socket)
+ {
+ if (0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) {
+ throw error_t();
+ }
+ }
+
+ void modify(zmq::socket_ref socket, event_flags events)
+ {
+ if (0
+ != zmq_poller_modify(poller_ptr.get(), socket.handle(),
+ static_cast<short>(events))) {
+ throw error_t();
+ }
+ }
+
+ size_t wait_all(std::vector<event_type> &poller_events,
+ const std::chrono::milliseconds timeout)
+ {
+ int rc = zmq_poller_wait_all(
+ poller_ptr.get(),
+ reinterpret_cast<zmq_poller_event_t *>(poller_events.data()),
+ static_cast<int>(poller_events.size()),
+ static_cast<long>(timeout.count()));
+ if (rc > 0)
+ return static_cast<size_t>(rc);
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ if (zmq_errno() == EAGAIN)
+#else
+ if (zmq_errno() == ETIMEDOUT)
+#endif
+ return 0;
+
+ throw error_t();
+ }
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3)
+ size_t size() const noexcept
+ {
+ int rc = zmq_poller_size(const_cast<void *>(poller_ptr.get()));
+ ZMQ_ASSERT(rc >= 0);
+ return static_cast<size_t>(std::max(rc, 0));
+ }
+#endif
+
+ private:
+ struct destroy_poller_t
+ {
+ void operator()(void *ptr) noexcept
+ {
+ int rc = zmq_poller_destroy(&ptr);
+ ZMQ_ASSERT(rc == 0);
+ }
+ };
+
+ std::unique_ptr<void, destroy_poller_t> poller_ptr;
+
+ void add_impl(zmq::socket_ref socket, event_flags events, T *user_data)
+ {
+ if (0
+ != zmq_poller_add(poller_ptr.get(), socket.handle(), user_data,
+ static_cast<short>(events))) {
+ throw error_t();
+ }
+ }
+};
+#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
+
+inline std::ostream &operator<<(std::ostream &os, const message_t &msg)
+{
+ return os << msg.str();
+}
+
+} // namespace zmq
+
+#endif // __ZMQ_HPP_INCLUDED__
+
Changelog for package behaviortree_cpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+3.6.1 (2022-03-06)
+------------------
+* remove windows tests
+* fix thread safety
+* fix CI
+* Don't restart SequenceStar on halt (`#329 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/329>`_)
+ * Add more SequenceStar tests
+ * Fix typo in test name
+ * Don't reset SequenceStar on halt
+* [docs] add missing node `SmashDoor` (`#342 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/342>`_)
+* ROS2 include ros_pkg attribute support (`#351 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/351>`_)
+ * ROS2 include pkg support
+ * ros2 build fixed
+ Co-authored-by: Benjamin Linne <benjamin.linne.civ@army.mil>
+* [ImgBot] Optimize images (`#334 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/334>`_)
+ *Total -- 90.34kb -> 61.77kb (31.63%)
+ /docs/images/Tutorial1.svg -- 10.08kb -> 6.33kb (37.19%)
+ /docs/images/FetchBeerFails.svg -- 9.00kb -> 5.93kb (34.13%)
+ /docs/images/FetchBeer2.svg -- 21.19kb -> 14.41kb (32%)
+ /docs/images/Tutorial2.svg -- 34.19kb -> 23.75kb (30.54%)
+ /docs/images/DecoratorEnterRoom.svg -- 15.88kb -> 11.35kb (28.54%)
+ Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
+* [Docs] BT_basics fix typo (`#343 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/343>`_)
+* [docs] Clarify sentence (`#344 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/344>`_)
+ `... will sleep up to 8 hours or less, if he/she is fully rested.` was not clear. It can also be understood as `If he/she is fully rested, the character will sleep ...`
+* [docs] match text to graphics (`#340 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/340>`_)
+* Docs: BT_basics fix typo (`#337 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/337>`_)
+* Merge branch 'master' of github.com:BehaviorTree/BehaviorTree.CPP
+* fix svg
+* Fix CMake ENABLE_COROUTINES flag with Boost < 1.59 (`#335 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/335>`_)
+ Co-authored-by: Cam Fulton <cfulton@symbotic.com>
+* Add ENABLE_COROUTINES CMake option (`#316 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/316>`_)
+ * Add DISABLE_COROUTINES CMake option
+ * Change convention of CMake coroutine flag to ENABLE
+ Co-authored-by: Cam Fulton <cfulton@symbotic.com>
+* [ImgBot] Optimize images (`#333 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/333>`_)
+ *Total -- 152.97kb -> 114.57kb (25.1%)
+ /docs/images/ReactiveSequence.svg -- 7.58kb -> 4.59kb (39.47%)
+ /docs/images/SequenceNode.svg -- 11.28kb -> 7.12kb (36.87%)
+ /docs/images/SequenceStar.svg -- 11.22kb -> 7.09kb (36.8%)
+ /docs/images/DecoratorEnterRoom.svg -- 20.71kb -> 13.30kb (35.77%)
+ /docs/images/FallbackBasic.svg -- 19.09kb -> 12.64kb (33.79%)
+ /docs/images/FetchBeer.svg -- 24.30kb -> 16.36kb (32.66%)
+ /docs/images/SequenceBasic.svg -- 6.32kb -> 5.49kb (13.04%)
+ /docs/images/Tutorial1.svg -- 6.67kb -> 5.94kb (10.98%)
+ /docs/images/FetchBeerFails.svg -- 6.46kb -> 5.83kb (9.76%)
+ /docs/images/FetchBeer2.svg -- 14.99kb -> 13.76kb (8.18%)
+ /docs/images/Tutorial2.svg -- 24.35kb -> 22.44kb (7.85%)
+ Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
+* doc fix
+* Merge branch 'new_doc'
+* remove deprecated code
+* updated documentation
+* [Fix] Fix cmake version warning and -Wformat warning (`#319 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/319>`_)
+ Co-authored-by: Homalozoa <xuhaiwang@xiaomi.com>
+* Update README.md
+* Fix Windows shared lib build (`#323 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/323>`_)
+* fix shadowed variable in string_view.hpp (`#327 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/327>`_)
+* Build Sample Nodes By Default to Fix Github Action (`#332 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/332>`_)
+ * Fix github action
+ * Change working directory in github action step
+ * Build samples by default
+* Added BlackboardCheckBool decorator node (`#326 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/326>`_)
+ * Added tests for BlackboardCheck decorator node
+ * Added BlackboardCheckBool decorator node
+* Fixed typo "Exeption" -> "Exception" (`#331 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/331>`_)
+* WIP
+* fix `#325 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/325>`_
+* Contributors: Adam Sasine, Affonso, Guilherme, Alberto Soragna, Davide Faconti, Homalozoa X, Jake Keller, Philippe Couvignou, Tobias Fischer, benjinne, fultoncjb, goekce, imgbot[bot]
+
+3.6.0 (2021-11-10)
+------------------
+* Build samples independently of examples (`#315 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/315>`_)
+* Fix dependency in package.xml (`#313 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/313>`_)
+* Fix doc statement (`#309 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/309>`_)
+ Fix sentence
+* Fix references to RetryUntilSuccesful (`#308 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/308>`_)
+ * Fix github action
+ * Fix references to RetryUntilSuccesful
+* added subclass RetryNodeTypo (`#295 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/295>`_)
+ Co-authored-by: Subaru Arai <SubaruArai@local>
+* Fix github action (`#302 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/302>`_)
+* Minor spelling correction (`#305 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/305>`_)
+ Corrected `the_aswer` to `the_answer`
+* Update FallbackNode.md (`#306 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/306>`_)
+ typo correction.
+* Add signal handler for Windows (`#307 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/307>`_)
+* fix
+* file renamed and documentation fixed
+* Update documentation for reactive sequence (`#286 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/286>`_)
+* Update FallbackNode.md (`#287 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/287>`_)
+ Fix the pseudocode in the documentation of 'Reactive Fallback' according to its source code.
+* Update fallback documentation to V3 (`#288 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/288>`_)
+ * Update FallbackNode.md description to V3
+ * Fix typo
+* Use pedantic for non MSVC builds (`#289 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/289>`_)
+* Merge branch 'master' of https://github.com/BehaviorTree/BehaviorTree.CPP
+* updated to latest flatbuffers
+* Update README.md
+* Fix issue `#273 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/273>`_
+* remove potential crash when an unfinished tree throws an exception
+* remove appveyor
+* Merge branch 'git_actions'
+* Fixes for compilation on windows. (`#248 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/248>`_)
+ * Fix for detecting ZeroMQ on windows
+ Naming convention is a bit different for ZeroMQ, specifically on Windows with vcpkg. While ZMQ and ZeroMQ are valid on linux, the ZMQ naming convention only works on linux.
+ * Compilation on windows not working with /WX
+ * Macro collision on Windows
+ On windows, the macros defined in the abstract logger collides with other in windows.h. Made them lowercase to avoid collision
+* Remove native support for Conan (`#280 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/280>`_)
+* add github workflow
+* Registered missing dummy nodes for examples (`#275 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/275>`_)
+ * Added CheckTemperature dummy node
+ * Added SayHello dummy node
+* add zmq.hpp in 3rdparty dirfectory
+* add test
+* fix some warnings
+* Fix bug on halt of delay node (`#272 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/272>`_)
+ - When DelayNode is halted and ticked again, it always returned FAILURE since the state of DelayNode was not properly reset.
+ - This commit fixes unexpected behavior of DelayNode when it is halted.
+ Co-authored-by: Jinwoo Choi <jinwoos.choi@samsung.com>
+* Clear all of blackboard's content (`#269 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/269>`_)
+* Added printTreeRecursively overload with ostream parameter (`#264 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/264>`_)
+ * Added overload to printTreeRecursively
+ * Changed include to iosfwd
+ * Added test to verify function writes to stream
+ * Added call to overload without stream parameter
+ * Fixed conversion error
+ * Removed overload in favor of default argument
+* Fix typo (`#260 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/260>`_)
+ Co-authored-by: Francesco Vigni <francesco.vigni@sttech.de>
+* Update README.md
+* abstract_logger.h: fixed a typo (`#257 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/257>`_)
+* Contributors: Adam Sasine, Affonso, Guilherme, Akash, Billy, Cong Liu, Daisuke Nishimatsu, Davide Faconti, Francesco Vigni, Heben, Jake Keller, Per-Arne Andersen, Ross Weir, Steve Macenski, SubaruArai, Taehyeon, Uilian Ries, Yadu, Yuwei Liang, matthews-jca, swarajpeppermint
+
3.5.6 (2021-02-03)
------------------
* fix issue `#227 <https://github.com/BehaviorTree/BehaviorTree.CPP/issues/227>`_
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
-endif()
-
-#---- Include boost to add coroutines ----
-find_package(Boost COMPONENTS coroutine QUIET)
-
-if(Boost_FOUND)
- string(REPLACE "." "0" Boost_VERSION_NODOT ${Boost_VERSION})
- if(NOT Boost_VERSION_NODOT VERSION_LESS 105900)
- message(STATUS "Found boost::coroutine2.")
- add_definitions(-DBT_BOOST_COROUTINE2)
- set(BT_COROUTINES true)
- elseif(NOT Boost_VERSION_NODOT VERSION_LESS 105300)
- message(STATUS "Found boost::coroutine.")
- add_definitions(-DBT_BOOST_COROUTINE)
- set(BT_COROUTINES true)
- endif()
- include_directories(${Boost_INCLUDE_DIRS})
-endif()
-
-
-if(NOT DEFINED BT_COROUTINES)
- message(STATUS "Coroutines disabled. Install Boost to enable them (version 1.59+ recommended).")
- add_definitions(-DBT_NO_COROUTINES)
+else()
+ add_definitions(-Wpedantic)
endif()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
#---- project configuration ----
option(BUILD_EXAMPLES "Build tutorials and examples" ON)
+option(BUILD_SAMPLES "Build sample nodes" ON)
option(BUILD_UNIT_TESTS "Build the unit tests" ON)
option(BUILD_TOOLS "Build commandline tools" ON)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
+option(ENABLE_COROUTINES "Enable boost coroutines" ON)
+
+#---- Include boost to add coroutines ----
+if(ENABLE_COROUTINES)
+ find_package(Boost COMPONENTS coroutine QUIET)
+
+ if(Boost_FOUND)
+ string(REPLACE "." "0" Boost_VERSION_NODOT ${Boost_VERSION})
+ if(NOT Boost_VERSION_NODOT VERSION_LESS 105900)
+ message(STATUS "Found boost::coroutine2.")
+ add_definitions(-DBT_BOOST_COROUTINE2)
+ set(BT_COROUTINES true)
+ elseif(NOT Boost_VERSION_NODOT VERSION_LESS 105300)
+ message(STATUS "Found boost::coroutine.")
+ add_definitions(-DBT_BOOST_COROUTINE)
+ set(BT_COROUTINES true)
+ endif()
+ include_directories(${Boost_INCLUDE_DIRS})
+ endif()
+
+ if(NOT DEFINED BT_COROUTINES)
+ message(STATUS "Boost coroutines disabled. Install Boost (version 1.59+ recommended).")
+ endif()
+else()
+ message(STATUS "Boost coroutines disabled by CMake option.")
+endif()
+
+if(NOT DEFINED BT_COROUTINES)
+ add_definitions(-DBT_NO_COROUTINES)
+endif()
#---- Find other packages ----
find_package(Threads)
if ( ament_cmake_FOUND )
- # Not adding -DUSING_ROS since xml_parsing.cpp hasn't been ported to ROS2
-
+ add_definitions( -DUSING_ROS2 )
message(STATUS "------------------------------------------")
message(STATUS "BehaviourTree is being built using AMENT.")
message(STATUS "------------------------------------------")
set(BUILD_TOOL_INCLUDE_DIRS ${catkin_INCLUDE_DIRS})
elseif(BUILD_UNIT_TESTS)
- find_package(GTest REQUIRED)
-endif()
-
-
-#############################################################
-if(ament_cmake_FOUND)
- set( BEHAVIOR_TREE_LIB_DESTINATION lib )
- set( BEHAVIOR_TREE_INC_DESTINATION include )
- set( BEHAVIOR_TREE_BIN_DESTINATION bin )
-
- ament_export_include_directories(include)
- ament_export_libraries(${BEHAVIOR_TREE_LIBRARY})
- ament_package()
-elseif(catkin_FOUND)
- set( BEHAVIOR_TREE_LIB_DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} )
- set( BEHAVIOR_TREE_INC_DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION} )
- set( BEHAVIOR_TREE_BIN_DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} )
-else()
- set( BEHAVIOR_TREE_LIB_DESTINATION lib )
- set( BEHAVIOR_TREE_INC_DESTINATION include )
- set( BEHAVIOR_TREE_BIN_DESTINATION bin )
-
- set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_BIN_DESTINATION}" )
- set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_LIB_DESTINATION}" )
- set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_BIN_DESTINATION}" )
+ include(FetchContent)
+ FetchContent_Declare(
+ googletest
+ URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
+ )
+ # For Windows: Prevent overriding the parent project's compiler/linker settings
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+ FetchContent_MakeAvailable(googletest)
endif()
-message( STATUS "BEHAVIOR_TREE_LIB_DESTINATION: ${BEHAVIOR_TREE_LIB_DESTINATION} " )
-message( STATUS "BEHAVIOR_TREE_BIN_DESTINATION: ${BEHAVIOR_TREE_BIN_DESTINATION} " )
-message( STATUS "CMAKE_RUNTIME_OUTPUT_DIRECTORY: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} " )
-message( STATUS "CMAKE_LIBRARY_OUTPUT_DIRECTORY: ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} " )
-message( STATUS "CMAKE_ARCHIVE_OUTPUT_DIRECTORY: ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} " )
#############################################################
# LIBRARY
if (UNIX)
list(APPEND BT_SOURCE src/shared_library_UNIX.cpp )
- if (BUILD_SHARED_LIBS)
- add_library(${BEHAVIOR_TREE_LIBRARY} SHARED ${BT_SOURCE})
- else()
- add_library(${BEHAVIOR_TREE_LIBRARY} STATIC ${BT_SOURCE})
- endif()
endif()
if (WIN32)
set(CMAKE_DEBUG_POSTFIX "d")
list(APPEND BT_SOURCE src/shared_library_WIN.cpp )
- add_library(${BEHAVIOR_TREE_LIBRARY} STATIC ${BT_SOURCE} )
+endif()
+
+if (BUILD_SHARED_LIBS)
+ set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
+ add_library(${BEHAVIOR_TREE_LIBRARY} SHARED ${BT_SOURCE})
+else()
+ add_library(${BEHAVIOR_TREE_LIBRARY} STATIC ${BT_SOURCE})
endif()
if( ZMQ_FOUND )
endif()
if(MSVC)
- target_compile_options(${BEHAVIOR_TREE_LIBRARY} PRIVATE /W3 /WX)
else()
target_compile_options(${BEHAVIOR_TREE_LIBRARY} PRIVATE
-Wall -Wextra -Werror=return-type)
endif()
+#############################################################
+if(ament_cmake_FOUND)
+ find_package(ament_index_cpp REQUIRED)
+ ament_target_dependencies(${BEHAVIOR_TREE_LIBRARY} PUBLIC ament_index_cpp)
+
+ set( BEHAVIOR_TREE_LIB_DESTINATION lib )
+ set( BEHAVIOR_TREE_INC_DESTINATION include )
+ set( BEHAVIOR_TREE_BIN_DESTINATION bin )
+
+ ament_export_include_directories(include)
+ ament_export_libraries(${BEHAVIOR_TREE_LIBRARY})
+ ament_package()
+elseif(catkin_FOUND)
+ set( BEHAVIOR_TREE_LIB_DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} )
+ set( BEHAVIOR_TREE_INC_DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION} )
+ set( BEHAVIOR_TREE_BIN_DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} )
+else()
+ set( BEHAVIOR_TREE_LIB_DESTINATION lib )
+ set( BEHAVIOR_TREE_INC_DESTINATION include )
+ set( BEHAVIOR_TREE_BIN_DESTINATION bin )
+
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_BIN_DESTINATION}" )
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_LIB_DESTINATION}" )
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${BEHAVIOR_TREE_BIN_DESTINATION}" )
+endif()
+
+message( STATUS "BEHAVIOR_TREE_LIB_DESTINATION: ${BEHAVIOR_TREE_LIB_DESTINATION} " )
+message( STATUS "BEHAVIOR_TREE_BIN_DESTINATION: ${BEHAVIOR_TREE_BIN_DESTINATION} " )
+message( STATUS "CMAKE_RUNTIME_OUTPUT_DIRECTORY: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} " )
+message( STATUS "CMAKE_LIBRARY_OUTPUT_DIRECTORY: ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} " )
+message( STATUS "CMAKE_ARCHIVE_OUTPUT_DIRECTORY: ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} " )
+
+######################################################
+# Samples
+if (BUILD_SAMPLES)
+ add_subdirectory(sample_nodes)
+endif()
+
######################################################
# Test
-if (BUILD_UNIT_TESTS)
+if (BUILD_UNIT_TESTS AND BUILD_SAMPLES)
add_subdirectory(tests)
endif()
add_subdirectory(tools)
endif()
-if( BUILD_EXAMPLES )
- add_subdirectory(sample_nodes)
+if(BUILD_EXAMPLES AND BUILD_SAMPLES)
add_subdirectory(examples)
endif()
![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)
-![Version](https://img.shields.io/badge/version-3.5-blue.svg)
-[![Build Status](https://travis-ci.org/BehaviorTree/BehaviorTree.CPP.svg?branch=master)](https://travis-ci.org/BehaviorTree/BehaviorTree.CPP)
+![Version](https://img.shields.io/badge/version-3.6-blue.svg)
+[![CMake Build](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/build_vanilla.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/build_vanilla.yml)
[![ros1](https://github.com/BehaviorTree/BehaviorTree.CPP/workflows/ros1/badge.svg?branch=master)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions?query=workflow%3Aros1)
[![ros2](https://github.com/BehaviorTree/BehaviorTree.CPP/workflows/ros2/badge.svg?branch=master)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions?query=workflow%3Aros2)
-[![Build status](https://ci.appveyor.com/api/projects/status/8lawroklgnrkg38f?svg=true)](https://ci.appveyor.com/project/facontidavide59577/behaviortree-cpp)
-[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f7489a1758ab47d49f62342f9649b62a)](https://www.codacy.com/manual/davide.faconti/BehaviorTree.CPP?utm_source=github.com&utm_medium=referral&utm_content=BehaviorTree/BehaviorTree.CPP&utm_campaign=Badge_Grade)
[![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/BehaviorTree/BehaviorTree.CPP)](https://lgtm.com/projects/g/BehaviorTree/BehaviorTree.CPP/context:cpp)
[![Join the chat at https://gitter.im/BehaviorTree-ROS/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/BehaviorTree-ROS/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge)
# Does your company use BehaviorTree.CPP?
-No company, institution or public/private funding is currently supporting the development of BehaviorTree.CPP and Groot.
+No company, institution or public/private funding is currently supporting the development of BehaviorTree.CPP and Groot. As a consequence, my time to support **BehaviorTree.CPP** is very limited and I decided won't spend any time at all supporting **Groot**.
+Pull Requests are welcome and will be reviewed, even if with some delay.
-Consider becoming a **sponsor** to support bug fixing and development of new features. You can find contact details in [package.xml](package.xml).
+If your company use this software, consider becoming a **sponsor** to support bug fixing and development of new features. You can find contact details in [package.xml](package.xml).
# Design principles
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
+if(ZeroMQ_FOUND)
+ set(ZMQ_FOUND ${ZeroMQ_FOUND})
+ set(ZMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR})
+ set(ZMQ_LIBRARIES ${ZeroMQ_LIBRARY})
+else()
+
+
+
if (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS)
# in cache already
set(ZMQ_FOUND TRUE)
mark_as_advanced(ZMQ_INCLUDE_DIRS ZMQ_LIBRARIES)
endif (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS)
-
+endif(ZeroMQ_FOUND)
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import re
-from cpt.packager import ConanMultiPackager
-from cpt.ci_manager import CIManager
-from cpt.printer import Printer
-
-
-class BuilderSettings(object):
-
- @property
- def branch(self):
- """ Get branch name
- """
- printer = Printer(None)
- ci_manager = CIManager(printer)
- return ci_manager.get_branch()
-
- @property
- def username(self):
- """ Set BehaviorTree as package's owner
- """
- return os.getenv("CONAN_USERNAME", "BehaviorTree")
-
- @property
- def upload(self):
- """ Set BehaviorTree repository to be used on upload.
- The upload server address could be customized by env var
- CONAN_UPLOAD. If not defined, the method will check the branch name.
- Only master or CONAN_STABLE_BRANCH_PATTERN will be accepted.
- The master branch will be pushed to testing channel, because it does
- not match the stable pattern. Otherwise it will upload to stable
- channel.
- """
- if os.getenv("CONAN_UPLOAD", None) is not None:
- return os.getenv("CONAN_UPLOAD")
-
- prog = re.compile(self.stable_branch_pattern)
- if self.branch and prog.match(self.branch):
- return "https://api.bintray.com/conan/BehaviorTree/conan"
-
- return None
-
- @property
- def upload_only_when_stable(self):
- """ Force to upload when match stable pattern branch
- """
- return os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True)
-
- @property
- def stable_branch_pattern(self):
- """ Only upload the package the branch name is like a tag
- """
- return os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"\d+\.\d+\.\d+")
-
- @property
- def version(self):
- return self.branch if re.match(self.stable_branch_pattern, self.branch) else "latest"
-
- @property
- def reference(self):
- """ Read project version from branch name to create Conan referece
- """
- return os.getenv("CONAN_REFERENCE", "BehaviorTree.CPP/{}".format(self.version))
-
-if __name__ == "__main__":
- settings = BuilderSettings()
- builder = ConanMultiPackager(
- reference=settings.reference,
- username=settings.username,
- upload=settings.upload,
- upload_only_when_stable=settings.upload_only_when_stable,
- stable_branch_pattern=settings.stable_branch_pattern,
- test_folder=os.path.join("conan", "test_package"))
- builder.add_common_builds(pure_c=False)
- builder.run()
+++ /dev/null
-project(test_package CXX)
-cmake_minimum_required(VERSION 2.8.11)
-
-include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
-conan_basic_setup()
-
-add_executable(${PROJECT_NAME} test_package.cpp)
-target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
-set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 11)
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import os
-from conans import ConanFile, CMake
-
-
-class TestPackageConan(ConanFile):
- settings = "os", "compiler", "build_type", "arch"
- generators = "cmake"
-
- def build(self):
- cmake = CMake(self)
- cmake.configure()
- cmake.build()
-
- def test(self):
- assert os.path.isfile(os.path.join(self.deps_cpp_info["BehaviorTree.CPP"].rootpath, "licenses", "LICENSE"))
- bin_path = os.path.join("bin", "test_package")
- self.run(bin_path, run_environment=True)
+++ /dev/null
-#include "behaviortree_cpp_v3/behavior_tree.h"
-#include "behaviortree_cpp_v3/bt_factory.h"
-
-using namespace BT;
-
-NodeStatus SayHello()
-{
- printf("hello\n");
- return NodeStatus::SUCCESS;
-}
-
-class ActionTestNode : public ActionNode
-{
- public:
- ActionTestNode(const std::string& name) : ActionNode(name)
- {
- }
-
- NodeStatus tick() override
- {
- time_ = 5;
- stop_loop_ = false;
- int i = 0;
- while (!stop_loop_ && i++ < time_)
- {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- return NodeStatus::SUCCESS;
- }
-
- virtual void halt() override
- {
- stop_loop_ = true;
- setStatus(NodeStatus::IDLE);
- }
-
- private:
- int time_;
- std::atomic_bool stop_loop_;
-};
-
-int main()
-{
- BT::SequenceNode root("root");
- BT::SimpleActionNode action1("say_hello", std::bind(SayHello));
- ActionTestNode action2("async_action");
-
- root.addChild(&action1);
- root.addChild(&action2);
-
- int count = 0;
-
- NodeStatus status = NodeStatus::RUNNING;
-
- while (status == NodeStatus::RUNNING)
- {
- status = root.executeTick();
-
- std::cout << count++ << " : " << root.status() << " / " << action1.status() << " / "
- << action2.status() << std::endl;
-
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
-
-
-
- return 0;
-}
+++ /dev/null
-#!/bin/bash
-
-set -e
-set -x
-
-if [[ "$(uname -s)" == 'Darwin' ]]; then
- if which pyenv > /dev/null; then
- eval "$(pyenv init -)"
- fi
- pyenv activate conan
-fi
-
-conan user
-python conan/build.py
+++ /dev/null
-#!/bin/bash
-
-set -ex
-
-if [[ "$(uname -s)" == 'Darwin' ]]; then
- brew update || brew update
- brew outdated pyenv || brew upgrade pyenv
- brew install pyenv-virtualenv
- brew install cmake || true
-
- if which pyenv > /dev/null; then
- eval "$(pyenv init -)"
- fi
-
- pyenv install 3.7.1
- pyenv virtualenv 3.7.1 conan
- pyenv rehash
- pyenv activate conan
-fi
-
-pip install -U conan==1.10.2 conan_package_tools
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""Conan recipe package for BehaviorTree.CPP
-"""
-from conans import ConanFile, CMake, tools
-from conans.model.version import Version
-from conans.errors import ConanInvalidConfiguration
-
-
-class BehaviorTreeConan(ConanFile):
- name = "BehaviorTree.CPP"
- license = "MIT"
- url = "https://github.com/BehaviorTree/BehaviorTree.CPP"
- author = "Davide Faconti <davide.faconti@gmail.com>"
- topics = ("conan", "behaviortree", "ai", "robotics", "games", "coordination")
- description = "This C++ library provides a framework to create BehaviorTrees. It was designed to be flexible, easy to use and fast."
- settings = "os", "compiler", "build_type", "arch"
- options = {"shared": [True, False]}
- default_options = {"shared": False}
- generators = "cmake"
- exports = "LICENSE"
- exports_sources = ("cmake/*", "include/*", "src/*", "3rdparty/*", "CMakeLists.txt")
- requires = "cppzmq/4.3.0@bincrafters/stable"
-
- def configure(self):
- if self.settings.os == "Linux" and \
- self.settings.compiler == "gcc" and \
- Version(self.settings.compiler.version.value) < "5":
- raise ConanInvalidConfiguration("BehaviorTree.CPP can not be built by GCC < 5")
- if self.settings.os == "Windows":
- raise ConanInvalidConfiguration("BehaviorTree.CPP is not prepared to be built on Windows yet")
-
- def _configure_cmake(self):
- """Create CMake instance and execute configure step
- """
- cmake = CMake(self)
- cmake.definitions["BUILD_EXAMPLES"] = False
- cmake.definitions["BUILD_UNIT_TESTS"] = False
- cmake.configure()
- return cmake
-
- def build(self):
- """Configure, build and install BehaviorTree using CMake.
- """
- tools.replace_in_file("CMakeLists.txt",
- "project(behaviortree_cpp)",
- """project(behaviortree_cpp)
- include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
- conan_basic_setup()""")
- # INFO (uilian): zmq could require libsodium
- tools.replace_in_file("CMakeLists.txt",
- "BEHAVIOR_TREE_EXTERNAL_LIBRARIES zmq",
- "BEHAVIOR_TREE_EXTERNAL_LIBRARIES ${CONAN_LIBS}")
- cmake = self._configure_cmake()
- cmake.build()
-
- def package(self):
- """Copy BehaviorTree artifacts to package folder
- """
- self.copy(pattern="LICENSE", dst="licenses")
- cmake = self._configure_cmake()
- cmake.install()
-
- def package_info(self):
- """Collect built libraries names and solve pthread path.
- """
- self.cpp_info.libs = tools.collect_libs(self)
- if self.settings.os == "Linux":
- self.cpp_info.libs.append("pthread")
# Introduction to BTs
Unlike a Finite State Machine, a Behaviour Tree is a __tree of hierarchical nodes__
-that controls the flow of decision and the execution of "tasks" or, as we
-will call them further, "__Actions__".
+that controls the flow of execution of "tasks".
-The __leaves__ of the tree are the actual commands, i.e. the place where
-our coordinating component interacts with the rest of the system.
+## Basic Concepts
-For instance, in a service-oriented architecture, the leaves would contain
-the "client" code that communicates with the "server" that performs the
-operation.
+- A signal called "__tick__" is sent to the root of the tree
+and propagates through the tree until it reaches a leaf node.
-In the following example, we can see two Actions executed in a sequence,
-`DetectObject` and `GraspObject`.
+- A TreeNode that receives a __tick__ signal executes it's callback.
+ This callback must return either
-![Leaf To Component Communication](images/LeafToComponentCommunication.png)
+ - SUCCESS,
+ - FAILURE or
+ - RUNNING, if the action is asynchronous and it needs more time
+ to complete.
-The other nodes of the tree, those which are __not leaves__, control the
-"flow of execution".
+- If a TreeNode has one or more children, it is in charge for ticking
+ them, based on its state, external parameters or the result of the
+ previous sibling.
-To better understand how this control flow takes place, imagine a signal
-called "__tick__"; it is executed at the __root__ of the tree and it propagates
-through the branches until it reaches one or multiple leaves.
+ - The __LeafNodes__, those TreeNodes which don't have any children,
+ are the actual commands, i.e. the place where the behavior tree
+ interacts with the rest of the system.
+ __Actions__ nodes are the most common type of LeafNodes.
!!! Note
The word __tick__ will be often used as a *verb* (to tick / to be ticked) and it means
"To invoke the callback `tick()` of a `TreeNode`".
-When a `TreeNode` is ticked, it returns a `NodeStatus` that can be either:
+In a service-oriented architecture, the leaves would contain
+the "client" code that communicates with the "server",
+that performs the actual operation.
-- __SUCCESS__
-- __FAILURE__
-- __RUNNING__
+## How tick works
+To mentally visualize how ticking the tree works, consider the example below.
-The first two, as their names suggests, inform their parent that their operation
- was a success or a failure.
+![basic sequence](images/bt_intro_01.gif)
-RUNNING is returned by __asynchronous__ nodes when their execution is not
-completed and they needs more time to return a valid result.
+A __Sequence__ is the simplest __ControlNode__: it execute
+its children one after the other and, if they all Succeed,
+it returns SUCCESS (green) too.
-__Asynchronous nodes can be halted__.
+1. The first tick set the Sequence node to RUNNING (orange).
+2. Sequence tick the first child, "DetectObject", that eventually returns SUCCESS.
+3. As a result, the second child "GraspObject" is ticked and the entire Sequence switch from RUNNING to SUCCESS.
-The result of a node is propagated back to its parent, that will decide
-which child should be ticked next or may return a result to its own parent.
## Types of nodes
-__ControlNodes__ are nodes which can have 1 to N children. Once a tick
-is received, this tick may be propagated to one or more of the children.
-__DecoratorNodes__ are similar to the ControlNode, but can only have a single child.
+![UML hierarchy](images/TypeHierarchy.png)
-__ActionNodes__ are leaves and do not have any children. The user should
-implement their own ActionNodes to perform the actual tasks.
+| Type of TreeNode | Children Count | Notes |
+| ----------- | ------------------ | ------------------ |
+| ControlNode | 1...N | Usually, ticks a child based on the result of its siblings or/and its own state. |
+| DecoratorNode | 1 | Among other things, it may alter the result of the children or tick it multiple times.
+| ConditionNode | 0 | Should not alter the system. Shall not return RUNNING. |
+| ActionNode | 0 | It can alter the system. |
-__ConditionNodes__ are equivalent to ActionNodes, but
-they are always atomic and synchronous, i.e. they must not return RUNNING.
-They should not alter the state of the system.
-![UML hierarchy](images/TypeHierarchy.png)
+In the context of __ActionNodes__, we may further distinguish between
+synchronous and asynchronous nodes.
+
+The former are executed atomically and block the tree until a SUCCESS or FAILURE is returned.
+
+Asynchronous actions, instead, may return RUNNING to communicate that
+the action is still being executed.
+We need to tick them again, until SUCCESS or FAILURE is eventually returned.
-## Examples
+# Examples
To better understand how BehaviorTrees work, let's focus on some practical
-examples. For the sake of simplicity we will not take into account what happens
-when an action returns RUNNING.
+examples. For the sake of simplicity we will not take into account what happens when an action returns RUNNING.
We will assume that each Action is executed atomically and synchronously.
The children of a ControlNode are always __ordered__; in the graphical
representation, the order of execution is __from left to right__.
-![Simple Sequence: fridge](images/SequenceBasic.png)
+![Simple Sequence: fridge](images/SequenceBasic.svg)
In short:
Depending on the type of [DecoratorNode](DecoratorNode.md), the goal of
this node could be either:
-- to transform the result it received from the child
-- to halt the execution of the child,
+- to transform the result it received from the child.
+- to halt the execution of the child.
- to repeat ticking the child, depending on the type of Decorator.
-You can extend your grammar creating your own Decorators.
-![Simple Decorator: Enter Room](images/DecoratorEnterRoom.png)
+![Simple Decorator: Enter Room](images/DecoratorEnterRoom.svg)
The node __Inverter__ is a Decorator that inverts
the result returned by its child; An Inverter followed by the node called
-__DoorOpen__ is therefore equivalent to
+__isDoorOpen__ is therefore equivalent to
"Is the door closed?".
-The node __Retry__ will repeat ticking the child up to N times (3 in this case)
+The node __Retry__ will repeat ticking the child up to __num_attempts__ times (5 in this case)
if the child returns FAILURE.
__Apparently__, the branch on the right side means:
If the door is closed, then try to open it.
- Try up to 3 times, otherwise give up and return FAILURE.
+ Try up to 5 times, otherwise give up and return FAILURE.
But...
!!! warning "Have you spotted the bug?"
- If __DoorOpen__ returns FAILURE, we have the desired behaviour.
+ If __isDoorOpen__ returns FAILURE, we have the desired behaviour.
But if it returns SUCCESS, the left branch fails and the entire Sequence
is interrupted.
In the next example, you can see how Sequences and Fallbacks can be combined:
-![FallbackNodes](images/FallbackBasic.png)
+![FallbackNodes](images/FallbackBasic.svg)
> Is the door open?
SUCCESS and "red" for those which return FAILURE. Black nodes haven't
been executed.
-![FetchBeer failure](images/FetchBeerFails.png)
+![FetchBeer failure](images/FetchBeerFails.svg)
Let's create an alternative tree that closes the door even when __GrabBeer__
returns FAILURE.
-![FetchBeer failure](images/FetchBeer.png)
+![FetchBeer failure](images/FetchBeer.svg)
Both these trees will close the door of the fridge, eventually, but:
Everything works as expected if __GrabBeer__ returns SUCCESS.
-![FetchBeer success](images/FetchBeer2.png)
+![FetchBeer success](images/FetchBeer2.svg)
Their purpose is to try different strategies, until we find one that "works".
+Currently the framework provides two kinds of nodes:
+
+- Fallback
+- ReactiveFallback
+
They share the following rules:
- Before ticking the first child, the node status becomes __RUNNING__.
- If a child returns __FAILURE__, the fallback ticks the next child.
- If the __last__ child returns __FAILURE__ too, all the children are halted and
- the sequence returns __FAILURE__.
+ the fallback returns __FAILURE__.
- If a child returns __SUCCESS__, it stops and returns __SUCCESS__.
All the children are halted.
-The two versions of Fallback differ in the way they react when a child returns
-RUNNING:
+To understand how the two ControlNodes differ, refer to the following table:
-- FallbackStar will return RUNNING and the next time it is ticked,
- it will tick the same child where it left off before.
-
-- Plain old Fallback will return RUNNING and the index of the next child to
- execute is reset after each execution.
+| Type of ControlNode | Child returns RUNNING |
+|---|:---:|
+| Fallback | Tick again |
+| ReactiveFallback | Restart |
+
+- "__Restart__" means that the entire fallback is restarted from the first
+ child of the list.
+
+- "__Tick again__" means that the next time the fallback is ticked, the
+ same child is ticked again. Previous sibling, which returned FAILURE already,
+ are not ticked again.
## Fallback
child if one of the previous Conditions changes its state from
FAILURE to SUCCESS.
-In the following example, character will sleep up to 8 hours or less,
-if he/she is fully rested.
+In the following example, the character will sleep *up to* 8 hours. If he/she has fully rested, then the node `areYouRested?` will return SUCCESS and the asynchronous nodes `Timeout (8 hrs)` and `Sleep` will be interrupted.
![ReactiveFallback](images/ReactiveFallback.png)
??? example "See the pseudocode"
``` c++
- // index is initialized to 0 in the constructor
status = RUNNING;
for (int index=0; index < number_of_children; index++)
child_status = child[index]->tick();
if( child_status == RUNNING ) {
+ // Suspend all subsequent siblings and return RUNNING.
+ HaltSubsequentSiblings();
return RUNNING;
}
- else if( child_status == FAILURE ) {
- // continue the while loop
- index++;
- }
- else if( child_status == SUCCESS ) {
+
+ // if child_status == FAILURE, continue to tick next sibling
+
+ if( child_status == SUCCESS ) {
// Suspend execution and return SUCCESS.
- // At the next tick, index will be the same.
- HaltAllChildren();
+ HaltAllChildren();
return SUCCESS;
}
}
// all the children returned FAILURE. Return FAILURE too.
- index = 0;
HaltAllChildren();
return FAILURE;
```
This tree represents the behavior of a sniper in a computer game.
-![SequenceNode](images/SequenceNode.png)
+![SequenceNode](images/SequenceNode.svg)
??? example "See the pseudocode"
``` c++
Let's take a look at another example:
-![ReactiveSequence](images/ReactiveSequence.png)
+![ReactiveSequence](images/ReactiveSequence.svg)
`ApproachEnemy` is an __asynchronous__ action that returns RUNNING until
it is, eventually, completed.
On the other hand, __isBatteryOK__ must be checked at every tick,
for this reason its parent must be a `ReactiveSequence`.
-![SequenceStar](images/SequenceStar.png)
+![SequenceStar](images/SequenceStar.svg)
??? example "See the pseudocode"
``` c++
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg106" width="579" height="301" content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:50:36.713Z" agent="5.0 (X11)" version="16.2.7" etag="4Ga_fKNebX7HASdBwP-D" type="google"><diagram id="7MuUSBjBQZxBBGgICeBk">7VhRb5swEP41PC4COwbyuKbtNmnTpkbT1qfKAxfYjE2NSch+/QzYAQKd0o40qbS8wJ3vzufvO98pWHCZlu8EzuJPPCTUAnZYWvDSAsABjqMelWarNdBfNJpIJKHWtYpV8ptopa21RRKSvGcoOacyyfrKgDNGAtnTYSH4pm92z2l/1wxHZKBYBZgOtd+SUMZa67iLduE9SaJYb+0Dr1lIsTHWJ8ljHPJNRwWvLLgUnMvmLS2XhFboGVwav+tHVneJCcLkIQ5ApyG35mwkVEfVIuNMPS4EL1hIKg9bSVzImEecYfqR80wpHaX8SaTcaqJwIblSxTKlepWUifxeuc+Qlm47K5eljlwLWyMwKbaNk4eMfNtdbP1qyTjecyZ1Jo6v5OaE1bF6GOW8EIFWIV1DWETEUDZE0tnxoyqb8JSoXZWJIBTLZN2PjnWFRTu7lgT1onkY5wSeLSf70HY4ehJFz6NkMT0l2vULT1REYOsOBT1Q1VztpVsUhN4M9eM0qWnXPXZ3uRxE+PxsCe8QDE5wCU2/P8Ut1OmsMS100BV5KAhT6e6z1admEyeSrDJcn2ujJmCfhnFk1kRIUlqPNfFHDm3q1RnUK/CgqddNZ0KZsRN3htPc/new3NdQwc8uYHRYAfsjBQxPV8Def07GOXFPx4n/epqKg4ZDEL1oU1kMwLpikogbztOzQ2ukBb8wWmZWduBaUp6TS87F2cE197wZOC1czgCuD3mF1eeMsL/g5TwdLywCLUJ3IvjsHnTIGYUOHQu6V/Gn8diTxYz23miZn260mHw6BW0Bl6osLsJkrV6j6vWmCvuVyYSuiiAgeX5fUGOmduhY9pw7XLsPRfVxoobsTV5j9lYZOCgr20UThBXpHZaSpJnMlVEdwEYm9A+xv9l+ClO2rVwK/ossOVX9sIoGbfW7vp7mRoJh/5/D0Vvpg13n615MNMXFnA9KoOpmZzkBFDwz3zukjR1tAqCzbWPPaUfuSDs6nJXp25E7nK+srpiJa/Go1xoddKUnqlAlth9+m89I7fdzePUH</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 579 301"><metadata id="metadata110"/><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path4" stroke-miterlimit="10" d="M 342.75,49.5 218.84,87.63" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path6" stroke-miterlimit="10" d="m 213.82,89.17 5.66,-5.4 -0.64,3.86 2.7,2.83 z" pointer-events="all"/><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path8" stroke-miterlimit="10" d="M 342.75,49.5 V 83.13" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path10" stroke-miterlimit="10" d="m 342.75,88.38 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all"/><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path12" stroke-miterlimit="10" d="m 342.75,49.5 128.4,38.18" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path14" stroke-miterlimit="10" d="m 476.18,89.18 -7.71,1.36 2.68,-2.86 -0.68,-3.85 z" pointer-events="all"/><rect style="fill:#fff;stroke:#000" id="rect16" width="120" height="40" x="282.75" y="9.5" pointer-events="all"/><g id="g22" transform="translate(-0.5,-0.5)"><switch id="switch20"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:284px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text18" x="343" y="35" font-size="18">Sequence</text></switch></g><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path24" stroke-miterlimit="10" d="m 182.75,129.5 85.29,37.44" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path26" stroke-miterlimit="10" d="m 272.85,169.05 -7.82,0.39 3.01,-2.5 -0.19,-3.91 z" pointer-events="all"/><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path28" stroke-miterlimit="10" d="M 182.75,129.5 90.89,167.09" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path30" stroke-miterlimit="10" d="m 86.03,169.08 5.16,-5.89 -0.3,3.9 2.95,2.57 z" pointer-events="all"/><rect style="fill:#fff;stroke:#000" id="rect32" width="120" height="40" x="122.75" y="89.5" pointer-events="all"/><g id="g38" transform="translate(-0.5,-0.5)"><switch id="switch36"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:110px;margin-left:124px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text34" x="183" y="115" font-size="18">Sequence</text></switch></g><rect style="fill:#fff;stroke:#000" id="rect40" width="120" height="40" x="282.75" y="89.5" pointer-events="all"/><g id="g46" transform="translate(-0.5,-0.5)"><switch id="switch44"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:110px;margin-left:284px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">EnterRoom</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text42" x="343" y="115" font-size="18">EnterRoom</text></switch></g><rect style="fill:#fff;stroke:#000" id="rect48" width="120" height="40" x="447.25" y="89.5" pointer-events="all"/><g id="g54" transform="translate(-0.5,-0.5)"><switch id="switch52"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:110px;margin-left:448px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseDoor</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text50" x="507" y="115" font-size="18">CloseDoor</text></switch></g><rect style="fill:#fff;stroke:#000" id="rect56" width="150" height="40" x="10" y="249.5" pointer-events="all" rx="14.4" ry="14.4"/><g id="g62" transform="translate(-0.5,-0.5)"><switch id="switch60"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:269px;margin-left:11px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">IsDoorOpen</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text58" x="85" y="275" font-size="18">IsDoorOpen</text></switch></g><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path64" stroke-miterlimit="10" d="m 273.88,219.5 -0.01,23.63" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path66" stroke-miterlimit="10" d="m 273.87,248.38 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all"/><rect style="fill:#fff;stroke:#00f" id="rect68" width="182.25" height="50" x="182.75" y="169.5" pointer-events="all"/><rect style="fill:#fff;stroke:#000" id="rect76" width="120" height="40" x="213.87" y="249.5" pointer-events="all"/><g id="g82" transform="translate(-0.5,-0.5)"><switch id="switch80"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:269px;margin-left:215px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenDoor</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text78" x="274" y="275" font-size="18">OpenDoor</text></switch></g><path style="fill:none;stroke:#000;stroke-miterlimit:10" id="path84" stroke-miterlimit="10" d="m 85,209.5 v 33.63" pointer-events="stroke"/><path style="fill:#000;stroke:#000;stroke-miterlimit:10" id="path86" stroke-miterlimit="10" d="m 85,248.38 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all"/><rect style="fill:#fff;stroke:#00f" id="rect88" width="120" height="40" x="25" y="169.5" pointer-events="all"/><g id="g94" transform="translate(-0.5,-0.5)"><switch id="switch92"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:190px;margin-left:26px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Inverter</xhtml:div></xhtml:div></xhtml:div></foreignObject><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text90" x="85" y="195" font-size="18">Inverter</text></switch></g><g id="g983" transform="translate(1.1685879,-9.2600865)"><switch id="switch981"><text style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000" id="text977" x="274" y="200" font-size="18">RetryUntilSuccessful</text><text id="text979" x="274" y="200" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">...</text></switch></g><text id="text987" x="213.552" y="208.716" transform="scale(0.99249594,1.0075608)" xml:space="preserve" style="font-style:normal;font-weight:400;font-size:13.66238117px;line-height:1.25;font-family:sans-serif;letter-spacing:0;word-spacing:0;fill:#000;fill-opacity:1;stroke:none;stroke-width:1.02467859"><tspan style="stroke-width:1.02467859" id="tspan985" x="213.552" y="208.716">num_attempts = 5</tspan></text></svg>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ style="background-color:#fff"
+ id="svg118"
+ width="611"
+ height="301"
+ content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:51:02.162Z" agent="5.0 (X11)" version="16.2.7" etag="r2FI06e1Bz4JBFBCcYux" type="google"><diagram id="bD_avIc9hnmej6XGDE4-">7Vnfk5owEP5rfHUIEYHXetrrtJ126nTae0xhT+jFrI3xV//6BgkKhut4Vw9wer7ofsmG5Pt2Nwn26Gi+fSvZIvmIMfCe68TbHr3puS5xCdFfGbIzCA3CHJnJNDbYEZimv8GAjkFXaQzLSkeFyFW6qIIRCgGRqmBMStxUu90jrz51wWZgAdOIcRv9lsYqMSgZhseGW0hniXl04Pp5w5wVnc1KlgmLcVOC6LhHRxJR5b/m2xHwjL2Cl9xv8kjrYWIShDrHwTXTULtibRDrpRpToNBfbySuRAyZh6MtlCrBGQrGPyAuNEg0+BOU2hmh2EqhhhI156YVtqn6nrn3PWPdlVputmbkvbErDKHkLnfyvcK+Kzce/fZW4XiPQpmZkEDb+QqzZVU4WuJKRgYamBhicgaFZDaT5KCPjmzAOein6i4SOFPpujo6MxE2O/Q7iqB/GB3qNaGd1eSU2pJGT5LoeZKEl5fEuH7GVI/oOkWFckI3C7q9W1GjyKA6TD4z43ki7mEqZ+ltBl4zvjIznsKvFQhNxWkgVFXfJKmC6YLtOdvoWltVuD4R1iAVbHuPlYtHGD0wQy1mPFPKNqVKWJS3pFQEB86/p4bX2dR4biqURfLOS43ATo0iOJvIDZf2g5MQcJ2wH5Y+vv9SuTJ8DYD6AHCaCoCAtqW9/6p9vfZ+e2eVwNq7JozzHyx6uOzetVQSH2CEHOV+NOroz2RymV0t8O3tnja4q4UWiWOhQH5BnHfvBECGzglTg+aYKspciap3yxtE+WkB4i9ckadzxWRkTDq8DHW+U2XusJoyc95LMUcsev7D6klqrhXEa698kqu4f7eiyrBFVbp7A29blRZfi5Cruie7Z9T6F9slPYupr4Jj9JDtlFfAVdgkV0OLq1u2hvegJzPp+omi5uDaLHm+RV52EOtkmNVx1WhS2lelznJFiN9cSmrz+H9H/lrg+LcRHf8B</diagram></mxfile>"
+ version="1.1"
+ viewBox="-0.5 -0.5 611 301"
+ sodipodi:docname="FallbackBasic.svg"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:xhtml="http://www.w3.org/1999/xhtml">
+ <defs
+ id="defs141" />
+ <sodipodi:namedview
+ id="namedview139"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="2.5432896"
+ inkscape:cx="427.20263"
+ inkscape:cy="203.67323"
+ inkscape:window-width="1916"
+ inkscape:window-height="1041"
+ inkscape:window-x="0"
+ inkscape:window-y="18"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g108" />
+ <metadata
+ id="metadata122" />
+ <g
+ id="g108">
+ <path
+ id="path74826"
+ stroke-miterlimit="10"
+ d="m 542.92,169.71 -7.67,1.54 2.6,-2.92 -0.77,-3.84 z"
+ pointer-events="all"
+ style="fill:#000000;stroke:#000000;stroke-miterlimit:10" />
+ <path
+ id="path4"
+ stroke-miterlimit="10"
+ d="M 402.75,50 278.84,88.13"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path6"
+ stroke-miterlimit="10"
+ d="m 273.82,89.67 5.66,-5.4 -0.64,3.86 2.7,2.83 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path8"
+ stroke-miterlimit="10"
+ d="M 402.75,50 523.98,91.92"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path10"
+ stroke-miterlimit="10"
+ d="m 528.94,93.63 -7.76,1.02 2.8,-2.73 -0.51,-3.88 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <rect
+ id="rect12"
+ width="120"
+ height="40"
+ x="342.75"
+ y="10"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g18"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch16">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:344px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text14"
+ x="403"
+ y="35"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Sequence</text>
+ </switch>
+ </g>
+ <path
+ id="path20"
+ stroke-miterlimit="10"
+ d="m 242.75,130 141.1,38.33"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path22"
+ stroke-miterlimit="10"
+ d="m 388.92,169.71 -7.67,1.54 2.6,-2.92 -0.77,-3.84 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path24"
+ stroke-miterlimit="10"
+ d="M 242.75,130 91.17,168.43"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path26"
+ stroke-miterlimit="10"
+ d="m 86.08,169.73 5.93,-5.12 -0.84,3.82 2.56,2.97 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path28"
+ stroke-miterlimit="10"
+ d="m 242.75,130 v 33.63"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path30"
+ stroke-miterlimit="10"
+ d="m 242.75,168.88 -3.5,-7 3.5,1.75 3.5,-1.75 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <rect
+ id="rect32"
+ width="120"
+ height="40"
+ x="182.75"
+ y="90"
+ pointer-events="all"
+ style="fill:#fff;stroke:#00f" />
+ <g
+ id="g38"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch36">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:110px;margin-left:184px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Fallback</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text34"
+ x="243"
+ y="115"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Fallback</text>
+ </switch>
+ </g>
+ <rect
+ id="rect40"
+ width="120"
+ height="40"
+ x="470"
+ y="94"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g46"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch44">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:114px;margin-left:471px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">EnterRoom</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text42"
+ x="530"
+ y="119"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">EnterRoom</text>
+ </switch>
+ </g>
+ <rect
+ id="rect48"
+ width="150"
+ height="40"
+ x="10"
+ y="170"
+ pointer-events="all"
+ rx="14.4"
+ ry="14.4"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g54"
+ transform="translate(-0.5,-1.0787354)">
+ <switch
+ id="switch52">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:190px;margin-left:11px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">IsDoorOpen</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text50"
+ x="85"
+ y="195"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000000">IsDoorOpen</text>
+ </switch>
+ </g>
+ <path
+ id="path56"
+ stroke-miterlimit="10"
+ d="m 390,210 v 33.63"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path58"
+ stroke-miterlimit="10"
+ d="m 390,248.88 -3.5,-7 3.5,1.75 3.5,-1.75 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path60"
+ stroke-miterlimit="10"
+ d="M 390,210 248.9,248.33"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path62"
+ stroke-miterlimit="10"
+ d="m 243.83,249.71 5.84,-5.22 -0.77,3.84 2.6,2.92 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path64"
+ stroke-miterlimit="10"
+ d="m 390,210 143.85,38.36"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000;stroke-miterlimit:10" />
+ <path
+ id="path66"
+ stroke-miterlimit="10"
+ d="m 538.92,249.71 -7.67,1.58 2.6,-2.93 -0.79,-3.83 z"
+ pointer-events="all"
+ style="fill:#000;stroke:#000;stroke-miterlimit:10" />
+ <rect
+ id="rect68"
+ width="120"
+ height="40"
+ x="330"
+ y="170"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g74"
+ transform="translate(-0.5,-1.0787354)">
+ <switch
+ id="switch72">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:190px;margin-left:331px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text70"
+ x="390"
+ y="195"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000000">Sequence</text>
+ </switch>
+ </g>
+ <rect
+ id="rect76"
+ width="120"
+ height="40"
+ x="330"
+ y="250"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g82"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch80">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:270px;margin-left:331px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">UnlockDoor</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text78"
+ x="390"
+ y="275"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">UnlockDoor</text>
+ </switch>
+ </g>
+ <rect
+ id="rect84"
+ width="120"
+ height="40"
+ x="182.75"
+ y="250"
+ pointer-events="all"
+ rx="14.4"
+ ry="14.4"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g90"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch88">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:270px;margin-left:184px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">HaveKey?</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text86"
+ x="243"
+ y="275"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">HaveKey?</text>
+ </switch>
+ </g>
+ <rect
+ id="rect92"
+ width="120"
+ height="40"
+ x="182.75"
+ y="170"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g98"
+ transform="translate(-0.5,-1.0787354)">
+ <switch
+ id="switch96">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:190px;margin-left:184px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenDoor</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text94"
+ x="243"
+ y="195"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000000">OpenDoor</text>
+ </switch>
+ </g>
+ <rect
+ id="rect100"
+ width="120"
+ height="40"
+ x="480"
+ y="250"
+ pointer-events="all"
+ style="fill:#fff;stroke:#000" />
+ <g
+ id="g106"
+ transform="translate(-0.5,-0.5)">
+ <switch
+ id="switch104">
+ <foreignObject
+ style="overflow:visible;text-align:left"
+ width="100%"
+ height="100%"
+ pointer-events="none"
+ requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
+ <xhtml:div
+ style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:270px;margin-left:481px">
+ <xhtml:div
+ style="box-sizing:border-box;font-size:0;text-align:center"
+ data-drawio-colors="color: rgb(0, 0, 0);">
+ <xhtml:div
+ style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenDoor</xhtml:div>
+ </xhtml:div>
+ </xhtml:div>
+ </foreignObject>
+ <text
+ id="text102"
+ x="540"
+ y="275"
+ font-size="18"
+ style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">OpenDoor</text>
+ </switch>
+ </g>
+ <rect
+ id="rect74822"
+ width="120"
+ height="40"
+ x="478"
+ y="170"
+ pointer-events="all"
+ style="fill:#ffffff;stroke:#000000" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18px;line-height:1.25;font-family:helvetica;-inkscape-font-specification:helvetica;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
+ x="490.87717"
+ y="195.67627"
+ id="text40449"><tspan
+ sodipodi:role="line"
+ id="tspan40447"
+ x="490.87717"
+ y="195.67627"
+ style="font-size:18px">SmashDoor</tspan></text>
+ <path
+ id="path74824"
+ stroke-miterlimit="10"
+ d="m 242.75,130 295.1,38.33"
+ pointer-events="stroke"
+ style="fill:none;stroke:#000000;stroke-miterlimit:10"
+ sodipodi:nodetypes="cc" />
+ </g>
+</svg>
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg150" width="986" height="321" content="<mxfile host="app.diagrams.net" modified="2022-01-14T23:10:56.098Z" agent="5.0 (X11)" version="16.2.7" etag="ESTEkqebV6YRSOniH-rU" type="google"><diagram id="mKUtwOxgaR0kJFD4DNnV">7VrLcpswFP0abxmQAONtnLhdtNPOeNFmqcAN0MjIFSK2+/UVRjJPdzwJj9jTldHVA+mcY+49DDO83Ow/cbKNvrIA6AyZwX6G72cIWa7nyZ88cigiC8cpAiGPAzWoDKzjP6CCpopmcQBpbaBgjIp4Ww/6LEnAF7UY4Zzt6sOeGa3fdUtCaAXWPqHt6I84EJGKWu6i7PgMcRipW3toXnRsiB6sTpJGJGC7Sgg/zPCSMyaKq81+CTQHT+NSzFud6T1tjEMiLpmA1DbEQZ8NAnlU1UxYIn/uOMuSAPIZpmwxLiIWsoTQL4xtZdCSwV8gxEERRTLBZCgSG6p6YR+Ln/l0w1Gtx0rP/V6tfGwcdCMR/FBMmju6/VjtLOcdW3riM0uE2onlyXZxwvxYNYxSlnFfhZT6BOEhKNjcNpLWiR8pbGAbkHeVQzhQIuLX+upEKSw8jStJkBeKh25O8DVwgibgxJuOE3scTkp83wyv80Zu30aJfiL3yYma+p3FckVk6qwxt9W9VNLA2KmvUWxVTWtQe9rHRWyrhV8JzdR21/A7g0QC0ZRBnfNdFAtYb8kRsZ1MfnV+m3+DVHD2AktGGT+uhj30hF03HxlTWokHDniBfWLpFbiAfQ3ICwgo028dSOR6hjrxrpLNdIqKKolMU/Cev5LbAvfbFpKVTPRhz/CeA/E87H3A66CGTp1R4Z234M2LsDsAPii4z54Pvt8F7pPn2I7Zn3YN06rha+NR8fVa+C4pS+F29OvZtsZzGgUvrqH+eVd+viDX6sRaTbbz6eofvZ2K7FdMbnad+T6k6S3o3p1jQxvTfwsfI0OXCH1r37JaUH5A8Q9uyCy7rX7LmVD+V+GTB/dknbRM+VQaySp/bFvWxQoazZfJgr5R76KFMZgz02cd35qdythzhW8POUjS1rRmeLy6y2rb3ptyZshsvkFwxkS37XtvyJjJwqWOrW2PiW3b9A5myvoAC7ttizUmXN41VDNDOyzU4bCsCV/7W/997xlWEJqOFdRhfAmlT8R/uYnH9qLD9HY8iga0vOgqLO/gukcduscT6h61dZ+/8FmRmGb8JspB5F1QsgwpfDxe1TIVwq6hPboC2UHDFTqyWX45UZjL8vsT/PAX</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 986 321"><metadata id="metadata154"/><g id="g140"><path id="path4" stroke-miterlimit="10" d="M 230,55.5 105.94,103.21" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path6" stroke-miterlimit="10" d="m 101.04,105.1 5.28,-5.78 -0.38,3.89 2.89,2.64 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path8" stroke-miterlimit="10" d="m 230,55.5 128.53,47.78" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path10" stroke-miterlimit="10" d="m 363.45,105.11 -7.78,0.84 2.86,-2.67 -0.42,-3.89 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path12" stroke-miterlimit="10" d="M 230,55.5 V 99.13" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path14" stroke-miterlimit="10" d="m 230,104.38 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect16" width="120" height="40" x="170" y="15.5" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g22" transform="translate(-0.5,-0.5)"><switch id="switch20"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:36px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text18" x="230" y="41" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Sequence</text></switch></g><rect id="rect24" width="120" height="40" x="10" y="105.5" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g30" transform="translate(-0.5,-0.5)"><switch id="switch28"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:125px;margin-left:11px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text26" x="70" y="131" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">OpenFridge</text></switch></g><rect id="rect32" width="120" height="40" x="170.01" y="185.5" pointer-events="all" style="fill:#f8cecc;stroke:#b85450"/><g id="g38" transform="translate(-0.5,-0.5)"><switch id="switch36"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:205px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text34" x="230" y="211" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">GrabBeer</text></switch></g><rect id="rect40" width="120" height="40" x="334.5" y="105.5" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g46" transform="translate(-0.5,-0.5)"><switch id="switch44"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:125px;margin-left:336px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text42" x="395" y="131" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">CloseFridge</text></switch></g><path id="path48" stroke-miterlimit="10" d="m 230.01,145.5 v 33.63" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path50" stroke-miterlimit="10" d="m 230.01,184.38 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect52" width="132.25" height="40" x="163.88" y="105.5" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g58" transform="translate(-0.5,-0.5)"><switch id="switch56"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:125px;margin-left:165px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ForceSuccess</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text54" x="230" y="131" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">ForceSuccess</text></switch></g><path id="path60" stroke-miterlimit="10" d="M 750,50 625.94,97.71" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path62" stroke-miterlimit="10" d="m 621.04,99.6 5.28,-5.78 -0.38,3.89 2.89,2.64 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path64" stroke-miterlimit="10" d="M 750,50 878.53,97.78" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path66" stroke-miterlimit="10" d="m 883.45,99.61 -7.78,0.84 2.86,-2.67 -0.42,-3.89 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path68" stroke-miterlimit="10" d="M 750,50 V 93.63" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path70" stroke-miterlimit="10" d="m 750,98.88 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect72" width="120" height="40" x="690" y="10" pointer-events="all" style="fill:#f8cecc;stroke:#b85450"/><g id="g78" transform="translate(-0.5,-0.5)"><switch id="switch76"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:691px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text74" x="750" y="35" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Sequence</text></switch></g><rect id="rect80" width="120" height="40" x="530" y="100" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g86" transform="translate(-0.5,-0.5)"><switch id="switch84"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:531px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text82" x="590" y="125" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">OpenFridge</text></switch></g><rect id="rect88" width="120" height="40" x="600" y="190" pointer-events="all" style="fill:#f8cecc;stroke:#b85450"/><g id="g94" transform="translate(-0.5,-0.5)"><switch id="switch92"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:210px;margin-left:601px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text90" x="660" y="215" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">GrabBeer</text></switch></g><rect id="rect96" width="120" height="40" x="854.5" y="100" pointer-events="all" style="fill:#fff;stroke:#000"/><g id="g102" transform="translate(-0.5,-0.5)"><switch id="switch100"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:856px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text98" x="915" y="125" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">CloseFridge</text></switch></g><path id="path104" stroke-miterlimit="10" d="m 750.01,140 -84.44,46.91" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path106" stroke-miterlimit="10" d="m 660.98,189.46 4.42,-6.46 0.17,3.91 3.23,2.21 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path108" stroke-miterlimit="10" d="m 750.01,140 80.61,46.8" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path110" stroke-miterlimit="10" d="m 835.16,189.44 -7.81,-0.49 3.27,-2.15 0.24,-3.9 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect112" width="132.25" height="40" x="683.88" y="100" pointer-events="all" style="fill:#f8cecc;stroke:#b85450"/><g id="g118" transform="translate(-0.5,-0.5)"><switch id="switch116"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:120px;margin-left:685px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Fallback</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text114" x="750" y="125" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Fallback</text></switch></g><path id="path120" stroke-miterlimit="10" d="m 836.13,230 v 33.63" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path122" stroke-miterlimit="10" d="m 836.13,268.88 -3.5,-7 3.5,1.75 3.5,-1.75 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect124" width="132.25" height="40" x="770" y="190" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g130" transform="translate(-0.5,-0.5)"><switch id="switch128"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:210px;margin-left:771px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ForceFailure</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text126" x="836" y="215" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">ForceFailure</text></switch></g><rect id="rect132" width="120" height="40" x="776.13" y="270" pointer-events="all" style="fill:#d5e8d4;stroke:#82b366"/><g id="g138" transform="translate(-0.5,-0.5)"><switch id="switch136"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:290px;margin-left:777px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text134" x="836" y="295" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">CloseFridge</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg146" width="986" height="321" version="1.1" viewBox="-0.5 -0.5 986 321"><metadata id="metadata152"/><g id="g138"><path id="path2" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 55.5 L 105.94 103.21" pointer-events="stroke"/><path id="path4" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 101.04 105.1 L 106.32 99.32 L 105.94 103.21 L 108.83 105.85 Z" pointer-events="all"/><path id="path6" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 55.5 L 358.53 103.28" pointer-events="stroke"/><path id="path8" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 363.45 105.11 L 355.67 105.95 L 358.53 103.28 L 358.11 99.39 Z" pointer-events="all"/><path id="path10" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 55.5 L 230 99.13" pointer-events="stroke"/><path id="path12" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 230 104.38 L 226.5 97.38 L 230 99.13 L 233.5 97.38 Z" pointer-events="all"/><rect id="rect14" width="120" height="40" x="170" y="15.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g20" transform="translate(-0.5 -0.5)"><switch id="switch18"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:36px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text16" x="230" y="41" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect id="rect22" width="120" height="40" x="10" y="105.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g28" transform="translate(-0.5 -0.5)"><switch id="switch26"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:126px;margin-left:11px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text24" x="70" y="131" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">OpenFridge</text></switch></g><rect id="rect30" width="120" height="40" x="170.01" y="185.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g36" transform="translate(-0.5 -0.5)"><switch id="switch34"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:206px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text32" x="230" y="211" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GrabBeer</text></switch></g><rect id="rect38" width="120" height="40" x="334.5" y="105.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g44" transform="translate(-0.5 -0.5)"><switch id="switch42"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:126px;margin-left:336px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text40" x="395" y="131" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseFridge</text></switch></g><path id="path46" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230.01 145.5 L 230.01 179.13" pointer-events="stroke"/><path id="path48" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 230.01 184.38 L 226.51 177.38 L 230.01 179.13 L 233.51 177.38 Z" pointer-events="all"/><rect id="rect50" width="132.25" height="40" x="163.88" y="105.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g56" transform="translate(-0.5 -0.5)"><switch id="switch54"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:126px;margin-left:165px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ForceSuccess</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text52" x="230" y="131" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ForceSuccess</text></switch></g><path id="path58" fill="none" stroke="#000" stroke-miterlimit="10" d="M 750 50 L 625.94 97.71" pointer-events="stroke"/><path id="path60" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 621.04 99.6 L 626.32 93.82 L 625.94 97.71 L 628.83 100.35 Z" pointer-events="all"/><path id="path62" fill="none" stroke="#000" stroke-miterlimit="10" d="M 750 50 L 878.53 97.78" pointer-events="stroke"/><path id="path64" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 883.45 99.61 L 875.67 100.45 L 878.53 97.78 L 878.11 93.89 Z" pointer-events="all"/><path id="path66" fill="none" stroke="#000" stroke-miterlimit="10" d="M 750 50 L 750 93.63" pointer-events="stroke"/><path id="path68" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 750 98.88 L 746.5 91.88 L 750 93.63 L 753.5 91.88 Z" pointer-events="all"/><rect id="rect70" width="120" height="40" x="690" y="10" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g76" transform="translate(-0.5 -0.5)"><switch id="switch74"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:691px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text72" x="750" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect id="rect78" width="120" height="40" x="530" y="100" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g84" transform="translate(-0.5 -0.5)"><switch id="switch82"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:531px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text80" x="590" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">OpenFridge</text></switch></g><rect id="rect86" width="120" height="40" x="600" y="190" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g92" transform="translate(-0.5 -0.5)"><switch id="switch90"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:210px;margin-left:601px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text88" x="660" y="215" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GrabBeer</text></switch></g><rect id="rect94" width="120" height="40" x="854.5" y="100" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g100" transform="translate(-0.5 -0.5)"><switch id="switch98"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:856px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text96" x="915" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseFridge</text></switch></g><path id="path102" fill="none" stroke="#000" stroke-miterlimit="10" d="M 750.01 140 L 665.57 186.91" pointer-events="stroke"/><path id="path104" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 660.98 189.46 L 665.4 183 L 665.57 186.91 L 668.8 189.12 Z" pointer-events="all"/><path id="path106" fill="none" stroke="#000" stroke-miterlimit="10" d="M 750.01 140 L 830.62 186.8" pointer-events="stroke"/><path id="path108" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 835.16 189.44 L 827.35 188.95 L 830.62 186.8 L 830.86 182.9 Z" pointer-events="all"/><rect id="rect110" width="132.25" height="40" x="683.88" y="100" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g116" transform="translate(-0.5 -0.5)"><switch id="switch114"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:120px;margin-left:685px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Fallback</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text112" x="750" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Fallback</text></switch></g><path id="path118" fill="none" stroke="#000" stroke-miterlimit="10" d="M 836.13 230 L 836.13 263.63" pointer-events="stroke"/><path id="path120" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 836.13 268.88 L 832.63 261.88 L 836.13 263.63 L 839.63 261.88 Z" pointer-events="all"/><rect id="rect122" width="132.25" height="40" x="770" y="190" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g128" transform="translate(-0.5 -0.5)"><switch id="switch126"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:130px;height:1px;padding-top:210px;margin-left:771px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ForceFailure</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text124" x="836" y="215" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ForceFailure</text></switch></g><rect id="rect130" width="120" height="40" x="776.13" y="270" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g136" transform="translate(-0.5 -0.5)"><switch id="switch134"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:290px;margin-left:777px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text132" x="836" y="295" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseFridge</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg54" width="466" height="151" content="<mxfile host="app.diagrams.net" modified="2022-01-14T23:10:32.547Z" agent="5.0 (X11)" version="16.2.7" etag="TPDlYKIcIQ83eQUnMAhy" type="google"><diagram id="zDTOZIV2HBfoVE9N0Co8">zVdLc9owEP41XBn8RLmGPHpop53h0Oao2IutRmhdIYPpr6+M1w/hZIa2QHKy95NW2v2+tVaeBIt19ah5kX/BFOTEn6XVJLib+L4XM2YfNbJvkJsoaoBMi5Qm9cBS/AYCZ4SWIoWNM9EgSiMKF0xQKUiMg3GtcedOW6F0dy14BiNgmXA5Rr+L1OSEevFNP/AJRJbT1syfNwNr3k6mTDY5T3E3gIL7SbDQiKZ5W1cLkDV5LS+N38Mbo11gGpQ5xcGnMMy+zQ1SmyqZCpV93GosVQq1x8xaqE2OGSouPyMWFvQs+BOM2ZNQvDRoodysJY1CJcyP2n0akfU0GLmraOWDsW8NZfS+cZpHrf00HOz9DlbruEJlKBKPWbvJsE7L4WiDpU4IouozXGdAtMVjJr1OH1vYgGuwu9opGiQ3YuuuzqnCsm5eL4J9IR1e1yT4sJocUzvQ6K8k+jdJ5ueXhFy/obAr+rP2gJqH/rSNgI4oLwqmkbtQExv5HsnbBXOS4uGHVXygsP8OXyF7v6+QotlyWdKiS/hVgrLRHovlKrPLhYFlwQ9p7WwDdFU4JmZjNL7AAiXqw2rBM4vC6EChkHKAr1gCSdJRuQVtoHLSPYGmvgWPKrwv8N2gp7WNKh+0s3D2//TGI3q/FqAebLvPzkzwEY1pBCwNXyOe+c9BHJ+J4MgfHyHhVRmejxiub2O3APqi/HZl+nZhX6iAr8wvG/G7kLiBS5TwOQhjYVxfpK5HmDX7e2zTFPu/geD+Dw==</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 466 151"><metadata id="metadata60"/><g id="g46"><path id="path2" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230.25 49.5 L 106.19 97.21" pointer-events="stroke"/><path id="path4" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 101.29 99.1 L 106.57 93.32 L 106.19 97.21 L 109.08 99.85 Z" pointer-events="all"/><path id="path6" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230.25 49.5 L 230.25 93.13" pointer-events="stroke"/><path id="path8" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 230.25 98.38 L 226.75 91.38 L 230.25 93.13 L 233.75 91.38 Z" pointer-events="all"/><path id="path10" fill="none" stroke="#000" stroke-miterlimit="10" d="M 230.25 49.5 L 358.78 97.28" pointer-events="stroke"/><path id="path12" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 363.7 99.11 L 355.92 99.95 L 358.78 97.28 L 358.36 93.39 Z" pointer-events="all"/><rect id="rect14" width="120" height="40" x="170.25" y="9.5" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g id="g20" transform="translate(-0.5 -0.5)"><switch id="switch18"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text16" x="230" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect id="rect22" width="120" height="40" x="10.25" y="99.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/><g id="g28" transform="translate(-0.5 -0.5)"><switch id="switch26"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:11px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text24" x="70" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">OpenFridge</text></switch></g><rect id="rect30" width="120" height="40" x="170.25" y="99.5" fill="#f8cecc" stroke="#b85450" pointer-events="all"/><g id="g36" transform="translate(-0.5 -0.5)"><switch id="switch34"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text32" x="230" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GrabBeer</text></switch></g><rect id="rect38" width="120" height="40" x="334.75" y="99.5" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g44" transform="translate(-0.5 -0.5)"><switch id="switch42"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:336px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text40" x="395" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseFridge</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg46" width="321" height="181" content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:13:56.421Z" agent="5.0 (X11)" version="16.2.7" etag="toPCHC2ffeMfGQfw0vyv" type="google"><diagram id="8JSPdVEl9xWM1446yXQM">7VbBkpQwEP0artZAFnY86uysWqUXp0rdY4ReiBXSGJoB/HoDNAMsM1tauurBE7zX3Un6vUwzntjlzSsri+wdJqC9YJM0nrjxgsAX4cY9OqYdmOvoaiBSqxJOmoiD+gZMcl1aqQTKRSIhalLFkozRGIhpwUlrsV6m3aNe7lrIFFbEIZZ6zX5UCWXM+tHzKfAaVJrx1tvgegjkckzmTspMJljPKLH3xM4i0vCWNzvQnXijLkPd7YXo6WAWDP1IQcDHoHbsDRLXKkODxj1eWqxMAl3FxiG0lGGKRuq3iIUjfUd+AaKWjZIVoaMyyjVHoVH0qSt/FjK6m0VuGl65B+0IDNl2VtTBu3lsKuvRWHePhvgg/tbhocGuq4VEJVY2ZoovH0mbAqsWrYX0T/a4ew2Yg9vVpVjQktRxubrkC5ae8iYP3AvbcN4S8d+Ss5aEf88SPs1R6ooXfQ8y7rY4wNcKjDv1Q8+WBtWZIjgUsm+vdkNxacZ5gY5gCRrv0g/7QvNcEAieMDxixYjr2cDaMpfNZlW0+XW9wpVeL4rCooyzvYG8/efEEg/EugrPiBU+kVjRSqw3ZS/TB1Wqz/qxq+X/vFrSxgxF9HvE86M/J56D07exj83+YYj9dw==</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 321 181"><metadata id="metadata50"/><g id="g36"><path id="path4" fill="none" stroke="#000" stroke-miterlimit="10" d="M 160 60 L 80.2 116.33" pointer-events="stroke"/><path id="path6" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 75.91 119.36 L 79.61 112.46 L 80.2 116.33 L 83.65 118.18 Z" pointer-events="all"/><path id="path8" fill="none" stroke="#000" stroke-miterlimit="10" d="M 160 60 L 239.8 116.33" pointer-events="stroke"/><path id="path10" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 244.09 119.36 L 236.35 118.18 L 239.8 116.33 L 240.39 112.46 Z" pointer-events="all"/><rect id="rect12" width="180" height="60" x="70" y="0" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g18" transform="translate(-0.5 -0.5)"><switch id="switch16"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:178px;height:1px;padding-top:30px;margin-left:71px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ReactiveSequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text14" x="160" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ReactiveSequence</text></switch></g><rect id="rect20" width="150" height="60" x="170" y="120" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g26" transform="translate(-0.5 -0.5)"><switch id="switch24"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:150px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ApproachEnemy</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text22" x="245" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ApproachEnemy</text></switch></g><rect id="rect28" width="150" height="60" x="0" y="120" fill="#FFF" stroke="#000" pointer-events="all" rx="21.6" ry="21.6"/><g id="g34" transform="translate(-0.5 -0.5)"><switch id="switch32"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:150px;margin-left:1px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">IsEnemyVisible</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text30" x="75" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">IsEnemyVisible</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background-color:#fff" width="466" height="151" content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:50:18.155Z" agent="5.0 (X11)" version="16.2.7" etag="mGPcX5_LkMqR0Xt8hK01" type="google"><diagram id="-Dek54CL9MAO_r9ihOgc">zVZNc5swEP01vmb4MNi5hsTtoZ12xoc2R43ZgFKhpULYuL++wqwAQTvjNh7HXGCftNLueytWizApmg+KlflnTEEsAi9tFuHjIgj8wPfNq0WOhITr+w7JFE8JG4At/wUEeoTWPIXKmagRhealC+5QSthpB2NK4cGd9oLC3bVkGcyA7Y6JOfqNpzon1I/vh4GPwLOctl4Hq26gYHYyZVLlLMXDCAqfFmGiEHX3VTQJiJY9y0vnt/nLaB+YAqnPcQgoDH20uUFqUiVTojSvB4W1TKH18IyFSueYoWTiE2JpQN+Ar6D1kYRitUYD5boQNAoN199b97uIrOfRyGNDK5+MozWkVsfOaRVZ+3k8OPidLOv4glJTJP7a2F2GbVoORxXWakdQRDXEVAZEWzxn0u/1MZUNWIDZ1UxRIJjme3d1RhWW9fMGEcwH6fBnTcKb1WRK7Uijf5Lo/yRZXV4Scv2K3KwYePSHCmM6nfb/ZG27RBcVeU2E7cM4S+vlzWo90jZ4h/O3fr/zR9Hsmahp0S38rEGaaKdiucoccq5hW7JTWgfT+1wVpsRUWuEPSFCgOq0WeubZbHrK9qA0NE5aZ9Bha9hza9iW8GHUr2wTyketaum9ncB4RuCXEuTGtPLswhRegCh/OTns0RWZWs2Yaq9MDwDq5niaFtRVeVrPeEoEVnCjJbWMl3fR1cgy5nBf7FrQcO0On34D</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 466 151"><g><path fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 50 L 105.94 97.71" pointer-events="stroke"/><path fill="#000" stroke="#000" stroke-miterlimit="10" d="M 101.04 99.6 L 106.32 93.82 L 105.94 97.71 L 108.83 100.35 Z" pointer-events="all"/><path fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 50 L 230 93.63" pointer-events="stroke"/><path fill="#000" stroke="#000" stroke-miterlimit="10" d="M 230 98.88 L 226.5 91.88 L 230 93.63 L 233.5 91.88 Z" pointer-events="all"/><path fill="none" stroke="#000" stroke-miterlimit="10" d="M 230 50 L 358.53 97.78" pointer-events="stroke"/><path fill="#000" stroke="#000" stroke-miterlimit="10" d="M 363.45 99.61 L 355.67 100.45 L 358.53 97.78 L 358.11 93.89 Z" pointer-events="all"/><rect width="120" height="40" x="170" y="10" fill="#FFF" stroke="#00f" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:171px"><div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</div></div></div></foreignObject><text x="230" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect width="120" height="40" x="10" y="100" fill="#FFF" stroke="#000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:11px"><div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenFridge</div></div></div></foreignObject><text x="70" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">OpenFridge</text></switch></g><rect width="120" height="40" x="170" y="100" fill="#FFF" stroke="#000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:171px"><div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GrabBeer</div></div></div></foreignObject><text x="230" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GrabBeer</text></switch></g><rect width="120" height="40" x="334.5" y="100" fill="#FFF" stroke="#000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:120px;margin-left:336px"><div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseFridge</div></div></div></foreignObject><text x="395" y="125" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseFridge</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg70" width="571" height="181" content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:18:42.496Z" agent="5.0 (X11)" version="16.2.7" etag="Sk5wmU9rWodo_YCvxg8Y" type="google"><diagram id="QDv7YM2D_S_Yd1SvJyLI">1Zddk5owFIZ/DbcdISp4uevaj5ntTZ1pu5epHCGdkENDUOivb5CDguh+dR3cG+W8JzkkzxtIcNg8KT5pnsZfMQTpeKOwcNid43m+79rfSihrYcz8Woi0CGvJPQhL8RdIHJGaixCyTkODKI1Iu+IKlYKV6Whca9x2m61Rdu+a8gh6wnLFZV/9IUITk+pOZ4fEZxBRTLcOPJpfwpvGNJMs5iFuWxJbOGyuEU19lRRzkBW7hkvd7+OZ7H5gGpR5TgePhmHKZm4acxVClR457Ba1iTFCxeU9YmpF14q/wZiSXOG5QSvFJpGUhUKYn1X3DxOKHlqZu4Iq74KSgjUqQwXdoEoqo8u6iD9p4oemZxUc6uyiplA9HQiP/Msw1yuSprRguI6AGM362Ny9GXYRAyZg72KbaJDciE23OqflFO3bHYjbC4J+2gDWM6Aa/JJChcr+3Q7iScuDF1lw7OXrLGke9SE8Gb8HT7wBTPGH82TyHjwZwJJgOEtoNBsucyq6hD85KDvaxzeVbSwMLFO+m9bWnhC6JpwGswFtoDi7zZ2ZNHVgI3qd0IHDp3Db2r2bLTlubdzT0f9j8nuYbkRyYxYKkvLqQI2PQLmzE6TYhUgF/QUVVyeha4M0mT4NKbgQo1mP0Zdst5S+i0z8ko89eu7LYXG9opBN34ad/4z1NbkQu+ZM0YInsm9iLeEeecXoytnZp/NpeOO3gWfDw1fILtf6lGOLfw==</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 571 181"><metadata id="metadata74"/><g id="g60"><path id="path4" fill="none" stroke="#000" stroke-miterlimit="10" d="M 290 60 L 118.53 117.96" pointer-events="stroke"/><path id="path6" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 113.56 119.64 L 119.07 114.08 L 118.53 117.96 L 121.31 120.72 Z" pointer-events="all"/><path id="path8" fill="none" stroke="#000" stroke-miterlimit="10" d="M 290 60 L 244.08 115.11" pointer-events="stroke"/><path id="path10" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 240.72 119.14 L 242.51 111.52 L 244.08 115.11 L 247.89 116 Z" pointer-events="all"/><path id="path12" fill="none" stroke="#000" stroke-miterlimit="10" d="M 290 60 L 357.59 115.94" pointer-events="stroke"/><path id="path14" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 361.64 119.29 L 354.01 117.52 L 357.59 115.94 L 358.48 112.13 Z" pointer-events="all"/><path id="path16" fill="none" stroke="#000" stroke-miterlimit="10" d="M 290 60 L 523.82 118.46" pointer-events="stroke"/><path id="path18" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 528.92 119.73 L 521.28 121.43 L 523.82 118.46 L 522.97 114.64 Z" pointer-events="all"/><rect id="rect20" width="120" height="60" x="230" y="0" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g26" transform="translate(-0.5 -0.5)"><switch id="switch24"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:231px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text22" x="290" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect id="rect28" width="130" height="60" x="330" y="120" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g34" transform="translate(-0.5 -0.5)"><switch id="switch32"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:128px;height:1px;padding-top:150px;margin-left:331px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">AimAtEnemy</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text30" x="395" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">AimAtEnemy</text></switch></g><rect id="rect36" width="80" height="60" x="490" y="120" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g42" transform="translate(-0.5 -0.5)"><switch id="switch40"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:78px;height:1px;padding-top:150px;margin-left:491px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Shoot</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text38" x="530" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Shoot</text></switch></g><rect id="rect44" width="150" height="60" x="0" y="120" fill="#FFF" stroke="#000" pointer-events="all" rx="21.6" ry="21.6"/><g id="g50" transform="translate(-0.5 -0.5)"><switch id="switch48"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:150px;margin-left:1px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">IsEnemyVisible</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text46" x="75" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">IsEnemyVisible</text></switch></g><rect id="rect52" width="140" height="60" x="170" y="120" fill="#FFF" stroke="#000" pointer-events="all" rx="21.6" ry="21.6"/><g id="g58" transform="translate(-0.5 -0.5)"><switch id="switch56"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:138px;height:1px;padding-top:150px;margin-left:171px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">isRifleLoaded</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text54" x="240" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">isRifleLoaded</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg70" width="441" height="302" content="<mxfile host="app.diagrams.net" modified="2022-01-14T22:23:12.908Z" agent="5.0 (X11)" version="16.2.7" etag="18veofEKuKUwUDRxzVob" type="google"><diagram id="BNCKIlJ_xest3XBbFGQC">1VdLj5swEP41SO1lxSMhcOxmt9tDK1VNq3aPFkzAK8eTOiYh/fU1YRxwSapK3U3YE8xnz3jmm4fBi+ar+kGxdfkJcxBe6Oe1F915YRgE4dQ8GmTfInE0a4FC8Zw2dcCC/wICfUIrnsPG2agRheZrF8xQSsi0gzGlcOduW6JwT12zAgbAImNiiH7nuS5tXHHaLXwAXpR0dBJSfCtmN1Mkm5LluOtB0b0XzRWibt9W9RxEQ57lpdV7f2b16JgCqf9FISQ39N7GBrkJlUSJ0jxuFVYyh0bDNxIqXWKBkomPiGsDBgZ8Aq33lChWaTRQqVeCVqHm+kejfjMl6bG3cleT5YOwt4LUat8qzaZWfuwvdnoHySouUWryJEiM3EbYhOVwtMFKZQRROWqmCiDa0iGTwTE/prABV2BONVsUCKb51rXOqMKK476j6mfkxmLoUzckPp1OvTCJfNdE6xVpdak0Lz03OuiQ4NPJjl5Dsq+Q6/j5c+1k6S8pmYw2Jc9DrZ3Y1+CW3NkyUZHRBfysQGaGX6YGxLss70quYbFmh9h25hpzGT3NzhaUhto7N4rPBG4Hgb0TaBBEVt71rpgJYWXvdon9/+cqHnD1gF/xzfzt6GhKkzPzsk9TMH0ZmmajbdcLTtBk2ObT63V5MqjcL42Jb1JzsaiyDDbLSoyuimepW8XHQdmv4vSFmj093ezvxtfsceLOxIs2u03Jnzzdjo+nJApvLjcWjdj9obTfod1/XnT/Gw==</diagram></mxfile>" version="1.1" viewBox="-0.5 -0.5 441 302"><metadata id="metadata74"/><g id="g60"><path id="path4" fill="none" stroke="#000" stroke-miterlimit="10" d="M 220 180 L 102.15 227.61" pointer-events="stroke"/><path id="path6" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 97.29 229.58 L 102.47 223.71 L 102.15 227.61 L 105.09 230.2 Z" pointer-events="all"/><path id="path8" fill="none" stroke="#000" stroke-miterlimit="10" d="M 220 180 L 366.45 228.02" pointer-events="stroke"/><path id="path10" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 371.44 229.65 L 363.7 230.8 L 366.45 228.02 L 365.88 224.15 Z" pointer-events="all"/><path id="path12" fill="none" stroke="#000" stroke-miterlimit="10" d="M 220 180 L 220 223.63" pointer-events="stroke"/><path id="path14" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 220 228.88 L 216.5 221.88 L 220 223.63 L 223.5 221.88 Z" pointer-events="all"/><rect id="rect16" width="140" height="60" x="150" y="120" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g22" transform="translate(-0.5 -0.5)"><switch id="switch20"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:138px;height:1px;padding-top:150px;margin-left:151px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">SequenceStar</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text18" x="220" y="155" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">SequenceStar</text></switch></g><rect id="rect24" width="115" height="60" x="315" y="230" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g30" transform="translate(-0.5 -0.5)"><switch id="switch28"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:113px;height:1px;padding-top:260px;margin-left:316px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GoTo(C)</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text26" x="373" y="265" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GoTo(C)</text></switch></g><path id="path32" fill="none" stroke="#000" stroke-miterlimit="10" d="M 220 70 L 220 113.63" pointer-events="stroke"/><path id="path34" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 220 118.88 L 216.5 111.88 L 220 113.63 L 223.5 111.88 Z" pointer-events="all"/><rect id="rect36" width="190" height="60" x="125" y="10" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g42" transform="translate(-0.5 -0.5)"><switch id="switch40"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:188px;height:1px;padding-top:40px;margin-left:126px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">RetryUntilSuccessful</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text38" x="220" y="45" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">RetryUntilSuccessful</text></switch></g><rect id="rect44" width="115" height="60" x="10" y="230" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g50" transform="translate(-0.5 -0.5)"><switch id="switch48"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:113px;height:1px;padding-top:260px;margin-left:11px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GoTo(A)</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text46" x="68" y="265" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GoTo(A)</text></switch></g><rect id="rect52" width="115" height="60" x="162.5" y="230" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g58" transform="translate(-0.5 -0.5)"><switch id="switch56"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:113px;height:1px;padding-top:260px;margin-left:164px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">GoTo(B)</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text54" x="220" y="265" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">GoTo(B)</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg66" width="651" height="161" version="1.1" viewBox="-0.5 -0.5 651 161"><metadata id="metadata72"/><g id="g58"><path id="path2" stroke-miterlimit="10" d="m 350,50 -65.17,55.86" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path4" stroke-miterlimit="10" d="m 280.85,109.27 3.04,-7.21 0.94,3.8 3.61,1.51 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path6" stroke-miterlimit="10" d="m 350,50 60.32,55.68" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path8" stroke-miterlimit="10" d="m 414.18,109.24 -7.52,-2.17 3.66,-1.39 1.09,-3.76 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path10" stroke-miterlimit="10" d="m 350,50 193.9,58.17" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path12" stroke-miterlimit="10" d="m 548.93,109.68 -7.71,1.34 2.68,-2.85 -0.67,-3.86 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><path id="path14" stroke-miterlimit="10" d="M 350,50 91.21,108.59" pointer-events="stroke" style="fill:none;stroke:#000;stroke-miterlimit:10"/><path id="path16" stroke-miterlimit="10" d="m 86.09,109.75 6.05,-4.96 -0.93,3.8 2.48,3.03 z" pointer-events="all" style="fill:#000;stroke:#000;stroke-miterlimit:10"/><rect id="rect18" width="120" height="40" x="290" y="10" pointer-events="all" style="fill:#fff;stroke:#000"/><g id="g24" transform="translate(-0.5,-0.5)"><switch id="switch22"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:291px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text20" x="350" y="35" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">Sequence</text></switch></g><rect id="rect26" width="120" height="40" x="190" y="110" pointer-events="all" style="fill:#fff;stroke:#000"/><g id="g32" transform="translate(-0.5,-0.5)"><switch id="switch30"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:130px;margin-left:191px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenGripper</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text28" x="250" y="135" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">OpenGripper</text></switch></g><rect id="rect34" width="150" height="40" x="340" y="110" pointer-events="all" style="fill:#fff;stroke:#000"/><g id="g40" transform="translate(-0.5,-0.5)"><switch id="switch38"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:130px;margin-left:341px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ApproachObject</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text36" x="415" y="135" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">ApproachObject</text></switch></g><rect id="rect42" width="120" height="40" x="520" y="110" pointer-events="all" style="fill:#fff;stroke:#000"/><g id="g48" transform="translate(-0.5,-0.5)"><switch id="switch46"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:130px;margin-left:521px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseGripper</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text44" x="580" y="135" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">CloseGripper</text></switch></g><rect id="rect50" width="150" height="40" x="10" y="110" pointer-events="all" rx="14.4" ry="14.4" style="fill:#fff;stroke:#000"/><g id="g56" transform="translate(-0.5,-0.5)"><switch id="switch54"><foreignObject style="overflow:visible;text-align:left" width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:130px;margin-left:11px"><xhtml:div style="box-sizing:border-box;font-size:0;text-align:center" data-drawio-colors="color: rgb(0, 0, 0);"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CheckBattery</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text52" x="85" y="135" font-size="18" style="font-size:18px;font-family:Helvetica;text-anchor:middle;fill:#000">CheckBattery</text></switch></g></g></svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" style="background-color:#fff" id="svg220" width="901" height="601" version="1.1" viewBox="-0.5 -0.5 901 601"><metadata id="metadata226"/><g id="g212"><path id="path2" fill="none" stroke="#000" stroke-miterlimit="10" d="M 350 50 L 284.83 105.86" pointer-events="stroke"/><path id="path4" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 280.85 109.27 L 283.89 102.06 L 284.83 105.86 L 288.44 107.37 Z" pointer-events="all"/><path id="path6" fill="none" stroke="#000" stroke-miterlimit="10" d="M 350 50 L 410.32 105.68" pointer-events="stroke"/><path id="path8" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 414.18 109.24 L 406.66 107.07 L 410.32 105.68 L 411.41 101.92 Z" pointer-events="all"/><path id="path10" fill="none" stroke="#000" stroke-miterlimit="10" d="M 350 50 L 543.9 108.17" pointer-events="stroke"/><path id="path12" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 548.93 109.68 L 541.22 111.02 L 543.9 108.17 L 543.23 104.31 Z" pointer-events="all"/><path id="path14" fill="none" stroke="#000" stroke-miterlimit="10" d="M 350 50 L 91.21 108.59" pointer-events="stroke"/><path id="path16" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 86.09 109.75 L 92.14 104.79 L 91.21 108.59 L 93.69 111.62 Z" pointer-events="all"/><rect id="rect18" width="120" height="40" x="290" y="10" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g24" transform="translate(-0.5 -0.5)"><switch id="switch22"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:30px;margin-left:291px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text20" x="350" y="35" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><rect id="rect26" width="120" height="40" x="190" y="110" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g32" transform="translate(-0.5 -0.5)"><switch id="switch30"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:130px;margin-left:191px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">OpenGripper</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text28" x="250" y="135" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">OpenGripper</text></switch></g><rect id="rect34" width="150" height="40" x="340" y="110" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g40" transform="translate(-0.5 -0.5)"><switch id="switch38"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:130px;margin-left:341px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">ApproachObject</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text36" x="415" y="135" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ApproachObject</text></switch></g><rect id="rect42" width="120" height="40" x="520" y="110" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g48" transform="translate(-0.5 -0.5)"><switch id="switch46"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:130px;margin-left:521px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CloseGripper</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text44" x="580" y="135" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CloseGripper</text></switch></g><rect id="rect50" width="150" height="40" x="10" y="110" fill="#FFF" stroke="#000" pointer-events="all" rx="14.4" ry="14.4"/><g id="g56" transform="translate(-0.5 -0.5)"><switch id="switch54"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:148px;height:1px;padding-top:130px;margin-left:11px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">CheckBattery</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text52" x="85" y="135" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">CheckBattery</text></switch></g><path id="path58" fill="none" stroke="#000" stroke-miterlimit="10" d="M 470 224 L 608.6 280.59" pointer-events="stroke"/><path id="path60" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 613.46 282.58 L 605.66 283.17 L 608.6 280.59 L 608.31 276.69 Z" pointer-events="all"/><path id="path62" fill="none" stroke="#000" stroke-miterlimit="10" d="M 470 224 L 380.41 279.64" pointer-events="stroke"/><path id="path64" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 375.95 282.41 L 380.05 275.74 L 380.41 279.64 L 383.74 281.69 Z" pointer-events="all"/><path id="path66" fill="none" stroke="#000" stroke-miterlimit="10" d="M 470 224 L 768.75 281.79" pointer-events="stroke"/><path id="path68" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 773.9 282.79 L 766.37 284.89 L 768.75 281.79 L 767.69 278.02 Z" pointer-events="all"/><path id="path70" fill="none" stroke="#000" stroke-miterlimit="10" d="M 470 224 L 186.24 281.73" pointer-events="stroke"/><path id="path72" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 181.1 282.78 L 187.26 277.95 L 186.24 281.73 L 188.65 284.81 Z" pointer-events="all"/><rect id="rect74" width="120" height="40" x="410" y="184" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g80" transform="translate(-0.5 -0.5)"><switch id="switch78"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:118px;height:1px;padding-top:204px;margin-left:411px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal">Sequence</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text76" x="470" y="209" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">Sequence</text></switch></g><path id="path82" fill="none" stroke="#000" stroke-dasharray="3 3" stroke-miterlimit="10" d="M 574.5 359 Q 574.5 430 522.25 430 Q 470 430 470 485 Q 470 540 513.63 540" pointer-events="stroke"/><path id="path84" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 518.88 540 L 511.88 543.5 L 513.63 540 L 511.88 536.5 Z" pointer-events="all"/><rect id="rect86" width="160" height="76" x="494.5" y="283" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g92" transform="translate(-0.5 -0.5)"><switch id="switch90"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:158px;height:1px;padding-top:321px;margin-left:496px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal"><xhtml:div>ThinkWhatToSay</xhtml:div><xhtml:div style="font-size:15px"><xhtml:br/></xhtml:div><xhtml:div><xhtml:font style="font-size:15px">text={the_answer}</xhtml:font><xhtml:br/></xhtml:div></xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text88" x="575" y="326" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">ThinkWhatToSay...</text></switch></g><rect id="rect94" width="190" height="76" x="280" y="283" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g100" transform="translate(-0.5 -0.5)"><switch id="switch98"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:188px;height:1px;padding-top:321px;margin-left:281px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal"><xhtml:div>SaySomething2</xhtml:div><xhtml:div style="font-size:15px"><xhtml:br/></xhtml:div><xhtml:div style="font-size:15px">message="this works too"<xhtml:br/></xhtml:div></xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text96" x="375" y="326" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">SaySomething2...</text></switch></g><rect id="rect102" width="190" height="76" x="680" y="283" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g108" transform="translate(-0.5 -0.5)"><switch id="switch106"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:188px;height:1px;padding-top:321px;margin-left:681px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal"><xhtml:div style="font-size:15px"><xhtml:font style="font-size:18px">SaySomething</xhtml:font><xhtml:div><xhtml:br/></xhtml:div>message={the_answer}</xhtml:div></xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text104" x="775" y="326" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">SaySomething...</text></switch></g><rect id="rect110" width="160" height="82" x="100" y="283" fill="#FFF" stroke="#000" pointer-events="all"/><g id="g116" transform="translate(-0.5 -0.5)"><switch id="switch114"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:158px;height:1px;padding-top:324px;margin-left:101px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:all;white-space:normal;overflow-wrap:normal"><xhtml:div>SaySomething</xhtml:div><xhtml:div><xhtml:br/></xhtml:div><xhtml:div style="font-size:15px">message="hello"<xhtml:br/></xhtml:div></xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text112" x="180" y="329" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">SaySomething...</text></switch></g><path id="path118" fill="#FFF" stroke="#00f" stroke-miterlimit="10" d="M 520 490 L 520 460 L 850 460 L 850 490" pointer-events="all"/><path id="path120" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 520 490 L 520 590 L 850 590 L 850 490" pointer-events="none"/><path id="path122" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 520 490 L 850 490" pointer-events="none"/><path id="path124" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 520 523 L 630 523 L 710 523 L 850 523" pointer-events="none"/><path id="path126" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 520 557 L 630 557 L 710 557 L 850 557" pointer-events="none"/><path id="path128" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 630 490 L 630 523 L 630 557 L 630 590" pointer-events="none"/><path id="path130" fill="none" stroke="#00f" stroke-miterlimit="10" d="M 710 490 L 710 523 L 710 557 L 710 590" pointer-events="none"/><g id="g134" fill="#000" font-family="Helvetica" font-size="18" font-weight="bold" pointer-events="none" text-anchor="middle"><text id="text132" x="684.5" y="482.5">Blackboard</text></g><path id="path136" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 520 490 M 630 490 M 630 523 M 520 523" pointer-events="none"/><g id="g142" transform="translate(-0.5 -0.5)"><switch id="switch140"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:108px;height:1px;padding-top:507px;margin-left:521px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:16px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;font-weight:700;white-space:normal;overflow-wrap:normal">KEY</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text138" x="575" y="511" fill="#000" font-family="Helvetica" font-size="16" font-weight="bold" text-anchor="middle">KEY</text></switch></g><path id="path144" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 630 490 M 710 490 M 710 523 M 630 523" pointer-events="none"/><g id="g150" transform="translate(-0.5 -0.5)"><switch id="switch148"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:78px;height:1px;padding-top:507px;margin-left:631px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:16px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;font-weight:700;white-space:normal;overflow-wrap:normal">TYPE</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text146" x="670" y="511" fill="#000" font-family="Helvetica" font-size="16" font-weight="bold" text-anchor="middle">TYPE</text></switch></g><path id="path152" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 710 490 M 850 490 M 850 523 M 710 523" pointer-events="none"/><g id="g158" transform="translate(-0.5 -0.5)"><switch id="switch156"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:138px;height:1px;padding-top:507px;margin-left:711px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:16px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;font-weight:700;white-space:normal;overflow-wrap:normal">VALUE</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text154" x="780" y="511" fill="#000" font-family="Helvetica" font-size="16" font-weight="bold" text-anchor="middle">VALUE</text></switch></g><path id="path160" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 520 523 M 630 523 M 630 557 M 520 557" pointer-events="none"/><g id="g166" transform="translate(-0.5 -0.5)"><switch id="switch164"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:108px;height:1px;padding-top:540px;margin-left:521px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:30px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:15px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal">the_answer</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text162" x="575" y="545" fill="#000" font-family="Helvetica" font-size="15" text-anchor="middle">the_answer</text></switch></g><path id="path168" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 630 523 M 710 523 M 710 557 M 630 557" pointer-events="none"/><g id="g174" transform="translate(-0.5 -0.5)"><switch id="switch172"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:78px;height:1px;padding-top:540px;margin-left:631px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:30px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:15px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal">string</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text170" x="670" y="545" fill="#000" font-family="Helvetica" font-size="15" text-anchor="middle">string</text></switch></g><path id="path176" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 710 523 M 850 523 M 850 557 M 710 557" pointer-events="none"/><g id="g182" transform="translate(-0.5 -0.5)"><switch id="switch180"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:138px;height:1px;padding-top:540px;margin-left:711px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:30px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:15px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal"><xhtml:font style="font-size:15px">"the answer is 42"<xhtml:br style="font-size:15px"/></xhtml:font></xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text178" x="780" y="545" fill="#000" font-family="Helvetica" font-size="15" text-anchor="middle">"the answer is 42"</text></switch></g><path id="path184" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 520 557 M 630 557 M 630 590 M 520 590" pointer-events="none"/><g id="g190" transform="translate(-0.5 -0.5)"><switch id="switch188"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:108px;height:1px;padding-top:574px;margin-left:521px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal">...</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text186" x="575" y="579" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">...</text></switch></g><path id="path192" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 630 557 M 710 557 M 710 590 M 630 590" pointer-events="none"/><g id="g198" transform="translate(-0.5 -0.5)"><switch id="switch196"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:78px;height:1px;padding-top:574px;margin-left:631px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal">...</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text194" x="670" y="579" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">...</text></switch></g><path id="path200" fill="none" stroke="#00f" stroke-linecap="square" stroke-miterlimit="10" d="M 710 557 M 850 557 M 850 590 M 710 590" pointer-events="none"/><g id="g206" transform="translate(-0.5 -0.5)"><switch id="switch204"><foreignObject width="100%" height="100%" pointer-events="none" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow:visible;text-align:left"><xhtml:div style="display:flex;align-items:unsafe center;justify-content:unsafe center;width:138px;height:1px;padding-top:574px;margin-left:711px"><xhtml:div data-drawio-colors="color: rgb(0, 0, 0);" style="box-sizing:border-box;font-size:0;text-align:center;max-height:29px;overflow:hidden"><xhtml:div style="display:inline-block;font-size:18px;font-family:Helvetica;color:#000;line-height:1.2;pointer-events:none;white-space:normal;overflow-wrap:normal">...</xhtml:div></xhtml:div></xhtml:div></foreignObject><text id="text202" x="780" y="579" fill="#000" font-family="Helvetica" font-size="18" text-anchor="middle">...</text></switch></g><path id="path208" fill="none" stroke="#000" stroke-dasharray="3 3" stroke-miterlimit="10" d="M 850 540 Q 890 540 890 480 Q 890 420 832.5 420 Q 775 420 775 365.37" pointer-events="none"/><path id="path210" fill="#000" stroke="#000" stroke-miterlimit="10" d="M 775 360.12 L 778.5 367.12 L 775 365.37 L 771.5 367.12 Z" pointer-events="none"/></g></svg>
\ No newline at end of file
__BehaviorTree.CPP__ has many interesting features, when compared to other implementations:
-- It makes asynchronous Actions, i.e. non-blocking, a first-class citizen.
+- It makes asynchronous Actions, i.e. non-blocking routines, a first-class citizen.
- It allows the creation of trees at run-time, using a textual representation (XML).
-- You can link staticaly you custom TreeNodes or convert them into plugins
+- You can link staticaly your custom TreeNodes or convert them into plugins
which are loaded at run-time.
- It includes a __logging/profiling__ infrastructure that allows the user
to visualize, record, replay and analyze state transitions.
- __They are intrinsically Hierarchical__: this means that we can _compose_
complex behaviors including entire trees as sub-branches of a bigger tree.
-For instance, the behavior "Fetch Beer" may reuse in one of its nodes the tree
+For instance, the behavior "Fetch Beer" may reuse the tree
"Grasp Object".
- __Their graphical representation has a semantic meaning__: it is easier to
## "Ok, but WHY do we need BehaviorTrees (or FSM)?"
-Many software systems, being robotics a notable example, are inherently
+Many software systems, robotics being a notable example, are inherently
complex.
The usual approach to manage complexity, heterogeneity and scalability is to
software modules/components which are highly coupled to a particular application,
instead of being reusable.
-Frequently, the concern of __Coordination__ is mixed with __Computation__.
-In other words, people address the problems of coordinating actions and take decisions
-locally.
-
-The business logic becomes "spread" in many locations and it is __hard for the developer
+Frequently, the business logic is "spread" in many locations and it is __hard for the developer
to reason about it and to debug errors__ in the control flow.
To achieve strong separation of concerns it is better to centralize
but keep in mind that real "production" code would probably do something
more complicated.
+Further, we will create this simple tree:
+
+
+![Tutorial1](images/Tutorial1.svg)
+
## How to create your own ActionNodes
The default (and recommended) way to create a TreeNode is by inheritance.
BehaviorTree.CPP provides a basic mechanism of __dataflow__
through __ports__, that is simple to use but also flexible and type safe.
+In this tutorial we will create the following tree:
+
+![Tutorial2](images/Tutorial2.svg)
+
+You may notice already as the 2nd child of the Sequence will write on a row
+of a Key/Value table (the __Blackboard__) and the 4th node read
+from the same row.
+
## Inputs ports
A valid Input can be either:
-- static strings which can be parsed by the Node, or
-- "pointers" to an entry of the Blackboard, identified by a __key__.
+- a static string which can be parsed by the Node, or
+- a "pointer" to an entry of the Blackboard, identified by a __key__.
A "blackboard" is a simple __key/value storage__ shared by all the nodes
of the Tree.
Consider these alternative XML syntaxes:
```XML
- <SaySomething name="first" message="hello world" />
- <SaySomething name="second" message="{greetings}" />
+ <SaySomething message="hello world" />
+ <SaySomething message="{greetings}" />
```
The attribute `message` in the __first node__ means:
"Read the current value in the entry of the blackboard called 'greetings' ".
-This value can (and probably will) change at run-time.
+The value of the entry can (and probably will) change at run-time.
The ActionNode `SaySomething` can be implemented as follows:
<root main_tree_to_execute = "MainTree" >
<BehaviorTree ID="MainTree">
<Sequence name="root_sequence">
- <SaySomething message="start thinking..." />
+ <SaySomething message="hello" />
+ <SaySomething2 message="this works too" />
<ThinkWhatToSay text="{the_answer}"/>
<SaySomething message="{the_answer}" />
- <SaySomething2 message="SaySomething2 works too..." />
- <SaySomething2 message="{the_answer}" />
</Sequence>
</BehaviorTree>
</root>
/* Expected output:
- Robot says: start thinking...
- Robot says: The answer is 42
- Robot says: SaySomething2 works too...
+ Robot says: hello
+ Robot says: this works too
Robot says: The answer is 42
*/
return 0;
}
```
-We "connect" output ports to input ports using the same key (`the_aswer`);
+We "connect" output ports to input ports using the same key (`the_answer`);
this means that they "point" to the same entry of the blackboard.
These ports can be connected to each other because their type is the same,
<Inverter>
<IsDoorOpen/>
</Inverter>
- <RetryUntilSuccesful num_attempts="4">
+ <RetryUntilSuccessful num_attempts="4">
<OpenDoor/>
- </RetryUntilSuccesful>
+ </RetryUntilSuccessful>
<PassThroughDoor/>
</Sequence>
</BehaviorTree>
<w>150</w>
<h>40</h>
</coordinates>
- <panel_attributes>RetryUntilSuccesful
+ <panel_attributes>RetryUntilSuccesfful
(num_attempts=4)</panel_attributes>
<additional_attributes/>
</element>
<w>150</w>
<h>40</h>
</coordinates>
- <panel_attributes>RetryUntilSuccesful</panel_attributes>
+ <panel_attributes>RetryUntilSuccessful</panel_attributes>
<additional_attributes/>
</element>
<element>
<BehaviorTree ID="MainTree">
<Sequence>
<Action ID="SaySomething" message="Hello World"/>
- <Subtree ID="GraspObject"/>
+ <SubTree ID="GraspObject"/>
</Sequence>
</BehaviorTree>
<BehaviorTree ID="MainTree">
<Sequence>
<Action ID="SaySomething" message="Hello World"/>
- <Subtree ID="GraspObject"/>
+ <SubTree ID="GraspObject"/>
</Sequence>
</BehaviorTree>
</root>
add_executable(t12_ncurses_manual_selector t12_ncurses_manual_selector.cpp )
target_link_libraries(t12_ncurses_manual_selector ${BEHAVIOR_TREE_LIBRARY} bt_sample_nodes )
endif()
+
<BehaviorTree ID="MainTree">
<Sequence name="root">
- <SaySomething message="start thinking..." />
+ <SaySomething message="hello" />
+ <SaySomething2 message="this works too" />
<ThinkWhatToSay text="{the_answer}"/>
- <SaySomething message="{the_answer}" />
- <SaySomething2 message="SaySomething2 works too..." />
<SaySomething2 message="{the_answer}" />
</Sequence>
</BehaviorTree>
/* An INPUT can be either a string, for instance:
*
- * <SaySomething message="start thinking..." />
+ * <SaySomething message="hello" />
*
* or contain a "pointer" to a type erased entry in the Blackboard,
* using this syntax: {name_of_entry}. Example:
/* Expected output:
*
- Robot says: start thinking...
- Robot says: The answer is 42
- Robot says: SaySomething2 works too...
+ Robot says: hello
+ Robot says: this works too
Robot says: The answer is 42
*
* The way we "connect" output ports to input ports is to "point" to the same
* Blackboard entry.
*
* This means that ThinkSomething will write into the entry with key "the_answer";
- * SaySomething and SaySomething2 will read the message from the same entry.
+ * SaySomething and SaySomething will read the message from the same entry.
*
*/
return 0;
/** This tutorial will teach you:
*
- * - The difference between Sequence and SequenceStar
+ * - The difference between Sequence and ReactiveSequence
*
* - How to create an asynchronous ActionNode (an action that is execute in
* its own thread).
[ MoveBase: STARTED ]. goal: x=1 y=2.0 theta=3.00
--- 2nd executeTick() ---
-[ Battery: OK ]
[ MoveBase: FINISHED ]
--- 3rd executeTick() ---
-[ Battery: OK ]
Robot says: "mission completed!"
-
------------ BUILDING A NEW TREE ------------
--- 1st executeTick() ---
[ MoveBase: STARTED ]. goal: x=1 y=2.0 theta=3.00
--- 2nd executeTick() ---
+[ Battery: OK ]
[ MoveBase: FINISHED ]
--- 3rd executeTick() ---
+[ Battery: OK ]
Robot says: "mission completed!"
*/
<Inverter>
<Condition ID="IsDoorOpen"/>
</Inverter>
- <RetryUntilSuccesful num_attempts="4">
+ <RetryUntilSuccessful num_attempts="4">
<OpenDoor/>
- </RetryUntilSuccesful>
+ </RetryUntilSuccessful>
<PassThroughDoor/>
</Sequence>
</BehaviorTree>
// more verbose way
PortsList think_ports = {BT::OutputPort<std::string>("text")};
factory.registerBuilder(CreateManifest<ThinkRuntimePort>("ThinkRuntimePort", think_ports),
- CreateBuilder<ThinkRuntimePort>());
+ CreateBuilder<ThinkRuntimePort>());
// less verbose way
PortsList say_ports = {BT::InputPort<std::string>("message")};
factory.registerNodeType<SayRuntimePort>("SayRuntimePort", say_ports);
#include <atomic>
#include <thread>
#include <future>
+#include <mutex>
+
#include "leaf_node.h"
namespace BT
std::exception_ptr exptr_;
std::atomic_bool halt_requested_;
std::future<NodeStatus> thread_handle_;
+ std::mutex m_;
};
/**
#include "behaviortree_cpp_v3/decorators/timeout_node.h"
#include "behaviortree_cpp_v3/decorators/delay_node.h"
+#include <iostream>
namespace BT
{
void applyRecursiveVisitor(TreeNode* root_node, const std::function<void(TreeNode*)>& visitor);
/**
- * Debug function to print on screen the hierarchy of the tree.
+ * Debug function to print the hierarchy of the tree. Prints to std::cout by default.
*/
-void printTreeRecursively(const TreeNode* root_node);
+void printTreeRecursively(const TreeNode* root_node, std::ostream& stream = std::cout);
typedef std::vector<std::pair<uint16_t, uint8_t>> SerializedTreeStatus;
void set(const std::string& key, const T& value)
{
std::unique_lock<std::mutex> lock(mutex_);
+ std::unique_lock<std::mutex> lock_entry(entry_mutex_);
auto it = storage_.find(key);
if( auto parent = parent_bb_.lock())
auto parent_info = parent->portInfo(remapped_key);
if( parent_info )
{
- storage_.insert( {key, Entry( *parent_info ) } );
+ storage_.emplace( key, Entry( *parent_info ) );
}
else{
- storage_.insert( {key, Entry( PortInfo() ) } );
+ storage_.emplace( key, Entry( PortInfo() ) );
}
}
parent->set( remapped_key, value );
std::vector<StringView> getKeys() const;
+ void clear()
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ storage_.clear();
+ internal_to_external_.clear();
+ }
+
+ // Lock this mutex before using get() and getAny() and unlock it while you have
+ // done using the value.
+ std::mutex& entryMutex()
+ {
+ return entry_mutex_;
+ }
+
private:
struct Entry{
+
Any value;
const PortInfo port_info;
};
mutable std::mutex mutex_;
+ mutable std::mutex entry_mutex_;
std::unordered_map<std::string, Entry> storage_;
std::weak_ptr<Blackboard> parent_bb_;
std::unordered_map<std::string,std::string> internal_to_external_;
* @brief The ReactiveSequence is similar to a ParallelNode.
* All the children are ticked from first to last:
*
- * - If a child returns RUNNING, tick the next sibling.
+ * - If a child returns RUNNING, halt the remaining siblings in the sequence and return RUNNING.
* - If a child returns SUCCESS, tick the next sibling.
* - If a child returns FAILURE, stop and return FAILURE.
*
setRegistrationID("BlackboardCheckDouble");
else if( std::is_same<T,std::string>::value)
setRegistrationID("BlackboardCheckString");
+ else if( std::is_same<T,bool>::value)
+ setRegistrationID("BlackboardCheckBool");
}
virtual ~BlackboardPreconditionNode() override = default;
}
void halt() override
{
+ delay_started_ = false;
timer_.cancelAll();
DecoratorNode::halt();
}
*
* Example:
*
- * <RetryUntilSuccesful num_attempts="3">
+ * <RetryUntilSuccessful num_attempts="3">
* <OpenDoor/>
- * </RetryUntilSuccesful>
+ * </RetryUntilSuccessful>
+ *
+ * Note:
+ * RetryNodeTypo is only included to support the depricated typo
+ * "RetryUntilSuccesful" (note the single 's' in Succesful)
*/
class RetryNode : public DecoratorNode
{
virtual BT::NodeStatus tick() override;
};
+
+class
+[[deprecated("RetryUntilSuccesful was a typo and deprecated, use RetryUntilSuccessful instead.")]]
+RetryNodeTypo : public RetryNode{
+ public:
+ RetryNodeTypo(const std::string& name, int NTries)
+ : RetryNode(name, NTries)
+ { };
+
+ RetryNodeTypo(const std::string& name, const NodeConfiguration& config)
+ : RetryNode(name, config)
+ { };
+
+ virtual ~RetryNodeTypo() override = default;
+};
+
}
#endif
namespace Serialization {
struct PortModel;
+struct PortModelBuilder;
struct PortConfig;
+struct PortConfigBuilder;
struct TreeNode;
+struct TreeNodeBuilder;
struct NodeModel;
+struct NodeModelBuilder;
struct BehaviorTree;
+struct BehaviorTreeBuilder;
struct Timestamp;
struct StatusChange;
struct StatusChangeLog;
+struct StatusChangeLogBuilder;
enum class NodeStatus : int8_t {
IDLE = 0,
}
inline const char * const *EnumNamesNodeStatus() {
- static const char * const names[] = {
+ static const char * const names[5] = {
"IDLE",
"RUNNING",
"SUCCESS",
}
inline const char *EnumNameNodeStatus(NodeStatus e) {
- if (e < NodeStatus::IDLE || e > NodeStatus::FAILURE) return "";
- const size_t index = static_cast<int>(e);
+ if (flatbuffers::IsOutRange(e, NodeStatus::IDLE, NodeStatus::FAILURE)) return "";
+ const size_t index = static_cast<size_t>(e);
return EnumNamesNodeStatus()[index];
}
}
inline const char * const *EnumNamesNodeType() {
- static const char * const names[] = {
+ static const char * const names[7] = {
"UNDEFINED",
"ACTION",
"CONDITION",
}
inline const char *EnumNameNodeType(NodeType e) {
- if (e < NodeType::UNDEFINED || e > NodeType::SUBTREE) return "";
- const size_t index = static_cast<int>(e);
+ if (flatbuffers::IsOutRange(e, NodeType::UNDEFINED, NodeType::SUBTREE)) return "";
+ const size_t index = static_cast<size_t>(e);
return EnumNamesNodeType()[index];
}
}
inline const char * const *EnumNamesPortDirection() {
- static const char * const names[] = {
+ static const char * const names[4] = {
"INPUT",
"OUTPUT",
"INOUT",
}
inline const char *EnumNamePortDirection(PortDirection e) {
- if (e < PortDirection::INPUT || e > PortDirection::INOUT) return "";
- const size_t index = static_cast<int>(e);
+ if (flatbuffers::IsOutRange(e, PortDirection::INPUT, PortDirection::INOUT)) return "";
+ const size_t index = static_cast<size_t>(e);
return EnumNamesPortDirection()[index];
}
uint64_t usec_since_epoch_;
public:
- Timestamp() {
- memset(this, 0, sizeof(Timestamp));
+ Timestamp()
+ : usec_since_epoch_(0) {
}
Timestamp(uint64_t _usec_since_epoch)
: usec_since_epoch_(flatbuffers::EndianScalar(_usec_since_epoch)) {
int8_t prev_status_;
int8_t status_;
int32_t padding0__;
- Timestamp timestamp_;
+ Serialization::Timestamp timestamp_;
public:
- StatusChange() {
- memset(this, 0, sizeof(StatusChange));
+ StatusChange()
+ : uid_(0),
+ prev_status_(0),
+ status_(0),
+ padding0__(0),
+ timestamp_() {
+ (void)padding0__;
}
- StatusChange(uint16_t _uid, NodeStatus _prev_status, NodeStatus _status, const Timestamp &_timestamp)
+ StatusChange(uint16_t _uid, Serialization::NodeStatus _prev_status, Serialization::NodeStatus _status, const Serialization::Timestamp &_timestamp)
: uid_(flatbuffers::EndianScalar(_uid)),
prev_status_(flatbuffers::EndianScalar(static_cast<int8_t>(_prev_status))),
status_(flatbuffers::EndianScalar(static_cast<int8_t>(_status))),
uint16_t uid() const {
return flatbuffers::EndianScalar(uid_);
}
- NodeStatus prev_status() const {
- return static_cast<NodeStatus>(flatbuffers::EndianScalar(prev_status_));
+ Serialization::NodeStatus prev_status() const {
+ return static_cast<Serialization::NodeStatus>(flatbuffers::EndianScalar(prev_status_));
}
- NodeStatus status() const {
- return static_cast<NodeStatus>(flatbuffers::EndianScalar(status_));
+ Serialization::NodeStatus status() const {
+ return static_cast<Serialization::NodeStatus>(flatbuffers::EndianScalar(status_));
}
- const Timestamp ×tamp() const {
+ const Serialization::Timestamp ×tamp() const {
return timestamp_;
}
};
FLATBUFFERS_STRUCT_END(StatusChange, 16);
struct PortModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef PortModelBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PORT_NAME = 4,
VT_DIRECTION = 6,
const flatbuffers::String *port_name() const {
return GetPointer<const flatbuffers::String *>(VT_PORT_NAME);
}
- PortDirection direction() const {
- return static_cast<PortDirection>(GetField<int8_t>(VT_DIRECTION, 0));
+ Serialization::PortDirection direction() const {
+ return static_cast<Serialization::PortDirection>(GetField<int8_t>(VT_DIRECTION, 0));
}
const flatbuffers::String *type_info() const {
return GetPointer<const flatbuffers::String *>(VT_TYPE_INFO);
};
struct PortModelBuilder {
+ typedef PortModel Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_port_name(flatbuffers::Offset<flatbuffers::String> port_name) {
fbb_.AddOffset(PortModel::VT_PORT_NAME, port_name);
}
- void add_direction(PortDirection direction) {
+ void add_direction(Serialization::PortDirection direction) {
fbb_.AddElement<int8_t>(PortModel::VT_DIRECTION, static_cast<int8_t>(direction), 0);
}
void add_type_info(flatbuffers::Offset<flatbuffers::String> type_info) {
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- PortModelBuilder &operator=(const PortModelBuilder &);
flatbuffers::Offset<PortModel> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<PortModel>(end);
inline flatbuffers::Offset<PortModel> CreatePortModel(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> port_name = 0,
- PortDirection direction = PortDirection::INPUT,
+ Serialization::PortDirection direction = Serialization::PortDirection::INPUT,
flatbuffers::Offset<flatbuffers::String> type_info = 0,
flatbuffers::Offset<flatbuffers::String> description = 0) {
PortModelBuilder builder_(_fbb);
inline flatbuffers::Offset<PortModel> CreatePortModelDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *port_name = nullptr,
- PortDirection direction = PortDirection::INPUT,
+ Serialization::PortDirection direction = Serialization::PortDirection::INPUT,
const char *type_info = nullptr,
const char *description = nullptr) {
auto port_name__ = port_name ? _fbb.CreateString(port_name) : 0;
}
struct PortConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef PortConfigBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_PORT_NAME = 4,
VT_REMAP = 6
};
struct PortConfigBuilder {
+ typedef PortConfig Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_port_name(flatbuffers::Offset<flatbuffers::String> port_name) {
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- PortConfigBuilder &operator=(const PortConfigBuilder &);
flatbuffers::Offset<PortConfig> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<PortConfig>(end);
}
struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef TreeNodeBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_UID = 4,
VT_CHILDREN_UID = 6,
const flatbuffers::Vector<uint16_t> *children_uid() const {
return GetPointer<const flatbuffers::Vector<uint16_t> *>(VT_CHILDREN_UID);
}
- NodeStatus status() const {
- return static_cast<NodeStatus>(GetField<int8_t>(VT_STATUS, 0));
+ Serialization::NodeStatus status() const {
+ return static_cast<Serialization::NodeStatus>(GetField<int8_t>(VT_STATUS, 0));
}
const flatbuffers::String *instance_name() const {
return GetPointer<const flatbuffers::String *>(VT_INSTANCE_NAME);
const flatbuffers::String *registration_name() const {
return GetPointer<const flatbuffers::String *>(VT_REGISTRATION_NAME);
}
- const flatbuffers::Vector<flatbuffers::Offset<PortConfig>> *port_remaps() const {
- return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<PortConfig>> *>(VT_PORT_REMAPS);
+ const flatbuffers::Vector<flatbuffers::Offset<Serialization::PortConfig>> *port_remaps() const {
+ return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Serialization::PortConfig>> *>(VT_PORT_REMAPS);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
};
struct TreeNodeBuilder {
+ typedef TreeNode Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_uid(uint16_t uid) {
void add_children_uid(flatbuffers::Offset<flatbuffers::Vector<uint16_t>> children_uid) {
fbb_.AddOffset(TreeNode::VT_CHILDREN_UID, children_uid);
}
- void add_status(NodeStatus status) {
+ void add_status(Serialization::NodeStatus status) {
fbb_.AddElement<int8_t>(TreeNode::VT_STATUS, static_cast<int8_t>(status), 0);
}
void add_instance_name(flatbuffers::Offset<flatbuffers::String> instance_name) {
void add_registration_name(flatbuffers::Offset<flatbuffers::String> registration_name) {
fbb_.AddOffset(TreeNode::VT_REGISTRATION_NAME, registration_name);
}
- void add_port_remaps(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<PortConfig>>> port_remaps) {
+ void add_port_remaps(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::PortConfig>>> port_remaps) {
fbb_.AddOffset(TreeNode::VT_PORT_REMAPS, port_remaps);
}
explicit TreeNodeBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- TreeNodeBuilder &operator=(const TreeNodeBuilder &);
flatbuffers::Offset<TreeNode> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<TreeNode>(end);
flatbuffers::FlatBufferBuilder &_fbb,
uint16_t uid = 0,
flatbuffers::Offset<flatbuffers::Vector<uint16_t>> children_uid = 0,
- NodeStatus status = NodeStatus::IDLE,
+ Serialization::NodeStatus status = Serialization::NodeStatus::IDLE,
flatbuffers::Offset<flatbuffers::String> instance_name = 0,
flatbuffers::Offset<flatbuffers::String> registration_name = 0,
- flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<PortConfig>>> port_remaps = 0) {
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::PortConfig>>> port_remaps = 0) {
TreeNodeBuilder builder_(_fbb);
builder_.add_port_remaps(port_remaps);
builder_.add_registration_name(registration_name);
flatbuffers::FlatBufferBuilder &_fbb,
uint16_t uid = 0,
const std::vector<uint16_t> *children_uid = nullptr,
- NodeStatus status = NodeStatus::IDLE,
+ Serialization::NodeStatus status = Serialization::NodeStatus::IDLE,
const char *instance_name = nullptr,
const char *registration_name = nullptr,
- const std::vector<flatbuffers::Offset<PortConfig>> *port_remaps = nullptr) {
+ const std::vector<flatbuffers::Offset<Serialization::PortConfig>> *port_remaps = nullptr) {
auto children_uid__ = children_uid ? _fbb.CreateVector<uint16_t>(*children_uid) : 0;
auto instance_name__ = instance_name ? _fbb.CreateString(instance_name) : 0;
auto registration_name__ = registration_name ? _fbb.CreateString(registration_name) : 0;
- auto port_remaps__ = port_remaps ? _fbb.CreateVector<flatbuffers::Offset<PortConfig>>(*port_remaps) : 0;
+ auto port_remaps__ = port_remaps ? _fbb.CreateVector<flatbuffers::Offset<Serialization::PortConfig>>(*port_remaps) : 0;
return Serialization::CreateTreeNode(
_fbb,
uid,
}
struct NodeModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef NodeModelBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_REGISTRATION_NAME = 4,
VT_TYPE = 6,
const flatbuffers::String *registration_name() const {
return GetPointer<const flatbuffers::String *>(VT_REGISTRATION_NAME);
}
- NodeType type() const {
- return static_cast<NodeType>(GetField<int8_t>(VT_TYPE, 0));
+ Serialization::NodeType type() const {
+ return static_cast<Serialization::NodeType>(GetField<int8_t>(VT_TYPE, 0));
}
- const flatbuffers::Vector<flatbuffers::Offset<PortModel>> *ports() const {
- return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<PortModel>> *>(VT_PORTS);
+ const flatbuffers::Vector<flatbuffers::Offset<Serialization::PortModel>> *ports() const {
+ return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Serialization::PortModel>> *>(VT_PORTS);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
};
struct NodeModelBuilder {
+ typedef NodeModel Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_registration_name(flatbuffers::Offset<flatbuffers::String> registration_name) {
fbb_.AddOffset(NodeModel::VT_REGISTRATION_NAME, registration_name);
}
- void add_type(NodeType type) {
+ void add_type(Serialization::NodeType type) {
fbb_.AddElement<int8_t>(NodeModel::VT_TYPE, static_cast<int8_t>(type), 0);
}
- void add_ports(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<PortModel>>> ports) {
+ void add_ports(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::PortModel>>> ports) {
fbb_.AddOffset(NodeModel::VT_PORTS, ports);
}
explicit NodeModelBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- NodeModelBuilder &operator=(const NodeModelBuilder &);
flatbuffers::Offset<NodeModel> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<NodeModel>(end);
inline flatbuffers::Offset<NodeModel> CreateNodeModel(
flatbuffers::FlatBufferBuilder &_fbb,
flatbuffers::Offset<flatbuffers::String> registration_name = 0,
- NodeType type = NodeType::UNDEFINED,
- flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<PortModel>>> ports = 0) {
+ Serialization::NodeType type = Serialization::NodeType::UNDEFINED,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::PortModel>>> ports = 0) {
NodeModelBuilder builder_(_fbb);
builder_.add_ports(ports);
builder_.add_registration_name(registration_name);
inline flatbuffers::Offset<NodeModel> CreateNodeModelDirect(
flatbuffers::FlatBufferBuilder &_fbb,
const char *registration_name = nullptr,
- NodeType type = NodeType::UNDEFINED,
- const std::vector<flatbuffers::Offset<PortModel>> *ports = nullptr) {
+ Serialization::NodeType type = Serialization::NodeType::UNDEFINED,
+ const std::vector<flatbuffers::Offset<Serialization::PortModel>> *ports = nullptr) {
auto registration_name__ = registration_name ? _fbb.CreateString(registration_name) : 0;
- auto ports__ = ports ? _fbb.CreateVector<flatbuffers::Offset<PortModel>>(*ports) : 0;
+ auto ports__ = ports ? _fbb.CreateVector<flatbuffers::Offset<Serialization::PortModel>>(*ports) : 0;
return Serialization::CreateNodeModel(
_fbb,
registration_name__,
}
struct BehaviorTree FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef BehaviorTreeBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_ROOT_UID = 4,
VT_NODES = 6,
uint16_t root_uid() const {
return GetField<uint16_t>(VT_ROOT_UID, 0);
}
- const flatbuffers::Vector<flatbuffers::Offset<TreeNode>> *nodes() const {
- return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<TreeNode>> *>(VT_NODES);
+ const flatbuffers::Vector<flatbuffers::Offset<Serialization::TreeNode>> *nodes() const {
+ return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Serialization::TreeNode>> *>(VT_NODES);
}
- const flatbuffers::Vector<flatbuffers::Offset<NodeModel>> *node_models() const {
- return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<NodeModel>> *>(VT_NODE_MODELS);
+ const flatbuffers::Vector<flatbuffers::Offset<Serialization::NodeModel>> *node_models() const {
+ return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Serialization::NodeModel>> *>(VT_NODE_MODELS);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
};
struct BehaviorTreeBuilder {
+ typedef BehaviorTree Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
void add_root_uid(uint16_t root_uid) {
fbb_.AddElement<uint16_t>(BehaviorTree::VT_ROOT_UID, root_uid, 0);
}
- void add_nodes(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TreeNode>>> nodes) {
+ void add_nodes(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::TreeNode>>> nodes) {
fbb_.AddOffset(BehaviorTree::VT_NODES, nodes);
}
- void add_node_models(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<NodeModel>>> node_models) {
+ void add_node_models(flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::NodeModel>>> node_models) {
fbb_.AddOffset(BehaviorTree::VT_NODE_MODELS, node_models);
}
explicit BehaviorTreeBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- BehaviorTreeBuilder &operator=(const BehaviorTreeBuilder &);
flatbuffers::Offset<BehaviorTree> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<BehaviorTree>(end);
inline flatbuffers::Offset<BehaviorTree> CreateBehaviorTree(
flatbuffers::FlatBufferBuilder &_fbb,
uint16_t root_uid = 0,
- flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<TreeNode>>> nodes = 0,
- flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<NodeModel>>> node_models = 0) {
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::TreeNode>>> nodes = 0,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Serialization::NodeModel>>> node_models = 0) {
BehaviorTreeBuilder builder_(_fbb);
builder_.add_node_models(node_models);
builder_.add_nodes(nodes);
inline flatbuffers::Offset<BehaviorTree> CreateBehaviorTreeDirect(
flatbuffers::FlatBufferBuilder &_fbb,
uint16_t root_uid = 0,
- const std::vector<flatbuffers::Offset<TreeNode>> *nodes = nullptr,
- const std::vector<flatbuffers::Offset<NodeModel>> *node_models = nullptr) {
- auto nodes__ = nodes ? _fbb.CreateVector<flatbuffers::Offset<TreeNode>>(*nodes) : 0;
- auto node_models__ = node_models ? _fbb.CreateVector<flatbuffers::Offset<NodeModel>>(*node_models) : 0;
+ const std::vector<flatbuffers::Offset<Serialization::TreeNode>> *nodes = nullptr,
+ const std::vector<flatbuffers::Offset<Serialization::NodeModel>> *node_models = nullptr) {
+ auto nodes__ = nodes ? _fbb.CreateVector<flatbuffers::Offset<Serialization::TreeNode>>(*nodes) : 0;
+ auto node_models__ = node_models ? _fbb.CreateVector<flatbuffers::Offset<Serialization::NodeModel>>(*node_models) : 0;
return Serialization::CreateBehaviorTree(
_fbb,
root_uid,
}
struct StatusChangeLog FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef StatusChangeLogBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_BEHAVIOR_TREE = 4,
VT_STATE_CHANGES = 6
};
- const BehaviorTree *behavior_tree() const {
- return GetPointer<const BehaviorTree *>(VT_BEHAVIOR_TREE);
+ const Serialization::BehaviorTree *behavior_tree() const {
+ return GetPointer<const Serialization::BehaviorTree *>(VT_BEHAVIOR_TREE);
}
- const flatbuffers::Vector<const StatusChange *> *state_changes() const {
- return GetPointer<const flatbuffers::Vector<const StatusChange *> *>(VT_STATE_CHANGES);
+ const flatbuffers::Vector<const Serialization::StatusChange *> *state_changes() const {
+ return GetPointer<const flatbuffers::Vector<const Serialization::StatusChange *> *>(VT_STATE_CHANGES);
}
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
};
struct StatusChangeLogBuilder {
+ typedef StatusChangeLog Table;
flatbuffers::FlatBufferBuilder &fbb_;
flatbuffers::uoffset_t start_;
- void add_behavior_tree(flatbuffers::Offset<BehaviorTree> behavior_tree) {
+ void add_behavior_tree(flatbuffers::Offset<Serialization::BehaviorTree> behavior_tree) {
fbb_.AddOffset(StatusChangeLog::VT_BEHAVIOR_TREE, behavior_tree);
}
- void add_state_changes(flatbuffers::Offset<flatbuffers::Vector<const StatusChange *>> state_changes) {
+ void add_state_changes(flatbuffers::Offset<flatbuffers::Vector<const Serialization::StatusChange *>> state_changes) {
fbb_.AddOffset(StatusChangeLog::VT_STATE_CHANGES, state_changes);
}
explicit StatusChangeLogBuilder(flatbuffers::FlatBufferBuilder &_fbb)
: fbb_(_fbb) {
start_ = fbb_.StartTable();
}
- StatusChangeLogBuilder &operator=(const StatusChangeLogBuilder &);
flatbuffers::Offset<StatusChangeLog> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<StatusChangeLog>(end);
inline flatbuffers::Offset<StatusChangeLog> CreateStatusChangeLog(
flatbuffers::FlatBufferBuilder &_fbb,
- flatbuffers::Offset<BehaviorTree> behavior_tree = 0,
- flatbuffers::Offset<flatbuffers::Vector<const StatusChange *>> state_changes = 0) {
+ flatbuffers::Offset<Serialization::BehaviorTree> behavior_tree = 0,
+ flatbuffers::Offset<flatbuffers::Vector<const Serialization::StatusChange *>> state_changes = 0) {
StatusChangeLogBuilder builder_(_fbb);
builder_.add_state_changes(state_changes);
builder_.add_behavior_tree(behavior_tree);
inline flatbuffers::Offset<StatusChangeLog> CreateStatusChangeLogDirect(
flatbuffers::FlatBufferBuilder &_fbb,
- flatbuffers::Offset<BehaviorTree> behavior_tree = 0,
- const std::vector<StatusChange> *state_changes = nullptr) {
- auto state_changes__ = state_changes ? _fbb.CreateVectorOfStructs<StatusChange>(*state_changes) : 0;
+ flatbuffers::Offset<Serialization::BehaviorTree> behavior_tree = 0,
+ const std::vector<Serialization::StatusChange> *state_changes = nullptr) {
+ auto state_changes__ = state_changes ? _fbb.CreateVectorOfStructs<Serialization::StatusChange>(*state_changes) : 0;
return Serialization::CreateStatusChangeLog(
_fbb,
behavior_tree,
#include <iterator>
#include <memory>
+#if defined(__unix__) && !defined(FLATBUFFERS_LOCALE_INDEPENDENT)
+ #include <unistd.h>
+#endif
+
#ifdef _STLPORT_VERSION
#define FLATBUFFERS_CPP98_STL
#endif
-#ifndef FLATBUFFERS_CPP98_STL
- #include <functional>
-#endif
-#include "behaviortree_cpp_v3/flatbuffers/stl_emulation.h"
+#ifdef __ANDROID__
+ #include <android/api-level.h>
+#endif
#if defined(__ICCARM__)
#include <intrinsics.h>
#endif // !defined(FLATBUFFERS_LITTLEENDIAN)
#define FLATBUFFERS_VERSION_MAJOR 1
-#define FLATBUFFERS_VERSION_MINOR 11
+#define FLATBUFFERS_VERSION_MINOR 12
#define FLATBUFFERS_VERSION_REVISION 0
#define FLATBUFFERS_STRING_EXPAND(X) #X
#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X)
defined(__clang__)
#define FLATBUFFERS_FINAL_CLASS final
#define FLATBUFFERS_OVERRIDE override
+ #define FLATBUFFERS_EXPLICIT_CPP11 explicit
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t
#else
#define FLATBUFFERS_FINAL_CLASS
#define FLATBUFFERS_OVERRIDE
+ #define FLATBUFFERS_EXPLICIT_CPP11
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE
#endif
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \
(defined(__cpp_constexpr) && __cpp_constexpr >= 200704)
#define FLATBUFFERS_CONSTEXPR constexpr
+ #define FLATBUFFERS_CONSTEXPR_CPP11 constexpr
+ #define FLATBUFFERS_CONSTEXPR_DEFINED
#else
#define FLATBUFFERS_CONSTEXPR const
+ #define FLATBUFFERS_CONSTEXPR_CPP11
#endif
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
(defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
- #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR
+ #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11
#else
#define FLATBUFFERS_CONSTEXPR_CPP14
#endif
#if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \
defined(__clang__)
- #define FLATBUFFERS_DELETE_FUNC(func) func = delete;
+ #define FLATBUFFERS_DELETE_FUNC(func) func = delete
#else
- #define FLATBUFFERS_DELETE_FUNC(func) private: func;
+ #define FLATBUFFERS_DELETE_FUNC(func) private: func
+#endif
+
+#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \
+ (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \
+ defined(__clang__)
+ #define FLATBUFFERS_DEFAULT_DECLARATION
+#endif
+
+// Check if we can use template aliases
+// Not possible if Microsoft Compiler before 2012
+// Possible is the language feature __cpp_alias_templates is defined well
+// Or possible if the C++ std is C+11 or newer
+#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \
+ || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \
+ || (defined(__cplusplus) && __cplusplus >= 201103L)
+ #define FLATBUFFERS_TEMPLATES_ALIASES
#endif
#ifndef FLATBUFFERS_HAS_STRING_VIEW
typedef std::experimental::string_view string_view;
}
#define FLATBUFFERS_HAS_STRING_VIEW 1
+ // Check for absl::string_view
+ #elif __has_include("absl/strings/string_view.h")
+ #include "absl/strings/string_view.h"
+ namespace flatbuffers {
+ typedef absl::string_view string_view;
+ }
+ #define FLATBUFFERS_HAS_STRING_VIEW 1
#endif
#endif // __has_include
#endif // !FLATBUFFERS_HAS_STRING_VIEW
#ifndef FLATBUFFERS_LOCALE_INDEPENDENT
// Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}.
- // They are part of the POSIX-2008 but not part of the C/C++ standard.
- // GCC/Clang have definition (_XOPEN_SOURCE>=700) if POSIX-2008.
#if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \
- (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE>=700)))
+ (defined(_XOPEN_VERSION) && (_XOPEN_VERSION>=700)) && (!defined(__ANDROID_API__) || (defined(__ANDROID_API__) && (__ANDROID_API__>=21))))
#define FLATBUFFERS_LOCALE_INDEPENDENT 1
#else
#define FLATBUFFERS_LOCALE_INDEPENDENT 0
// We support aligning the contents of buffers up to this size.
#define FLATBUFFERS_MAX_ALIGNMENT 16
+inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) {
+ return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) &&
+ (align & (align - 1)) == 0; // must be power of 2
+}
+
#if defined(_MSC_VER)
+ #pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized
#pragma warning(push)
#pragma warning(disable: 4127) // C4127: conditional expression is constant
#endif
return EndianScalar(*reinterpret_cast<const T *>(p));
}
+// See https://github.com/google/flatbuffers/issues/5950
+
+#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000)
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
template<typename T>
// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details.
__supress_ubsan__("alignment")
*reinterpret_cast<uoffset_t *>(p) = EndianScalar(t.o);
}
+#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000)
+ #pragma GCC diagnostic pop
+#endif
+
// Computes how many bytes you'd have to pad to be able to write an
// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in
// memory).
+__supress_ubsan__("unsigned-integer-overflow")
inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) {
return ((~buf_size) + 1) & (scalar_size - 1);
}
#define FLATBUFFERS_H_
#include "behaviortree_cpp_v3/flatbuffers/base.h"
+#include "behaviortree_cpp_v3/flatbuffers/stl_emulation.h"
+
+#ifndef FLATBUFFERS_CPP98_STL
+# include <functional>
+#endif
#if defined(FLATBUFFERS_NAN_DEFAULTS)
# include <cmath>
}
#endif
+// Check 'v' is out of closed range [low; high].
+// Workaround for GCC warning [-Werror=type-limits]:
+// comparison is always true due to limited range of data type.
+template<typename T>
+inline bool IsOutRange(const T &v, const T &low, const T &high) {
+ return (v < low) || (high < v);
+}
+
+// Check 'v' is in closed range [low; high].
+template<typename T>
+inline bool IsInRange(const T &v, const T &low, const T &high) {
+ return !IsOutRange(v, low, high);
+}
+
// Wrapper for uoffset_t to allow safe template specialization.
// Value is allowed to be 0 to indicate a null object (see e.g. AddOffset).
template<typename T> struct Offset {
typedef typename IndirectHelper<T>::return_type return_type;
typedef typename IndirectHelper<T>::mutable_return_type mutable_return_type;
+ typedef return_type value_type;
return_type Get(uoffset_t i) const {
FLATBUFFERS_ASSERT(i < size());
// This class is a pointer. Copying will therefore create an invalid object.
// Private and unimplemented copy constructor.
Vector(const Vector &);
+ Vector &operator=(const Vector &);
template<typename K> static int KeyCompare(const void *ap, const void *bp) {
const K *key = reinterpret_cast<const K *>(ap);
private:
VectorOfAny(const VectorOfAny &);
+ VectorOfAny &operator=(const VectorOfAny &);
};
#ifndef FLATBUFFERS_CPP98_STL
IndirectHelperType;
public:
+ typedef uint16_t size_type;
typedef typename IndirectHelper<IndirectHelperType>::return_type return_type;
typedef VectorIterator<T, return_type> const_iterator;
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
return_type operator[](uoffset_t i) const { return Get(i); }
+ // If this is a Vector of enums, T will be its storage type, not the enum
+ // type. This function makes it convenient to retrieve value with enum
+ // type E.
+ template<typename E> E GetEnum(uoffset_t i) const {
+ return static_cast<E>(Get(i));
+ }
+
const_iterator begin() const { return const_iterator(Data(), 0); }
const_iterator end() const { return const_iterator(Data(), size()); }
const T *data() const { return reinterpret_cast<const T *>(Data()); }
T *data() { return reinterpret_cast<T *>(Data()); }
+ // Copy data from a span with endian conversion.
+ // If this Array and the span overlap, the behavior is undefined.
+ void CopyFromSpan(flatbuffers::span<const T, length> src) {
+ const auto p1 = reinterpret_cast<const uint8_t *>(src.data());
+ const auto p2 = Data();
+ FLATBUFFERS_ASSERT(!(p1 >= p2 && p1 < (p2 + length)) &&
+ !(p2 >= p1 && p2 < (p1 + length)));
+ (void)p1;
+ (void)p2;
+
+ CopyFromSpanImpl(
+ flatbuffers::integral_constant < bool,
+ !scalar_tag::value || sizeof(T) == 1 || FLATBUFFERS_LITTLEENDIAN > (),
+ src);
+ }
+
protected:
void MutateImpl(flatbuffers::integral_constant<bool, true>, uoffset_t i,
const T &val) {
*(GetMutablePointer(i)) = val;
}
+ void CopyFromSpanImpl(flatbuffers::integral_constant<bool, true>,
+ flatbuffers::span<const T, length> src) {
+ // Use std::memcpy() instead of std::copy() to avoid preformance degradation
+ // due to aliasing if T is char or unsigned char.
+ // The size is known at compile time, so memcpy would be inlined.
+ std::memcpy(data(), src.data(), length * sizeof(T));
+ }
+
+ // Copy data from flatbuffers::span with endian conversion.
+ void CopyFromSpanImpl(flatbuffers::integral_constant<bool, false>,
+ flatbuffers::span<const T, length> src) {
+ for (size_type k = 0; k < length; k++) { Mutate(k, src[k]); }
+ }
+
// This class is only used to access pre-existing data. Don't ever
// try to construct these manually.
// 'constexpr' allows us to use 'size()' at compile time.
static_assert(flatbuffers::is_same<T, void>::value, "unexpected type T");
public:
+ typedef const void *return_type;
+
const uint8_t *Data() const { return data_; }
// Make idl_gen_text.cpp::PrintContainer happy.
- const void *operator[](uoffset_t) const {
+ return_type operator[](uoffset_t) const {
FLATBUFFERS_ASSERT(false);
return nullptr;
}
uint8_t data_[1];
};
+// Cast a raw T[length] to a raw flatbuffers::Array<T, length>
+// without endian conversion. Use with care.
+template<typename T, uint16_t length>
+Array<T, length> &CastToArray(T (&arr)[length]) {
+ return *reinterpret_cast<Array<T, length> *>(arr);
+}
+
+template<typename T, uint16_t length>
+const Array<T, length> &CastToArray(const T (&arr)[length]) {
+ return *reinterpret_cast<const Array<T, length> *>(arr);
+}
+
+template<typename E, typename T, uint16_t length>
+Array<E, length> &CastToArrayOfEnum(T (&arr)[length]) {
+ static_assert(sizeof(E) == sizeof(T), "invalid enum type E");
+ return *reinterpret_cast<Array<E, length> *>(arr);
+}
+
+template<typename E, typename T, uint16_t length>
+const Array<E, length> &CastToArrayOfEnum(const T (&arr)[length]) {
+ static_assert(sizeof(E) == sizeof(T), "invalid enum type E");
+ return *reinterpret_cast<const Array<E, length> *>(arr);
+}
+
// Lexicographically compare two strings (possibly containing nulls), and
// return true if the first is less than the second.
static inline bool StringLessThan(const char *a_data, uoffset_t a_size,
return str ? str->c_str() : "";
}
+#ifdef FLATBUFFERS_HAS_STRING_VIEW
+// Convenience function to get string_view from a String returning an empty
+// string_view on null pointer.
+static inline flatbuffers::string_view GetStringView(const String *str) {
+ return str ? str->string_view() : flatbuffers::string_view();
+}
+#endif // FLATBUFFERS_HAS_STRING_VIEW
+
// Allocator interface. This is flatbuffers-specific and meant only for
// `vector_downward` usage.
class Allocator {
#if !defined(FLATBUFFERS_CPP98_STL)
// clang-format on
// These may change access mode, leave these at end of public section
- FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other))
+ FLATBUFFERS_DELETE_FUNC(DetachedBuffer(const DetachedBuffer &other));
FLATBUFFERS_DELETE_FUNC(
- DetachedBuffer &operator=(const DetachedBuffer &other))
+ DetachedBuffer &operator=(const DetachedBuffer &other));
// clang-format off
#endif // !defined(FLATBUFFERS_CPP98_STL)
// clang-format on
Allocator *get_custom_allocator() { return allocator_; }
uoffset_t size() const {
- return static_cast<uoffset_t>(reserved_ - (cur_ - buf_));
+ return static_cast<uoffset_t>(reserved_ - static_cast<size_t>(cur_ - buf_));
}
uoffset_t scratch_size() const {
private:
// You shouldn't really be copying instances of this class.
- FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &))
- FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &))
+ FLATBUFFERS_DELETE_FUNC(vector_downward(const vector_downward &));
+ FLATBUFFERS_DELETE_FUNC(vector_downward &operator=(const vector_downward &));
Allocator *allocator_;
bool own_allocator_;
return buf_.data();
}
+ /// @brief Get the serialized buffer (after you call `Finish()`) as a span.
+ /// @return Returns a constructed flatbuffers::span that is a view over the
+ /// FlatBuffer data inside the buffer.
+ flatbuffers::span<uint8_t> GetBufferSpan() const {
+ Finished();
+ return flatbuffers::span<uint8_t>(buf_.data(), buf_.size());
+ }
+
/// @brief Get a pointer to an unfinished buffer.
/// @return Returns a `uint8_t` pointer to the unfinished buffer.
uint8_t *GetCurrentBufferPointer() const { return buf_.data(); }
/// you call Finish()). You can use this information if you need to embed
/// a FlatBuffer in some other buffer, such that you can later read it
/// without first having to copy it into its own buffer.
- size_t GetBufferMinAlignment() {
+ size_t GetBufferMinAlignment() const {
Finished();
return minalign_;
}
TrackField(field, off);
}
+ template<typename T> void AddElement(voffset_t field, T e) {
+ auto off = PushElement(e);
+ TrackField(field, off);
+ }
+
template<typename T> void AddOffset(voffset_t field, Offset<T> off) {
if (off.IsNull()) return; // Don't store.
AddElement(field, ReferTo(off.o), static_cast<uoffset_t>(0));
it += sizeof(uoffset_t)) {
auto vt_offset_ptr = reinterpret_cast<uoffset_t *>(it);
auto vt2 = reinterpret_cast<voffset_t *>(buf_.data_at(*vt_offset_ptr));
- auto vt2_size = *vt2;
+ auto vt2_size = ReadScalar<voffset_t>(vt2);
if (vt1_size != vt2_size || 0 != memcmp(vt2, vt1, vt1_size)) continue;
vt_use = *vt_offset_ptr;
buf_.pop(GetSize() - vtableoffsetloc);
return off;
}
+#ifdef FLATBUFFERS_HAS_STRING_VIEW
+ /// @brief Store a string in the buffer, which can contain any binary data.
+ /// If a string with this exact contents has already been serialized before,
+ /// instead simply returns the offset of the existing string.
+ /// @param[in] str A const std::string_view to store in the buffer.
+ /// @return Returns the offset in the buffer where the string starts
+ Offset<String> CreateSharedString(const flatbuffers::string_view str) {
+ return CreateSharedString(str.data(), str.size());
+ }
+#else
/// @brief Store a string in the buffer, which null-terminated.
/// If a string with this exact contents has already been serialized before,
/// instead simply returns the offset of the existing string.
Offset<String> CreateSharedString(const std::string &str) {
return CreateSharedString(str.c_str(), str.length());
}
+#endif
/// @brief Store a string in the buffer, which can contain any binary data.
/// If a string with this exact contents has already been serialized before,
// This is useful when storing a nested_flatbuffer in a vector of bytes,
// or when storing SIMD floats, etc.
void ForceVectorAlignment(size_t len, size_t elemsize, size_t alignment) {
+ FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment));
PreAlign(len * elemsize, alignment);
}
// Similar to ForceVectorAlignment but for String fields.
void ForceStringAlignment(size_t len, size_t alignment) {
+ FLATBUFFERS_ASSERT(VerifyAlignmentRequirements(alignment));
PreAlign((len + 1) * sizeof(char), alignment);
}
// causing the wrong overload to be selected, remove it.
AssertScalarT<T>();
StartVector(len, sizeof(T));
+ if (len == 0) { return Offset<Vector<T>>(EndVector(len)); }
// clang-format off
#if FLATBUFFERS_LITTLEENDIAN
PushBytes(reinterpret_cast<const uint8_t *>(v), len * sizeof(T));
/// @param[in] v A pointer to the array of type `S` to serialize into the
/// buffer as a `vector`.
/// @param[in] len The number of elements to serialize.
+ /// @param[in] pack_func Pointer to a function to convert the native struct
+ /// to the FlatBuffer struct.
+ /// @return Returns a typed `Offset` into the serialized data indicating
+ /// where the vector is stored.
+ template<typename T, typename S>
+ Offset<Vector<const T *>> CreateVectorOfNativeStructs(
+ const S *v, size_t len, T((*const pack_func)(const S &))) {
+ FLATBUFFERS_ASSERT(pack_func);
+ std::vector<T> vv(len);
+ std::transform(v, v + len, vv.begin(), pack_func);
+ return CreateVectorOfStructs<T>(data(vv), vv.size());
+ }
+
+ /// @brief Serialize an array of native structs into a FlatBuffer `vector`.
+ /// @tparam T The data type of the struct array elements.
+ /// @tparam S The data type of the native struct array elements.
+ /// @param[in] v A pointer to the array of type `S` to serialize into the
+ /// buffer as a `vector`.
+ /// @param[in] len The number of elements to serialize.
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T, typename S>
Offset<Vector<const T *>> CreateVectorOfNativeStructs(const S *v,
size_t len) {
extern T Pack(const S &);
- std::vector<T> vv(len);
- std::transform(v, v + len, vv.begin(), Pack);
- return CreateVectorOfStructs<T>(data(vv), vv.size());
+ return CreateVectorOfNativeStructs(v, len, Pack);
}
// clang-format off
/// @tparam S The data type of the `std::vector` native struct elements.
/// @param[in] v A const reference to the `std::vector` of structs to
/// serialize into the buffer as a `vector`.
+ /// @param[in] pack_func Pointer to a function to convert the native struct
+ /// to the FlatBuffer struct.
+ /// @return Returns a typed `Offset` into the serialized data indicating
+ /// where the vector is stored.
+ template<typename T, typename S>
+ Offset<Vector<const T *>> CreateVectorOfNativeStructs(
+ const std::vector<S> &v, T((*const pack_func)(const S &))) {
+ return CreateVectorOfNativeStructs<T, S>(data(v), v.size(), pack_func);
+ }
+
+ /// @brief Serialize a `std::vector` of native structs into a FlatBuffer
+ /// `vector`.
+ /// @tparam T The data type of the `std::vector` struct elements.
+ /// @tparam S The data type of the `std::vector` native struct elements.
+ /// @param[in] v A const reference to the `std::vector` of structs to
+ /// serialize into the buffer as a `vector`.
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T, typename S>
return a.KeyCompareLessThan(&b);
}
- private:
- StructKeyComparator &operator=(const StructKeyComparator &);
+ FLATBUFFERS_DELETE_FUNC(
+ StructKeyComparator &operator=(const StructKeyComparator &));
};
/// @endcond
/// @cond FLATBUFFERS_INTERNAL
template<typename T> struct TableKeyComparator {
TableKeyComparator(vector_downward &buf) : buf_(buf) {}
+ TableKeyComparator(const TableKeyComparator &other) : buf_(other.buf_) {}
bool operator()(const Offset<T> &a, const Offset<T> &b) const {
auto table_a = reinterpret_cast<T *>(buf_.data_at(a.o));
auto table_b = reinterpret_cast<T *>(buf_.data_at(b.o));
vector_downward &buf_;
private:
- TableKeyComparator &operator=(const TableKeyComparator &);
+ FLATBUFFERS_DELETE_FUNC(
+ TableKeyComparator &operator=(const TableKeyComparator &other));
};
/// @endcond
}
template<typename T> bool VerifyAlignment(size_t elem) const {
- return (elem & (sizeof(T) - 1)) == 0 || !check_alignment_;
+ return Check((elem & (sizeof(T) - 1)) == 0 || !check_alignment_);
}
// Verify a range indicated by sizeof(T).
template<typename T>
bool VerifyBufferFromStart(const char *identifier, size_t start) {
- if (identifier && (size_ < 2 * sizeof(flatbuffers::uoffset_t) ||
- !BufferHasIdentifier(buf_ + start, identifier))) {
+ if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
+ BufferHasIdentifier(buf_ + start, identifier)))) {
return false;
}
uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; }
private:
+ // private constructor & copy constructor: you obtain instances of this
+ // class by pointing to existing data only
+ Struct();
+ Struct(const Struct &);
+ Struct &operator=(const Struct &);
+
uint8_t data_[1];
};
return field_offset ? reinterpret_cast<P>(p) : nullptr;
}
+ template<typename Raw, typename Face>
+ flatbuffers::Optional<Face> GetOptional(voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ auto p = data_ + field_offset;
+ return field_offset ? Optional<Face>(static_cast<Face>(ReadScalar<Raw>(p)))
+ : Optional<Face>();
+ }
+
template<typename T> bool SetField(voffset_t field, T val, T def) {
auto field_offset = GetOptionalFieldOffset(field);
if (!field_offset) return IsTheSameAs(val, def);
WriteScalar(data_ + field_offset, val);
return true;
}
+ template<typename T> bool SetField(voffset_t field, T val) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ if (!field_offset) return false;
+ WriteScalar(data_ + field_offset, val);
+ return true;
+ }
bool SetPointer(voffset_t field, const uint8_t *val) {
auto field_offset = GetOptionalFieldOffset(field);
// class by pointing to existing data only
Table();
Table(const Table &other);
+ Table &operator=(const Table &);
uint8_t data_[1];
};
+// This specialization allows avoiding warnings like:
+// MSVC C4800: type: forcing value to bool 'true' or 'false'.
+template<>
+inline flatbuffers::Optional<bool> Table::GetOptional<uint8_t, bool>(
+ voffset_t field) const {
+ auto field_offset = GetOptionalFieldOffset(field);
+ auto p = data_ + field_offset;
+ return field_offset ? Optional<bool>(ReadScalar<uint8_t>(p) != 0)
+ : Optional<bool>();
+}
+
template<typename T>
void FlatBufferBuilder::Required(Offset<T> table, voffset_t field) {
auto table_ptr = reinterpret_cast<const Table *>(buf_.data_at(table.o));
// clang-format on
// Basic type info cost just 16bits per field!
+// We're explicitly defining the signedness since the signedness of integer
+// bitfields is otherwise implementation-defined and causes warnings on older
+// GCC compilers.
struct TypeCode {
- uint16_t base_type : 4; // ElementaryType
- uint16_t is_vector : 1;
- int16_t sequence_ref : 11; // Index into type_refs below, or -1 for none.
+ // ElementaryType
+ unsigned short base_type : 4;
+ // Either vector (in table) or array (in struct)
+ unsigned short is_repeating : 1;
+ // Index into type_refs below, or -1 for none.
+ signed short sequence_ref : 11;
};
static_assert(sizeof(TypeCode) == 2, "TypeCode");
size_t num_elems; // of type_codes, values, names (but not type_refs).
const TypeCode *type_codes; // num_elems count
const TypeFunction *type_refs; // less than num_elems entries (see TypeCode).
+ const int16_t *array_sizes; // less than num_elems entries (see TypeCode).
const int64_t *values; // Only set for non-consecutive enum/union or structs.
const char *const *names; // Only set if compiled with --reflect-names.
};
#define FLATBUFFERS_STL_EMULATION_H_
// clang-format off
+#include "behaviortree_cpp_v3/flatbuffers/base.h"
#include <string>
#include <type_traits>
#include <cctype>
#endif // defined(FLATBUFFERS_CPP98_STL)
-// Check if we can use template aliases
-// Not possible if Microsoft Compiler before 2012
-// Possible is the language feature __cpp_alias_templates is defined well
-// Or possible if the C++ std is C+11 or newer
-#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \
- || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \
- || (defined(__cplusplus) && __cplusplus >= 201103L)
- #define FLATBUFFERS_TEMPLATES_ALIASES
-#endif
+// Detect C++17 compatible compiler.
+// __cplusplus >= 201703L - a compiler has support of 'static inline' variables.
+#if defined(FLATBUFFERS_USE_STD_OPTIONAL) \
+ || (defined(__cplusplus) && __cplusplus >= 201703L) \
+ || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))
+ #include <optional>
+ #ifndef FLATBUFFERS_USE_STD_OPTIONAL
+ #define FLATBUFFERS_USE_STD_OPTIONAL
+ #endif
+#endif // defined(FLATBUFFERS_USE_STD_OPTIONAL) ...
+
+// The __cpp_lib_span is the predefined feature macro.
+#if defined(FLATBUFFERS_USE_STD_SPAN)
+ #include <span>
+#elif defined(__cpp_lib_span) && defined(__has_include)
+ #if __has_include(<span>)
+ #include <span>
+ #define FLATBUFFERS_USE_STD_SPAN
+ #endif
+#else
+ // Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined.
+ #if !defined(FLATBUFFERS_TEMPLATES_ALIASES) || defined(FLATBUFFERS_CPP98_STL)
+ #define FLATBUFFERS_SPAN_MINIMAL
+ #else
+ // Enable implicit construction of a span<T,N> from a std::array<T,N>.
+ #include <array>
+ #endif
+#endif // defined(FLATBUFFERS_USE_STD_SPAN)
// This header provides backwards compatibility for C++98 STLs like stlport.
namespace flatbuffers {
}
};
- template <> class numeric_limits<float> :
+ template <> class numeric_limits<float> :
public std::numeric_limits<float> {
public:
static float lowest() { return -FLT_MAX; }
};
- template <> class numeric_limits<double> :
+ template <> class numeric_limits<double> :
public std::numeric_limits<double> {
public:
static double lowest() { return -DBL_MAX; }
template <typename T, typename U> using is_same = std::is_same<T,U>;
template <typename T> using is_floating_point = std::is_floating_point<T>;
template <typename T> using is_unsigned = std::is_unsigned<T>;
+ template <typename T> using is_enum = std::is_enum<T>;
template <typename T> using make_unsigned = std::make_unsigned<T>;
template<bool B, class T, class F>
using conditional = std::conditional<B, T, F>;
template<class T, T v>
using integral_constant = std::integral_constant<T, v>;
-#else
+ template <bool B>
+ using bool_constant = integral_constant<bool, B>;
+ #else
// Map C++ TR1 templates defined by stlport.
template <typename T> using is_scalar = std::tr1::is_scalar<T>;
template <typename T, typename U> using is_same = std::tr1::is_same<T,U>;
template <typename T> using is_floating_point =
std::tr1::is_floating_point<T>;
template <typename T> using is_unsigned = std::tr1::is_unsigned<T>;
+ template <typename T> using is_enum = std::tr1::is_enum<T>;
// Android NDK doesn't have std::make_unsigned or std::tr1::make_unsigned.
template<typename T> struct make_unsigned {
static_assert(is_unsigned<T>::value, "Specialization not implemented!");
using conditional = std::tr1::conditional<B, T, F>;
template<class T, T v>
using integral_constant = std::tr1::integral_constant<T, v>;
-#endif // !FLATBUFFERS_CPP98_STL
+ template <bool B>
+ using bool_constant = integral_constant<bool, B>;
+ #endif // !FLATBUFFERS_CPP98_STL
#else
// MSVC 2010 doesn't support C++11 aliases.
template <typename T> struct is_scalar : public std::is_scalar<T> {};
template <typename T> struct is_floating_point :
public std::is_floating_point<T> {};
template <typename T> struct is_unsigned : public std::is_unsigned<T> {};
+ template <typename T> struct is_enum : public std::is_enum<T> {};
template <typename T> struct make_unsigned : public std::make_unsigned<T> {};
template<bool B, class T, class F>
struct conditional : public std::conditional<B, T, F> {};
template<class T, T v>
struct integral_constant : public std::integral_constant<T, v> {};
+ template <bool B>
+ struct bool_constant : public integral_constant<bool, B> {};
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
#ifndef FLATBUFFERS_CPP98_STL
// MSVC 2010 doesn't support C++11 aliases.
// We're manually "aliasing" the class here as we want to bring unique_ptr
// into the flatbuffers namespace. We have unique_ptr in the flatbuffers
- // namespace we have a completely independent implemenation (see below)
+ // namespace we have a completely independent implementation (see below)
// for C++98 STL implementations.
template <class T> class unique_ptr : public std::unique_ptr<T> {
public:
template <class T> bool operator==(const unique_ptr<T>& x, intptr_t y) {
return reinterpret_cast<intptr_t>(x.get()) == y;
}
+
+ template <class T> bool operator!=(const unique_ptr<T>& x, decltype(nullptr)) {
+ return !!x;
+ }
+
+ template <class T> bool operator!=(decltype(nullptr), const unique_ptr<T>& x) {
+ return !!x;
+ }
+
+ template <class T> bool operator==(const unique_ptr<T>& x, decltype(nullptr)) {
+ return !x;
+ }
+
+ template <class T> bool operator==(decltype(nullptr), const unique_ptr<T>& x) {
+ return !x;
+ }
+
#endif // !FLATBUFFERS_CPP98_STL
+#ifdef FLATBUFFERS_USE_STD_OPTIONAL
+template<class T>
+using Optional = std::optional<T>;
+using nullopt_t = std::nullopt_t;
+inline constexpr nullopt_t nullopt = std::nullopt;
+
+#else
+// Limited implementation of Optional<T> type for a scalar T.
+// This implementation limited by trivial types compatible with
+// std::is_arithmetic<T> or std::is_enum<T> type traits.
+
+// A tag to indicate an empty flatbuffers::optional<T>.
+struct nullopt_t {
+ explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {}
+};
+
+#if defined(FLATBUFFERS_CONSTEXPR_DEFINED)
+ namespace internal {
+ template <class> struct nullopt_holder {
+ static constexpr nullopt_t instance_ = nullopt_t(0);
+ };
+ template<class Dummy>
+ constexpr nullopt_t nullopt_holder<Dummy>::instance_;
+ }
+ static constexpr const nullopt_t &nullopt = internal::nullopt_holder<void>::instance_;
+
+#else
+ namespace internal {
+ template <class> struct nullopt_holder {
+ static const nullopt_t instance_;
+ };
+ template<class Dummy>
+ const nullopt_t nullopt_holder<Dummy>::instance_ = nullopt_t(0);
+ }
+ static const nullopt_t &nullopt = internal::nullopt_holder<void>::instance_;
+
+#endif
+
+template<class T>
+class Optional FLATBUFFERS_FINAL_CLASS {
+ // Non-scalar 'T' would extremely complicated Optional<T>.
+ // Use is_scalar<T> checking because flatbuffers flatbuffers::is_arithmetic<T>
+ // isn't implemented.
+ static_assert(flatbuffers::is_scalar<T>::value, "unexpected type T");
+
+ public:
+ ~Optional() {}
+
+ FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT
+ : value_(), has_value_(false) {}
+
+ FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT
+ : value_(), has_value_(false) {}
+
+ FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT
+ : value_(val), has_value_(true) {}
+
+ FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT
+ : value_(other.value_), has_value_(other.has_value_) {}
+
+ FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT {
+ value_ = other.value_;
+ has_value_ = other.has_value_;
+ return *this;
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT {
+ value_ = T();
+ has_value_ = false;
+ return *this;
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT {
+ value_ = val;
+ has_value_ = true;
+ return *this;
+ }
+
+ void reset() FLATBUFFERS_NOEXCEPT {
+ *this = nullopt;
+ }
+
+ void swap(Optional &other) FLATBUFFERS_NOEXCEPT {
+ std::swap(value_, other.value_);
+ std::swap(has_value_, other.has_value_);
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT {
+ return has_value_;
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT {
+ return has_value_;
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT {
+ return value_;
+ }
+
+ const T& value() const {
+ FLATBUFFERS_ASSERT(has_value());
+ return value_;
+ }
+
+ T value_or(T default_value) const FLATBUFFERS_NOEXCEPT {
+ return has_value() ? value_ : default_value;
+ }
+
+ private:
+ T value_;
+ bool has_value_;
+};
+
+template<class T>
+FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& opt, nullopt_t) FLATBUFFERS_NOEXCEPT {
+ return !opt;
+}
+template<class T>
+FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional<T>& opt) FLATBUFFERS_NOEXCEPT {
+ return !opt;
+}
+
+template<class T, class U>
+FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT {
+ return static_cast<bool>(lhs) && (*lhs == rhs);
+}
+
+template<class T, class U>
+FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional<U>& rhs) FLATBUFFERS_NOEXCEPT {
+ return static_cast<bool>(rhs) && (lhs == *rhs);
+}
+
+template<class T, class U>
+FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) FLATBUFFERS_NOEXCEPT {
+ return static_cast<bool>(lhs) != static_cast<bool>(rhs)
+ ? false
+ : !static_cast<bool>(lhs) ? false : (*lhs == *rhs);
+}
+#endif // FLATBUFFERS_USE_STD_OPTIONAL
+
+
+// Very limited and naive partial implementation of C++20 std::span<T,Extent>.
+#if defined(FLATBUFFERS_USE_STD_SPAN)
+ inline constexpr std::size_t dynamic_extent = std::dynamic_extent;
+ template<class T, std::size_t Extent = std::dynamic_extent>
+ using span = std::span<T, Extent>;
+
+#else // !defined(FLATBUFFERS_USE_STD_SPAN)
+FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast<std::size_t>(-1);
+
+// Exclude this code if MSVC2010 or non-STL Android is active.
+// The non-STL Android doesn't have `std::is_convertible` required for SFINAE.
+#if !defined(FLATBUFFERS_SPAN_MINIMAL)
+namespace internal {
+ // This is SFINAE helper class for checking of a common condition:
+ // > This overload only participates in overload resolution
+ // > Check whether a pointer to an array of U can be converted
+ // > to a pointer to an array of E.
+ // This helper is used for checking of 'U -> const U'.
+ template<class E, std::size_t Extent, class U, std::size_t N>
+ struct is_span_convertable {
+ using type =
+ typename std::conditional<std::is_convertible<U (*)[], E (*)[]>::value
+ && (Extent == dynamic_extent || N == Extent),
+ int, void>::type;
+ };
+
+} // namespace internal
+#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
+
+// T - element type; must be a complete type that is not an abstract
+// class type.
+// Extent - the number of elements in the sequence, or dynamic.
+template<class T, std::size_t Extent = dynamic_extent>
+class span FLATBUFFERS_FINAL_CLASS {
+ public:
+ typedef T element_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef std::size_t size_type;
+
+ static FLATBUFFERS_CONSTEXPR size_type extent = Extent;
+
+ // Returns the number of elements in the span.
+ FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT {
+ return count_;
+ }
+
+ // Returns the size of the sequence in bytes.
+ FLATBUFFERS_CONSTEXPR_CPP11
+ size_type size_bytes() const FLATBUFFERS_NOEXCEPT {
+ return size() * sizeof(element_type);
+ }
+
+ // Checks if the span is empty.
+ FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT {
+ return size() == 0;
+ }
+
+ // Returns a pointer to the beginning of the sequence.
+ FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT {
+ return data_;
+ }
+
+ // Returns a reference to the idx-th element of the sequence.
+ // The behavior is undefined if the idx is greater than or equal to size().
+ FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const {
+ return data()[idx];
+ }
+
+ FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT
+ : data_(other.data_), count_(other.count_) {}
+
+ FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other)
+ FLATBUFFERS_NOEXCEPT {
+ data_ = other.data_;
+ count_ = other.count_;
+ }
+
+ // Limited implementation of
+ // `template <class It> constexpr std::span(It first, size_type count);`.
+ //
+ // Constructs a span that is a view over the range [first, first + count);
+ // the resulting span has: data() == first and size() == count.
+ // The behavior is undefined if [first, first + count) is not a valid range,
+ // or if (extent != flatbuffers::dynamic_extent && count != extent).
+ FLATBUFFERS_CONSTEXPR_CPP11
+ explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT
+ : data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)),
+ count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) {
+ // Make span empty if the count argument is incompatible with span<T,N>.
+ }
+
+ // Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11
+ // compliant, it doesn't support default template arguments for functions.
+ #if defined(FLATBUFFERS_SPAN_MINIMAL)
+ FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
+ count_(0) {
+ static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
+ }
+
+ #else
+ // Constructs an empty span whose data() == nullptr and size() == 0.
+ // This overload only participates in overload resolution if
+ // extent == 0 || extent == flatbuffers::dynamic_extent.
+ // A dummy template argument N is need dependency for SFINAE.
+ template<std::size_t N = 0,
+ typename internal::is_span_convertable<element_type, Extent, element_type, (N - N)>::type = 0>
+ FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
+ count_(0) {
+ static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
+ }
+
+ // Constructs a span that is a view over the array arr; the resulting span
+ // has size() == N and data() == std::data(arr). These overloads only
+ // participate in overload resolution if
+ // extent == std::dynamic_extent || N == extent is true and
+ // std::remove_pointer_t<decltype(std::data(arr))>(*)[]
+ // is convertible to element_type (*)[].
+ template<std::size_t N,
+ typename internal::is_span_convertable<element_type, Extent, element_type, N>::type = 0>
+ FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT
+ : data_(arr), count_(N) {}
+
+ template<class U, std::size_t N,
+ typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
+ FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
+ : data_(arr.data()), count_(N) {}
+
+ //template<class U, std::size_t N,
+ // int = 0>
+ //FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
+ // : data_(arr.data()), count_(N) {}
+
+ template<class U, std::size_t N,
+ typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
+ FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
+ : data_(arr.data()), count_(N) {}
+
+ // Converting constructor from another span s;
+ // the resulting span has size() == s.size() and data() == s.data().
+ // This overload only participates in overload resolution
+ // if extent == std::dynamic_extent || N == extent is true and U (*)[]
+ // is convertible to element_type (*)[].
+ template<class U, std::size_t N,
+ typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
+ FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span<U, N> &s) FLATBUFFERS_NOEXCEPT
+ : span(s.data(), s.size()) {
+ }
+
+ #endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
+
+ private:
+ // This is a naive implementation with 'count_' member even if (Extent != dynamic_extent).
+ pointer const data_;
+ const size_type count_;
+};
+
+ #if !defined(FLATBUFFERS_SPAN_MINIMAL)
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<U, N> make_span(U(&arr)[N]) FLATBUFFERS_NOEXCEPT {
+ return span<U, N>(arr);
+ }
+
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<const U, N> make_span(const U(&arr)[N]) FLATBUFFERS_NOEXCEPT {
+ return span<const U, N>(arr);
+ }
+
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<U, N> make_span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT {
+ return span<U, N>(arr);
+ }
+
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<const U, N> make_span(const std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT {
+ return span<const U, N>(arr);
+ }
+
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<U, dynamic_extent> make_span(U *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
+ return span<U, dynamic_extent>(first, count);
+ }
+
+ template<class U, std::size_t N>
+ FLATBUFFERS_CONSTEXPR_CPP11
+ flatbuffers::span<const U, dynamic_extent> make_span(const U *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
+ return span<const U, dynamic_extent>(first, count);
+ }
+#endif
+
+#endif // defined(FLATBUFFERS_USE_STD_SPAN)
+
} // namespace flatbuffers
#endif // FLATBUFFERS_STL_EMULATION_H_
{
enum class TimestampType
{
- ABSOLUTE,
- RELATIVE
+ absolute,
+ relative
};
typedef std::array<uint8_t, 12> SerializedTransition;
enabled_ = enabled;
}
- void seTimestampType(TimestampType type)
+ void setTimestampType(TimestampType type)
{
type_ = type;
}
//--------------------------------------------
inline StatusChangeLogger::StatusChangeLogger(TreeNode* root_node)
- : enabled_(true), show_transition_to_idle_(true), type_(TimestampType::ABSOLUTE)
+ : enabled_(true), show_transition_to_idle_(true), type_(TimestampType::absolute)
{
first_timestamp_ = std::chrono::high_resolution_clock::now();
NodeStatus status) {
if (enabled_ && (status != NodeStatus::IDLE || show_transition_to_idle_))
{
- if (type_ == TimestampType::ABSOLUTE)
+ if (type_ == TimestampType::absolute)
{
this->callback(timestamp.time_since_epoch(), node, prev, status);
}
"but BB is invalid");
}
+ std::unique_lock<std::mutex> entry_lock( config_.blackboard->entryMutex() );
const Any* val = config_.blackboard->getAny(static_cast<std::string>(remapped_key));
if (val && val->empty() == false)
{
{
const basic_string_view v;
- nssv_constexpr explicit not_in_view( basic_string_view v ) : v( v ) {}
+ nssv_constexpr explicit not_in_view( basic_string_view view ) : v( view ) {}
nssv_constexpr bool operator()( CharT c ) const
{
site_description: Introduction to Behavior Trees
site_author: Davide Faconti
-copyright: 'Copyright © 2018-2019 Davide Faconti, Eurecat'
+copyright: 'Copyright © 2018-2022 Davide Faconti, Eurecat'
theme:
name: 'material'
- "Tutorial 1: Create a Tree": tutorial_01_first_tree.md
- "Tutorial 2: Basic Ports": tutorial_02_basic_ports.md
- "Tutorial 3: Generic ports": tutorial_03_generic_ports.md
- - "Tutorial 4: Sequences": tutorial_04_sequence_star.md
+ - "Tutorial 4: Sequences": tutorial_04_sequence.md
- "Tutorial 5: Subtrees and Loggers": tutorial_05_subtrees.md
- "Tutorial 6: Ports remapping": tutorial_06_subtree_ports.md
- "Tutorial 7: Wrap legacy code": tutorial_07_legacy.md
<?xml version="1.0"?>
<package format="3">
<name>behaviortree_cpp_v3</name>
- <version>3.5.6</version>
+ <version>3.6.1</version>
<description>
This package provides the Behavior Trees core library.
</description>
<buildtool_depend condition="$ROS_VERSION == 2">ament_cmake</buildtool_depend>
<depend condition="$ROS_VERSION == 2">rclcpp</depend>
+ <depend condition="$ROS_VERSION == 2">ament_index_cpp</depend>
+ <depend>boost</depend>
<depend>libzmq3-dev</depend>
<depend>libncurses-dev</depend>
return BT::NodeStatus::SUCCESS;
}
+BT::NodeStatus CheckTemperature()
+{
+ std::cout << "[ Temperature: OK ]" << std::endl;
+ return BT::NodeStatus::SUCCESS;
+}
+
+BT::NodeStatus SayHello()
+{
+ std::cout << "Robot says: Hello World" << std::endl;
+ return BT::NodeStatus::SUCCESS;
+}
+
BT::NodeStatus GripperInterface::open()
{
_opened = true;
BT::NodeStatus CheckBattery();
+BT::NodeStatus CheckTemperature();
+BT::NodeStatus SayHello();
+
class GripperInterface
{
public:
static GripperInterface grip_singleton;
factory.registerSimpleCondition("CheckBattery", std::bind(CheckBattery));
+ factory.registerSimpleCondition("CheckTemperature", std::bind(CheckTemperature));
+ factory.registerSimpleAction("SayHello", std::bind(SayHello));
factory.registerSimpleAction("OpenGripper", std::bind(&GripperInterface::open, &grip_singleton));
factory.registerSimpleAction("CloseGripper", std::bind(&GripperInterface::close, &grip_singleton));
factory.registerNodeType<ApproachObject>("ApproachObject");
NodeStatus BT::AsyncActionNode::executeTick()
{
+ using lock_type = std::unique_lock<std::mutex>;
//send signal to other thread.
// The other thread is in charge for changing the status
if (status() == NodeStatus::IDLE)
{
std::cerr << "\nUncaught exception from the method tick(): ["
<< registrationName() << "/" << name() << "]\n" << std::endl;
+ // Set the exception pointer and the status atomically.
+ lock_type l(m_);
exptr_ = std::current_exception();
- thread_handle_.wait();
+ setStatus(BT::NodeStatus::IDLE);
}
return status();
});
}
+ lock_type l(m_);
if( exptr_ )
{
- std::rethrow_exception(exptr_);
+ // The official interface of std::exception_ptr does not define any move
+ // semantics. Thus, we copy and reset exptr_ manually.
+ const auto exptr_copy = exptr_;
+ exptr_ = nullptr;
+ std::rethrow_exception(exptr_copy);
}
return status();
}
}
else if (auto decorator = dynamic_cast<BT::DecoratorNode*>(node))
{
- applyRecursiveVisitor(decorator->child(), visitor);
+ if( decorator->child() ){
+ applyRecursiveVisitor(decorator->child(), visitor);
+ }
}
}
-void printTreeRecursively(const TreeNode* root_node)
+void printTreeRecursively(const TreeNode* root_node, std::ostream& stream)
{
std::function<void(unsigned, const BT::TreeNode*)> recursivePrint;
- recursivePrint = [&recursivePrint](unsigned indent, const BT::TreeNode* node) {
+ recursivePrint = [&recursivePrint, &stream](unsigned indent, const BT::TreeNode* node) {
for (unsigned i = 0; i < indent; i++)
{
- std::cout << " ";
+ stream << " ";
}
if (!node)
{
- std::cout << "!nullptr!" << std::endl;
+ stream << "!nullptr!" << std::endl;
return;
}
- std::cout << node->name() << std::endl;
+ stream << node->name() << std::endl;
indent++;
if (auto control = dynamic_cast<const BT::ControlNode*>(node))
}
};
- std::cout << "----------------" << std::endl;
+ stream << "----------------" << std::endl;
recursivePrint(0, root_node);
- std::cout << "----------------" << std::endl;
+ stream << "----------------" << std::endl;
}
void buildSerializedStatusSnapshot(TreeNode* root_node, SerializedTreeStatus& serialized_buffer)
auto it = storage_.find(key);
if( it == storage_.end() )
{
- storage_.insert( { std::move(key), Entry(info) } );
+ storage_.emplace( key, Entry(info) );
}
else{
auto old_type = it->second.port_info.type();
registerNodeType<WhileDoElseNode>("WhileDoElse");
registerNodeType<InverterNode>("Inverter");
- registerNodeType<RetryNode>("RetryUntilSuccesful");
+ //registerNodeType<RetryNodeTypo>("RetryUntilSuccesful"); //typo but back compatibility
+ registerNodeType<RetryNode>("RetryUntilSuccessful"); // correct one
registerNodeType<KeepRunningUntilFailureNode>("KeepRunningUntilFailure");
registerNodeType<RepeatNode>("Repeat");
registerNodeType<TimeoutNode<>>("Timeout");
registerNodeType<BlackboardPreconditionNode<int>>("BlackboardCheckInt");
registerNodeType<BlackboardPreconditionNode<double>>("BlackboardCheckDouble");
registerNodeType<BlackboardPreconditionNode<std::string>>("BlackboardCheckString");
+ registerNodeType<BlackboardPreconditionNode<bool>>("BlackboardCheckBool");
registerNodeType<SwitchNode<2>>("Switch2");
registerNodeType<SwitchNode<3>>("Switch3");
// now print all the menu items and highlight the first one
for(size_t i=0; i<list.size(); i++ )
{
- mvwprintw( win, i+5, 0, "%2d. %s", i+1, list[i].c_str() );
+ mvwprintw( win, i+5, 0, "%2ld. %s", i+1, list[i].c_str() );
}
wrefresh( win ); // update the terminal screen
void SequenceStarNode::halt()
{
- current_child_idx_ = 0;
+ // DO NOT reset current_child_idx_ on halt
ControlNode::halt();
}
void DecoratorNode::haltChild()
{
+ if( !child_node_ ){
+ return;
+ }
if (child_node_->status() == NodeStatus::RUNNING)
{
child_node_->halt();
if (!delay_started_)
{
delay_complete_ = false;
+ delay_aborted_ = false;
delay_started_ = true;
setStatus(NodeStatus::RUNNING);
+#include <future>
#include "behaviortree_cpp_v3/loggers/bt_zmq_publisher.h"
#include "behaviortree_cpp_v3/flatbuffers/bt_flatbuffer_helper.h"
-#include <future>
-#include "zmq.hpp"
+#include "cppzmq/zmq.hpp"
namespace BT
{
{
std::cout << "[PublisherZMQ] Server quitting." << std::endl;
}
- std::cout << "[PublisherZMQ] just died. Exeption " << err.what() << std::endl;
+ std::cout << "[PublisherZMQ] just died. Exception " << err.what() << std::endl;
active_server_ = false;
}
}
{
std::cout << "[PublisherZMQ] Publisher quitting." << std::endl;
}
- std::cout << "[PublisherZMQ] just died. Exeption " << err.what() << std::endl;
+ std::cout << "[PublisherZMQ] just died. Exception " << err.what() << std::endl;
}
send_pending_ = false;
#include <list>
#if defined(__linux) || defined(__linux__)
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wattributes"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
#endif
#ifdef _MSC_VER
#include <ros/package.h>
#endif
+#ifdef USING_ROS2
+#include <ament_index_cpp/get_package_share_directory.hpp>
+#endif
+
#include "behaviortree_cpp_v3/blackboard.h"
#include "behaviortree_cpp_v3/utils/demangle_util.h"
{
using namespace BT_TinyXML2;
+auto StrEqual = [](const char* str1, const char* str2) -> bool {
+ return strcmp(str1, str2) == 0;
+};
+
+
struct XMLParser::Pimpl
{
TreeNode::Ptr createNodeFromXML(const XMLElement* element,
Blackboard::Ptr blackboard,
const TreeNode::Ptr& root_parent);
+ void getPortsRecursively(const XMLElement* element, std::vector<std::string> &output_ports);
+
void loadDocImpl(BT_TinyXML2::XMLDocument* doc);
std::list<std::unique_ptr<BT_TinyXML2::XMLDocument> > opened_documents;
int suffix_count;
explicit Pimpl(const BehaviorTreeFactory &fact):
- factory(fact),
- current_path( filesystem::path::getcwd() ),
- suffix_count(0)
+ factory(fact),
+ current_path( filesystem::path::getcwd() ),
+ suffix_count(0)
{}
void clear()
#endif
XMLParser::XMLParser(const BehaviorTreeFactory &factory) :
- _p( new Pimpl(factory) )
+ _p( new Pimpl(factory) )
{
}
{
filesystem::path file_path( include_node->Attribute("path") );
+ const char* ros_pkg_relative_path = include_node->Attribute("ros_pkg");
+ std::string ros_pkg_path;
- if( include_node->Attribute("ros_pkg") )
+ if( ros_pkg_relative_path )
{
-#ifdef USING_ROS
if( file_path.is_absolute() )
{
- std::cout << "WARNING: <include path=\"...\"> containes an absolute path.\n"
+ std::cout << "WARNING: <include path=\"...\"> contains an absolute path.\n"
<< "Attribute [ros_pkg] will be ignored."<< std::endl;
}
- else {
- auto ros_pkg_path = ros::package::getPath( include_node->Attribute("ros_pkg") );
- file_path = filesystem::path( ros_pkg_path ) / file_path;
- }
+ else
+ {
+#ifdef USING_ROS
+ ros_pkg_path = ros::package::getPath(ros_pkg_relative_path);
+#elif defined USING_ROS2
+ ros_pkg_path = ament_index_cpp::get_package_share_directory(ros_pkg_relative_path);
#else
throw RuntimeError("Using attribute [ros_pkg] in <include>, but this library was compiled "
"without ROS support. Recompile the BehaviorTree.CPP using catkin");
#endif
+ file_path = filesystem::path( ros_pkg_path ) / file_path;
+ }
}
if( !file_path.is_absolute() )
}
//-------- Helper functions (lambdas) -----------------
- auto StrEqual = [](const char* str1, const char* str2) -> bool {
- return strcmp(str1, str2) == 0;
- };
-
auto ThrowError = [&](int line_num, const std::string& text) {
char buffer[256];
sprintf(buffer, "Error at line %d: -> %s", line_num, text.c_str());
if (meta_sibling)
{
- ThrowError(meta_sibling->GetLineNum(),
- " Only a single node <TreeNodesModel> is supported");
+ ThrowError(meta_sibling->GetLineNum(),
+ " Only a single node <TreeNodesModel> is supported");
}
if (models_root)
{
{
const char* name = node->Name();
if (StrEqual(name, "Action") || StrEqual(name, "Decorator") ||
- StrEqual(name, "SubTree") || StrEqual(name, "Condition") || StrEqual(name, "Control"))
+ StrEqual(name, "SubTree") || StrEqual(name, "Condition") || StrEqual(name, "Control"))
{
const char* ID = node->Attribute("ID");
if (!ID)
{
- ThrowError(node->GetLineNum(),
- "Error at line %d: -> The attribute [ID] is mandatory");
+ ThrowError(node->GetLineNum(),
+ "Error at line %d: -> The attribute [ID] is mandatory");
}
}
}
{
if (children_count != 1)
{
- ThrowError(node->GetLineNum(),
- "The node <Decorator> must have exactly 1 child");
+ ThrowError(node->GetLineNum(),
+ "The node <Decorator> must have exactly 1 child");
}
if (!node->Attribute("ID"))
{
- ThrowError(node->GetLineNum(),
- "The node <Decorator> must have the attribute [ID]");
+ ThrowError(node->GetLineNum(),
+ "The node <Decorator> must have the attribute [ID]");
}
}
else if (StrEqual(name, "Action"))
{
if (children_count != 0)
{
- ThrowError(node->GetLineNum(),
- "The node <Action> must not have any child");
+ ThrowError(node->GetLineNum(),
+ "The node <Action> must not have any child");
}
if (!node->Attribute("ID"))
{
- ThrowError(node->GetLineNum(),
- "The node <Action> must have the attribute [ID]");
+ ThrowError(node->GetLineNum(),
+ "The node <Action> must have the attribute [ID]");
}
}
else if (StrEqual(name, "Condition"))
{
if (children_count != 0)
{
- ThrowError(node->GetLineNum(),
- "The node <Condition> must not have any child");
+ ThrowError(node->GetLineNum(),
+ "The node <Condition> must not have any child");
}
if (!node->Attribute("ID"))
{
- ThrowError(node->GetLineNum(),
- "The node <Condition> must have the attribute [ID]");
+ ThrowError(node->GetLineNum(),
+ "The node <Condition> must have the attribute [ID]");
}
}
else if (StrEqual(name, "Control"))
{
if (children_count == 0)
{
- ThrowError(node->GetLineNum(),
- "The node <Control> must have at least 1 child");
+ ThrowError(node->GetLineNum(),
+ "The node <Control> must have at least 1 child");
}
if (!node->Attribute("ID"))
{
- ThrowError(node->GetLineNum(),
- "The node <Control> must have the attribute [ID]");
+ ThrowError(node->GetLineNum(),
+ "The node <Control> must have the attribute [ID]");
}
}
else if (StrEqual(name, "Sequence") ||
{
if (children_count == 0)
{
- ThrowError(node->GetLineNum(),
- "A Control node must have at least 1 child");
+ ThrowError(node->GetLineNum(),
+ "A Control node must have at least 1 child");
}
}
else if (StrEqual(name, "SubTree"))
if (!node->Attribute("ID"))
{
- ThrowError(node->GetLineNum(),
- "The node <SubTree> must have the attribute [ID]");
+ ThrowError(node->GetLineNum(),
+ "The node <SubTree> must have the attribute [ID]");
}
}
else
bool found = ( registered_nodes.find(name) != registered_nodes.end() );
if (!found)
{
- ThrowError(node->GetLineNum(),
- std::string("Node not recognized: ") + name);
+ ThrowError(node->GetLineNum(),
+ std::string("Node not recognized: ") + name);
}
}
//recursion
}
if (ChildrenCount(bt_root) != 1)
{
- ThrowError(bt_root->GetLineNum(),
- "The node <BehaviorTree> must have exactly 1 child");
+ ThrowError(bt_root->GetLineNum(),
+ "The node <BehaviorTree> must have exactly 1 child");
}
else
{
{
if (tree_count != 1)
{
- throw RuntimeError("If you don't specify the attribute [main_tree_to_execute], "
- "Your file must contain a single BehaviorTree");
+ throw RuntimeError("If you don't specify the attribute [main_tree_to_execute], "
+ "Your file must contain a single BehaviorTree");
}
}
}
}
}
}
+
// use default value if available for empty ports. Only inputs
for (const auto& port_it: manifest.ports)
{
- const std::string& port_name = port_it.first;
+ const std::string& port_name = port_it.first;
const PortInfo& port_info = port_it.second;
auto direction = port_info.direction();
config.input_ports.insert( { port_name, port_info.defaultValue() } );
}
}
+
child_node = factory.instantiateTreeNode(instance_name, ID, config);
}
else if( tree_roots.count(ID) != 0) {
Tree& output_tree,
Blackboard::Ptr blackboard,
const TreeNode::Ptr& root_parent)
-{
+{
std::function<void(const TreeNode::Ptr&, const XMLElement*)> recursiveStep;
recursiveStep = [&](const TreeNode::Ptr& parent,
const XMLElement* element)
{
+ // create the node
auto node = createNodeFromXML(element, blackboard, parent);
output_tree.nodes.push_back(node);
recursivelyCreateTree( node->name(), output_tree, blackboard, node );
}
else{
- // Creating an isolated
- auto new_bb = Blackboard::create(blackboard);
+ // Creating an isolated
+ auto new_bb = Blackboard::create(blackboard);
- for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
- {
- if( strcmp(attr->Name(), "ID") == 0 )
+ for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
{
- continue;
+ if( strcmp(attr->Name(), "ID") == 0 )
+ {
+ continue;
+ }
+ new_bb->addSubtreeRemapping( attr->Name(), attr->Value() );
}
- new_bb->addSubtreeRemapping( attr->Name(), attr->Value() );
- }
- output_tree.blackboard_stack.emplace_back(new_bb);
- recursivelyCreateTree( node->name(), output_tree, new_bb, node );
+ output_tree.blackboard_stack.emplace_back(new_bb);
+ recursivelyCreateTree( node->name(), output_tree, new_bb, node );
}
}
else if( dynamic_cast<const SubtreePlusNode*>(node.get()) )
for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
{
- if( strcmp(attr->Name(), "ID") == 0 )
+ const char* attr_name = attr->Name();
+ const char* attr_value = attr->Value();
+
+ if( StrEqual(attr_name, "ID") )
{
continue;
}
- if( strcmp(attr->Name(), "__autoremap") == 0 )
+ if( StrEqual(attr_name, "__autoremap") )
{
- if( convertFromString<bool>(attr->Value()) )
- {
- do_autoremap = true;
- }
+ do_autoremap = convertFromString<bool>(attr_value);
continue;
}
- StringView str = attr->Value();
- if( TreeNode::isBlackboardPointer(str))
+ if( TreeNode::isBlackboardPointer(attr_value))
{
- StringView port_name = TreeNode::stripBlackboardPointer(str);
- new_bb->addSubtreeRemapping( attr->Name(), port_name);
- mapped_keys.insert(attr->Name());
+ // do remapping
+ StringView port_name = TreeNode::stripBlackboardPointer(attr_value);
+ new_bb->addSubtreeRemapping( attr_name, port_name );
+ mapped_keys.insert(attr_name);
}
else{
- new_bb->set(attr->Name(), static_cast<std::string>(str) );
- mapped_keys.insert(attr->Name());
+ // constant string: just set that constant value into the BB
+ new_bb->set(attr_name, static_cast<std::string>(attr_value) );
+ mapped_keys.insert(attr_name);
}
}
- recursivelyCreateTree( node->name(), output_tree, new_bb, node );
if( do_autoremap )
{
- auto keys = new_bb->getKeys();
- for( StringView key: keys)
+ std::vector<std::string> remapped_ports;
+ auto new_root_element = tree_roots[node->name()]->FirstChildElement();
+
+ getPortsRecursively( new_root_element, remapped_ports );
+ for( const auto& port: remapped_ports)
{
- if( mapped_keys.count(key) == 0)
+ if( mapped_keys.count(port) == 0)
{
- new_bb->addSubtreeRemapping( key, key );
+ new_bb->addSubtreeRemapping( port, port );
}
}
}
- }
+
+ recursivelyCreateTree( node->name(), output_tree, new_bb, node );
+ }
}
else
{
recursiveStep(root_parent, root_element);
}
+void XMLParser::Pimpl::getPortsRecursively(const XMLElement *element,
+ std::vector<std::string>& output_ports)
+{
+ for (const XMLAttribute* attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
+ {
+ const char* attr_name = attr->Name();
+ const char* attr_value = attr->Value();
+ if( !StrEqual(attr_name, "ID") &&
+ !StrEqual(attr_name, "name") &&
+ TreeNode::isBlackboardPointer(attr_value) )
+ {
+ auto port_name = TreeNode::stripBlackboardPointer(attr_value);
+ output_ports.push_back( static_cast<std::string>(port_name) );
+ }
+ }
+
+ for (auto child_element = element->FirstChildElement(); child_element;
+ child_element = child_element->NextSiblingElement())
+ {
+ getPortsRecursively(child_element, output_ports);
+ }
+}
+
std::string writeTreeNodesModelXML(const BehaviorTreeFactory& factory)
{
XMLElement* port_element = nullptr;
switch( port_info.direction() )
{
- case PortDirection::INPUT: port_element = doc.NewElement("input_port"); break;
- case PortDirection::OUTPUT: port_element = doc.NewElement("output_port"); break;
- case PortDirection::INOUT: port_element = doc.NewElement("inout_port"); break;
+ case PortDirection::INPUT: port_element = doc.NewElement("input_port"); break;
+ case PortDirection::OUTPUT: port_element = doc.NewElement("output_port"); break;
+ case PortDirection::INOUT: port_element = doc.NewElement("inout_port"); break;
}
port_element->SetAttribute("name", port_name.c_str() );
gtest_factory.cpp
gtest_decorator.cpp
gtest_blackboard.cpp
+ gtest_blackboard_precondition.cpp
gtest_ports.cpp
navigation_test.cpp
gtest_subtree.cpp
gtest_switch.cpp
)
-if (BT_COROUTINES)
- list(APPEND BT_TESTS gtest_coroutines.cpp)
+if( BT_COROUTINES )
+ LIST( APPEND BT_TESTS gtest_coroutines.cpp)
endif()
+#if(ament_cmake_FOUND OR catkin_FOUND)
+# # This test requires gmock. Since we don't have a uniform way to include
+# # gmock for non-users, it is turned of when build without ros.
+# list(APPEND BT_TESTS gtest_async_action_node.cpp)
+#endif()
+
if(ament_cmake_FOUND AND BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
${catkin_LIBRARIES})
target_include_directories(${BEHAVIOR_TREE_LIBRARY}_test PRIVATE gtest/include)
-elseif(GTEST_FOUND AND BUILD_UNIT_TESTS)
+elseif(BUILD_UNIT_TESTS)
enable_testing()
add_executable(${BEHAVIOR_TREE_LIBRARY}_test ${BT_TESTS})
target_link_libraries(${PROJECT_NAME}_test ${BEHAVIOR_TREE_LIBRARY}
- bt_sample_nodes
- ${GTEST_LIBRARIES}
- ${GTEST_MAIN_LIBRARIES})
+ bt_sample_nodes gtest gtest_main)
target_include_directories(${BEHAVIOR_TREE_LIBRARY}_test PRIVATE gtest/include ${GTEST_INCLUDE_DIRS})
add_test(BehaviorTreeCoreTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${BEHAVIOR_TREE_LIBRARY}_test)
--- /dev/null
+#include "behaviortree_cpp_v3/action_node.h"
+#include "behaviortree_cpp_v3/basic_types.h"
+
+#include <chrono>
+#include <condition_variable>
+#include <future>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+// The mocked version of the base.
+struct MockedAsyncActionNode : public BT::AsyncActionNode
+{
+ using BT::AsyncActionNode::AsyncActionNode;
+ MOCK_METHOD0(tick, BT::NodeStatus());
+
+ // Tick while the node is running.
+ BT::NodeStatus spinUntilDone()
+ {
+ do
+ {
+ executeTick();
+ } while (status() == BT::NodeStatus::RUNNING);
+ return status();
+ }
+
+ // Expose the setStatus method.
+ using BT::AsyncActionNode::setStatus;
+};
+
+// The fixture taking care of the node-setup.
+struct MockedAsyncActionFixture : public testing::Test
+{
+ BT::NodeConfiguration config;
+ MockedAsyncActionNode sn;
+ MockedAsyncActionFixture() : sn("node", config)
+ {
+ }
+};
+
+// Parameters for the terminal node states.
+struct NodeStatusFixture : public testing::WithParamInterface<BT::NodeStatus>,
+ public MockedAsyncActionFixture
+{
+};
+
+INSTANTIATE_TEST_CASE_P(/**/, NodeStatusFixture,
+ testing::Values(BT::NodeStatus::SUCCESS, BT::NodeStatus::FAILURE));
+
+TEST_P(NodeStatusFixture, normal_routine)
+{
+ // Test verifies the "normal" operation: We correctly propagate the result
+ // from the tick to the caller.
+ const BT::NodeStatus state = GetParam();
+
+ // Setup the mock-expectations.
+ EXPECT_CALL(sn, tick()).WillOnce(testing::Invoke([&]() {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ return state;
+ }));
+
+ // Spin the node and check the final status.
+ ASSERT_EQ(sn.spinUntilDone(), state);
+}
+
+TEST_F(MockedAsyncActionFixture, no_halt)
+{
+ // Test verifies that halt returns immediately, if the node is idle. It
+ // further checks if the halt-flag is resetted correctly.
+ sn.halt();
+ ASSERT_TRUE(sn.isHaltRequested());
+
+ // Below we further verify that the halt flag is cleaned up properly.
+ const BT::NodeStatus state{BT::NodeStatus::SUCCESS};
+ EXPECT_CALL(sn, tick()).WillOnce(testing::Return(state));
+
+ // Spin the node and check.
+ ASSERT_EQ(sn.spinUntilDone(), state);
+ ASSERT_FALSE(sn.isHaltRequested());
+}
+
+TEST_F(MockedAsyncActionFixture, halt)
+{
+ // Test verifies that calling halt() is blocking.
+ bool release = false;
+ std::mutex m;
+ std::condition_variable cv;
+
+ const BT::NodeStatus state{BT::NodeStatus::SUCCESS};
+ EXPECT_CALL(sn, tick()).WillOnce(testing::Invoke([&]() {
+ // Sleep until we send the release signal.
+ std::unique_lock<std::mutex> l(m);
+ while (!release)
+ cv.wait(l);
+
+ return state;
+ }));
+
+ // Start the execution.
+ sn.executeTick();
+
+ // Try to halt the node (cv will block it...)
+ std::future<void> halted = std::async(std::launch::async, [&]() { sn.halt(); });
+ ASSERT_EQ(halted.wait_for(std::chrono::milliseconds(10)), std::future_status::timeout);
+ ASSERT_EQ(sn.status(), BT::NodeStatus::RUNNING);
+
+ // Release the method.
+ {
+ std::unique_lock<std::mutex> l(m);
+ release = true;
+ cv.notify_one();
+ }
+
+ // Wait for the future to return.
+ halted.wait();
+ ASSERT_EQ(sn.status(), state);
+}
+
+TEST_F(MockedAsyncActionFixture, exception)
+{
+ // Verifies that we can recover from the exceptions in the tick method:
+ // 1) catch the exception, 2) re-raise it in the caller thread.
+
+ // Setup the mock.
+ EXPECT_CALL(sn, tick()).WillOnce(testing::Invoke([&]() {
+ throw std::runtime_error("This is not good!");
+ return BT::NodeStatus::SUCCESS;
+ }));
+
+ ASSERT_ANY_THROW(sn.spinUntilDone());
+ testing::Mock::VerifyAndClearExpectations(&sn);
+
+ // Now verify that the exception is cleared up (we succeed).
+ sn.setStatus(BT::NodeStatus::IDLE);
+ const BT::NodeStatus state{BT::NodeStatus::SUCCESS};
+ EXPECT_CALL(sn, tick()).WillOnce(testing::Return(state));
+ ASSERT_EQ(sn.spinUntilDone(), state);
+}
--- /dev/null
+#include <gtest/gtest.h>
+#include <string>
+#include "behaviortree_cpp_v3/basic_types.h"
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+using namespace BT;
+
+TEST(BlackboardPreconditionTest, IntEquals)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="1" />
+ <SetBlackboard output_key="b" value="1" />
+
+ <BlackboardCheckInt value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckInt>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::FAILURE);
+}
+
+TEST(BlackboardPreconditionTest, IntDoesNotEqual)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="1" />
+ <SetBlackboard output_key="b" value="2" />
+
+ <BlackboardCheckInt value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckInt>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::SUCCESS);
+}
+
+TEST(BlackboardPreconditionTest, DoubleEquals)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="1.1" />
+ <SetBlackboard output_key="b" value="1.1" />
+
+ <BlackboardCheckDouble value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckDouble>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::FAILURE);
+}
+
+TEST(BlackboardPreconditionTest, DoubleDoesNotEqual)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="1.1" />
+ <SetBlackboard output_key="b" value="2.1" />
+
+ <BlackboardCheckDouble value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckDouble>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::SUCCESS);
+}
+
+TEST(BlackboardPreconditionTest, StringEquals)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="foo" />
+ <SetBlackboard output_key="b" value="foo" />
+
+ <BlackboardCheckString value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckString>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::FAILURE);
+}
+
+TEST(BlackboardPreconditionTest, StringDoesNotEqual)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="foo" />
+ <SetBlackboard output_key="b" value="bar" />
+
+ <BlackboardCheckString value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckString>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::SUCCESS);
+}
+
+TEST(BlackboardPreconditionTest, BoolEquals)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="true" />
+ <SetBlackboard output_key="b" value="true" />
+
+ <BlackboardCheckBool value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckBool>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::FAILURE);
+}
+
+TEST(BlackboardPreconditionTest, BoolDoesNotEqual)
+{
+ BehaviorTreeFactory factory;
+
+ const std::string xml_text = R"(
+
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SetBlackboard output_key="a" value="true" />
+ <SetBlackboard output_key="b" value="false" />
+
+ <BlackboardCheckBool value_A="{a}" value_B="{b}" return_on_mismatch="SUCCESS">
+ <AlwaysFailure />
+ </BlackboardCheckBool>
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ auto tree = factory.createTreeFromText(xml_text);
+ const auto status = tree.tickRoot();
+ ASSERT_EQ(status, NodeStatus::SUCCESS);
+}
NodeStatus status = tree.tickRoot();
ASSERT_EQ( status, NodeStatus::SUCCESS );
+}
+
+struct MyType
+{
+ std::string value;
+};
+
+
+class NodeInPorts : public SyncActionNode
+{
+ public:
+ NodeInPorts(const std::string &name, const NodeConfiguration &config)
+ : SyncActionNode(name, config)
+ {}
+
+
+ NodeStatus tick()
+ {
+ int val_A = 0;
+ MyType val_B;
+ if( getInput("int_port", val_A) &&
+ getInput("any_port", val_B) )
+ {
+ return NodeStatus::SUCCESS;
+ }
+ return NodeStatus::FAILURE;
+ }
+
+ static PortsList providedPorts()
+ {
+ return { BT::InputPort<int>("int_port"),
+ BT::InputPort<MyType>("any_port") };
+ }
+};
+
+class NodeOutPorts : public SyncActionNode
+{
+ public:
+ NodeOutPorts(const std::string &name, const NodeConfiguration &config)
+ : SyncActionNode(name, config)
+ {}
+
+
+ NodeStatus tick()
+ {
+ return NodeStatus::SUCCESS;
+ }
+
+ static PortsList providedPorts()
+ {
+ return { BT::OutputPort<int>("int_port"),
+ BT::OutputPort<MyType>("any_port") };
+ }
+};
+
+TEST(PortTest, EmptyPort)
+{
+ std::string xml_txt = R"(
+ <root main_tree_to_execute = "MainTree" >
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <NodeInPorts int_port="{ip}" any_port="{ap}" />
+ <NodeOutPorts int_port="{ip}" any_port="{ap}" />
+ </Sequence>
+ </BehaviorTree>
+ </root>)";
+
+ BehaviorTreeFactory factory;
+ factory.registerNodeType<NodeOutPorts>("NodeOutPorts");
+ factory.registerNodeType<NodeInPorts>("NodeInPorts");
+
+ auto tree = factory.createTreeFromText(xml_txt);
+ NodeStatus status = tree.tickRoot();
+ // expect failure because port is not set yet
+ ASSERT_EQ( status, NodeStatus::FAILURE );
}
ASSERT_EQ(NodeStatus::IDLE, action_2.status());
}
-TEST_F(ComplexSequenceWithMemoryTest, Conditions1ToFase)
+TEST_F(ComplexSequenceWithMemoryTest, Conditions1ToFalse)
{
BT::NodeStatus state = root.executeTick();
ASSERT_EQ(NodeStatus::IDLE, action_1.status());
ASSERT_EQ(NodeStatus::IDLE, action_2.status());
}
+
+TEST_F(ComplexSequenceWithMemoryTest, Action2FailureSeq)
+{
+ root.executeTick();
+ std::this_thread::sleep_for(milliseconds(150));
+ root.executeTick();
+
+ ASSERT_EQ(NodeStatus::SUCCESS, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::RUNNING, seq_actions.status());
+ ASSERT_EQ(NodeStatus::SUCCESS, action_1.status());
+ ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
+
+ action_2.setExpectedResult(NodeStatus::FAILURE);
+ std::this_thread::sleep_for(milliseconds(150));
+ root.executeTick();
+
+ // failure in action_2 does not affect the state of already
+ // executed nodes (seq_conditions and action_1)
+ ASSERT_EQ(NodeStatus::FAILURE, root.status());
+ ASSERT_EQ(NodeStatus::SUCCESS, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_actions.status());
+ ASSERT_EQ(NodeStatus::SUCCESS, action_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_2.status());
+
+ action_2.setExpectedResult(NodeStatus::SUCCESS);
+ root.executeTick();
+
+ ASSERT_EQ(NodeStatus::SUCCESS, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::RUNNING, seq_actions.status());
+ ASSERT_EQ(NodeStatus::SUCCESS, action_1.status());
+ ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
+
+ std::this_thread::sleep_for(milliseconds(150));
+ root.executeTick();
+
+ ASSERT_EQ(NodeStatus::SUCCESS, root.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_actions.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_2.status());
+}
+
+TEST_F(ComplexSequenceWithMemoryTest, Action2HaltSeq)
+{
+ root.executeTick();
+ std::this_thread::sleep_for(milliseconds(150));
+ root.executeTick();
+
+ root.halt();
+
+ ASSERT_EQ(NodeStatus::IDLE, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_actions.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_2.status());
+
+ root.executeTick();
+
+ // tree retakes execution from action_2
+ ASSERT_EQ(NodeStatus::IDLE, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::RUNNING, seq_actions.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_1.status());
+ ASSERT_EQ(NodeStatus::RUNNING, action_2.status());
+
+ std::this_thread::sleep_for(milliseconds(150));
+ root.executeTick();
+
+ ASSERT_EQ(NodeStatus::SUCCESS, root.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_conditions.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, condition_2.status());
+ ASSERT_EQ(NodeStatus::IDLE, seq_actions.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_1.status());
+ ASSERT_EQ(NodeStatus::IDLE, action_2.status());
+}
}
+class ReadInConstructor : public BT::SyncActionNode
+{
+ public:
+ ReadInConstructor(const std::string& name, const BT::NodeConfiguration& config)
+ : BT::SyncActionNode(name, config)
+ {
+ auto msg = getInput<std::string>("message");
+ if (!msg) {
+ throw BT::RuntimeError("missing required input [message]: ", msg.error());
+ }
+ }
+
+ BT::NodeStatus tick() override { return BT::NodeStatus::SUCCESS; }
+ static BT::PortsList providedPorts() { return {BT::InputPort<std::string>("message")}; }
+};
+
+TEST(SubTree, SubtreePlusD)
+{
+ BT::NodeConfiguration config;
+ config.blackboard = BT::Blackboard::create();
+ static const char* xml_text = R"(
+
+<root main_tree_to_execute = "MainTree" >
+
+ <BehaviorTree ID="MainTree">
+ <Sequence>
+ <SubTreePlus ID="mySubtree" __autoremap="1"/>
+ </Sequence>
+ </BehaviorTree>
+ <BehaviorTree ID="mySubtree">
+ <ReadInConstructor message="{message}" />
+ </BehaviorTree>
+</root> )";
+
+ BT::BehaviorTreeFactory factory;
+ factory.registerNodeType<ReadInConstructor>("ReadInConstructor");
+ config.blackboard->set("message", "hello");
+ BT::Tree tree = factory.createTreeFromText(xml_text, config.blackboard);
+ auto ret = tree.tickRoot();
+ ASSERT_EQ(ret, BT::NodeStatus::SUCCESS);
+}
+
+
#include "condition_test_node.h"
#include "behaviortree_cpp_v3/behavior_tree.h"
+#include <sstream>
+#include <string>
+
using BT::NodeStatus;
using std::chrono::milliseconds;
ASSERT_EQ(NodeStatus::RUNNING, action_1.status());
}
+TEST_F(BehaviorTreeTest, PrintWithStream)
+{
+ // no stream parameter should go to default stream (std::cout)
+ BT::printTreeRecursively(&root);
+
+ // verify value for with custom stream parameter
+ std::stringstream stream;
+ BT::printTreeRecursively(&root, stream);
+ const auto string = stream.str();
+ std::string line;
+
+ // first line is all dashes
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ("----------------", line.c_str());
+
+ // each line is the name of the node, indented by depth * 3 spaces
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ(root.name().c_str(), line.c_str());
+
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ((" " + fal_conditions.name()).c_str(), line.c_str());
+
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ((" " + condition_1.name()).c_str(), line.c_str());
+
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ((" " + condition_2.name()).c_str(), line.c_str());
+
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ((" " + action_1.name()).c_str(), line.c_str());
+
+ // last line is all dashes
+ ASSERT_FALSE(std::getline(stream, line, '\n').fail());
+ ASSERT_STREQ("----------------", line.c_str());
+
+ // no more lines
+ ASSERT_TRUE(std::getline(stream, line, '\n').fail());
+}
+
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.5)
add_executable(bt3_log_cat bt_log_cat.cpp )
}
fseek(file, 0L, SEEK_END);
- const size_t length = ftell(file);
+ const size_t length = static_cast<size_t>(ftell(file));
fseek(file, 0L, SEEK_SET);
std::vector<char> buffer(length);
fread(buffer.data(), sizeof(char), length, file);
fclose(file);
- const int bt_header_size = flatbuffers::ReadScalar<uint32_t>(&buffer[0]);
+ const auto bt_header_size = flatbuffers::ReadScalar<uint32_t>(&buffer[0]);
auto behavior_tree = Serialization::GetBehaviorTree(&buffer[4]);
#include <iostream>
#include <fstream>
#include <signal.h>
-#include <zmq.hpp>
#include <fstream>
+#include "cppzmq/zmq.hpp"
#include "behaviortree_cpp_v3/flatbuffers/BT_logger_generated.h"
// http://zguide.zeromq.org/cpp:interrupt
static void CatchSignals(void)
{
+#ifdef _WIN32
+ signal(SIGINT, s_signal_handler);
+ signal(SIGTERM, s_signal_handler);
+#else
struct sigaction action;
action.sa_handler = s_signal_handler;
action.sa_flags = 0;
sigemptyset(&action.sa_mask);
- sigaction(SIGINT, &action, NULL);
- sigaction(SIGTERM, &action, NULL);
+ sigaction(SIGINT, &action, nullptr);
+ sigaction(SIGTERM, &action, nullptr);
+#endif
}
int main(int argc, char* argv[])
subscriber.connect("tcp://localhost:1666");
// Subscribe to everything
- subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0);
+ subscriber.set(zmq::sockopt::subscribe, "");
printf("----------- Started -----------------\n");
zmq::message_t msg;
try
{
- subscriber.recv(&update, 0);
+ subscriber.recv(update, zmq::recv_flags::none);
}
catch (zmq::error_t& e)
{