diff --git a/.codespell_ignore_words b/.codespell_ignore_words new file mode 100644 index 000000000..ab09b3c2f --- /dev/null +++ b/.codespell_ignore_words @@ -0,0 +1,7 @@ +INOUT +InOut +delimeter +Succesful +worl +valu +Exeption diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml new file mode 100644 index 000000000..b8fb8f46b --- /dev/null +++ b/.github/workflows/doxygen-gh-pages.yml @@ -0,0 +1,18 @@ +name: Doxygen GitHub Pages Deploy Action + +on: + push: + branches: + - main + - master + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: DenverCoder1/doxygen-github-pages-action@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + folder: doc/html diff --git a/.github/workflows/ros2.yaml b/.github/workflows/ros2.yaml index 51eabc44b..099cc04f2 100644 --- a/.github/workflows/ros2.yaml +++ b/.github/workflows/ros2.yaml @@ -13,7 +13,7 @@ jobs: matrix: env: - {ROS_DISTRO: humble, ROS_REPO: main} - - {ROS_DISTRO: iron, ROS_REPO: main} + - {ROS_DISTRO: jazzy, ROS_REPO: main} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/sonarcube.yml b/.github/workflows/sonarcube.yml.bkp similarity index 100% rename from .github/workflows/sonarcube.yml rename to .github/workflows/sonarcube.yml.bkp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6ace8262..d491f36d9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,3 +42,13 @@ repos: hooks: - id: clang-format args: ['-fallback-style=none', '-i'] + + # Spell check + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + additional_dependencies: + - tomli + args: + [--toml=./pyproject.toml] diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 49aa120c2..600abbaa3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,83 @@ Changelog for package behaviortree_cpp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +4.7.2 (2025-05-29) +------------------ +* Fix issue `#978 `_ : skipped was not working properly +* Added codespell as a pre-commit hook. (`#977 `_) +* fix: Make impossible to accidentally copy JsonExporter singleton (`#975 `_) +* Contributors: Davide Faconti, Leander Stephen D'Souza, tony-p + +4.7.1 (2025-05-13) +------------------ +* fix ROS CI +* Add action to publish Doxygen documentation as GH Page (`#972 `_) +* Update Doxyfile +* Make BT::Any::copyInto const (`#970 `_) +* more changes related to TestNode +* Contributors: David Sobek, Davide Faconti, Marcus Ebner von Eschenbach + +4.7.0 (2025-04-24) +------------------ +* change TestNodeConfig preferred constructor +* Fix dangling‐capture in TestNodeConfig +* Fix Precondition to only check condition once (`#904 `_) +* fix issue 945 +* extend JSON conversion to include vectors (`#965 `_) +* Fix CI, add BUILD_TESTS and remove catkin support +* Fix testing CMake issue to resolve Rolling regression (`#961 `_) +* Bug fix/set blackboard (`#955 `_) +* feat: add fuzzing harnesses (`#925 `_) +* fix warnings +* Add const to applyVisitor (`#935 `_) +* try fix (`#941 `_) +* add workflow for sonarcube (`#936 `_) +* Fix issue `#909 `_: static queue in Loop +* apply changes suggested in `#893 `_ +* apply fix mentioned in `#916 `_ +* apply fixes suggested in `#919 `_ +* fix issue `#918 `_ (introduced in `#885 `_) +* add fix suggested in `#920 `_ +* add unit test related to `#931 `_ +* Fix compilation error when targeting C++23 (`#926 `_) ^~~~~~~~~~~~~ +* Fixes issue # `#929 `_ and `#921 `_ +* apply check suggested in `#924 `_ +* Fix ROS 2 build when ZeroMQ or SQlite3 include are not in the default include path (`#911 `_) + * Fix ROS 2 build when ZeroMQ or SQlite3 include are not in the default include path + * Update ament_build.cmake +* Fix/use correct compiler pixi/conda (`#914 `_) + * fix: Use the cxx-compiler package which will set the correct compiler for the platform, and setup the required environment for it to work as expected + * misc: update pixi versions in pipeline +* Add "other ports" to NodeConfig (`#910 `_) +* [retry_node] Refresh max_attempts\_ in case it changed (`#905 `_) + Co-authored-by: Guillaume Doisy +* use relative path in .Doxyfile (`#882 `_) +* Additional XML verification for ReactiveSequence nodes (`#885 `_) + Co-authored-by: AndyZe +* fix script parse error while 'A==-1' (`#896 `_) + Co-authored-by: wangzheng +* Expose return value of wait_for (`#887 `_) +* fix(examples): update t11_groot_howto log filename (`#886 `_) +* put minitrace in the build_interface link library (`#874 `_) + fixes the cmake export set when building behavior tree on standard cmake: CMake Error: install(EXPORT "behaviortree_cppTargets" ...) includes target "behaviortree_cpp" which requires target "minitrace" that is not in any export set. +* Improved XML parsing error message to say where in the XML the offending port is found. (`#876 `_) + Example output: + a port with name [ball_pose] is found in the XML (, line 7) but not in the providedPorts() of its registered node type. +* Refactored the TreeNode::executeTick() function to use a scoped timer for performance monitoring. (`#861 `_) (`#863 `_) + Update src/tree_node.cpp + Co-authored-by: wangzheng + Co-authored-by: Davide Faconti +* fix issue `#852 `_: thread safety in Loggers +* Lexy updated +* tinyXML updated to version 10.0 +* cppzmq updated to version 4.10 +* fix the "all_skipped" logic +* fixed: support utf-8 path xml-file (`#845 `_) + * fixed: 1. added compile version check to support Chinese path xml-file parsing 2. cmake add msvc /utf-8 options + * change cmake /utf-8 option add mode +* Export plugins to share directory & register CrossDoor plugin (`#804 `_) +* Contributors: Aglargil, AndyZe, Antoine Hoarau, David Sobek, Davide Faconti, Guillaume Doisy, Isar Meijer, Jake Keller, Marq Rasmussen, Michele Tartari, Silvio Traversaro, Tony Najjar, b-adkins, ckrah, devis12, kinly, tony-p, vincent-hui + 4.6.2 (2024-06-26) ------------------ * Initialize template variable `T out` (`#839 `_) @@ -46,7 +123,7 @@ Changelog for package behaviortree_cpp * warn about overwritten enums * fix ambiguous to_json * Extend unit test for blackboard backup to run the second tree (`#789 `_) -* json convertion changed and +* json conversion changed and * issue `#755 `_ : add backchaining test and change reactive nodes checks (`#770 `_) * Update switch_node.h * test moved and port remapping fixed @@ -323,7 +400,7 @@ Changelog for package behaviortree_cpp * better include paths * Control node and Decorators RUNNING before first child * blackboard: update getKeys and add mutex to scripting -* add [[nodiscard]] and some othe minor changes +* add [[nodiscard]] and some other minor changes * add screenshot * change the behavior of tickOnce to actually loop is wake up signal is… (`#522 `_) * change the behavior of tickOnce to actually loop is wake up signal is received @@ -422,7 +499,7 @@ Changelog for package behaviortree_cpp dependency explicitly. * Change order of lock to prevent deadlock. (`#368 `_) Resolves `#367 `_. -* Fix `#320 `_ : forbit refrences in Any +* Fix `#320 `_ : forbid references in Any * Update action_node.h * Contributors: Adam Sasine, Davide Faconti, Fabian Schurig, Griswald Brooks, Hyeongsik Min, Robodrome, imgbot[bot], panwauu @@ -769,9 +846,9 @@ Changelog for package behaviortree_cpp * Conan package distribution (#39) * Non-functional refactoring of xml_parsing to clean up the code * cosmetic changes in the code of BehaviorTreeFactory -* XML schema. Related to enchancement #40 +* XML schema. Related to enhancement #40 * call setRegistrationName() for built-in Nodes - The methos is called by BehaviorTreefactory, therefore it + The method is called by BehaviorTreefactory, therefore it registrationName is empty if trees are created programmatically. * Reset reference count when destroying logger (issue #38) * Contributors: Davide Facont, Davide Faconti, Uilian Ries @@ -787,7 +864,7 @@ Changelog for package behaviortree_cpp ------------------ * adding virtual TreeNode::onInit() [issue #33] * fix issue #34 : if you don't implement convertFromString, it will compile but it may throw -* Pretty demangled names and obsolate comments removed +* Pretty demangled names and obsolete comments removed * bug fixes * more comments * [enhancement #32]: add CoroActionNode and rename ActionNode as "AsynActionNode" @@ -854,7 +931,7 @@ Changelog for package behaviortree_cpp * Fix: registerBuilder did not register the manifest. It was "broken" as public API method * Use the Pimpl idiom to hide zmq from the header file * move header of minitrace in the cpp file -* Fixed a crash occuring when you didn't initialized a Tree object (#20) +* Fixed a crash occurring when you didn't initialized a Tree object (#20) * Fix issue #16 * add ParallelNode to pre-registered entries in factory (issue #13) * removed M_PI diff --git a/CMakeLists.txt b/CMakeLists.txt index f0809a4e7..e69c9e96c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.3) # version on Ubuntu Focal -project(behaviortree_cpp VERSION 4.6.2 LANGUAGES C CXX) +project(behaviortree_cpp VERSION 4.7.2 LANGUAGES C CXX) # create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/Doxyfile b/Doxyfile index 353afcde5..d06db140e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -864,7 +864,8 @@ RECURSIVE = YES # run. EXCLUDE = ./3rdparty \ - ./gtest + ./gtest \ + ./include/behaviortree_cpp/contrib # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/README.md b/README.md index 17d79e7f3..6fda200c7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ ![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue) -![Version](https://img.shields.io/badge/version-4.6-blue.svg) [![conan Ubuntu](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_ubuntu.yml) [![conan Windows](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/cmake_windows.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) +[![ros2](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/ros2.yaml) [![pixi (Conda)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml/badge.svg)](https://github.com/BehaviorTree/BehaviorTree.CPP/actions/workflows/pixi.yaml) -# BehaviorTree.CPP 4.6 +# BehaviorTree.CPP 4.7

@@ -37,6 +35,8 @@ to visualize, record, replay and analyze state transitions. You can learn about the main concepts, the API and the tutorials here: https://www.behaviortree.dev/ +An automatically generated API documentation can be found here: https://BehaviorTree.github.io/BehaviorTree.CPP/ + If the documentation doesn't answer your questions and/or you want to connect with the other **BT.CPP** users, visit [our forum](https://github.com/BehaviorTree/BehaviorTree.CPP/discussions) diff --git a/cmake/conan.cmake b/cmake/conan.cmake index 3fa9a26ba..d36c5ed44 100644 --- a/cmake/conan.cmake +++ b/cmake/conan.cmake @@ -116,7 +116,7 @@ macro(_conan_check_language) set(LANGUAGE C) set(USING_CXX 0) else () - message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.") + message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unable to detect compiler version.") endif() endmacro() @@ -1050,7 +1050,7 @@ macro(conan_config_install) endif() if(DEFINED CONAN_ARGS) - # Convert ; seperated multi arg list into space seperated string + # Convert ; separated multi arg list into space separated string string(REPLACE ";" " " l_CONAN_ARGS "${CONAN_ARGS}") set(CONAN_ARGS_ARGS "--args=${l_CONAN_ARGS}") endif() diff --git a/examples/t12_default_ports.cpp b/examples/t12_default_ports.cpp index 8fa866a93..91d2f72aa 100644 --- a/examples/t12_default_ports.cpp +++ b/examples/t12_default_ports.cpp @@ -22,7 +22,7 @@ struct Point2D } }; -// Allow bi-directional convertion to JSON +// Allow bi-directional conversion to JSON BT_JSON_CONVERTER(Point2D, point) { add_field("x", &point.x); diff --git a/examples/t15_nodes_mocking.cpp b/examples/t15_nodes_mocking.cpp index 28806da7e..caef9e224 100644 --- a/examples/t15_nodes_mocking.cpp +++ b/examples/t15_nodes_mocking.cpp @@ -97,7 +97,7 @@ int main(int argc, char** argv) // this will be synchronous (async_delay is 0) BT::TestNodeConfig counting_config; - test_config.return_status = BT::NodeStatus::SUCCESS; + counting_config.return_status = BT::NodeStatus::SUCCESS; //--------------------------------------------------------------- // Next, we want to substitute one or more of out Nodes with this mocks @@ -147,7 +147,7 @@ int main(int argc, char** argv) factory.loadSubstitutionRuleFromJSON(json_text); } //--------------------------------------------------------------- - // IMPORTANT: all substiutions must be done BEFORE creating the tree + // IMPORTANT: all substitutions must be done BEFORE creating the tree // During the construction phase of the tree, the substitution // rules will be used to instantiate the test nodes, instead of the // original ones. @@ -158,7 +158,7 @@ int main(int argc, char** argv) return 0; } -/* Expecte output: +/* Expected output: ----- Nodes fullPath() ------- Sequence::1 diff --git a/examples/t16_global_blackboard.cpp b/examples/t16_global_blackboard.cpp index 956bbe2c2..38bf35b14 100644 --- a/examples/t16_global_blackboard.cpp +++ b/examples/t16_global_blackboard.cpp @@ -97,7 +97,7 @@ int main() return 0; } -/* Expecte output: +/* Expected output: [main_print] val: 1 [sub_print] val: 1 diff --git a/include/behaviortree_cpp/action_node.h b/include/behaviortree_cpp/action_node.h index 3f9b64bf4..409e57987 100644 --- a/include/behaviortree_cpp/action_node.h +++ b/include/behaviortree_cpp/action_node.h @@ -99,10 +99,10 @@ class SimpleActionNode : public SyncActionNode * IMPORTANT: this action is quite hard to implement correctly. * Please make sure that you know what you are doing. * - * - In your overriden tick() method, you must check periodically + * - In your overridden tick() method, you must check periodically * the result of the method isHaltRequested() and stop your execution accordingly. * - * - in the overriden halt() method, you can do some cleanup, but do not forget to + * - in the overridden halt() method, you can do some cleanup, but do not forget to * invoke the base class method ThreadedAction::halt(); * * - remember, with few exceptions, a halted ThreadedAction must return NodeStatus::IDLE. diff --git a/include/behaviortree_cpp/actions/pop_from_queue.hpp b/include/behaviortree_cpp/actions/pop_from_queue.hpp index a2ebb1fe1..34b905fde 100644 --- a/include/behaviortree_cpp/actions/pop_from_queue.hpp +++ b/include/behaviortree_cpp/actions/pop_from_queue.hpp @@ -20,7 +20,7 @@ /** * Template Action used in ex04_waypoints.cpp example. * - * Its purpose is to do make it easy to create while loops wich consume the elements of a queue. + * Its purpose is to do make it easy to create while loops which consume the elements of a queue. * * Note that modifying the queue is not thread safe, therefore the action that creates the queue * or push elements into it, must be Synchronous. @@ -47,7 +47,7 @@ struct ProtectedQueue * * We avoid this using reference semantic (wrapping the object in a shared_ptr). * Unfortunately, remember that this makes our access to the list not thread-safe! - * This is the reason why we add a mutex to be used when modyfying the ProtectedQueue::items + * This is the reason why we add a mutex to be used when modifying the ProtectedQueue::items * * */ @@ -95,7 +95,7 @@ class PopFromQueue : public SyncActionNode }; /** - * Get the size of a queue. Usefull is you want to write something like: + * Get the size of a queue. Useful when you want to write something like: * * * diff --git a/include/behaviortree_cpp/actions/set_blackboard_node.h b/include/behaviortree_cpp/actions/set_blackboard_node.h index 1a240c3db..05282c0c0 100644 --- a/include/behaviortree_cpp/actions/set_blackboard_node.h +++ b/include/behaviortree_cpp/actions/set_blackboard_node.h @@ -44,7 +44,7 @@ class SetBlackboardNode : public SyncActionNode static PortsList providedPorts() { - return { InputPort("value", "Value to be written int othe output_key"), + return { InputPort("value", "Value to be written into the output_key"), BidirectionalPort("output_key", "Name of the blackboard entry where the " "value should be written") }; } diff --git a/include/behaviortree_cpp/actions/test_node.h b/include/behaviortree_cpp/actions/test_node.h index c144fd43a..9aaabb829 100644 --- a/include/behaviortree_cpp/actions/test_node.h +++ b/include/behaviortree_cpp/actions/test_node.h @@ -37,9 +37,9 @@ struct TestNodeConfig /// if async_delay > 0, this action become asynchronous and wait this amount of time std::chrono::milliseconds async_delay = std::chrono::milliseconds(0); - /// Function invoked when the action is completed. By default just return [return_status] - /// Override it to intorduce more comple cases - std::function complete_func = [this]() { return return_status; }; + /// Function invoked when the action is completed. + /// If not specified, the node will return [return_status] + std::function complete_func; }; /** @@ -50,21 +50,28 @@ struct TestNodeConfig * 3. Either complete immediately (synchronous action), or after a * given period of time (asynchronous action) * - * This behavior is changed by the parameters pased with TestNodeConfig. + * This behavior is changed by the parameters passed with TestNodeConfig. * * This particular node is created by the factory when TestNodeConfig is * added as a substitution rule: * - * TestNodeConfig test_config; + * auto test_config = std::make_shared(); * // change fields of test_config * factory.addSubstitutionRule(pattern, test_config); * - * See tutorial 11 for more details. + * See tutorial 15 for more details. */ class TestNode : public BT::StatefulActionNode { public: - TestNode(const std::string& name, const NodeConfig& config, TestNodeConfig test_config); + // This constructor is deprecated, because it may cause problems if TestNodeConfig::complete_func is capturing + // a reference to the TestNode, i.e. [this]. Use the constructor with std::shared_ptr instead. + // For more details, see https://github.com/BehaviorTree/BehaviorTree.CPP/pull/967 + [[deprecated("prefer the constructor with std::shared_ptr")]] TestNode( + const std::string& name, const NodeConfig& config, TestNodeConfig test_config); + + TestNode(const std::string& name, const NodeConfig& config, + std::shared_ptr test_config); static PortsList providedPorts() { @@ -80,7 +87,7 @@ class TestNode : public BT::StatefulActionNode NodeStatus onCompleted(); - TestNodeConfig _test_config; + std::shared_ptr _config; ScriptFunction _success_executor; ScriptFunction _failure_executor; ScriptFunction _post_executor; diff --git a/include/behaviortree_cpp/bt_factory.h b/include/behaviortree_cpp/bt_factory.h index 880099125..020c9ea1d 100644 --- a/include/behaviortree_cpp/bt_factory.h +++ b/include/behaviortree_cpp/bt_factory.h @@ -469,12 +469,13 @@ class BehaviorTreeFactory void clearSubstitutionRules(); - using SubstitutionRule = std::variant; + using SubstitutionRule = + std::variant>; /** * @brief addSubstitutionRule replace a node with another one when the tree is * created. - * If the rule ia a string, we will use a diferent node type (already registered) + * If the rule ia a string, we will use a different node type (already registered) * instead. * If the rule is a TestNodeConfig, a test node with that configuration will be created instead. * @@ -525,7 +526,7 @@ std::vector BlackboardBackup(const BT::Tree& tree); * @brief BlackboardRestore uses Blackboard::cloneInto to restore * all the blackboards of the tree * - * @param backup a vectror of blackboards + * @param backup a vector of blackboards * @param tree the destination */ void BlackboardRestore(const std::vector& backup, BT::Tree& tree); diff --git a/include/behaviortree_cpp/decorators/run_once_node.h b/include/behaviortree_cpp/decorators/run_once_node.h index a40d86cdb..a3083d97e 100644 --- a/include/behaviortree_cpp/decorators/run_once_node.h +++ b/include/behaviortree_cpp/decorators/run_once_node.h @@ -42,7 +42,7 @@ class RunOnceNode : public DecoratorNode { return { InputPort("then_skip", true, "If true, skip after the first execution, " - "otherwise return the same NodeStatus returned once bu the " + "otherwise return the same NodeStatus returned once by the " "child.") }; } diff --git a/include/behaviortree_cpp/decorators/script_precondition.h b/include/behaviortree_cpp/decorators/script_precondition.h index 4fa50b717..e244c7a50 100644 --- a/include/behaviortree_cpp/decorators/script_precondition.h +++ b/include/behaviortree_cpp/decorators/script_precondition.h @@ -48,20 +48,23 @@ class PreconditionNode : public DecoratorNode throw RuntimeError("Missing parameter [else] in Precondition"); } + // Only check the 'if' script if we haven't started ticking the children yet. Ast::Environment env = { config().blackboard, config().enums }; - if(_executor(env).cast()) + bool tick_children = + _children_running || (_children_running = _executor(env).cast()); + + if(!tick_children) { - auto const child_status = child_node_->executeTick(); - if(isStatusCompleted(child_status)) - { - resetChild(); - } - return child_status; + return else_return; } - else + + auto const child_status = child_node_->executeTick(); + if(isStatusCompleted(child_status)) { - return else_return; + resetChild(); + _children_running = false; } + return child_status; } void loadExecutor() @@ -89,6 +92,7 @@ class PreconditionNode : public DecoratorNode std::string _script; ScriptFunction _executor; + bool _children_running = false; }; } // namespace BT diff --git a/include/behaviortree_cpp/flatbuffers/bt_flatbuffer_helper.h b/include/behaviortree_cpp/flatbuffers/bt_flatbuffer_helper.h index 426ccbe2d..6629cdf54 100644 --- a/include/behaviortree_cpp/flatbuffers/bt_flatbuffer_helper.h +++ b/include/behaviortree_cpp/flatbuffers/bt_flatbuffer_helper.h @@ -136,7 +136,7 @@ inline void CreateFlatbuffersBehaviorTree(flatbuffers::FlatBufferBuilder& builde builder.Finish(behavior_tree); } -/** Serialize manually the informations about state transition +/** Serialize manually the information about state transition * No flatbuffer serialization here */ inline SerializedTransition SerializeTransition(uint16_t UID, Duration timestamp, diff --git a/include/behaviortree_cpp/json_export.h b/include/behaviortree_cpp/json_export.h index 3eeee714b..dfe9b0fee 100644 --- a/include/behaviortree_cpp/json_export.h +++ b/include/behaviortree_cpp/json_export.h @@ -51,6 +51,10 @@ class JsonExporter public: static JsonExporter& get(); + // Delete copy constructors as can only be this one global instance. + JsonExporter& operator=(JsonExporter&&) = delete; + JsonExporter& operator=(JsonExporter&) = delete; + /** * @brief toJson adds the content of "any" to the JSON "destination". * @@ -83,17 +87,17 @@ class JsonExporter /** * @brief Register new JSON converters with addConverter(). * You should used first the macro BT_JSON_CONVERTER. - * The convertions from/to vector are automatically registered. + * The conversions from/to vector are automatically registered. */ template void addConverter(); /** * @brief addConverter register a to_json function that converts a json to a type T. - * The convertion to std:vector is automatically registered. + * The conversion to std:vector is automatically registered. * * @param to_json the function with signature void(const T&, nlohmann::json&) - * @param add_type if true, add a field called [__type] with the name ofthe type. + * @param add_type if true, add a field called [__type] with the name of the type. */ template void addConverter(std::function to_json, @@ -101,7 +105,7 @@ class JsonExporter /** * @brief addConverter register a from_json function that converts a json to a type T. - * The convertions from std::vector is automatically registered. + * The conversions from std::vector is automatically registered. * * @param from_json the function with signature void(const nlohmann::json&, T&) */ diff --git a/include/behaviortree_cpp/loggers/groot2_protocol.h b/include/behaviortree_cpp/loggers/groot2_protocol.h index 507f2a00a..5c27b5cb5 100644 --- a/include/behaviortree_cpp/loggers/groot2_protocol.h +++ b/include/behaviortree_cpp/loggers/groot2_protocol.h @@ -27,11 +27,11 @@ namespace BT::Monitor enum RequestType : uint8_t { - // Request the entire tree defintion as XML + // Request the entire tree definition as XML FULLTREE = 'T', - // Request the staus of all the nodes + // Request the status of all the nodes STATUS = 'S', - // retrieve the valus in a set of blackboards + // retrieve the values in a set of blackboards BLACKBOARD = 'B', // Groot requests the insertion of a hook diff --git a/include/behaviortree_cpp/scripting/operators.hpp b/include/behaviortree_cpp/scripting/operators.hpp index 3033767de..4d41b6a88 100644 --- a/include/behaviortree_cpp/scripting/operators.hpp +++ b/include/behaviortree_cpp/scripting/operators.hpp @@ -27,7 +27,7 @@ using SimpleString = SafeAny::SimpleString; using expr_ptr = std::shared_ptr; -// extended strin to number that consider enums and booleans +// extended string to number that consider enums and booleans inline double StringToDouble(const Any& value, const Environment& env) { const auto str = value.cast(); diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index bd77c4c72..0087ce210 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -99,7 +99,7 @@ struct NodeConfig const TreeNodeManifest* manifest = nullptr; - // Numberic unique identifier + // Numeric unique identifier uint16_t uid = 0; // Unique human-readable name, that encapsulate the subtree // hierarchy, for instance, given 2 nested trees, it should be: @@ -209,7 +209,7 @@ class TreeNode * NodeStatus myCallback(TreeNode& node, NodeStatus status) * * This callback is executed AFTER the tick() and, if it returns SUCCESS or FAILURE, - * the value returned by the actual tick() is overriden with this one. + * the value returned by the actual tick() is overridden with this one. */ void setPostTickFunction(PostTickCallback callback); @@ -251,7 +251,7 @@ class TreeNode /** * @brief getInputStamped is similar to getInput(dey, destination), - * but it returne also the Timestamp object, that can be used to check if + * but it returns also the Timestamp object, that can be used to check if * a value was updated and when. * * @param key the name of the port. @@ -298,7 +298,7 @@ class TreeNode * @brief setOutput modifies the content of an Output port * @param key the name of the port. * @param value new value - * @return valid Result, if succesful. + * @return valid Result, if successful. */ template Result setOutput(const std::string& key, const T& value); diff --git a/include/behaviortree_cpp/utils/safe_any.hpp b/include/behaviortree_cpp/utils/safe_any.hpp index e056394ac..ce94b2eb7 100644 --- a/include/behaviortree_cpp/utils/safe_any.hpp +++ b/include/behaviortree_cpp/utils/safe_any.hpp @@ -134,7 +134,7 @@ class Any } // copy the value (casting into dst). We preserve the destination type. - void copyInto(Any& dst); + void copyInto(Any& dst) const; // this is different from any_cast, because if allows safe // conversions between arithmetic values and from/to string. @@ -321,7 +321,7 @@ inline bool Any::isIntegral() const return _any.type() == typeid(int64_t) || _any.type() == typeid(uint64_t); } -inline void Any::copyInto(Any& dst) +inline void Any::copyInto(Any& dst) const { if(dst.empty()) { @@ -494,7 +494,7 @@ inline nonstd::expected Any::tryCast() const } // special case when the output is an enum. - // We will try first a int convertion + // We will try first a int conversion if constexpr(std::is_enum_v) { if(isNumber()) diff --git a/package.xml b/package.xml index 512d3ba89..1d245b625 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ behaviortree_cpp - 4.6.2 + 4.7.2 This package provides the Behavior Trees core library. @@ -14,18 +14,19 @@ ros_environment + git + ament_cmake - ament_cmake - rclcpp - ament_index_cpp + rclcpp + ament_index_cpp libsqlite3-dev libzmq3-dev - ament_cmake_gtest + ament_cmake_gtest - ament_cmake + ament_cmake diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..3fc95ccc4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.codespell] +ignore-words = ".codespell_ignore_words" diff --git a/sonar-project.properties b/sonar-project.properties index 6e0eb2293..31dc9a6d1 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -11,3 +11,4 @@ sonar.organization=behaviortree # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 +sonar.exclusions=3rdparty/**/* diff --git a/src/actions/test_node.cpp b/src/actions/test_node.cpp index 19b70b1aa..f2471c085 100644 --- a/src/actions/test_node.cpp +++ b/src/actions/test_node.cpp @@ -1,12 +1,20 @@ #include "behaviortree_cpp/actions/test_node.h" -BT::TestNode::TestNode(const std::string& name, const NodeConfig& config, - TestNodeConfig test_config) - : StatefulActionNode(name, config), _test_config(std::move(test_config)) +namespace BT +{ + +TestNode::TestNode(const std::string& name, const NodeConfig& config, + TestNodeConfig test_config) + : TestNode(name, config, std::make_shared(std::move(test_config))) +{} + +TestNode::TestNode(const std::string& name, const NodeConfig& config, + std::shared_ptr test_config) + : StatefulActionNode(name, config), _config(std::move(test_config)) { setRegistrationID("TestNode"); - if(_test_config.return_status == NodeStatus::IDLE) + if(_config->return_status == NodeStatus::IDLE) { throw RuntimeError("TestNode can not return IDLE"); } @@ -22,21 +30,21 @@ BT::TestNode::TestNode(const std::string& name, const NodeConfig& config, executor = result.value(); } }; - prepareScript(_test_config.success_script, _success_executor); - prepareScript(_test_config.failure_script, _failure_executor); - prepareScript(_test_config.post_script, _post_executor); + prepareScript(_config->success_script, _success_executor); + prepareScript(_config->failure_script, _failure_executor); + prepareScript(_config->post_script, _post_executor); } -BT::NodeStatus BT::TestNode::onStart() +NodeStatus TestNode::onStart() { - if(_test_config.async_delay <= std::chrono::milliseconds(0)) + if(_config->async_delay <= std::chrono::milliseconds(0)) { return onCompleted(); } // convert this in an asynchronous operation. Use another thread to count // a certain amount of time. _completed = false; - _timer.add(std::chrono::milliseconds(_test_config.async_delay), [this](bool aborted) { + _timer.add(std::chrono::milliseconds(_config->async_delay), [this](bool aborted) { if(!aborted) { _completed.store(true); @@ -50,7 +58,7 @@ BT::NodeStatus BT::TestNode::onStart() return NodeStatus::RUNNING; } -BT::NodeStatus BT::TestNode::onRunning() +NodeStatus TestNode::onRunning() { if(_completed) { @@ -59,16 +67,18 @@ BT::NodeStatus BT::TestNode::onRunning() return NodeStatus::RUNNING; } -void BT::TestNode::onHalted() +void TestNode::onHalted() { _timer.cancelAll(); } -BT::NodeStatus BT::TestNode::onCompleted() +NodeStatus TestNode::onCompleted() { Ast::Environment env = { config().blackboard, config().enums }; - auto status = _test_config.complete_func(); + auto status = + (_config->complete_func) ? _config->complete_func() : _config->return_status; + if(status == NodeStatus::SUCCESS && _success_executor) { _success_executor(env); @@ -83,3 +93,5 @@ BT::NodeStatus BT::TestNode::onCompleted() } return status; } + +} // namespace BT diff --git a/src/blackboard.cpp b/src/blackboard.cpp index 21a62c9d8..3b1ba9844 100644 --- a/src/blackboard.cpp +++ b/src/blackboard.cpp @@ -176,7 +176,7 @@ void Blackboard::cloneInto(Blackboard& dst) const auto it = dst_storage.find(src_key); if(it != dst_storage.end()) { - // overwite + // overwrite auto& dst_entry = it->second; dst_entry->string_converter = src_entry->string_converter; dst_entry->value = src_entry->value; diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index 36cf029b1..63f6652ef 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -263,12 +263,22 @@ std::unique_ptr BehaviorTreeFactory::instantiateTreeNode( } else if(const auto test_config = std::get_if(&rule)) { - // second case, the varian is a TestNodeConfig - auto test_node = new TestNode(name, config, *test_config); - node.reset(test_node); + node = std::make_unique(name, config, + std::make_shared(*test_config)); substituted = true; break; } + else if(const auto test_config = + std::get_if>(&rule)) + { + node = std::make_unique(name, config, *test_config); + substituted = true; + break; + } + else + { + throw LogicError("Substitution rule is not a string or a TestNodeConfig"); + } } } diff --git a/src/controls/fallback_node.cpp b/src/controls/fallback_node.cpp index 1654e3f2a..4b8fb3afb 100644 --- a/src/controls/fallback_node.cpp +++ b/src/controls/fallback_node.cpp @@ -28,7 +28,7 @@ NodeStatus FallbackNode::tick() { const size_t children_count = children_nodes_.size(); - if(status() == NodeStatus::IDLE) + if(!isStatusActive(status())) { skipped_count_ = 0; } @@ -55,7 +55,7 @@ NodeStatus FallbackNode::tick() case NodeStatus::FAILURE: { current_child_idx_++; // Return the execution flow if the child is async, - // to make this interruptable. + // to make this interruptible. if(asynch_ && requiresWakeUp() && prev_status == NodeStatus::IDLE && current_child_idx_ < children_count) { @@ -77,19 +77,22 @@ NodeStatus FallbackNode::tick() } // end while loop // The entire while loop completed. This means that all the children returned FAILURE. + const bool all_children_skipped = (skipped_count_ == children_count); if(current_child_idx_ == children_count) { resetChildren(); current_child_idx_ = 0; + skipped_count_ = 0; } // Skip if ALL the nodes have been skipped - return (skipped_count_ == children_count) ? NodeStatus::SKIPPED : NodeStatus::FAILURE; + return (all_children_skipped) ? NodeStatus::SKIPPED : NodeStatus::FAILURE; } void FallbackNode::halt() { current_child_idx_ = 0; + skipped_count_ = 0; ControlNode::halt(); } diff --git a/src/controls/sequence_node.cpp b/src/controls/sequence_node.cpp index 708c18d18..483e66c5b 100644 --- a/src/controls/sequence_node.cpp +++ b/src/controls/sequence_node.cpp @@ -27,6 +27,7 @@ SequenceNode::SequenceNode(const std::string& name, bool make_async) void SequenceNode::halt() { current_child_idx_ = 0; + skipped_count_ = 0; ControlNode::halt(); } @@ -34,7 +35,7 @@ NodeStatus SequenceNode::tick() { const size_t children_count = children_nodes_.size(); - if(status() == NodeStatus::IDLE) + if(!isStatusActive(status())) { skipped_count_ = 0; } @@ -62,7 +63,7 @@ NodeStatus SequenceNode::tick() case NodeStatus::SUCCESS: { current_child_idx_++; // Return the execution flow if the child is async, - // to make this interruptable. + // to make this interruptible. if(asynch_ && requiresWakeUp() && prev_status == NodeStatus::IDLE && current_child_idx_ < children_count) { @@ -86,13 +87,15 @@ NodeStatus SequenceNode::tick() } // end while loop // The entire while loop completed. This means that all the children returned SUCCESS. + const bool all_children_skipped = (skipped_count_ == children_count); if(current_child_idx_ == children_count) { resetChildren(); current_child_idx_ = 0; + skipped_count_ = 0; } // Skip if ALL the nodes have been skipped - return (skipped_count_ == children_count) ? NodeStatus::SKIPPED : NodeStatus::SUCCESS; + return (all_children_skipped) ? NodeStatus::SKIPPED : NodeStatus::SUCCESS; } } // namespace BT diff --git a/src/controls/sequence_with_memory_node.cpp b/src/controls/sequence_with_memory_node.cpp index f47c798b6..3031de9b2 100644 --- a/src/controls/sequence_with_memory_node.cpp +++ b/src/controls/sequence_with_memory_node.cpp @@ -25,7 +25,7 @@ NodeStatus SequenceWithMemory::tick() { const size_t children_count = children_nodes_.size(); - if(status() == NodeStatus::IDLE) + if(!isStatusActive(status())) { skipped_count_ = 0; } @@ -55,7 +55,7 @@ NodeStatus SequenceWithMemory::tick() case NodeStatus::SUCCESS: { current_child_idx_++; // Return the execution flow if the child is async, - // to make this interruptable. + // to make this interruptible. if(requiresWakeUp() && prev_status == NodeStatus::IDLE && current_child_idx_ < children_count) { @@ -79,13 +79,15 @@ NodeStatus SequenceWithMemory::tick() } // end while loop // The entire while loop completed. This means that all the children returned SUCCESS. + const bool all_children_skipped = (skipped_count_ == children_count); if(current_child_idx_ == children_count) { resetChildren(); current_child_idx_ = 0; + skipped_count_ = 0; } // Skip if ALL the nodes have been skipped - return (skipped_count_ == children_count) ? NodeStatus::SKIPPED : NodeStatus::SUCCESS; + return (all_children_skipped) ? NodeStatus::SKIPPED : NodeStatus::SUCCESS; } void SequenceWithMemory::halt() diff --git a/src/decorators/repeat_node.cpp b/src/decorators/repeat_node.cpp index d6b6f7c68..9ea023ee1 100644 --- a/src/decorators/repeat_node.cpp +++ b/src/decorators/repeat_node.cpp @@ -59,7 +59,7 @@ NodeStatus RepeatNode::tick() resetChild(); // Return the execution flow if the child is async, - // to make this interruptable. + // to make this interruptible. if(requiresWakeUp() && prev_status == NodeStatus::IDLE && do_loop) { emitWakeUpSignal(); diff --git a/src/decorators/retry_node.cpp b/src/decorators/retry_node.cpp index 1a40247dd..d8c689c78 100644 --- a/src/decorators/retry_node.cpp +++ b/src/decorators/retry_node.cpp @@ -74,7 +74,7 @@ NodeStatus RetryNode::tick() resetChild(); // Return the execution flow if the child is async, - // to make this interruptable. + // to make this interruptible. if(requiresWakeUp() && prev_status == NodeStatus::IDLE && do_loop) { emitWakeUpSignal(); diff --git a/src/loggers/zmq.hpp b/src/loggers/zmq.hpp index e029b88d8..26c2c6f42 100644 --- a/src/loggers/zmq.hpp +++ b/src/loggers/zmq.hpp @@ -439,7 +439,7 @@ class message_t // 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. + // it would be preferred over this function and break compatibility. template ::value>::type> ZMQ_DEPRECATED("from 4.7.0, use constructors taking iterators, (pointer, size) " diff --git a/tests/gtest_async_action_node.cpp b/tests/gtest_async_action_node.cpp index 7e54384d0..fd19f05ee 100644 --- a/tests/gtest_async_action_node.cpp +++ b/tests/gtest_async_action_node.cpp @@ -70,7 +70,7 @@ TEST_P(NodeStatusFixture, normal_routine) TEST_F(MockedThreadedActionFixture, no_halt) { // Test verifies that halt returns immediately, if the node is idle. It - // further checks if the halt-flag is resetted correctly. + // further checks if the halt-flag is reset correctly. sn.halt(); ASSERT_TRUE(sn.isHaltRequested()); diff --git a/tests/gtest_coroutines.cpp b/tests/gtest_coroutines.cpp index ea937fbe1..3854bf40a 100644 --- a/tests/gtest_coroutines.cpp +++ b/tests/gtest_coroutines.cpp @@ -97,10 +97,10 @@ TEST(CoroTest, do_action) node.will_fail_ = true; EXPECT_EQ(BT::NodeStatus::FAILURE, executeWhileRunning(node)) << "Should execute again " - "and retun failure"; + "and return failure"; EXPECT_FALSE(node.wasHalted()); - EXPECT_EQ(BT::NodeStatus::FAILURE, executeWhileRunning(node)) << "Shoudln't fail " + EXPECT_EQ(BT::NodeStatus::FAILURE, executeWhileRunning(node)) << "Shouldn't fail " "because we set " "status to idle"; EXPECT_FALSE(node.wasHalted()); diff --git a/tests/gtest_decorator.cpp b/tests/gtest_decorator.cpp index df72429c6..32abcff10 100644 --- a/tests/gtest_decorator.cpp +++ b/tests/gtest_decorator.cpp @@ -213,8 +213,8 @@ TEST(Decorator, RunOnce) NodeStatus status = tree.tickWhileRunning(); ASSERT_EQ(status, NodeStatus::SUCCESS); } - // counters[0] contains the number ot times TestA was ticked + // counters[0] contains the number of times TestA was ticked ASSERT_EQ(counters[0], 1); - // counters[1] contains the number ot times TestB was ticked + // counters[1] contains the number of times TestB was ticked ASSERT_EQ(counters[1], 5); } diff --git a/tests/gtest_ports.cpp b/tests/gtest_ports.cpp index c694fffa9..2a33178d3 100644 --- a/tests/gtest_ports.cpp +++ b/tests/gtest_ports.cpp @@ -455,7 +455,7 @@ class GetAny : public SyncActionNode NodeStatus tick() override { - // case 1: the port is Any, but we can cast dirrectly to string + // case 1: the port is Any, but we can cast directly to string auto res_str = getInput("val_str"); // case 2: the port is Any, and we retrieve an Any (to be casted later) auto res_int = getInput("val_int"); diff --git a/tests/gtest_preconditions.cpp b/tests/gtest_preconditions.cpp index 7a3df89f7..620b9a061 100644 --- a/tests/gtest_preconditions.cpp +++ b/tests/gtest_preconditions.cpp @@ -107,6 +107,111 @@ TEST(PreconditionsDecorator, StringEquals) ASSERT_EQ(counters[1], 1); } +class KeepRunning : public BT::StatefulActionNode +{ +public: + KeepRunning(const std::string& name, const BT::NodeConfig& config) + : BT::StatefulActionNode(name, config) + {} + + static BT::PortsList providedPorts() + { + return {}; + } + + BT::NodeStatus onStart() override + { + return BT::NodeStatus::RUNNING; + } + + BT::NodeStatus onRunning() override + { + return BT::NodeStatus::RUNNING; + } + + void onHalted() override + { + std::cout << "Node halted\n"; + } +}; + +TEST(PreconditionsDecorator, ChecksConditionOnce) +{ + BehaviorTreeFactory factory; + factory.registerNodeType("KeepRunning"); + + const std::string xml_text = R"( + + + + +