Skip to content

Commit b182f5d

Browse files
authored
feat: add a class to mange transform frames and entity (#36)
Signed-off-by: ktro2828 <[email protected]>
1 parent 9bd77b3 commit b182f5d

20 files changed

+628
-53
lines changed

Doxyfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
PROJECT_NAME = AWViz ROS C++ API
1+
PROJECT_NAME = AWViz-ROS C++ API Reference
22

33
DOXYFILE_ENCODING = UTF-8
44

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,11 @@ awviz
3535

3636
Some ROS messages are already covered by built-in plugins defined in `awviz_plugin`.
3737
For the detail of supported ROS messages and plugin customization, please refer to [docs/PLUGIN.md](./docs/PLUGIN.md).
38+
39+
## Contribution
40+
41+
Please refer to [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md).
42+
43+
## API Reference
44+
45+
Please refer to [AWViz-ROS C++ API Reference](https://ktro2828.github.io/awviz-ros/).

awviz_common/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ target_include_directories(${PROJECT_NAME} PUBLIC
3838
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
3939
$<INSTALL_INTERFACE:include>
4040
)
41-
target_link_libraries(${PROJECT_NAME} rerun_sdk)
41+
target_link_libraries(${PROJECT_NAME} rerun_sdk yaml-cpp)
4242
# NOTE: ament_target_dependencies ensures that all dependency include directories are ordered correctly when using an overlay workspace
43-
ament_target_dependencies(${PROJECT_NAME} rclcpp pluginlib)
43+
ament_target_dependencies(${PROJECT_NAME} rclcpp pluginlib tf2 tf2_ros)
4444

4545
# -------- install targets --------
4646
install(

awviz_common/awviz_commonConfig.cmake.in

+3
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ include(CMakeFindDependencyMacro)
44

55
find_dependency(rclcpp)
66
find_dependency(pluginlib)
7+
find_dependency(tf2)
8+
find_dependency(tf2_ros)
9+
find_dependency(yaml-cpp)
710

811
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")

awviz_common/include/awviz_common/display.hpp

+17-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <memory>
2626
#include <string>
27+
#include <unordered_map>
2728

2829
namespace awviz_common
2930
{
@@ -50,7 +51,9 @@ class Display
5051
* @param topic Name of topic.
5152
* @param entity Entity path of the record.
5253
*/
53-
virtual void setStatus(const std::string & topic, const std::string & entity) = 0;
54+
virtual void setStatus(
55+
const std::string & topic,
56+
const std::shared_ptr<std::unordered_map<std::string, std::string>> entity_roots) = 0;
5457

5558
/**
5659
* @brief Start to display.
@@ -66,12 +69,12 @@ class Display
6669
* @brief Return true if the initialization is completed.
6770
* @return bool Return the value of the private member named `is_initialized_`.
6871
*/
69-
bool isInitialized() const { return is_initialized_; }
72+
virtual bool isInitialized() const { return is_initialized_; }
7073

7174
protected:
72-
rclcpp::Node::SharedPtr node_;
73-
std::shared_ptr<rerun::RecordingStream> stream_;
74-
bool is_initialized_;
75+
rclcpp::Node::SharedPtr node_; //!< Node shared pointer.
76+
std::shared_ptr<rerun::RecordingStream> stream_; //!< RecordingStream shared pointer.
77+
bool is_initialized_; //!< Whether the object has been initialized.
7578
};
7679

7780
/**
@@ -113,10 +116,12 @@ class RosTopicDisplay : public Display
113116
* @param topic Name of topic.
114117
* @param entity Entity path of the record.
115118
*/
116-
void setStatus(const std::string & topic, const std::string & entity) override
119+
void setStatus(
120+
const std::string & topic,
121+
const std::shared_ptr<std::unordered_map<std::string, std::string>> entity_roots) override
117122
{
118123
property_.setTopic(topic);
119-
property_.setEntity(entity);
124+
property_.setEntityRoots(entity_roots);
120125
}
121126

122127
/**
@@ -129,11 +134,14 @@ class RosTopicDisplay : public Display
129134
*/
130135
void end() override { unsubscribe(); }
131136

137+
bool isInitialized() const override { return is_initialized_ && property_.isInitialized(); }
138+
132139
protected:
133140
static constexpr const char * TIMELINE_NAME = "timestamp"; //!< Entity name of timeline record.
134141

135142
/**
136143
* @brief Start to subscribing the specified topic.
144+
* @todo Currently, `rclcpp::SensorDataQoS` is used for QoS profile setting.
137145
*/
138146
virtual void subscribe()
139147
{
@@ -154,7 +162,8 @@ class RosTopicDisplay : public Display
154162

155163
/**
156164
* @brief Log subscribed ROS message to recording stream.
157-
* @param msg ROS message.
165+
* @param msg Constant shared pointer of ROS message.
166+
* @note Currently, if the corresponding entity path doesn't exist this just logs warning as text.
158167
*/
159168
virtual void logToStream(typename MsgType::ConstSharedPtr msg) = 0;
160169

awviz_common/include/awviz_common/property.hpp

+38-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
#ifndef AWVIZ_COMMON__PROPERTY_HPP_
1616
#define AWVIZ_COMMON__PROPERTY_HPP_
1717

18+
#include <memory>
19+
#include <optional>
1820
#include <string>
21+
#include <unordered_map>
1922

2023
namespace awviz_common
2124
{
@@ -31,10 +34,12 @@ class RosTopicProperty
3134
* @brief Construct instance.
3235
* @param type ROS msg type.
3336
* @param topic Name of topic.
34-
* @param entity Entity path to log.
37+
* @param entity_roots Root entity paths to recording.
3538
*/
36-
RosTopicProperty(const std::string & type, const std::string & topic, const std::string & entity)
37-
: type_(type), topic_(topic), entity_(entity)
39+
RosTopicProperty(
40+
const std::string & type, const std::string & topic,
41+
const std::shared_ptr<std::unordered_map<std::string, std::string>> entity_roots)
42+
: type_(type), topic_(topic), entity_roots_(entity_roots)
3843
{
3944
}
4045

@@ -54,7 +59,11 @@ class RosTopicProperty
5459
* @brief Set entity path of record.
5560
* @param entity Entity path of record.
5661
*/
57-
void setEntity(const std::string & entity) { entity_ = entity; }
62+
void setEntityRoots(
63+
const std::shared_ptr<std::unordered_map<std::string, std::string>> entity_roots)
64+
{
65+
entity_roots_ = entity_roots;
66+
}
5867

5968
/**
6069
* @brief Get ROS message type.
@@ -69,15 +78,34 @@ class RosTopicProperty
6978
const std::string & topic() const noexcept { return topic_; }
7079

7180
/**
72-
* @brief Get entity path of record.
73-
* @return Entity path of record.
81+
* @brief Return the entity path using topic name.
82+
* @return Topic name used as entity path.
83+
*/
84+
const std::string & entity() const noexcept { return topic_; }
85+
86+
/**
87+
* @brief Return the entity path using the corresponding root and its frame ID.
88+
*
89+
* @param frame_id Frame ID.
90+
* @return Return "/<Root>/<FrameID>/<Topic>" if the corresponding root exists, otherwise return
91+
* `std::nullopt`.
7492
*/
75-
const std::string & entity() const noexcept { return entity_; }
93+
std::optional<std::string> entity(const std::string & frame_id) const noexcept
94+
{
95+
if (entity_roots_ && entity_roots_->count(frame_id) > 0) {
96+
return entity_roots_->at(frame_id) + topic_;
97+
} else {
98+
return std::nullopt;
99+
}
100+
}
101+
102+
bool isInitialized() const { return !type_.empty() && !topic_.empty() && entity_roots_; }
76103

77104
private:
78-
std::string type_; //!< Type of ROS message.
79-
std::string topic_; //!< Name of topic.
80-
std::string entity_; //!< Entity path of recording.
105+
std::string type_; //!< Type of ROS message.
106+
std::string topic_; //!< Name of topic.
107+
std::shared_ptr<std::unordered_map<std::string, std::string>>
108+
entity_roots_; //!< Entity root paths of recording.
81109
};
82110
} // namespace awviz_common
83111

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Copyright 2024 Kotaro Uetake.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef AWVIZ_COMMON__TF_TREE_HPP_
16+
#define AWVIZ_COMMON__TF_TREE_HPP_
17+
18+
#include <algorithm>
19+
#include <memory>
20+
#include <optional>
21+
#include <string>
22+
#include <unordered_map>
23+
#include <vector>
24+
25+
namespace awviz_common
26+
{
27+
/**
28+
* @brief A class to represent a TF frame information.
29+
*/
30+
class TfFrame
31+
{
32+
public:
33+
/**
34+
* @brief Construct a new object.
35+
*
36+
* @param id Frame ID.
37+
* @param parent Parent frame ID.
38+
*/
39+
TfFrame(const std::string & id, const std::string & parent) : id_(id), parent_(parent) {}
40+
41+
/**
42+
* @brief Construct a new object with empty string for parent.
43+
*
44+
* @param id Frame ID.
45+
*/
46+
explicit TfFrame(const std::string & id) : id_(id), parent_("") {}
47+
48+
/**
49+
* @brief Return own frame ID.
50+
*
51+
* @return Own frame ID.
52+
*/
53+
const std::string & id() const { return id_; }
54+
55+
/**
56+
* @brief Return the parent frame ID.
57+
*
58+
* @return Parent frame ID.
59+
*/
60+
const std::string & parent() const { return parent_; }
61+
62+
/**
63+
* @brief Indicate whether the frame is root by checking if `parent_` is empty.
64+
*
65+
* @return Return true, if the `parent_` is empty.
66+
*/
67+
bool is_root() const { return parent_.empty(); }
68+
69+
/**
70+
* @brief Return whether the tf frame is static or not.
71+
* @note Currently, this returns true if the parent id is not `"map"`.
72+
*
73+
* @return Return true if the parent id is not `"map"`.
74+
*/
75+
bool is_static() const { return parent_ != "map"; }
76+
77+
private:
78+
std::string id_; //!< Frame ID.
79+
std::string parent_; //!< Parent frame ID.
80+
};
81+
82+
class TfTree
83+
{
84+
public:
85+
/**
86+
* @brief Add a new tf frame to the tree.
87+
*
88+
* @param frame A new tf frame. If it has been already registered, skip adding.
89+
*/
90+
void emplace(const TfFrame & frame)
91+
{
92+
if (!contains(frame.id())) {
93+
frames_.emplace(frame.id(), frame);
94+
}
95+
96+
if (!frame.is_root()) {
97+
emplace(frame.parent());
98+
}
99+
}
100+
101+
/**
102+
* @brief Add a new tf frame to the tree with the empty string parent.
103+
*
104+
* @param id Frame ID. If it has been already registered, skip adding.
105+
*/
106+
void emplace(const std::string & id)
107+
{
108+
if (!contains(id)) {
109+
frames_.emplace(id, id);
110+
}
111+
}
112+
113+
/**
114+
* @brief Return map of all frames.
115+
*
116+
* @return Shared pointer of all frames.
117+
*/
118+
const std::unordered_map<std::string, TfFrame> & get_frames() const { return frames_; }
119+
120+
/**
121+
* @brief Get the `TfFrame` object.
122+
*
123+
* @param id Frame ID.
124+
* @return Corresponding `TfFrame` object. If there is no target `TfFrame`, returns `nullptr`.
125+
*/
126+
std::optional<TfFrame> get_frame(const std::string & id) const
127+
{
128+
return contains(id) ? std::make_optional(frames_.at(id)) : std::nullopt;
129+
}
130+
131+
/**
132+
* @brief Get the parent `TfFrame` object.
133+
*
134+
* @param id Frame ID.
135+
* @return Parent `TfFrame` object. If there is no parent, returns `nullptr`.
136+
*/
137+
std::optional<TfFrame> get_parent(const std::string & id) const
138+
{
139+
auto frame = get_frame(id);
140+
return frame ? get_frame(frame->parent()) : std::nullopt;
141+
}
142+
143+
/**
144+
* @brief Whether to the specified frame is contained in the tree.
145+
*
146+
* @param id Frame ID.
147+
* @return Returns true, if the frame is contained.
148+
*/
149+
bool contains(const std::string & id) const { return frames_.count(id) > 0; }
150+
151+
/**
152+
* @brief Whether to the parent of specified frame is contained in the tree.
153+
*
154+
* @param id Frame ID.
155+
* @return Returns true, if the parent frame is contained.
156+
*/
157+
bool is_root(const std::string & id) const { return contains(id) && frames_.at(id).is_root(); }
158+
159+
private:
160+
std::unordered_map<std::string, TfFrame> frames_; //!< Map to store frames.
161+
};
162+
} // namespace awviz_common
163+
164+
#endif // AWVIZ_COMMON__TF_TREE_HPP_

0 commit comments

Comments
 (0)