Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates the backend of TopicInterfacePlugin. #385

Merged
merged 4 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions delphyne_gui/visualizer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ install(
)

add_subdirectory(display_plugins)
add_subdirectory(topic_interface_plugin)

if(BUILD_TESTING)
add_subdirectory(test)
Expand Down
54 changes: 54 additions & 0 deletions delphyne_gui/visualizer/layout2_with_teleop.config
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,57 @@
<!-- Must match the same name as Scene3D plugin's scene parameter -->
<scene>scene</scene>
</plugin>

<plugin filename="TopicInterfacePlugin" read_only="true">
<title>Scene tree</title>
<topic>/scene</topic>
<hide>ambient</hide>
<hide>background</hide>
<hide>fog</hide>
<hide>grid</hide>
<hide>header</hide>
<hide>joint</hide>
<hide>light</hide>
<hide>model::deleted</hide>
<hide>model::header</hide>
<hide>model::id</hide>
<hide>model::is_static</hide>
<hide>model::joint</hide>
<hide>model::joint::bounce</hide>
<hide>model::joint::cfm</hide>
<hide>model::joint::child_id</hide>
<hide>model::joint::fudge_factor</hide>
<hide>model::joint::gearbox</hide>
<hide>model::joint::header</hide>
<hide>model::joint::id</hide>
<hide>model::joint::limit_cfm</hide>
<hide>model::joint::limit_erp</hide>
<hide>model::joint::parent_id</hide>
<hide>model::joint::screw</hide>
<hide>model::joint::sensor</hide>
<hide>model::joint::suspension_cfm</hide>
<hide>model::joint::suspension_erp</hide>
<hide>model::link::battery</hide>
<hide>model::link::canonical</hide>
<hide>model::link::collision</hide>
<hide>model::link::density</hide>
<hide>model::link::enabled</hide>
<hide>model::link::gravity</hide>
<hide>model::link::header</hide>
<hide>model::link::id</hide>
<hide>model::link::inertial</hide>
<hide>model::link::kinematic</hide>
<hide>model::link::name</hide>
<hide>model::link::projector</hide>
<hide>model::link::self_collide</hide>
<hide>model::link::sensor</hide>
<hide>model::link::visual</hide>
<hide>model::model</hide>
<hide>model::scale</hide>
<hide>model::self_collide</hide>
<hide>model::visual</hide>
<hide>name</hide>
<hide>origin_visual</hide>
<hide>shadows</hide>
<hide>sky</hide>
</plugin>
42 changes: 42 additions & 0 deletions delphyne_gui/visualizer/topic_interface_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
include_directories(
${Qt5Core_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/include
${CMAKE_SOURCE_DIR}
)

#-------------------------------------------------------------------------------
# TopicInterfacePlugin (ign-gui 3)
QT5_WRAP_CPP(TopicInterfacePlugin_MOC topic_interface_plugin.h)
QT5_ADD_RESOURCES(TopicInterfacePlugin_RCC topic_interface_plugin.qrc)

add_library(TopicInterfacePlugin
${CMAKE_CURRENT_SOURCE_DIR}/topic_interface_plugin.cc
${CMAKE_CURRENT_SOURCE_DIR}/message_widget.cc
${TopicInterfacePlugin_MOC}
${TopicInterfacePlugin_RCC}
)
add_library(delphyne_gui::TopicInterfacePlugin ALIAS TopicInterfacePlugin)
set_target_properties(TopicInterfacePlugin
PROPERTIES
OUTPUT_NAME TopicInterfacePlugin
)

target_link_libraries(TopicInterfacePlugin
PUBLIC
ignition-common3::ignition-common3
ignition-gui3::ignition-gui3
ignition-math6::ignition-math6
ignition-rendering3::ignition-rendering3
${Qt5Core_LIBRARIES}
${Qt5Widgets_LIBRARIES}
PRIVATE
ignition-plugin1::register
)

install(
TARGETS TopicInterfacePlugin
EXPORT ${PROJECT_NAME}-targets
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3

Rectangle {
id: topicInterfacePlugin
color: "transparent"
Layout.minimumWidth: 290
Layout.minimumHeight: 110
Layout.fillWidth: true
}
109 changes: 109 additions & 0 deletions delphyne_gui/visualizer/topic_interface_plugin/message_widget.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2021 Toyota Research Institute
#include "message_widget.h"

namespace delphyne {
namespace gui {

void MessageWidget::Parse(google::protobuf::Message* _msg) {
typeName = _msg->GetTypeName();
auto reflection = _msg->GetReflection();
auto descriptor = _msg->GetDescriptor();

if (!descriptor) {
// TODO Should throw!
return;
}

const int fieldCount = descriptor->field_count();

for (int fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex) {
auto fieldDescriptor = descriptor->field(fieldIndex);

const auto fieldType = fieldDescriptor->type();

const std::string scopedName = fieldDescriptor->name();

if (fieldDescriptor->is_repeated()) {
// Parse all fields of the repeated message.
for (int count = 0; count < reflection->FieldSize(*_msg, fieldDescriptor); ++count) {
// Append number to name to differentiate between repeated variables.
const std::string name = scopedName + "::" + std::to_string(count);
// Evaluate the type.
if (fieldType == google::protobuf::FieldDescriptor::TYPE_MESSAGE) {
auto& value = reflection->GetRepeatedMessage(*_msg, fieldDescriptor, count);
children[scopedName] = std::make_unique<MessageWidget>(&value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_DOUBLE) {
const double value = reflection->GetRepeatedDouble(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_FLOAT) {
const float value = reflection->GetRepeatedFloat(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_INT64) {
const int64_t value = reflection->GetRepeatedInt64(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_INT32) {
const int32_t value = reflection->GetRepeatedInt32(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_UINT64) {
const uint64_t value = reflection->GetRepeatedUInt64(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_UINT32) {
const uint32_t value = reflection->GetRepeatedUInt32(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_BOOL) {
const bool value = reflection->GetRepeatedBool(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_STRING) {
const std::string value = reflection->GetRepeatedString(*_msg, fieldDescriptor, count);
children[name] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_ENUM) {
auto enumValueDescriptor = reflection->GetRepeatedEnum(*_msg, fieldDescriptor, count);
const int enumValue = enumValueDescriptor->number();
const std::string enumName = enumValueDescriptor->name();
children[name] = std::make_unique<MessageWidget>(EnumValue{enumValue, enumName});
} else {
ignwarn << "Unhandled message type [" << fieldType << "]" << std::endl;
}
}
} else { // It's not a repeated message, then we just need to parse them.
if (fieldType == google::protobuf::FieldDescriptor::TYPE_MESSAGE) {
auto& value = reflection->GetMessage(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(&value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_DOUBLE) {
const double value = reflection->GetDouble(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_FLOAT) {
const float value = reflection->GetFloat(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_INT64) {
const int64_t value = reflection->GetInt64(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_INT32) {
const int32_t value = reflection->GetInt32(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_UINT64) {
const uint64_t value = reflection->GetUInt64(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_UINT32) {
const uint32_t value = reflection->GetUInt32(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_BOOL) {
const bool value = reflection->GetBool(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_STRING) {
const std::string value = reflection->GetString(*_msg, fieldDescriptor);
children[scopedName] = std::make_unique<MessageWidget>(value);
} else if (fieldType == google::protobuf::FieldDescriptor::TYPE_ENUM) {
auto enumValueDescriptor = reflection->GetEnum(*_msg, fieldDescriptor);
const int enumValue = enumValueDescriptor->number();
const std::string enumName = enumValueDescriptor->name();
children[scopedName] = std::make_unique<MessageWidget>(EnumValue{enumValue, enumName});
} else {
ignwarn << "Unhandled message type [" << fieldType << "]" << std::endl;
}
}
}
}

} // namespace gui
} // namespace delphyne
124 changes: 124 additions & 0 deletions delphyne_gui/visualizer/topic_interface_plugin/message_widget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2021 Toyota Research Institute
#pragma once

#include <memory>
#include <optional>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <unordered_map>

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

#include <ignition/common/Console.hh>

namespace delphyne {
namespace gui {

/// @brief Holds the information of a google::protobuf::Message to be consumed
/// by a Qt widget.
/// @details Holds a variant like-struct when it is a leaf node in the message
/// tree ( @see MessageWidget::Variant ). Otherwise, when it is a
/// compound type, it populates a dictionary of MessageWidgets.
/// See Parse() implementation for a full description of how it uses
/// the reflection API in Google Protobuf Message class to get the
/// information of each field.
class MessageWidget {
public:
/// Wraps an enumeration field in Google Protobuf.
struct EnumValue {
int value; ///< The integer value of the enum.
std::string name; ///< The string label the enum has.
};

/// Operates as a std::variant from optionals. It simplifies the operation by
/// consumers when dealing with the type differences.
/// An instance is well-constructed iff none or just one field has a value.
struct Variant {
std::optional<double> doubleVal{std::nullopt};
std::optional<float> floatVal{std::nullopt};
std::optional<int64_t> int64Val{std::nullopt};
std::optional<int32_t> int32Val{std::nullopt};
std::optional<uint64_t> uInt64Val{std::nullopt};
std::optional<uint32_t> uInt32Val{std::nullopt};
std::optional<bool> boolVal{std::nullopt};
std::optional<std::string> stringVal{std::nullopt};
std::optional<EnumValue> enumVal{std::nullopt};
};

/// @{ Multiple constructors for the different types.
/// TODO(#332): Improve this using template constructors. Probably, Variant
/// needs to change to std::variant.
explicit MessageWidget(double _value) {
typeName = typeid(_value).name();
variantValue.doubleVal = _value;
}
explicit MessageWidget(float _value) {
typeName = typeid(_value).name();
variantValue.floatVal = _value;
}
explicit MessageWidget(int64_t _value) {
typeName = typeid(_value).name();
variantValue.int64Val = _value;
}
explicit MessageWidget(int32_t _value) {
typeName = typeid(_value).name();
variantValue.int32Val = _value;
}
explicit MessageWidget(uint64_t _value) {
typeName = typeid(_value).name();
variantValue.uInt64Val = _value;
}
explicit MessageWidget(uint32_t _value) {
typeName = typeid(_value).name();
variantValue.uInt32Val = _value;
}
explicit MessageWidget(bool _value) {
typeName = typeid(_value).name();
variantValue.boolVal = _value;
}
explicit MessageWidget(std::string _value) {
typeName = typeid(_value).name();
variantValue.stringVal = _value;
}
explicit MessageWidget(EnumValue _value) {
typeName = typeid(_value).name();
variantValue.enumVal = _value;
}
explicit MessageWidget(const google::protobuf::Message* _msg) {
auto msg = _msg->New();
msg->CopyFrom(*_msg);
Parse(msg);
}
/// @}

/// @return The type name of the message.
std::string TypeName() const { return typeName; }

/// @return Whether this type is compound or not.
bool IsCompound() const { return !children.empty(); }

/// @return The value this message holds.
Variant Value() const { return variantValue; }

/// @return The children dictionary.
const std::unordered_map<std::string, std::unique_ptr<MessageWidget>>& Children() const { return children; }

private:
/// @brief Parses @p _msg and stores children MessageWidget into
//// `variantValue`.
void Parse(google::protobuf::Message* _msg);

/// @brief Holds the type name of the data.
std::string typeName{""};

/// @brief Holds the data of a terminal node.
Variant variantValue;

/// @brief Holds the children, nested values.
std::unordered_map<std::string, std::unique_ptr<MessageWidget>> children;
};

} // namespace gui
} // namespace delphyne
Loading