Skip to content

Commit 3c4cc59

Browse files
committed
blackboard refactoring to fix buggy _autoremap
1 parent 8717081 commit 3c4cc59

File tree

6 files changed

+96
-115
lines changed

6 files changed

+96
-115
lines changed

include/behaviortree_cpp/blackboard.h

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -61,24 +61,9 @@ class Blackboard
6161

6262
virtual ~Blackboard() = default;
6363

64-
[[nodiscard]] const Entry* getEntry(const std::string& key) const
65-
{
66-
std::unique_lock<std::mutex> lock(mutex_);
67-
// search first if this port was remapped
68-
if(!internal_to_external_.empty())
69-
{
70-
if (auto parent = parent_bb_.lock())
71-
{
72-
auto remapping_it = internal_to_external_.find(key);
73-
if (remapping_it != internal_to_external_.end())
74-
{
75-
return parent->getEntry(remapping_it->second);
76-
}
77-
}
78-
}
79-
auto it = storage_.find(key);
80-
return (it == storage_.end()) ? nullptr : it->second.get();
81-
}
64+
void enableAutoRemapping(bool remapping);
65+
66+
[[nodiscard]] const Entry* getEntry(const std::string& key) const;
8267

8368
[[nodiscard]] Entry* getEntry(const std::string& key)
8469
{
@@ -158,30 +143,18 @@ class Blackboard
158143
{
159144
std::unique_lock lock(mutex_);
160145

161-
// search first if this port was remapped.
162-
// Change the parent_bb_ in that case
163-
if(!internal_to_external_.empty())
164-
{
165-
auto remapping_it = internal_to_external_.find(key);
166-
if (remapping_it != internal_to_external_.end())
167-
{
168-
const auto& remapped_key = remapping_it->second;
169-
if (auto parent = parent_bb_.lock())
170-
{
171-
parent->set(remapped_key, value);
172-
return;
173-
}
174-
}
175-
}
176-
177146
// check local storage
178147
auto it = storage_.find(key);
179148
if (it == storage_.end())
180149
{
181150
// create a new entry
182151
Any new_value(value);
183152
PortInfo new_port(PortDirection::INOUT, new_value.type(), {});
184-
storage_.emplace(key, std::make_unique<Entry>(std::move(new_value), new_port));
153+
lock.unlock();
154+
auto entry = createEntryImpl(key, new_port);
155+
lock.lock();
156+
storage_.insert( {key, entry} );
157+
entry->value = new_value;
185158
}
186159
else
187160
{
@@ -236,15 +209,13 @@ class Blackboard
236209
}
237210
}
238211

239-
void setPortInfo(const std::string &key, const PortInfo& info);
240-
241-
[[nodiscard]] const PortInfo* portInfo(const std::string& key);
212+
[[nodiscard]] const PortInfo* portInfo(const std::string& key);
242213

243214
void addSubtreeRemapping(StringView internal, StringView external);
244215

245216
void debugMessage() const;
246217

247-
[[nodiscard]] std::vector<StringView> getKeys(bool include_remapped = true) const;
218+
[[nodiscard]] std::vector<StringView> getKeys() const;
248219

249220
void clear()
250221
{
@@ -258,13 +229,21 @@ class Blackboard
258229
return entry_mutex_;
259230
}
260231

232+
void createEntry(const std::string& key, const PortInfo& info)
233+
{
234+
createEntryImpl(key, info);
235+
}
236+
261237
private:
262238
mutable std::mutex mutex_;
263239
mutable std::recursive_mutex entry_mutex_;
264-
std::unordered_map<std::string, std::unique_ptr<Entry>> storage_;
240+
std::unordered_map<std::string, std::shared_ptr<Entry>> storage_;
265241
std::weak_ptr<Blackboard> parent_bb_;
266242
std::unordered_map<std::string, std::string> internal_to_external_;
267243

244+
std::shared_ptr<Entry> createEntryImpl(const std::string &key, const PortInfo& info);
245+
246+
bool autoremapping_ = false;
268247
};
269248

270249
} // namespace BT

include/behaviortree_cpp/scripting/operators.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,17 @@ struct ExprAssignment : ExprBase
365365
// variable doesn't exist, create it if using operator assign_create
366366
if(op == assign_create)
367367
{
368-
env.vars->setPortInfo(key, PortInfo());
368+
env.vars->createEntry(key, PortInfo());
369369
entry = env.vars->getEntry(key);
370370
}
371371
else {
372372
// fail otherwise
373-
throw std::runtime_error("Can't create a new variable");
373+
char buffer[1014];
374+
sprintf(buffer,
375+
"The blackboard entry [%s] doesn't exist, yet.\n"
376+
"If you want to create a new one, use the operator "
377+
"[:=] instead of [=]", key.c_str());
378+
throw std::runtime_error(buffer);
374379
}
375380
}
376381
auto value = rhs->evaluate(env);

src/blackboard.cpp

Lines changed: 60 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,25 @@
22

33
namespace BT
44
{
5-
void Blackboard::setPortInfo(const std::string& key, const PortInfo& info)
6-
{
7-
std::unique_lock<std::mutex> lock(mutex_);
85

9-
if (auto parent = parent_bb_.lock())
10-
{
11-
auto remapping_it = internal_to_external_.find(key);
12-
if (remapping_it != internal_to_external_.end())
13-
{
14-
parent->setPortInfo(remapping_it->second, info);
15-
return;
16-
}
17-
}
6+
void Blackboard::enableAutoRemapping(bool remapping)
7+
{
8+
autoremapping_ = remapping;
9+
}
1810

11+
const Blackboard::Entry *Blackboard::getEntry(const std::string &key) const
12+
{
13+
std::unique_lock<std::mutex> lock(mutex_);
1914
auto it = storage_.find(key);
20-
if (it == storage_.end())
21-
{
22-
storage_.emplace(key, std::make_unique<Entry>(info));
23-
}
24-
else
25-
{
26-
auto old_type = it->second->port_info.type();
27-
if (old_type != info.type())
28-
{
29-
throw LogicError("Blackboard::set() failed: once declared, the type of a port "
30-
"shall not change. Declared type [",
31-
BT::demangle(old_type), "] != current type [",
32-
BT::demangle(info.type()), "]");
33-
}
34-
}
15+
return (it == storage_.end()) ? nullptr : it->second.get();
3516
}
3617

3718
const PortInfo* Blackboard::portInfo(const std::string& key)
3819
{
3920
std::unique_lock<std::mutex> lock(mutex_);
4021

41-
if (auto parent = parent_bb_.lock())
42-
{
43-
auto remapping_it = internal_to_external_.find(key);
44-
if (remapping_it != internal_to_external_.end())
45-
{
46-
return parent->portInfo(remapping_it->second);
47-
}
48-
}
49-
5022
auto it = storage_.find(key);
51-
if (it == storage_.end())
52-
{
53-
return nullptr;
54-
}
55-
return &(it->second->port_info);
23+
return (it == storage_.end()) ? nullptr : &(it->second->port_info);
5624
}
5725

5826
void Blackboard::addSubtreeRemapping(StringView internal, StringView external)
@@ -82,27 +50,70 @@ void Blackboard::debugMessage() const
8250
}
8351
}
8452

85-
std::vector<StringView> Blackboard::getKeys(bool include_remapped) const
53+
std::vector<StringView> Blackboard::getKeys() const
8654
{
87-
const size_t N = storage_.size() + (include_remapped ? internal_to_external_.size() : 0 );
88-
if (N == 0)
55+
if (storage_.empty())
8956
{
9057
return {};
9158
}
9259
std::vector<StringView> out;
93-
out.reserve(N);
60+
out.reserve(storage_.size());
9461
for (const auto& entry_it : storage_)
9562
{
9663
out.push_back(entry_it.first);
9764
}
98-
if(include_remapped)
65+
return out;
66+
}
67+
68+
std::shared_ptr<Blackboard::Entry>
69+
Blackboard::createEntryImpl(const std::string& key, const PortInfo& info)
70+
{
71+
std::unique_lock<std::mutex> lock(mutex_);
72+
// This function might be called recursively, when we do remapping, because we move
73+
// to the top scope to find already existing entries
74+
75+
// search if exists already
76+
auto storage_it = storage_.find(key);
77+
if(storage_it != storage_.end())
9978
{
100-
for (const auto& [key, remapped] : internal_to_external_)
79+
const auto old_type = storage_it->second->port_info.type();
80+
if (old_type != info.type() &&
81+
old_type != typeid(BT::PortInfo::AnyTypeAllowed) &&
82+
info.type() != typeid(BT::PortInfo::AnyTypeAllowed))
10183
{
102-
out.push_back(key);
84+
throw LogicError("Blackboard: once declared, the type of a port "
85+
"shall not change. Previously declared type [",
86+
BT::demangle(old_type), "] != new type [",
87+
BT::demangle(info.type()), "]");
10388
}
89+
return storage_it->second;
10490
}
105-
return out;
91+
92+
std::shared_ptr<Entry> entry;
93+
94+
// manual remapping first
95+
auto remapping_it = internal_to_external_.find(key);
96+
if (remapping_it != internal_to_external_.end())
97+
{
98+
const auto& remapped_key = remapping_it->second;
99+
if (auto parent = parent_bb_.lock())
100+
{
101+
entry = parent->createEntryImpl(remapped_key, info);
102+
}
103+
}
104+
else if(autoremapping_)
105+
{
106+
if (auto parent = parent_bb_.lock())
107+
{
108+
entry = parent->createEntryImpl(key, info);
109+
}
110+
}
111+
else // not remapped, nor found. Create locally.
112+
{
113+
entry = std::make_shared<Entry>(info);
114+
}
115+
storage_.insert( {key, entry} );
116+
return entry;
106117
}
107118

108119
} // namespace BT

src/xml_parsing.cpp

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ TreeNode::Ptr XMLParser::Pimpl::createNodeFromXML(const XMLElement* element,
646646
else
647647
{
648648
// not found, insert for the first time.
649-
blackboard->setPortInfo(port_key, port_info);
649+
blackboard->createEntry(port_key, port_info);
650650
}
651651
}
652652
}
@@ -743,16 +743,15 @@ void BT::XMLParser::Pimpl::recursivelyCreateSubtree(
743743

744744
std::set<StringView> mapped_keys;
745745

746-
bool do_autoremap = false;
747-
748746
for (auto attr = element->FirstAttribute(); attr != nullptr; attr = attr->Next())
749747
{
750748
const char* attr_name = attr->Name();
751749
const char* attr_value = attr->Value();
752750

753751
if (StrEqual(attr_name, "_autoremap"))
754752
{
755-
do_autoremap = convertFromString<bool>(attr_value);
753+
const bool do_autoremap = convertFromString<bool>(attr_value);
754+
new_bb->enableAutoRemapping(do_autoremap);
756755
continue;
757756
}
758757
if (!IsAllowedPortName(attr->Name()))
@@ -775,20 +774,6 @@ void BT::XMLParser::Pimpl::recursivelyCreateSubtree(
775774
}
776775
}
777776

778-
if (do_autoremap)
779-
{
780-
std::vector<std::string> remapped_ports;
781-
auto new_root_element = tree_roots[node->name()]->FirstChildElement();
782-
783-
getPortsRecursively(new_root_element, remapped_ports);
784-
for (const auto& port : remapped_ports)
785-
{
786-
if (mapped_keys.count(port) == 0)
787-
{
788-
new_bb->addSubtreeRemapping(port, port);
789-
}
790-
}
791-
}
792777
std::string subtree_ID = element->Attribute("ID");
793778
std::string subtree_name = subtree->instance_name;
794779
if(!subtree_name.empty()) {

tests/gtest_factory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ static const char* xml_ports_subtree = R"(
189189
<Sequence>
190190
<SaySomething message="{hello_msg}" />
191191
<SaySomething message="{bye_msg}" />
192-
<Script code=" output='done!' " />
192+
<Script code=" output:='done!' " />
193193
</Sequence>
194194
</BehaviorTree>
195195

tests/gtest_subtree.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,22 +330,22 @@ TEST(SubTree, StringConversions_Issue530)
330330
TEST(SubTree, SubtreeIssue563)
331331
{
332332
static const char* xml_text = R"(
333-
<root main_tree_to_execute="Tree1">
333+
<root BTCPP_format="4" >
334334
335335
<BehaviorTree ID="Tree1">
336336
<Sequence>
337337
<SetBlackboard output_key="the_message" value="hello world"/>
338-
<SubTreePlus ID="Tree2" __autoremap="true"/>
338+
<SubTree ID="Tree2" _autoremap="true"/>
339339
<SaySomething message="{reply}" />
340340
</Sequence>
341341
</BehaviorTree>
342342
343343
<BehaviorTree ID="Tree2">
344-
<SubTreePlus ID="Tree3" __autoremap="true"/>
344+
<SubTree ID="Tree3" _autoremap="true"/>
345345
</BehaviorTree>
346346
347347
<BehaviorTree ID="Tree3">
348-
<SubTreePlus ID="Talker" __autoremap="true"/>
348+
<SubTree ID="Talker" _autoremap="true"/>
349349
</BehaviorTree>
350350
351351
<BehaviorTree ID="Talker">
@@ -359,8 +359,9 @@ TEST(SubTree, SubtreeIssue563)
359359

360360
BehaviorTreeFactory factory;
361361
factory.registerNodeType<DummyNodes::SaySomething>("SaySomething");
362+
factory.registerBehaviorTreeFromText(xml_text);
363+
Tree tree = factory.createTree("Tree1");
362364

363-
Tree tree = factory.createTreeFromText(xml_text);
364365
auto ret = tree.tickOnce();
365366
ASSERT_EQ(ret, NodeStatus::SUCCESS);
366367

0 commit comments

Comments
 (0)