Imported Upstream version 3.6.1 upstream upstream/3.6.1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 14 Apr 2022 02:50:28 +0000 (11:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 14 Apr 2022 02:50:28 +0000 (11:50 +0900)
82 files changed:
.appveyor.yml [deleted file]
.github/workflows/build_vanilla.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.travis.yml [deleted file]
3rdparty/cppzmq/zmq.hpp [new file with mode: 0644]
CHANGELOG.rst
CMakeLists.txt
README.md
cmake/FindZMQ.cmake
conan/build.py [deleted file]
conan/test_package/CMakeLists.txt [deleted file]
conan/test_package/conanfile.py [deleted file]
conan/test_package/test_package.cpp [deleted file]
conan/travis/build.sh [deleted file]
conan/travis/install.sh [deleted file]
conanfile.py [deleted file]
docs/BT_basics.md
docs/FallbackNode.md
docs/SequenceNode.md
docs/images/DecoratorEnterRoom.svg [new file with mode: 0644]
docs/images/FallbackBasic.svg [new file with mode: 0644]
docs/images/FetchBeer.svg [new file with mode: 0644]
docs/images/FetchBeer2.svg [new file with mode: 0644]
docs/images/FetchBeerFails.svg [new file with mode: 0644]
docs/images/ReactiveSequence.svg [new file with mode: 0644]
docs/images/SequenceBasic.svg [new file with mode: 0644]
docs/images/SequenceNode.svg [new file with mode: 0644]
docs/images/SequenceStar.svg [new file with mode: 0644]
docs/images/Tutorial1.svg [new file with mode: 0644]
docs/images/Tutorial2.svg [new file with mode: 0644]
docs/images/bt_intro_01.gif [new file with mode: 0644]
docs/index.md
docs/tutorial_01_first_tree.md
docs/tutorial_02_basic_ports.md
docs/tutorial_04_sequence.md [moved from docs/tutorial_04_sequence_star.md with 100% similarity]
docs/tutorial_05_subtrees.md
docs/uml/CrossDoorSubtree.uxf
docs/uml/ReadTheDocs.uxf
docs/xml_format.md
examples/CMakeLists.txt
examples/t02_basic_ports.cpp
examples/t04_reactive_sequence.cpp
examples/t05_crossdoor.cpp
examples/t11_runtime_ports.cpp
include/behaviortree_cpp_v3/action_node.h
include/behaviortree_cpp_v3/behavior_tree.h
include/behaviortree_cpp_v3/blackboard.h
include/behaviortree_cpp_v3/controls/reactive_sequence.h
include/behaviortree_cpp_v3/decorators/blackboard_precondition.h
include/behaviortree_cpp_v3/decorators/delay_node.h
include/behaviortree_cpp_v3/decorators/retry_node.h
include/behaviortree_cpp_v3/flatbuffers/BT_logger_generated.h
include/behaviortree_cpp_v3/flatbuffers/base.h
include/behaviortree_cpp_v3/flatbuffers/flatbuffers.h
include/behaviortree_cpp_v3/flatbuffers/stl_emulation.h
include/behaviortree_cpp_v3/loggers/abstract_logger.h
include/behaviortree_cpp_v3/tree_node.h
include/behaviortree_cpp_v3/utils/string_view.hpp
mkdocs.yml
package.xml
sample_nodes/dummy_nodes.cpp
sample_nodes/dummy_nodes.h
src/action_node.cpp
src/behavior_tree.cpp
src/blackboard.cpp
src/bt_factory.cpp
src/controls/manual_node.cpp
src/controls/sequence_star_node.cpp
src/decorator_node.cpp
src/decorators/delay_node.cpp
src/loggers/bt_zmq_publisher.cpp
src/xml_parsing.cpp
tests/CMakeLists.txt
tests/gtest_async_action_node.cpp [new file with mode: 0644]
tests/gtest_blackboard_precondition.cpp [new file with mode: 0644]
tests/gtest_ports.cpp
tests/gtest_sequence.cpp
tests/gtest_subtree.cpp
tests/gtest_tree.cpp
tools/CMakeLists.txt
tools/bt_log_cat.cpp
tools/bt_recorder.cpp

diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644 (file)
index 0bb8c19..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-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 .
diff --git a/.github/workflows/build_vanilla.yml b/.github/workflows/build_vanilla.yml
new file mode 100644 (file)
index 0000000..88803bd
--- /dev/null
@@ -0,0 +1,52 @@
+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
+      
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5e1d496
--- /dev/null
@@ -0,0 +1,5 @@
+*~
+/CMakeLists.txt.user
+build*
+site/*
+/.vscode/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644 (file)
index c0d1fdb..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-# 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
-
-
diff --git a/3rdparty/cppzmq/zmq.hpp b/3rdparty/cppzmq/zmq.hpp
new file mode 100644 (file)
index 0000000..d59eb55
--- /dev/null
@@ -0,0 +1,2688 @@
+/*
+    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__
+
index 270ee83..2490eed 100644 (file)
@@ -2,6 +2,141 @@
 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>`_
index a390aed..b975d8d 100644 (file)
@@ -11,38 +11,48 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 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)
@@ -73,8 +83,7 @@ find_package(ament_cmake QUIET)
 
 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 "------------------------------------------")
@@ -102,38 +111,16 @@ elseif( CATKIN_DEVEL_PREFIX OR CATKIN_BUILD_BINARY_PACKAGE)
     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
@@ -190,17 +177,18 @@ endif()
 
 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 )
@@ -233,15 +221,52 @@ 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()
 
@@ -275,7 +300,6 @@ if(BUILD_TOOLS)
     add_subdirectory(tools)
 endif()
 
-if( BUILD_EXAMPLES )
-    add_subdirectory(sample_nodes)
+if(BUILD_EXAMPLES AND BUILD_SAMPLES)
     add_subdirectory(examples)
 endif()
index 968893a..14b15a3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,10 +1,8 @@
 ![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&amp;utm_medium=referral&amp;utm_content=BehaviorTree/BehaviorTree.CPP&amp;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)
 
@@ -45,9 +43,10 @@ To find more details about the conceptual ideas that make this implementation di
 
 # 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
 
index 2a6baf9..c4b8c23 100644 (file)
 #  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)
@@ -55,4 +63,4 @@ else (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS)
   mark_as_advanced(ZMQ_INCLUDE_DIRS ZMQ_LIBRARIES)
 
 endif (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS)
-
+endif(ZeroMQ_FOUND)
\ No newline at end of file
diff --git a/conan/build.py b/conan/build.py
deleted file mode 100644 (file)
index e86a056..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/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()
diff --git a/conan/test_package/CMakeLists.txt b/conan/test_package/CMakeLists.txt
deleted file mode 100644 (file)
index 9c1c78c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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)
diff --git a/conan/test_package/conanfile.py b/conan/test_package/conanfile.py
deleted file mode 100644 (file)
index 95695b2..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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)
diff --git a/conan/test_package/test_package.cpp b/conan/test_package/test_package.cpp
deleted file mode 100644 (file)
index 2602eac..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#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;
-}
diff --git a/conan/travis/build.sh b/conan/travis/build.sh
deleted file mode 100755 (executable)
index 069ced2..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/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
diff --git a/conan/travis/install.sh b/conan/travis/install.sh
deleted file mode 100755 (executable)
index f115909..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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
diff --git a/conanfile.py b/conanfile.py
deleted file mode 100644 (file)
index 311dca7..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/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")
index 14044e6..a9eca03 100644 (file)
@@ -1,73 +1,81 @@
 # 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.
 
@@ -80,7 +88,7 @@ ControlNode: the [SequenceNode](SequenceNode.md).
 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:
@@ -99,32 +107,31 @@ 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.
     
@@ -146,7 +153,7 @@ It ticks the children in order and:
 
 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?
@@ -168,13 +175,13 @@ We use the color "green" to represent nodes which return
 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:
 
@@ -186,7 +193,7 @@ FAILURE otherwise.
 
 Everything works as expected if __GrabBeer__ returns SUCCESS.
 
-![FetchBeer success](images/FetchBeer2.png)
+![FetchBeer success](images/FetchBeer2.svg)
 
 
 
index 3d6789a..7d0e023 100644 (file)
@@ -5,6 +5,11 @@ in other frameworks.
 
 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__.
@@ -12,19 +17,24 @@ They share the following rules:
 - 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
 
@@ -70,15 +80,13 @@ This ControlNode is used when you want to interrupt an __asynchronous__
 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++)
@@ -86,21 +94,20 @@ if he/she is fully rested.
                        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;
        ```     
index 7b42c51..fcced1d 100644 (file)
@@ -38,7 +38,7 @@ To understand how the three ControlNodes differ, refer to the following table:
 
 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++
@@ -76,7 +76,7 @@ sure that they are not ticked more often that expected.
 
 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.
@@ -118,7 +118,7 @@ If the action __GoTo(B)__ fails, __GoTo(A)__ will not be ticked again.
 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++
diff --git a/docs/images/DecoratorEnterRoom.svg b/docs/images/DecoratorEnterRoom.svg
new file mode 100644 (file)
index 0000000..7febd57
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:50:36.713Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;4Ga_fKNebX7HASdBwP-D&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;7MuUSBjBQZxBBGgICeBk&quot;&gt;7VhRb5swEP41PC4COwbyuKbtNmnTpkbT1qfKAxfYjE2NSch+/QzYAQKd0o40qbS8wJ3vzufvO98pWHCZlu8EzuJPPCTUAnZYWvDSAsABjqMelWarNdBfNJpIJKHWtYpV8ptopa21RRKSvGcoOacyyfrKgDNGAtnTYSH4pm92z2l/1wxHZKBYBZgOtd+SUMZa67iLduE9SaJYb+0Dr1lIsTHWJ8ljHPJNRwWvLLgUnMvmLS2XhFboGVwav+tHVneJCcLkIQ5ApyG35mwkVEfVIuNMPS4EL1hIKg9bSVzImEecYfqR80wpHaX8SaTcaqJwIblSxTKlepWUifxeuc+Qlm47K5eljlwLWyMwKbaNk4eMfNtdbP1qyTjecyZ1Jo6v5OaE1bF6GOW8EIFWIV1DWETEUDZE0tnxoyqb8JSoXZWJIBTLZN2PjnWFRTu7lgT1onkY5wSeLSf70HY4ehJFz6NkMT0l2vULT1REYOsOBT1Q1VztpVsUhN4M9eM0qWnXPXZ3uRxE+PxsCe8QDE5wCU2/P8Ut1OmsMS100BV5KAhT6e6z1admEyeSrDJcn2ujJmCfhnFk1kRIUlqPNfFHDm3q1RnUK/CgqddNZ0KZsRN3htPc/new3NdQwc8uYHRYAfsjBQxPV8Def07GOXFPx4n/epqKg4ZDEL1oU1kMwLpikogbztOzQ2ukBb8wWmZWduBaUp6TS87F2cE197wZOC1czgCuD3mF1eeMsL/g5TwdLywCLUJ3IvjsHnTIGYUOHQu6V/Gn8diTxYz23miZn260mHw6BW0Bl6osLsJkrV6j6vWmCvuVyYSuiiAgeX5fUGOmduhY9pw7XLsPRfVxoobsTV5j9lYZOCgr20UThBXpHZaSpJnMlVEdwEYm9A+xv9l+ClO2rVwK/ossOVX9sIoGbfW7vp7mRoJh/5/D0Vvpg13n615MNMXFnA9KoOpmZzkBFDwz3zukjR1tAqCzbWPPaUfuSDs6nJXp25E7nK+srpiJa/Go1xoddKUnqlAlth9+m89I7fdzePUH&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/FallbackBasic.svg b/docs/images/FallbackBasic.svg
new file mode 100644 (file)
index 0000000..2193f1f
--- /dev/null
@@ -0,0 +1,507 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   style="background-color:#fff"
+   id="svg118"
+   width="611"
+   height="301"
+   content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:51:02.162Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;r2FI06e1Bz4JBFBCcYux&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;bD_avIc9hnmej6XGDE4-&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"
+   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>
diff --git a/docs/images/FetchBeer.svg b/docs/images/FetchBeer.svg
new file mode 100644 (file)
index 0000000..880a142
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T23:10:56.098Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;ESTEkqebV6YRSOniH-rU&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;mKUtwOxgaR0kJFD4DNnV&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/FetchBeer2.svg b/docs/images/FetchBeer2.svg
new file mode 100644 (file)
index 0000000..aaca63d
--- /dev/null
@@ -0,0 +1 @@
+<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
diff --git a/docs/images/FetchBeerFails.svg b/docs/images/FetchBeerFails.svg
new file mode 100644 (file)
index 0000000..9a596e9
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T23:10:32.547Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;TPDlYKIcIQ83eQUnMAhy&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;zDTOZIV2HBfoVE9N0Co8&quot;&gt;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==&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/ReactiveSequence.svg b/docs/images/ReactiveSequence.svg
new file mode 100644 (file)
index 0000000..7f33658
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:13:56.421Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;toPCHC2ffeMfGQfw0vyv&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;8JSPdVEl9xWM1446yXQM&quot;&gt;7VbBkpQwEP0artZAFnY86uysWqUXp0rdY4ReiBXSGJoB/HoDNAMsM1tauurBE7zX3Un6vUwzntjlzSsri+wdJqC9YJM0nrjxgsAX4cY9OqYdmOvoaiBSqxJOmoiD+gZMcl1aqQTKRSIhalLFkozRGIhpwUlrsV6m3aNe7lrIFFbEIZZ6zX5UCWXM+tHzKfAaVJrx1tvgegjkckzmTspMJljPKLH3xM4i0vCWNzvQnXijLkPd7YXo6WAWDP1IQcDHoHbsDRLXKkODxj1eWqxMAl3FxiG0lGGKRuq3iIUjfUd+AaKWjZIVoaMyyjVHoVH0qSt/FjK6m0VuGl65B+0IDNl2VtTBu3lsKuvRWHePhvgg/tbhocGuq4VEJVY2ZoovH0mbAqsWrYX0T/a4ew2Yg9vVpVjQktRxubrkC5ae8iYP3AvbcN4S8d+Ss5aEf88SPs1R6ooXfQ8y7rY4wNcKjDv1Q8+WBtWZIjgUsm+vdkNxacZ5gY5gCRrv0g/7QvNcEAieMDxixYjr2cDaMpfNZlW0+XW9wpVeL4rCooyzvYG8/efEEg/EugrPiBU+kVjRSqw3ZS/TB1Wqz/qxq+X/vFrSxgxF9HvE86M/J56D07exj83+YYj9dw==&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/SequenceBasic.svg b/docs/images/SequenceBasic.svg
new file mode 100644 (file)
index 0000000..8c1fb00
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:50:18.155Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;mGPcX5_LkMqR0Xt8hK01&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;-Dek54CL9MAO_r9ihOgc&quot;&gt;zVZNc5swEP01vmb4MNi5hsTtoZ12xoc2R43ZgFKhpULYuL++wqwAQTvjNh7HXGCftNLueytWizApmg+KlflnTEEsAi9tFuHjIgj8wPfNq0WOhITr+w7JFE8JG4At/wUEeoTWPIXKmagRhealC+5QSthpB2NK4cGd9oLC3bVkGcyA7Y6JOfqNpzon1I/vh4GPwLOctl4Hq26gYHYyZVLlLMXDCAqfFmGiEHX3VTQJiJY9y0vnt/nLaB+YAqnPcQgoDH20uUFqUiVTojSvB4W1TKH18IyFSueYoWTiE2JpQN+Ar6D1kYRitUYD5boQNAoN199b97uIrOfRyGNDK5+MozWkVsfOaRVZ+3k8OPidLOv4glJTJP7a2F2GbVoORxXWakdQRDXEVAZEWzxn0u/1MZUNWIDZ1UxRIJjme3d1RhWW9fMGEcwH6fBnTcKb1WRK7Uijf5Lo/yRZXV4Scv2K3KwYePSHCmM6nfb/ZG27RBcVeU2E7cM4S+vlzWo90jZ4h/O3fr/zR9Hsmahp0S38rEGaaKdiucoccq5hW7JTWgfT+1wVpsRUWuEPSFCgOq0WeubZbHrK9qA0NE5aZ9Bha9hza9iW8GHUr2wTyketaum9ncB4RuCXEuTGtPLswhRegCh/OTns0RWZWs2Yaq9MDwDq5niaFtRVeVrPeEoEVnCjJbWMl3fR1cgy5nBf7FrQcO0On34D&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/SequenceNode.svg b/docs/images/SequenceNode.svg
new file mode 100644 (file)
index 0000000..ef7eec2
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:18:42.496Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;Sk5wmU9rWodo_YCvxg8Y&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;QDv7YM2D_S_Yd1SvJyLI&quot;&gt;1Zddk5owFIZ/DbcdISp4uevaj5ntTZ1pu5epHCGdkENDUOivb5CDguh+dR3cG+W8JzkkzxtIcNg8KT5pnsZfMQTpeKOwcNid43m+79rfSihrYcz8Woi0CGvJPQhL8RdIHJGaixCyTkODKI1Iu+IKlYKV6Whca9x2m61Rdu+a8gh6wnLFZV/9IUITk+pOZ4fEZxBRTLcOPJpfwpvGNJMs5iFuWxJbOGyuEU19lRRzkBW7hkvd7+OZ7H5gGpR5TgePhmHKZm4acxVClR457Ba1iTFCxeU9YmpF14q/wZiSXOG5QSvFJpGUhUKYn1X3DxOKHlqZu4Iq74KSgjUqQwXdoEoqo8u6iD9p4oemZxUc6uyiplA9HQiP/Msw1yuSprRguI6AGM362Ny9GXYRAyZg72KbaJDciE23OqflFO3bHYjbC4J+2gDWM6Aa/JJChcr+3Q7iScuDF1lw7OXrLGke9SE8Gb8HT7wBTPGH82TyHjwZwJJgOEtoNBsucyq6hD85KDvaxzeVbSwMLFO+m9bWnhC6JpwGswFtoDi7zZ2ZNHVgI3qd0IHDp3Db2r2bLTlubdzT0f9j8nuYbkRyYxYKkvLqQI2PQLmzE6TYhUgF/QUVVyeha4M0mT4NKbgQo1mP0Zdst5S+i0z8ko89eu7LYXG9opBN34ad/4z1NbkQu+ZM0YInsm9iLeEeecXoytnZp/NpeOO3gWfDw1fILtf6lGOLfw==&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/SequenceStar.svg b/docs/images/SequenceStar.svg
new file mode 100644 (file)
index 0000000..c3efde4
--- /dev/null
@@ -0,0 +1 @@
+<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="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2022-01-14T22:23:12.908Z&quot; agent=&quot;5.0 (X11)&quot; version=&quot;16.2.7&quot; etag=&quot;18veofEKuKUwUDRxzVob&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;BNCKIlJ_xest3XBbFGQC&quot;&gt;1VdLj5swEP41SO1lxSMhcOxmt9tDK1VNq3aPFkzAK8eTOiYh/fU1YRxwSapK3U3YE8xnz3jmm4fBi+ar+kGxdfkJcxBe6Oe1F915YRgE4dQ8GmTfInE0a4FC8Zw2dcCC/wICfUIrnsPG2agRheZrF8xQSsi0gzGlcOduW6JwT12zAgbAImNiiH7nuS5tXHHaLXwAXpR0dBJSfCtmN1Mkm5LluOtB0b0XzRWibt9W9RxEQ57lpdV7f2b16JgCqf9FISQ39N7GBrkJlUSJ0jxuFVYyh0bDNxIqXWKBkomPiGsDBgZ8Aq33lChWaTRQqVeCVqHm+kejfjMl6bG3cleT5YOwt4LUat8qzaZWfuwvdnoHySouUWryJEiM3EbYhOVwtMFKZQRROWqmCiDa0iGTwTE/prABV2BONVsUCKb51rXOqMKK476j6mfkxmLoUzckPp1OvTCJfNdE6xVpdak0Lz03OuiQ4NPJjl5Dsq+Q6/j5c+1k6S8pmYw2Jc9DrZ3Y1+CW3NkyUZHRBfysQGaGX6YGxLss70quYbFmh9h25hpzGT3NzhaUhto7N4rPBG4Hgb0TaBBEVt71rpgJYWXvdon9/+cqHnD1gF/xzfzt6GhKkzPzsk9TMH0ZmmajbdcLTtBk2ObT63V5MqjcL42Jb1JzsaiyDDbLSoyuimepW8XHQdmv4vSFmj093ezvxtfsceLOxIs2u03Jnzzdjo+nJApvLjcWjdj9obTfod1/XnT/Gw==&lt;/diagram&gt;&lt;/mxfile&gt;" 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
diff --git a/docs/images/Tutorial1.svg b/docs/images/Tutorial1.svg
new file mode 100644 (file)
index 0000000..9246de0
--- /dev/null
@@ -0,0 +1 @@
+<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
diff --git a/docs/images/Tutorial2.svg b/docs/images/Tutorial2.svg
new file mode 100644 (file)
index 0000000..06cf1dd
--- /dev/null
@@ -0,0 +1 @@
+<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=&quot;this works too&quot;<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=&quot;hello&quot;<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">&quot;the answer is 42&quot;<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">&quot;the answer is 42&quot;</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
diff --git a/docs/images/bt_intro_01.gif b/docs/images/bt_intro_01.gif
new file mode 100644 (file)
index 0000000..917db15
Binary files /dev/null and b/docs/images/bt_intro_01.gif differ
index c5da189..dbc3ee9 100644 (file)
@@ -11,9 +11,9 @@ __AI for games__, or to replace Finite State Machines in you application.
 
 __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.
@@ -37,7 +37,7 @@ The main advantages of Behavior Trees, when compared to FSMs are:
 
 - __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 
@@ -52,7 +52,7 @@ make possible to express more complex control flows. The user can extend the
 
 ## "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 
@@ -74,11 +74,7 @@ If we don't keep these concepts in mind from the very beginning, we create
 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
index 6a5b934..f1c60b6 100644 (file)
@@ -12,6 +12,11 @@ information on console,
 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.
index d5d9aee..80dee36 100644 (file)
@@ -15,12 +15,20 @@ Similar to functions, we often want to:
 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.
@@ -38,8 +46,8 @@ Such a string will be passed using an input port called `message`.
 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: 
@@ -52,7 +60,7 @@ The syntax of the __second node__ instead 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:
 
@@ -196,11 +204,10 @@ In this example, a Sequence of 5 Actions is executed:
 <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>
@@ -236,16 +243,15 @@ int main()
 
     /*  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,
index fd7d532..553494b 100644 (file)
@@ -23,9 +23,9 @@ It is also the first practical example that uses `Decorators` and `Fallback`.
             <Inverter>
                 <IsDoorOpen/>
             </Inverter>
-            <RetryUntilSuccesful num_attempts="4">
+            <RetryUntilSuccessful num_attempts="4">
                 <OpenDoor/>
-            </RetryUntilSuccesful>
+            </RetryUntilSuccessful>
             <PassThroughDoor/>
         </Sequence>
     </BehaviorTree>
index 8961c8b..b5a5e10 100644 (file)
@@ -53,7 +53,7 @@
       <w>150</w>
       <h>40</h>
     </coordinates>
-    <panel_attributes>RetryUntilSuccesful
+    <panel_attributes>RetryUntilSuccesfful
 (num_attempts=4)</panel_attributes>
     <additional_attributes/>
   </element>
index 363b8b1..7b1a773 100644 (file)
@@ -43,7 +43,7 @@ Robot Behaviors</panel_attributes>
       <w>150</w>
       <h>40</h>
     </coordinates>
-    <panel_attributes>RetryUntilSuccesful</panel_attributes>
+    <panel_attributes>RetryUntilSuccessful</panel_attributes>
     <additional_attributes/>
   </element>
   <element>
index d4ff9fc..62a49ff 100644 (file)
@@ -143,7 +143,7 @@ Let's say that we want to incapsulate few action into the behaviorTree "__GraspO
      <BehaviorTree ID="MainTree">
         <Sequence>
            <Action  ID="SaySomething"  message="Hello World"/>
-           <Subtree ID="GraspObject"/>
+           <SubTree ID="GraspObject"/>
         </Sequence>
      </BehaviorTree>
      
@@ -183,7 +183,7 @@ using the previous example, we may split the two behavior trees into two files:
      <BehaviorTree ID="MainTree">
         <Sequence>
            <Action  ID="SaySomething"  message="Hello World"/>
-           <Subtree ID="GraspObject"/>
+           <SubTree ID="GraspObject"/>
         </Sequence>
      </BehaviorTree>
   </root>
index 2ba4fa2..59b059c 100644 (file)
@@ -51,3 +51,4 @@ if(CURSES_FOUND)
     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()
+
index 8e5a7c8..f7f72d8 100644 (file)
@@ -32,10 +32,9 @@ static const char* xml_text = R"(
 
      <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>
@@ -91,7 +90,7 @@ int main()
 
     /* 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:
@@ -105,16 +104,15 @@ int main()
 
     /*  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;
index cec330a..6e24197 100644 (file)
@@ -7,7 +7,7 @@ using namespace BT;
 
 /** 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).
@@ -111,14 +111,11 @@ Robot says: "mission started..."
 [ 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() ---
@@ -127,9 +124,11 @@ Robot says: "mission started..."
 [ 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!"
 
 */
index 53de346..3539e0f 100644 (file)
@@ -29,9 +29,9 @@ static const char* xml_text = R"(
             <Inverter>
                 <Condition ID="IsDoorOpen"/>
             </Inverter>
-            <RetryUntilSuccesful num_attempts="4">
+            <RetryUntilSuccessful num_attempts="4">
                 <OpenDoor/>
-            </RetryUntilSuccesful>
+            </RetryUntilSuccessful>
             <PassThroughDoor/>
         </Sequence>
     </BehaviorTree>
index 96a725a..817c8ad 100644 (file)
@@ -58,7 +58,7 @@ int main()
     // 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);
index a7ef2ae..be19e74 100644 (file)
@@ -17,6 +17,8 @@
 #include <atomic>
 #include <thread>
 #include <future>
+#include <mutex>
+
 #include "leaf_node.h"
 
 namespace BT
@@ -135,6 +137,7 @@ class AsyncActionNode : public ActionNodeBase
     std::exception_ptr exptr_;
     std::atomic_bool halt_requested_;
     std::future<NodeStatus> thread_handle_;
+    std::mutex m_;
 };
 
 /**
index 0e70214..d3052f7 100644 (file)
@@ -44,6 +44,7 @@
 #include "behaviortree_cpp_v3/decorators/timeout_node.h"
 #include "behaviortree_cpp_v3/decorators/delay_node.h"
 
+#include <iostream>
 
 namespace BT
 {
@@ -56,9 +57,9 @@ void applyRecursiveVisitor(const TreeNode* root_node,
 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;
 
index c248f49..7600458 100644 (file)
@@ -117,6 +117,7 @@ class Blackboard
     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())
@@ -130,10 +131,10 @@ class Blackboard
                     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 );
@@ -189,9 +190,24 @@ class Blackboard
 
     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;
 
@@ -206,6 +222,7 @@ class Blackboard
     };
 
     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_;
index d621f97..aa0aa3b 100644 (file)
@@ -21,7 +21,7 @@ namespace BT
  * @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.
  *
index 4cc445d..1d7312f 100644 (file)
@@ -42,6 +42,8 @@ class BlackboardPreconditionNode : public DecoratorNode
             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;
index cf5392a..39cd1fd 100644 (file)
@@ -39,6 +39,7 @@ class DelayNode : public DecoratorNode
     }
     void halt() override
     {
+        delay_started_ = false;
         timer_.cancelAll();
         DecoratorNode::halt();
     }
index 60c4e24..61d3293 100644 (file)
@@ -29,9 +29,13 @@ namespace BT
  *
  * 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
 {
@@ -61,6 +65,22 @@ 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
index 70808c8..51ddcee 100644 (file)
@@ -9,20 +9,26 @@
 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,
@@ -44,7 +50,7 @@ inline const NodeStatus (&EnumValuesNodeStatus())[4] {
 }
 
 inline const char * const *EnumNamesNodeStatus() {
-  static const char * const names[] = {
+  static const char * const names[5] = {
     "IDLE",
     "RUNNING",
     "SUCCESS",
@@ -55,8 +61,8 @@ inline const char * const *EnumNamesNodeStatus() {
 }
 
 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];
 }
 
@@ -84,7 +90,7 @@ inline const NodeType (&EnumValuesNodeType())[6] {
 }
 
 inline const char * const *EnumNamesNodeType() {
-  static const char * const names[] = {
+  static const char * const names[7] = {
     "UNDEFINED",
     "ACTION",
     "CONDITION",
@@ -97,8 +103,8 @@ inline const char * const *EnumNamesNodeType() {
 }
 
 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];
 }
 
@@ -120,7 +126,7 @@ inline const PortDirection (&EnumValuesPortDirection())[3] {
 }
 
 inline const char * const *EnumNamesPortDirection() {
-  static const char * const names[] = {
+  static const char * const names[4] = {
     "INPUT",
     "OUTPUT",
     "INOUT",
@@ -130,8 +136,8 @@ inline const char * const *EnumNamesPortDirection() {
 }
 
 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];
 }
 
@@ -140,8 +146,8 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Timestamp FLATBUFFERS_FINAL_CLASS {
   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)) {
@@ -158,13 +164,18 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) StatusChange FLATBUFFERS_FINAL_CLASS {
   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))),
@@ -175,19 +186,20 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) StatusChange FLATBUFFERS_FINAL_CLASS {
   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 &timestamp() const {
+  const Serialization::Timestamp &timestamp() 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,
@@ -197,8 +209,8 @@ struct PortModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   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);
@@ -220,12 +232,13 @@ struct PortModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 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) {
@@ -238,7 +251,6 @@ struct PortModelBuilder {
         : 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);
@@ -249,7 +261,7 @@ struct PortModelBuilder {
 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);
@@ -263,7 +275,7 @@ inline flatbuffers::Offset<PortModel> CreatePortModel(
 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;
@@ -278,6 +290,7 @@ inline flatbuffers::Offset<PortModel> CreatePortModelDirect(
 }
 
 struct PortConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef PortConfigBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_PORT_NAME = 4,
     VT_REMAP = 6
@@ -299,6 +312,7 @@ struct PortConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 struct PortConfigBuilder {
+  typedef PortConfig Table;
   flatbuffers::FlatBufferBuilder &fbb_;
   flatbuffers::uoffset_t start_;
   void add_port_name(flatbuffers::Offset<flatbuffers::String> port_name) {
@@ -311,7 +325,6 @@ struct PortConfigBuilder {
         : 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);
@@ -342,6 +355,7 @@ inline flatbuffers::Offset<PortConfig> CreatePortConfigDirect(
 }
 
 struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef TreeNodeBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_UID = 4,
     VT_CHILDREN_UID = 6,
@@ -356,8 +370,8 @@ struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   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);
@@ -365,8 +379,8 @@ struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   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) &&
@@ -386,6 +400,7 @@ struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 struct TreeNodeBuilder {
+  typedef TreeNode Table;
   flatbuffers::FlatBufferBuilder &fbb_;
   flatbuffers::uoffset_t start_;
   void add_uid(uint16_t uid) {
@@ -394,7 +409,7 @@ struct TreeNodeBuilder {
   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) {
@@ -403,14 +418,13 @@ struct TreeNodeBuilder {
   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);
@@ -424,10 +438,10 @@ inline flatbuffers::Offset<TreeNode> CreateTreeNode(
     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);
@@ -442,14 +456,14 @@ inline flatbuffers::Offset<TreeNode> CreateTreeNodeDirect(
     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,
@@ -461,6 +475,7 @@ inline flatbuffers::Offset<TreeNode> CreateTreeNodeDirect(
 }
 
 struct NodeModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef NodeModelBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_REGISTRATION_NAME = 4,
     VT_TYPE = 6,
@@ -469,11 +484,11 @@ struct NodeModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   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) &&
@@ -488,22 +503,22 @@ struct NodeModel FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 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);
@@ -515,8 +530,8 @@ struct NodeModelBuilder {
 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);
@@ -527,10 +542,10 @@ inline flatbuffers::Offset<NodeModel> CreateNodeModel(
 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__,
@@ -539,6 +554,7 @@ inline flatbuffers::Offset<NodeModel> CreateNodeModelDirect(
 }
 
 struct BehaviorTree FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef BehaviorTreeBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_ROOT_UID = 4,
     VT_NODES = 6,
@@ -547,11 +563,11 @@ struct BehaviorTree FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   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) &&
@@ -567,22 +583,22 @@ struct BehaviorTree FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 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);
@@ -593,8 +609,8 @@ struct BehaviorTreeBuilder {
 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);
@@ -605,10 +621,10 @@ inline flatbuffers::Offset<BehaviorTree> CreateBehaviorTree(
 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,
@@ -617,15 +633,16 @@ inline flatbuffers::Offset<BehaviorTree> CreateBehaviorTreeDirect(
 }
 
 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) &&
@@ -638,19 +655,19 @@ struct StatusChangeLog FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
 };
 
 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);
@@ -660,8 +677,8 @@ struct StatusChangeLogBuilder {
 
 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);
@@ -670,9 +687,9 @@ inline flatbuffers::Offset<StatusChangeLog> CreateStatusChangeLog(
 
 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,
index 1686dc6..54a51aa 100644 (file)
 #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)
@@ -154,10 +157,12 @@ namespace flatbuffers {
     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
 
@@ -165,13 +170,16 @@ namespace flatbuffers {
     (!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
@@ -189,9 +197,25 @@ namespace flatbuffers {
 #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
@@ -212,6 +236,13 @@ namespace flatbuffers {
         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
@@ -229,10 +260,8 @@ namespace flatbuffers {
 
 #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
@@ -301,7 +330,13 @@ typedef uintmax_t largest_scalar_t;
 // 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
@@ -367,6 +402,13 @@ T ReadScalar(const void *p) {
   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")
@@ -379,9 +421,14 @@ template<typename T> __supress_ubsan__("alignment") void WriteScalar(void *p, Of
   *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);
 }
index 876285d..de7875f 100644 (file)
 #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>
@@ -43,6 +48,20 @@ template<> inline bool IsTheSameAs<double>(double e, double def) {
 }
 #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 {
@@ -238,6 +257,7 @@ template<typename T> class Vector {
 
   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());
@@ -351,6 +371,7 @@ template<typename T> class Vector {
   // 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);
@@ -381,6 +402,7 @@ class VectorOfAny {
 
  private:
   VectorOfAny(const VectorOfAny &);
+  VectorOfAny &operator=(const VectorOfAny &);
 };
 
 #ifndef FLATBUFFERS_CPP98_STL
@@ -414,6 +436,7 @@ template<typename T, uint16_t length> class Array {
           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;
@@ -427,6 +450,13 @@ template<typename T, uint16_t length> class Array {
 
   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()); }
 
@@ -464,6 +494,22 @@ template<typename T, uint16_t length> class Array {
   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) {
@@ -476,6 +522,20 @@ template<typename T, uint16_t length> class Array {
     *(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.
@@ -502,10 +562,12 @@ template<typename T, uint16_t length> class Array<Offset<T>, length> {
   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;
   }
@@ -519,6 +581,30 @@ template<typename T, uint16_t length> class Array<Offset<T>, length> {
   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,
@@ -556,6 +642,14 @@ static inline const char *GetCstring(const String *str) {
   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 {
@@ -728,9 +822,9 @@ class DetachedBuffer {
   #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
@@ -895,7 +989,7 @@ class vector_downward {
   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 {
@@ -973,8 +1067,8 @@ class vector_downward {
 
  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_;
@@ -1146,6 +1240,14 @@ class FlatBufferBuilder {
     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(); }
@@ -1186,7 +1288,7 @@ class FlatBufferBuilder {
   /// 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_;
   }
@@ -1270,6 +1372,11 @@ class FlatBufferBuilder {
     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));
@@ -1364,7 +1471,7 @@ class FlatBufferBuilder {
            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);
@@ -1505,6 +1612,16 @@ class FlatBufferBuilder {
     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.
@@ -1522,6 +1639,7 @@ class FlatBufferBuilder {
   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,
@@ -1552,11 +1670,13 @@ class FlatBufferBuilder {
   // 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);
   }
 
@@ -1574,6 +1694,7 @@ class FlatBufferBuilder {
     // 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));
@@ -1685,15 +1806,32 @@ class FlatBufferBuilder {
   /// @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
@@ -1756,6 +1894,22 @@ class FlatBufferBuilder {
   /// @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>
@@ -1770,8 +1924,8 @@ class FlatBufferBuilder {
       return a.KeyCompareLessThan(&b);
     }
 
-   private:
-    StructKeyComparator &operator=(const StructKeyComparator &);
+    FLATBUFFERS_DELETE_FUNC(
+        StructKeyComparator &operator=(const StructKeyComparator &));
   };
   /// @endcond
 
@@ -1837,6 +1991,7 @@ class FlatBufferBuilder {
   /// @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));
@@ -1845,7 +2000,8 @@ class FlatBufferBuilder {
     vector_downward &buf_;
 
    private:
-    TableKeyComparator &operator=(const TableKeyComparator &);
+    FLATBUFFERS_DELETE_FUNC(
+        TableKeyComparator &operator=(const TableKeyComparator &other));
   };
   /// @endcond
 
@@ -2139,7 +2295,7 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
   }
 
   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).
@@ -2240,8 +2396,8 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
 
   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;
     }
 
@@ -2373,6 +2529,12 @@ class Struct FLATBUFFERS_FINAL_CLASS {
   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];
 };
 
@@ -2417,12 +2579,26 @@ class Table {
     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);
@@ -2485,10 +2661,22 @@ class Table {
   // 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));
@@ -2666,10 +2854,16 @@ inline const char * const *ElementaryTypeNames() {
 // 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");
@@ -2684,6 +2878,7 @@ struct TypeTable {
   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.
 };
index e68089f..77e0f66 100644 (file)
@@ -18,6 +18,7 @@
 #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 {
@@ -96,13 +116,13 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
       }
   };
 
-  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; }
@@ -138,18 +158,22 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
     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!");
@@ -165,7 +189,9 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
     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> {};
@@ -173,11 +199,14 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
   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
@@ -187,7 +216,7 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
     // 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:
@@ -280,8 +309,365 @@ inline void vector_emplace_back(std::vector<T> *vector, V &&data) {
   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_
index f8aaec8..cfa966d 100644 (file)
@@ -8,8 +8,8 @@ namespace BT
 {
 enum class TimestampType
 {
-    ABSOLUTE,
-    RELATIVE
+    absolute,
+    relative
 };
 
 typedef std::array<uint8_t, 12> SerializedTransition;
@@ -30,7 +30,7 @@ class StatusChangeLogger
         enabled_ = enabled;
     }
 
-    void seTimestampType(TimestampType type)
+    void setTimestampType(TimestampType type)
     {
         type_ = type;
     }
@@ -62,7 +62,7 @@ class StatusChangeLogger
 //--------------------------------------------
 
 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();
 
@@ -70,7 +70,7 @@ inline StatusChangeLogger::StatusChangeLogger(TreeNode* root_node)
                                     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);
             }
index aa39138..9aa5d84 100644 (file)
@@ -215,6 +215,7 @@ inline Result TreeNode::getInput(const std::string& key, T& destination) const
                                            "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)
         {
index aa52d94..2cf140e 100644 (file)
@@ -877,7 +877,7 @@ nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
     {
       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
       {
index a76d768..9bbc64e 100644 (file)
@@ -2,7 +2,7 @@ site_name: BehaviorTree.CPP
 site_description: Introduction to Behavior Trees
 site_author: Davide Faconti
 
-copyright: 'Copyright &copy; 2018-2019 Davide Faconti, Eurecat'
+copyright: 'Copyright &copy; 2018-2022 Davide Faconti, Eurecat'
 
 theme:
   name: 'material'
@@ -45,7 +45,7 @@ nav:
        - "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
index 4bb9780..9549df0 100644 (file)
@@ -1,7 +1,7 @@
 <?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>
@@ -18,7 +18,9 @@
 
   <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>
   
index e3c8193..9c0a228 100644 (file)
@@ -16,6 +16,18 @@ BT::NodeStatus CheckBattery()
     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;
index 7a0d14e..9fd7096 100644 (file)
@@ -9,6 +9,9 @@ namespace DummyNodes
 
 BT::NodeStatus CheckBattery();
 
+BT::NodeStatus CheckTemperature();
+BT::NodeStatus SayHello();
+
 class GripperInterface
 {
   public:
@@ -69,6 +72,8 @@ inline void RegisterNodes(BT::BehaviorTreeFactory& factory)
     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");
index 79fe35e..e33f9c5 100644 (file)
@@ -167,6 +167,7 @@ void StatefulActionNode::halt()
 
 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)
@@ -182,16 +183,23 @@ NodeStatus BT::AsyncActionNode::executeTick()
             {
                 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();
 }
index f50b23e..4f25bc5 100644 (file)
@@ -56,25 +56,27 @@ void applyRecursiveVisitor(TreeNode* node, const std::function<void(TreeNode*)>&
     }
     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))
@@ -90,9 +92,9 @@ void printTreeRecursively(const TreeNode* root_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)
index f86fddd..fdf0bf3 100644 (file)
@@ -18,7 +18,7 @@ void Blackboard::setPortInfo(std::string key, const PortInfo& info)
     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();
index b850129..c312265 100644 (file)
@@ -33,7 +33,8 @@ BehaviorTreeFactory::BehaviorTreeFactory()
     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");
@@ -52,6 +53,7 @@ BehaviorTreeFactory::BehaviorTreeFactory()
     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");
index d27ec98..9b9d732 100644 (file)
@@ -155,7 +155,7 @@ uint8_t ManualSelectorNode::selectChild() const
     // 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
index 7bd10ce..216a125 100644 (file)
@@ -74,7 +74,7 @@ NodeStatus SequenceStarNode::tick()
 
 void SequenceStarNode::halt()
 {
-    current_child_idx_ = 0;
+    // DO NOT reset current_child_idx_ on halt
     ControlNode::halt();
 }
 
index 6c99676..d79da5d 100644 (file)
@@ -48,6 +48,9 @@ TreeNode* DecoratorNode::child()
 
 void DecoratorNode::haltChild()
 {
+    if( !child_node_ ){
+        return;
+    }
     if (child_node_->status() == NodeStatus::RUNNING)
     {
         child_node_->halt();
index 47b88d5..fb418a1 100644 (file)
@@ -37,6 +37,7 @@ NodeStatus DelayNode::tick()
     if (!delay_started_)
     {
         delay_complete_ = false;
+        delay_aborted_ = false;
         delay_started_ = true;
         setStatus(NodeStatus::RUNNING);
 
index 9f04223..442e2e2 100644 (file)
@@ -1,7 +1,7 @@
+#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
 {
@@ -79,7 +79,7 @@ PublisherZMQ::PublisherZMQ(const BT::Tree& tree,
                 {
                     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;
             }
         }
@@ -176,7 +176,7 @@ void PublisherZMQ::flush()
         {
             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;
index 62d0e45..f51472b 100644 (file)
@@ -14,8 +14,8 @@
 #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"
 
@@ -37,6 +41,11 @@ namespace BT
 {
 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,
@@ -48,6 +57,8 @@ struct XMLParser::Pimpl
                                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;
@@ -60,9 +71,9 @@ struct XMLParser::Pimpl
     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()
@@ -80,7 +91,7 @@ struct XMLParser::Pimpl
 #endif
 
 XMLParser::XMLParser(const BehaviorTreeFactory &factory) :
-    _p( new Pimpl(factory) )
+  _p( new Pimpl(factory) )
 {
 }
 
@@ -130,23 +141,28 @@ void XMLParser::Pimpl::loadDocImpl(BT_TinyXML2::XMLDocument* doc)
     {
 
         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() )
@@ -206,10 +222,6 @@ void VerifyXML(const std::string& xml_text,
     }
 
     //-------- 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());
@@ -239,8 +251,8 @@ void VerifyXML(const std::string& xml_text,
 
     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)
     {
@@ -251,13 +263,13 @@ void VerifyXML(const std::string& xml_text,
         {
             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");
                 }
             }
         }
@@ -274,52 +286,52 @@ void VerifyXML(const std::string& xml_text,
         {
             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") ||
@@ -328,8 +340,8 @@ void VerifyXML(const std::string& xml_text,
         {
             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"))
@@ -349,8 +361,8 @@ void VerifyXML(const std::string& xml_text,
 
             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
@@ -359,8 +371,8 @@ void VerifyXML(const std::string& xml_text,
             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
@@ -387,8 +399,8 @@ void VerifyXML(const std::string& xml_text,
         }
         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
         {
@@ -408,8 +420,8 @@ void VerifyXML(const std::string& xml_text,
     {
         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");
         }
     }
 }
@@ -572,10 +584,11 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement *element,
                 }
             }
         }
+
         // 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();
@@ -586,6 +599,7 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement *element,
                 config.input_ports.insert( { port_name, port_info.defaultValue() } );
             }
         }
+
         child_node = factory.instantiateTreeNode(instance_name, ID, config);
     }
     else if( tree_roots.count(ID) != 0) {
@@ -613,12 +627,13 @@ void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID,
                                                  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);
 
@@ -642,19 +657,19 @@ void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID,
                     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()) )
@@ -667,45 +682,50 @@ void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID,
 
                 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
         {
@@ -723,6 +743,29 @@ void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID,
     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)
 {
@@ -761,9 +804,9 @@ 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() );
index ca86ba3..0727b15 100644 (file)
@@ -13,16 +13,23 @@ set(BT_TESTS
   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)
@@ -42,15 +49,13 @@ elseif(catkin_FOUND AND CATKIN_ENABLE_TESTING)
                                                         ${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)
diff --git a/tests/gtest_async_action_node.cpp b/tests/gtest_async_action_node.cpp
new file mode 100644 (file)
index 0000000..e2201da
--- /dev/null
@@ -0,0 +1,142 @@
+#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);
+}
diff --git a/tests/gtest_blackboard_precondition.cpp b/tests/gtest_blackboard_precondition.cpp
new file mode 100644 (file)
index 0000000..0c36fd2
--- /dev/null
@@ -0,0 +1,198 @@
+#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);
+}
index 1e654cd..c0a86ac 100644 (file)
@@ -48,6 +48,81 @@ TEST(PortTest, DefaultPorts)
 
     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 );
 }
 
index 1141a41..c1bdc73 100644 (file)
@@ -387,7 +387,7 @@ TEST_F(ComplexSequenceWithMemoryTest, ConditionsTrue)
     ASSERT_EQ(NodeStatus::IDLE, action_2.status());
 }
 
-TEST_F(ComplexSequenceWithMemoryTest, Conditions1ToFase)
+TEST_F(ComplexSequenceWithMemoryTest, Conditions1ToFalse)
 {
     BT::NodeStatus state = root.executeTick();
 
@@ -458,3 +458,89 @@ TEST_F(ComplexSequenceWithMemoryTest, Action1DoneSeq)
     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());
+}
index 10ee486..c79fd81 100644 (file)
@@ -233,4 +233,47 @@ TEST(SubTree, SubtreePlusC)
 }
 
 
+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);
+}
+
+
 
index ab07d05..0a96304 100644 (file)
@@ -15,6 +15,9 @@
 #include "condition_test_node.h"
 #include "behaviortree_cpp_v3/behavior_tree.h"
 
+#include <sstream>
+#include <string>
+
 using BT::NodeStatus;
 using std::chrono::milliseconds;
 
@@ -76,6 +79,45 @@ TEST_F(BehaviorTreeTest, Condition2ToFalseCondition1True)
     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);
index 0801850..2ad33b3 100644 (file)
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.5)
 
 
 add_executable(bt3_log_cat         bt_log_cat.cpp )
index 8822d44..5153745 100644 (file)
@@ -21,13 +21,13 @@ int main(int argc, char* argv[])
     }
 
     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]);
 
index 3aa6740..e501528 100644 (file)
@@ -2,8 +2,8 @@
 #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
@@ -16,12 +16,17 @@ static void s_signal_handler(int)
 
 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[])
@@ -44,7 +49,7 @@ 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");
 
@@ -57,7 +62,7 @@ int main(int argc, char* argv[])
         zmq::message_t msg;
         try
         {
-            subscriber.recv(&update, 0);
+            subscriber.recv(update, zmq::recv_flags::none);
         }
         catch (zmq::error_t& e)
         {