Skip to content

Commit ecd41b1

Browse files
committed
bug fixes related to sequence_id and unit tests added
1 parent fc041fe commit ecd41b1

File tree

10 files changed

+285
-21
lines changed

10 files changed

+285
-21
lines changed

CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,15 @@ list(APPEND BT_SOURCE
9797

9898
src/actions/test_node.cpp
9999
src/actions/sleep_node.cpp
100+
src/actions/updated_action.cpp
100101

101102
src/decorators/delay_node.cpp
102-
src/decorators/entry_updated_nodes.cpp
103103
src/decorators/inverter_node.cpp
104104
src/decorators/repeat_node.cpp
105105
src/decorators/retry_node.cpp
106106
src/decorators/subtree_node.cpp
107107
src/decorators/timeout_node.cpp
108+
src/decorators/updated_decorator.cpp
108109

109110
src/controls/if_then_else_node.cpp
110111
src/controls/fallback_node.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* Copyright (C) 2024 Davide Faconti - All Rights Reserved
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4+
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5+
* 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:
6+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* 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,
10+
* 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.
11+
*/
12+
13+
#pragma once
14+
15+
#include "behaviortree_cpp/action_node.h"
16+
17+
namespace BT
18+
{
19+
/**
20+
* @brief The EntryUpdatedAction checks the Timestamp in an entry
21+
* to determine if the value was updated since the last time.
22+
*
23+
* SUCCESS if it was updated, since the last time it was checked,
24+
* FAILURE if it doesn't exist or was not updated.
25+
*/
26+
class EntryUpdatedAction : public SyncActionNode
27+
{
28+
public:
29+
EntryUpdatedAction(const std::string& name, const NodeConfig& config);
30+
31+
~EntryUpdatedAction() override = default;
32+
33+
static PortsList providedPorts()
34+
{
35+
return { InputPort<BT::Any>("entry", "Entry to check") };
36+
}
37+
38+
private:
39+
uint64_t sequence_id_ = 0;
40+
std::string entry_key_;
41+
42+
NodeStatus tick() override;
43+
};
44+
45+
} // namespace BT

include/behaviortree_cpp/behavior_tree.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#include "behaviortree_cpp/decorators/run_once_node.h"
3434
#include "behaviortree_cpp/decorators/subtree_node.h"
3535
#include "behaviortree_cpp/decorators/loop_node.h"
36-
#include "behaviortree_cpp/decorators/entry_updated_nodes.h"
36+
#include "behaviortree_cpp/decorators/updated_decorator.h"
3737

3838
#include "behaviortree_cpp/actions/always_success_node.h"
3939
#include "behaviortree_cpp/actions/always_failure_node.h"
@@ -43,6 +43,7 @@
4343
#include "behaviortree_cpp/actions/test_node.h"
4444
#include "behaviortree_cpp/actions/sleep_node.h"
4545
#include "behaviortree_cpp/actions/unset_blackboard_node.h"
46+
#include "behaviortree_cpp/actions/updated_action.h"
4647

4748
#include "behaviortree_cpp/decorators/force_success_node.h"
4849
#include "behaviortree_cpp/decorators/force_failure_node.h"

include/behaviortree_cpp/decorators/entry_updated_nodes.h renamed to include/behaviortree_cpp/decorators/updated_decorator.h

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@
1717
namespace BT
1818
{
1919
/**
20-
* @brief The EntryUpdatedNode checks the Timestamp in an entry
20+
* @brief The EntryUpdatedDecorator checks the Timestamp in an entry
2121
* to determine if the value was updated since the last time (true,
2222
* the first time).
2323
*
2424
* If it is, the child will be executed, otherwise [if_not_updated] value is returned.
2525
*/
26-
class EntryUpdatedNode : public DecoratorNode
26+
class EntryUpdatedDecorator : public DecoratorNode
2727
{
2828
public:
29-
EntryUpdatedNode(const std::string& name, const NodeConfig& config,
30-
NodeStatus if_not_updated);
29+
EntryUpdatedDecorator(const std::string& name, const NodeConfig& config,
30+
NodeStatus if_not_updated);
3131

32-
~EntryUpdatedNode() override = default;
32+
~EntryUpdatedDecorator() override = default;
3333

3434
static PortsList providedPorts()
3535
{
3636
return { InputPort<BT::Any>("entry", "Entry to check") };
3737
}
3838

3939
private:
40-
int64_t sequence_id_ = -1;
40+
uint64_t sequence_id_ = 0;
4141
std::string entry_key_;
4242
bool still_executing_child_ = false;
4343
NodeStatus if_not_updated_;

include/behaviortree_cpp/scripting/operators.hpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ struct ExprAssignment : ExprBase
529529
auto value = rhs->evaluate(env);
530530

531531
std::scoped_lock lock(entry->entry_mutex);
532-
auto dst_ptr = &entry->value;
532+
auto* dst_ptr = &entry->value;
533533

534534
auto errorPrefix = [dst_ptr, &key]() {
535535
return StrCat("Error assigning a value to entry [", key, "] with type [",
@@ -588,6 +588,8 @@ struct ExprAssignment : ExprBase
588588
throw RuntimeError(msg);
589589
}
590590
}
591+
entry->sequence_id++;
592+
entry->stamp = std::chrono::steady_clock::now().time_since_epoch();
591593
return *dst_ptr;
592594
}
593595

@@ -642,6 +644,8 @@ struct ExprAssignment : ExprBase
642644
}
643645

644646
temp_variable.copyInto(*dst_ptr);
647+
entry->sequence_id++;
648+
entry->stamp = std::chrono::steady_clock::now().time_since_epoch();
645649
return *dst_ptr;
646650
}
647651
};

src/actions/updated_action.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* Copyright (C) 2024 Davide Faconti - All Rights Reserved
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
4+
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
5+
* 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:
6+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
9+
* 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,
10+
* 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.
11+
*/
12+
13+
#include "behaviortree_cpp/actions/updated_action.h"
14+
#include "behaviortree_cpp/bt_factory.h"
15+
16+
namespace BT
17+
{
18+
19+
EntryUpdatedAction::EntryUpdatedAction(const std::string& name, const NodeConfig& config)
20+
: SyncActionNode(name, config)
21+
{
22+
auto it = config.input_ports.find("entry");
23+
if(it == config.input_ports.end() || it->second.empty())
24+
{
25+
throw LogicError("Missing port 'entry' in ", name);
26+
}
27+
const auto entry_str = it->second;
28+
StringView stripped_key;
29+
if(isBlackboardPointer(entry_str, &stripped_key))
30+
{
31+
entry_key_ = stripped_key;
32+
}
33+
else
34+
{
35+
entry_key_ = entry_str;
36+
}
37+
}
38+
39+
NodeStatus EntryUpdatedAction::tick()
40+
{
41+
if(auto entry = config().blackboard->getEntry(entry_key_))
42+
{
43+
std::unique_lock lk(entry->entry_mutex);
44+
const uint64_t current_id = entry->sequence_id;
45+
const uint64_t previous_id = sequence_id_;
46+
sequence_id_ = current_id;
47+
/*
48+
uint64_t previous_id = 0;
49+
auto& previous_id_registry = details::GlobalSequenceRegistry();
50+
51+
// find the previous id in the registry.
52+
auto it = previous_id_registry.find(entry.get());
53+
if(it != previous_id_registry.end())
54+
{
55+
previous_id = it->second;
56+
}
57+
if(previous_id != current_id)
58+
{
59+
previous_id_registry[entry.get()] = current_id;
60+
}*/
61+
return (previous_id != current_id) ? NodeStatus::SUCCESS : NodeStatus::FAILURE;
62+
}
63+
else
64+
{
65+
return NodeStatus::FAILURE;
66+
}
67+
}
68+
69+
} // namespace BT

src/bt_factory.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ BehaviorTreeFactory::BehaviorTreeFactory() : _p(new PImpl)
9595
registerNodeType<LoopNode<double>>("LoopDouble");
9696
registerNodeType<LoopNode<std::string>>("LoopString");
9797

98-
registerNodeType<EntryUpdatedNode>("SkipUnlessUpdated", NodeStatus::SKIPPED);
99-
registerNodeType<EntryUpdatedNode>("WaitValueUpdate", NodeStatus::RUNNING);
98+
registerNodeType<EntryUpdatedAction>("WasEntryUpdated");
99+
registerNodeType<EntryUpdatedDecorator>("SkipUnlessUpdated", NodeStatus::SKIPPED);
100+
registerNodeType<EntryUpdatedDecorator>("WaitValueUpdate", NodeStatus::RUNNING);
100101

101102
for(const auto& it : _p->builders)
102103
{

src/decorators/entry_updated_nodes.cpp renamed to src/decorators/updated_decorator.cpp

+23-10
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,23 @@
1010
* 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.
1111
*/
1212

13-
#include "behaviortree_cpp/decorators/entry_updated_nodes.h"
13+
#include "behaviortree_cpp/decorators/updated_decorator.h"
14+
#include "behaviortree_cpp/bt_factory.h"
1415

1516
namespace BT
1617
{
1718

18-
EntryUpdatedNode::EntryUpdatedNode(const std::string& name, const NodeConfig& config,
19-
NodeStatus if_not_updated)
19+
EntryUpdatedDecorator::EntryUpdatedDecorator(const std::string& name,
20+
const NodeConfig& config,
21+
NodeStatus if_not_updated)
2022
: DecoratorNode(name, config), if_not_updated_(if_not_updated)
2123
{
22-
const auto entry_str = config.input_ports.at("entry");
24+
auto it = config.input_ports.find("entry");
25+
if(it == config.input_ports.end() || it->second.empty())
26+
{
27+
throw LogicError("Missing port 'entry' in ", name);
28+
}
29+
const auto entry_str = it->second;
2330
StringView stripped_key;
2431
if(isBlackboardPointer(entry_str, &stripped_key))
2532
{
@@ -31,7 +38,7 @@ EntryUpdatedNode::EntryUpdatedNode(const std::string& name, const NodeConfig& co
3138
}
3239
}
3340

34-
NodeStatus EntryUpdatedNode::tick()
41+
NodeStatus EntryUpdatedDecorator::tick()
3542
{
3643
// continue executing an asynchronous child
3744
if(still_executing_child_)
@@ -41,23 +48,29 @@ NodeStatus EntryUpdatedNode::tick()
4148
return status;
4249
}
4350

51+
if(auto entry = config().blackboard->getEntry(entry_key_))
4452
{
45-
auto entry = config().blackboard->getEntry(entry_key_);
4653
std::unique_lock lk(entry->entry_mutex);
47-
auto seq = static_cast<int64_t>(entry->sequence_id);
48-
if(seq == sequence_id_)
54+
const uint64_t current_id = entry->sequence_id;
55+
const uint64_t previous_id = sequence_id_;
56+
sequence_id_ = current_id;
57+
58+
if(previous_id == current_id)
4959
{
5060
return if_not_updated_;
5161
}
52-
sequence_id_ = seq;
62+
}
63+
else
64+
{
65+
return if_not_updated_;
5366
}
5467

5568
auto status = child()->executeTick();
5669
still_executing_child_ = (status == NodeStatus::RUNNING);
5770
return status;
5871
}
5972

60-
void EntryUpdatedNode::halt()
73+
void EntryUpdatedDecorator::halt()
6174
{
6275
still_executing_child_ = false;
6376
}

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ set(BT_TESTS
2626
gtest_subtree.cpp
2727
gtest_switch.cpp
2828
gtest_tree.cpp
29+
gtest_updates.cpp
2930
gtest_wakeup.cpp
3031

3132
script_parser_test.cpp

0 commit comments

Comments
 (0)