Skip to content

Commit 67c41c9

Browse files
chapulinankoenig
andauthored
System inspector GUI widget (#1404)
Signed-off-by: Louise Poubel <[email protected]> Co-authored-by: Nate Koenig <[email protected]>
1 parent 26f67fa commit 67c41c9

17 files changed

+678
-32
lines changed

include/ignition/gazebo/Conversions.hh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include <sdf/Material.hh>
5050
#include <sdf/Noise.hh>
5151
#include <sdf/ParticleEmitter.hh>
52+
#include <sdf/Plugin.hh>
5253
#include <sdf/Physics.hh>
5354
#include <sdf/Scene.hh>
5455
#include <sdf/Sensor.hh>
@@ -709,6 +710,38 @@ namespace ignition
709710
/// \return Particle emitter message.
710711
template<>
711712
sdf::ParticleEmitter convert(const msgs::ParticleEmitter &_in);
713+
714+
/// \brief Generic conversion from an SDF element to another type.
715+
/// \param[in] _in SDF element.
716+
/// \return Conversion result.
717+
/// \tparam Out Output type.
718+
template<class Out>
719+
Out convert(const sdf::Element &/*_in*/)
720+
{
721+
Out::ConversionNotImplemented;
722+
}
723+
724+
/// \brief Specialized conversion from an SDF element to a plugin message.
725+
/// \param[in] _in SDF element.
726+
/// \return Plugin message.
727+
template<>
728+
msgs::Plugin convert(const sdf::Element &_in);
729+
730+
/// \brief Generic conversion from an SDF plugin to another type.
731+
/// \param[in] _in SDF plugin.
732+
/// \return Conversion result.
733+
/// \tparam Out Output type.
734+
template<class Out>
735+
Out convert(const sdf::Plugin &/*_in*/)
736+
{
737+
Out::ConversionNotImplemented;
738+
}
739+
740+
/// \brief Specialized conversion from an SDF plugin to a plugin message.
741+
/// \param[in] _in SDF plugin.
742+
/// \return Plugin message.
743+
template<>
744+
msgs::Plugin convert(const sdf::Plugin &_in);
712745
}
713746
}
714747
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) 2022 Open Source Robotics Foundation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
#ifndef IGNITION_GAZEBO_COMPONENTS_SYSTEMINFO_HH_
18+
#define IGNITION_GAZEBO_COMPONENTS_SYSTEMINFO_HH_
19+
20+
#include <ignition/msgs/plugin_v.pb.h>
21+
#include <ignition/gazebo/components/Factory.hh>
22+
#include <ignition/gazebo/components/Component.hh>
23+
#include <ignition/gazebo/components/Serialization.hh>
24+
#include <ignition/gazebo/config.hh>
25+
26+
namespace ignition
27+
{
28+
namespace gazebo
29+
{
30+
// Inline bracket to help doxygen filtering.
31+
inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
32+
namespace components
33+
{
34+
/// \brief This component holds information about all the system plugins that
35+
/// are attached to an entity. The content of each system is populated the
36+
/// moment the plugin is instantiated and isn't modified throughout
37+
/// simulation.
38+
using SystemPluginInfo = Component<msgs::Plugin_V, class SystemPluginInfoTag,
39+
serializers::MsgSerializer>;
40+
IGN_GAZEBO_REGISTER_COMPONENT("ign_gazebo_components.SystemPluginInfo",
41+
SystemPluginInfo)
42+
}
43+
}
44+
}
45+
}
46+
47+
#endif

src/Conversions.cc

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -711,28 +711,13 @@ msgs::GUI ignition::gazebo::convert(const sdf::Gui &_in)
711711
out.set_fullscreen(_in.Fullscreen());
712712

713713
// Set gui plugins
714-
auto elem = _in.Element();
715-
if (elem && elem->HasElement("plugin"))
714+
for (uint64_t i = 0; i < _in.PluginCount(); ++i)
716715
{
717-
auto pluginElem = elem->GetElement("plugin");
718-
while (pluginElem)
719-
{
720-
auto pluginMsg = out.add_plugin();
721-
pluginMsg->set_name(pluginElem->Get<std::string>("name"));
722-
pluginMsg->set_filename(pluginElem->Get<std::string>("filename"));
723-
724-
std::stringstream ss;
725-
for (auto innerElem = pluginElem->GetFirstElement();
726-
innerElem; innerElem = innerElem->GetNextElement(""))
727-
{
728-
ss << innerElem->ToString("");
729-
}
730-
pluginMsg->set_innerxml(ss.str());
731-
pluginElem = pluginElem->GetNextElement("plugin");
732-
}
716+
auto pluginMsg = out.add_plugin();
717+
pluginMsg->CopyFrom(convert<msgs::Plugin>(*_in.PluginByIndex(i)));
733718
}
734719

735-
if (elem->HasElement("camera"))
720+
if (_in.Element()->HasElement("camera"))
736721
{
737722
ignwarn << "<gui><camera> can't be converted yet" << std::endl;
738723
}
@@ -1759,3 +1744,51 @@ sdf::ParticleEmitter ignition::gazebo::convert(const msgs::ParticleEmitter &_in)
17591744

17601745
return out;
17611746
}
1747+
1748+
//////////////////////////////////////////////////
1749+
template<>
1750+
IGNITION_GAZEBO_VISIBLE
1751+
msgs::Plugin ignition::gazebo::convert(const sdf::Element &_in)
1752+
{
1753+
msgs::Plugin result;
1754+
1755+
if (_in.GetName() != "plugin")
1756+
{
1757+
ignerr << "Tried to convert SDF [" << _in.GetName()
1758+
<< "] into [plugin]" << std::endl;
1759+
return result;
1760+
}
1761+
1762+
result.set_name(_in.Get<std::string>("name"));
1763+
result.set_filename(_in.Get<std::string>("filename"));
1764+
1765+
std::stringstream ss;
1766+
for (auto innerElem = _in.GetFirstElement(); innerElem;
1767+
innerElem = innerElem->GetNextElement(""))
1768+
{
1769+
ss << innerElem->ToString("");
1770+
}
1771+
result.set_innerxml(ss.str());
1772+
1773+
return result;
1774+
}
1775+
1776+
//////////////////////////////////////////////////
1777+
template<>
1778+
IGNITION_GAZEBO_VISIBLE
1779+
msgs::Plugin ignition::gazebo::convert(const sdf::Plugin &_in)
1780+
{
1781+
msgs::Plugin result;
1782+
1783+
result.set_name(_in.Name());
1784+
result.set_filename(_in.Filename());
1785+
1786+
std::stringstream ss;
1787+
for (auto content : _in.Contents())
1788+
{
1789+
ss << content->ToString("");
1790+
}
1791+
result.set_innerxml(ss.str());
1792+
1793+
return result;
1794+
}

src/Conversions_TEST.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <sdf/Box.hh>
2525
#include <sdf/Capsule.hh>
2626
#include <sdf/Cylinder.hh>
27+
#include <sdf/Element.hh>
2728
#include <sdf/Ellipsoid.hh>
2829
#include <sdf/Gui.hh>
2930
#include <sdf/Heightmap.hh>
@@ -1060,3 +1061,51 @@ TEST(Conversions, ParticleEmitter)
10601061
EXPECT_EQ(emitter2.RawPose(), emitter.RawPose());
10611062
EXPECT_FLOAT_EQ(emitter2.ScatterRatio(), emitter.ScatterRatio());
10621063
}
1064+
1065+
/////////////////////////////////////////////////
1066+
TEST(Conversions, PluginElement)
1067+
{
1068+
sdf::Root root;
1069+
root.LoadSdfString("<?xml version='1.0'?><sdf version='1.6'>"
1070+
"<world name='default'>"
1071+
" <plugin filename='plum' name='peach'>"
1072+
" <avocado>0.5</avocado>"
1073+
" </plugin>"
1074+
"</world></sdf>");
1075+
1076+
auto world = root.WorldByIndex(0);
1077+
ASSERT_NE(nullptr, world);
1078+
1079+
auto worldElem = world->Element();
1080+
ASSERT_NE(nullptr, worldElem);
1081+
1082+
auto pluginElem = worldElem->GetElement("plugin");
1083+
ASSERT_NE(nullptr, pluginElem);
1084+
1085+
auto pluginMsg = convert<msgs::Plugin>(*(pluginElem.get()));
1086+
EXPECT_EQ("plum", pluginMsg.filename());
1087+
EXPECT_EQ("peach", pluginMsg.name());
1088+
1089+
EXPECT_NE(pluginMsg.innerxml().find("<avocado>0.5</avocado>"),
1090+
std::string::npos);
1091+
}
1092+
1093+
/////////////////////////////////////////////////
1094+
TEST(Conversions, Plugin)
1095+
{
1096+
sdf::Plugin pluginSdf;
1097+
pluginSdf.SetName("peach");
1098+
pluginSdf.SetFilename("plum");
1099+
1100+
auto content = std::make_shared<sdf::Element>();
1101+
content->SetName("avocado");
1102+
content->AddValue("double", "0.5", false, "");
1103+
pluginSdf.InsertContent(content);
1104+
1105+
auto pluginMsg = convert<msgs::Plugin>(pluginSdf);
1106+
EXPECT_EQ("plum", pluginMsg.filename());
1107+
EXPECT_EQ("peach", pluginMsg.name());
1108+
1109+
EXPECT_NE(pluginMsg.innerxml().find("<avocado>0.5</avocado>"),
1110+
std::string::npos) << pluginMsg.innerxml();
1111+
}

src/SystemManager.cc

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*
1616
*/
1717

18+
#include "ignition/gazebo/components/SystemPluginInfo.hh"
19+
#include "ignition/gazebo/Conversions.hh"
1820
#include "SystemManager.hh"
1921

2022
using namespace ignition;
@@ -120,6 +122,29 @@ void SystemManager::AddSystemImpl(
120122
SystemInternal _system,
121123
std::shared_ptr<const sdf::Element> _sdf)
122124
{
125+
// Add component
126+
if (this->entityCompMgr && kNullEntity != _system.parentEntity)
127+
{
128+
msgs::Plugin_V systemInfoMsg;
129+
auto systemInfoComp =
130+
this->entityCompMgr->Component<components::SystemPluginInfo>(
131+
_system.parentEntity);
132+
if (systemInfoComp)
133+
{
134+
systemInfoMsg = systemInfoComp->Data();
135+
}
136+
if (_sdf)
137+
{
138+
auto pluginMsg = systemInfoMsg.add_plugins();
139+
pluginMsg->CopyFrom(convert<msgs::Plugin>(*_sdf.get()));
140+
}
141+
142+
this->entityCompMgr->SetComponentData<components::SystemPluginInfo>(
143+
_system.parentEntity, systemInfoMsg);
144+
this->entityCompMgr->SetChanged(_system.parentEntity,
145+
components::SystemPluginInfo::typeId);
146+
}
147+
123148
// Configure the system, if necessary
124149
if (_system.configure && this->entityCompMgr && this->eventMgr)
125150
{
@@ -160,16 +185,15 @@ const std::vector<ISystemPostUpdate *>& SystemManager::SystemsPostUpdate()
160185
//////////////////////////////////////////////////
161186
std::vector<SystemInternal> SystemManager::TotalByEntity(Entity _entity)
162187
{
188+
auto checkEntity = [&](const SystemInternal &_system)
189+
{
190+
return _system.parentEntity == _entity;
191+
};
192+
163193
std::vector<SystemInternal> result;
164-
for (auto system : this->systems)
165-
{
166-
if (system.parentEntity == _entity)
167-
result.push_back(system);
168-
}
169-
for (auto system : this->pendingSystems)
170-
{
171-
if (system.parentEntity == _entity)
172-
result.push_back(system);
173-
}
194+
std::copy_if(this->systems.begin(), this->systems.end(),
195+
std::back_inserter(result), checkEntity);
196+
std::copy_if(this->pendingSystems.begin(), this->pendingSystems.end(),
197+
std::back_inserter(result), checkEntity);
174198
return result;
175199
}

src/SystemManager.hh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,20 @@ namespace ignition
112112
/// \return Vector of systems.
113113
public: std::vector<SystemInternal> TotalByEntity(Entity _entity);
114114

115+
/// \brief Implementation for AddSystem functions that takes an SDF
116+
/// element. This calls the AddSystemImpl that accepts an SDF Plugin.
117+
/// \param[in] _system Generic representation of a system.
118+
/// \param[in] _sdf SDF element.
119+
private: void AddSystemImpl(SystemInternal _system,
120+
std::shared_ptr<const sdf::Element> _sdf);
121+
115122
/// \brief Implementation for AddSystem functions. This only adds systems
116123
/// to a queue, the actual addition is performed by `AddSystemToRunner` at
117124
/// the appropriate time.
118125
/// \param[in] _system Generic representation of a system.
119126
/// \param[in] _sdf SDF received from AddSystem.
120127
private: void AddSystemImpl(SystemInternal _system,
121-
std::shared_ptr<const sdf::Element> _sdf);
128+
const sdf::Plugin &_sdf);
122129

123130
/// \brief All the systems.
124131
private: std::vector<SystemInternal> systems;

src/SystemManager_TEST.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "ignition/gazebo/System.hh"
2222
#include "ignition/gazebo/SystemLoader.hh"
2323
#include "ignition/gazebo/Types.hh"
24+
#include "ignition/gazebo/components/SystemPluginInfo.hh"
2425
#include "ignition/gazebo/test_config.hh" // NOLINT(build/include)
2526

2627
#include "SystemManager.hh"
@@ -202,3 +203,60 @@ TEST(SystemManager, AddSystemEcm)
202203
EXPECT_EQ(1u, systemMgr.SystemsPostUpdate().size());
203204
}
204205

206+
/////////////////////////////////////////////////
207+
TEST(SystemManager, AddSystemWithInfo)
208+
{
209+
auto loader = std::make_shared<SystemLoader>();
210+
211+
EntityComponentManager ecm;
212+
auto entity = ecm.CreateEntity();
213+
EXPECT_NE(kNullEntity, entity);
214+
215+
auto eventManager = EventManager();
216+
217+
SystemManager systemMgr(loader, &ecm, &eventManager);
218+
219+
// No element, no SystemPluginInfo component
220+
auto configSystem = std::make_shared<SystemWithConfigure>();
221+
systemMgr.AddSystem(configSystem, entity, nullptr);
222+
223+
// Element becomes SystemPluginInfo component
224+
auto pluginElem = std::make_shared<sdf::Element>();
225+
sdf::initFile("plugin.sdf", pluginElem);
226+
sdf::readString("<?xml version='1.0'?><sdf version='1.6'>"
227+
" <plugin filename='plum' name='peach'>"
228+
" <avocado>0.5</avocado>"
229+
" </plugin>"
230+
"</sdf>", pluginElem);
231+
232+
auto updateSystem = std::make_shared<SystemWithUpdates>();
233+
systemMgr.AddSystem(updateSystem, entity, pluginElem);
234+
235+
int entityCount{0};
236+
ecm.Each<components::SystemPluginInfo>(
237+
[&](const Entity &_entity,
238+
const components::SystemPluginInfo *_systemInfoComp) -> bool
239+
{
240+
EXPECT_EQ(entity, _entity);
241+
242+
EXPECT_NE(nullptr, _systemInfoComp);
243+
if (nullptr == _systemInfoComp)
244+
return true;
245+
246+
auto pluginsMsg = _systemInfoComp->Data();
247+
EXPECT_EQ(1, pluginsMsg.plugins().size());
248+
if (1u != pluginsMsg.plugins().size())
249+
return true;
250+
251+
auto pluginMsg = pluginsMsg.plugins(0);
252+
EXPECT_EQ("plum", pluginMsg.filename());
253+
EXPECT_EQ("peach", pluginMsg.name());
254+
EXPECT_NE(pluginMsg.innerxml().find("<avocado>0.5</avocado>"),
255+
std::string::npos);
256+
257+
entityCount++;
258+
return true;
259+
});
260+
EXPECT_EQ(1, entityCount);
261+
}
262+

0 commit comments

Comments
 (0)