diff --git a/CMakeLists.txt b/CMakeLists.txt
index ded63d5e79..406ee1b308 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -87,7 +87,7 @@ set(IGN_FUEL_TOOLS_VER ${ignition-fuel_tools7_VERSION_MAJOR})
#--------------------------------------
# Find ignition-gui
-ign_find_package(ignition-gui6 REQUIRED)
+ign_find_package(ignition-gui6 REQUIRED VERSION 6.1)
set(IGN_GUI_VER ${ignition-gui6_VERSION_MAJOR})
ign_find_package (Qt5
COMPONENTS
diff --git a/examples/plugin/rendering_plugins/rendering_plugins.sdf b/examples/plugin/rendering_plugins/rendering_plugins.sdf
index eb56737121..68f77eb736 100644
--- a/examples/plugin/rendering_plugins/rendering_plugins.sdf
+++ b/examples/plugin/rendering_plugins/rendering_plugins.sdf
@@ -92,6 +92,7 @@
true
true
true
+ true
diff --git a/examples/scripts/log_video_recorder/log_video_recorder.sdf b/examples/scripts/log_video_recorder/log_video_recorder.sdf
index e2cdc72a9d..e333935ddf 100644
--- a/examples/scripts/log_video_recorder/log_video_recorder.sdf
+++ b/examples/scripts/log_video_recorder/log_video_recorder.sdf
@@ -88,6 +88,7 @@
true
/world/default/control
/world/default/stats
+ true
diff --git a/examples/worlds/camera_sensor.sdf b/examples/worlds/camera_sensor.sdf
index 48d052d355..8e8c090728 100644
--- a/examples/worlds/camera_sensor.sdf
+++ b/examples/worlds/camera_sensor.sdf
@@ -105,6 +105,7 @@
true
true
true
+ true
diff --git a/examples/worlds/fuel_textured_mesh.sdf b/examples/worlds/fuel_textured_mesh.sdf
index 8b9e9147b2..2f42cf7608 100644
--- a/examples/worlds/fuel_textured_mesh.sdf
+++ b/examples/worlds/fuel_textured_mesh.sdf
@@ -108,6 +108,7 @@
true
true
true
+ true
diff --git a/examples/worlds/grid.sdf b/examples/worlds/grid.sdf
index efab4d2549..affe914178 100644
--- a/examples/worlds/grid.sdf
+++ b/examples/worlds/grid.sdf
@@ -80,6 +80,7 @@
true
true
true
+ true
diff --git a/examples/worlds/minimal_scene.sdf b/examples/worlds/minimal_scene.sdf
index 0221e08050..90f85eac6d 100644
--- a/examples/worlds/minimal_scene.sdf
+++ b/examples/worlds/minimal_scene.sdf
@@ -147,6 +147,7 @@ Features:
true
true
true
+ true
diff --git a/examples/worlds/optical_tactile_sensor_plugin.sdf b/examples/worlds/optical_tactile_sensor_plugin.sdf
index d458079a15..06262adb00 100644
--- a/examples/worlds/optical_tactile_sensor_plugin.sdf
+++ b/examples/worlds/optical_tactile_sensor_plugin.sdf
@@ -110,6 +110,7 @@
true
true
true
+ true
diff --git a/examples/worlds/plot_3d.sdf b/examples/worlds/plot_3d.sdf
index 59b7c09c6a..ed66fbf3a6 100644
--- a/examples/worlds/plot_3d.sdf
+++ b/examples/worlds/plot_3d.sdf
@@ -84,6 +84,7 @@
true
true
true
+ true
diff --git a/examples/worlds/segmentation_camera.sdf b/examples/worlds/segmentation_camera.sdf
index 4e0840a37e..30d515cb4c 100644
--- a/examples/worlds/segmentation_camera.sdf
+++ b/examples/worlds/segmentation_camera.sdf
@@ -98,6 +98,7 @@
true
true
true
+ true
diff --git a/examples/worlds/sensors_demo.sdf b/examples/worlds/sensors_demo.sdf
index c679e2ebeb..31374a1a3e 100644
--- a/examples/worlds/sensors_demo.sdf
+++ b/examples/worlds/sensors_demo.sdf
@@ -100,6 +100,7 @@
true
true
true
+ true
diff --git a/examples/worlds/sky.sdf b/examples/worlds/sky.sdf
index c22442bac2..be14b395c5 100644
--- a/examples/worlds/sky.sdf
+++ b/examples/worlds/sky.sdf
@@ -83,6 +83,7 @@ Currently only supported using ogre2 rendering engine plugin.
true
true
true
+ true
diff --git a/examples/worlds/thermal_camera.sdf b/examples/worlds/thermal_camera.sdf
index 03be5f2d64..cb37b61619 100644
--- a/examples/worlds/thermal_camera.sdf
+++ b/examples/worlds/thermal_camera.sdf
@@ -101,6 +101,7 @@
true
true
true
+ true
diff --git a/examples/worlds/tunnel.sdf b/examples/worlds/tunnel.sdf
index 496c2a1781..583b313641 100644
--- a/examples/worlds/tunnel.sdf
+++ b/examples/worlds/tunnel.sdf
@@ -129,6 +129,7 @@
true
true
true
+ true
diff --git a/examples/worlds/video_record_dbl_pendulum.sdf b/examples/worlds/video_record_dbl_pendulum.sdf
index d7cf86abbf..5f0775c046 100644
--- a/examples/worlds/video_record_dbl_pendulum.sdf
+++ b/examples/worlds/video_record_dbl_pendulum.sdf
@@ -180,6 +180,7 @@
true
true
true
+ true
diff --git a/examples/worlds/visibility.sdf b/examples/worlds/visibility.sdf
index 33480d9de5..31bec366b3 100644
--- a/examples/worlds/visibility.sdf
+++ b/examples/worlds/visibility.sdf
@@ -112,6 +112,7 @@
true
true
true
+ true
diff --git a/examples/worlds/visualize_contacts.sdf b/examples/worlds/visualize_contacts.sdf
index 357e4b5adb..f39994dced 100644
--- a/examples/worlds/visualize_contacts.sdf
+++ b/examples/worlds/visualize_contacts.sdf
@@ -102,6 +102,7 @@ Contacts will be visualized as blue spheres and green cylinders.
true
true
true
+ true
diff --git a/examples/worlds/visualize_lidar.sdf b/examples/worlds/visualize_lidar.sdf
index 7d01bf2a76..d38fe690ce 100644
--- a/examples/worlds/visualize_lidar.sdf
+++ b/examples/worlds/visualize_lidar.sdf
@@ -96,6 +96,7 @@
true
true
true
+ true
diff --git a/src/SimulationRunner.cc b/src/SimulationRunner.cc
index 250d0f2fb2..57041d66ad 100644
--- a/src/SimulationRunner.cc
+++ b/src/SimulationRunner.cc
@@ -195,12 +195,14 @@ SimulationRunner::SimulationRunner(const sdf::World *_world,
// TODO(louise) Combine both messages into one.
this->node->Advertise("control", &SimulationRunner::OnWorldControl, this);
+ this->node->Advertise("control/state", &SimulationRunner::OnWorldControlState,
+ this);
this->node->Advertise("playback/control",
&SimulationRunner::OnPlaybackControl, this);
ignmsg << "Serving world controls on [" << opts.NameSpace()
- << "/control] and [" << opts.NameSpace() << "/playback/control]"
- << std::endl;
+ << "/control], [" << opts.NameSpace() << "/control/state] and ["
+ << opts.NameSpace() << "/playback/control]" << std::endl;
// Publish empty GUI messages for worlds that have no GUI in the beginning.
// In the future, support modifying GUI from the server at runtime.
@@ -410,6 +412,12 @@ void SimulationRunner::PublishStats()
msg.set_paused(this->currentInfo.paused);
+ if (this->Stepping())
+ {
+ auto headerData = msg.mutable_header()->add_data();
+ headerData->set_key("step");
+ }
+
// Publish the stats message. The stats message is throttled.
this->statsPub.Publish(msg);
@@ -1079,6 +1087,18 @@ void SimulationRunner::SetPaused(const bool _paused)
this->currentInfo.paused = _paused;
}
+/////////////////////////////////////////////////
+void SimulationRunner::SetStepping(bool _stepping)
+{
+ this->stepping = _stepping;
+}
+
+/////////////////////////////////////////////////
+bool SimulationRunner::Stepping() const
+{
+ return this->stepping;
+}
+
/////////////////////////////////////////////////
void SimulationRunner::SetRunToSimTime(
const std::chrono::steady_clock::duration &_time)
@@ -1097,36 +1117,54 @@ void SimulationRunner::SetRunToSimTime(
/////////////////////////////////////////////////
bool SimulationRunner::OnWorldControl(const msgs::WorldControl &_req,
msgs::Boolean &_res)
+{
+ msgs::WorldControlState req;
+ req.mutable_world_control()->CopyFrom(_req);
+
+ return this->OnWorldControlState(req, _res);
+}
+
+/////////////////////////////////////////////////
+bool SimulationRunner::OnWorldControlState(const msgs::WorldControlState &_req,
+ msgs::Boolean &_res)
{
std::lock_guard lock(this->msgBufferMutex);
+ // update the server ECM if the request contains SerializedState information
+ if (_req.has_state())
+ this->entityCompMgr.SetState(_req.state());
+ // TODO(anyone) notify server systems of changes made to the ECM, if there
+ // were any?
+
WorldControl control;
- control.pause = _req.pause();
+ control.pause = _req.world_control().pause();
- if (_req.multi_step() != 0)
- control.multiStep = _req.multi_step();
- else if (_req.step())
+ if (_req.world_control().multi_step() != 0)
+ control.multiStep = _req.world_control().multi_step();
+ else if (_req.world_control().step())
control.multiStep = 1;
- if (_req.has_reset())
+ if (_req.world_control().has_reset())
{
- control.rewind = _req.reset().all() || _req.reset().time_only();
+ control.rewind = _req.world_control().reset().all() ||
+ _req.world_control().reset().time_only();
- if (_req.reset().model_only())
+ if (_req.world_control().reset().model_only())
{
ignwarn << "Model only reset is not supported." << std::endl;
}
}
- if (_req.seed() != 0)
+ if (_req.world_control().seed() != 0)
{
ignwarn << "Changing seed is not supported." << std::endl;
}
- if (_req.has_run_to_sim_time())
+ if (_req.world_control().has_run_to_sim_time())
{
- control.runToSimTime = std::chrono::seconds(_req.run_to_sim_time().sec()) +
- std::chrono::nanoseconds(_req.run_to_sim_time().nsec());
+ control.runToSimTime = std::chrono::seconds(
+ _req.world_control().run_to_sim_time().sec()) +
+ std::chrono::nanoseconds(_req.world_control().run_to_sim_time().nsec());
}
this->worldControls.push_back(control);
@@ -1175,6 +1213,10 @@ void SimulationRunner::ProcessMessages()
void SimulationRunner::ProcessWorldControl()
{
IGN_PROFILE("SimulationRunner::ProcessWorldControl");
+
+ // assume no stepping unless WorldControl msgs say otherwise
+ this->SetStepping(false);
+
for (const auto &control : this->worldControls)
{
// Play / pause
@@ -1186,6 +1228,7 @@ void SimulationRunner::ProcessWorldControl()
this->pendingSimIterations += control.multiStep;
// Unpause so that stepping can occur.
this->SetPaused(false);
+ this->SetStepping(true);
}
// Rewind / reset
diff --git a/src/SimulationRunner.hh b/src/SimulationRunner.hh
index 36754d3388..9f17d59b6b 100644
--- a/src/SimulationRunner.hh
+++ b/src/SimulationRunner.hh
@@ -38,6 +38,7 @@
#include
#include
#include
+#include
#include
#include "ignition/gazebo/config.hh"
@@ -291,6 +292,17 @@ namespace ignition
/// \return True if the simulation runner is paused, false otherwise.
public: bool Paused() const;
+ /// \brief Set if the simulation runner is stepping based on WorldControl
+ /// info
+ /// \param[in] _step True if stepping based on WorldControl info, false
+ /// otherwise
+ public: void SetStepping(bool _step);
+
+ /// \brief Get if the simulation runner is stepping based on WorldControl
+ /// info
+ /// \return True if stepping based on WorldControl info, false otherwise
+ public: bool Stepping() const;
+
/// \brief Set the run to simulation time.
/// \param[in] _time A simulation time in the future to run to and then
/// pause. A negative number or a time less than the current simulation
@@ -370,6 +382,16 @@ namespace ignition
private: bool OnWorldControl(const msgs::WorldControl &_req,
msgs::Boolean &_res);
+ /// \brief World control state service callback. This function stores the
+ /// the request which will then be processed by the ProcessMessages
+ /// function.
+ /// \param[in] _req Request from client, currently handling play / pause
+ /// and multistep. This also may contain SerializedState information.
+ /// \param[out] _res Response to client, true if successful.
+ /// \return True for success
+ private: bool OnWorldControlState(const msgs::WorldControlState &_req,
+ msgs::Boolean &_res);
+
/// \brief World control service callback. This function stores the
/// the request which will then be processed by the ProcessMessages
/// function.
@@ -601,6 +623,10 @@ namespace ignition
/// \brief True if Server::RunOnce triggered a blocking paused step
private: bool blockingPausedStepPending{false};
+ /// \brief Whether the simulation runner is currently stepping based on
+ /// WorldControl info (true) or not (false)
+ private: bool stepping{false};
+
friend class LevelManager;
};
}
diff --git a/src/gui/GuiRunner.cc b/src/gui/GuiRunner.cc
index 862e8188f0..50a2c3f075 100644
--- a/src/gui/GuiRunner.cc
+++ b/src/gui/GuiRunner.cc
@@ -19,7 +19,9 @@
#include
#include
#include
+#include
#include
+#include
#include
// Include all components so they have first-class support
@@ -63,6 +65,9 @@ class ignition::gazebo::GuiRunner::Implementation
/// \brief True if the initial state has been received and processed.
public: bool receivedInitialState{false};
+
+ /// \brief Name of WorldControl service
+ public: std::string controlService;
};
/////////////////////////////////////////////////
@@ -115,11 +120,53 @@ GuiRunner::GuiRunner(const std::string &_worldName)
QPointer timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &GuiRunner::UpdatePlugins);
timer->start(33);
+
+ this->dataPtr->controlService = "/world/" + _worldName + "/control/state";
+
+ ignition::gui::App()->findChild<
+ ignition::gui::MainWindow *>()->installEventFilter(this);
}
/////////////////////////////////////////////////
GuiRunner::~GuiRunner() = default;
+/////////////////////////////////////////////////
+bool GuiRunner::eventFilter(QObject *_obj, QEvent *_event)
+{
+ if (_event->type() == ignition::gui::events::WorldControl::kType)
+ {
+ auto worldControlEvent =
+ reinterpret_cast(_event);
+ if (worldControlEvent)
+ {
+ msgs::WorldControlState req;
+ req.mutable_world_control()->CopyFrom(
+ worldControlEvent->WorldControlInfo());
+
+ // share the GUI's ECM with the server if:
+ // 1. Play was pressed
+ // 2. Step was pressed while paused
+ const auto &info = worldControlEvent->WorldControlInfo();
+ const bool pressedStep = info.multi_step() > 0u;
+ const bool pressedPlay = !info.pause() && !pressedStep;
+ const bool pressedStepWhilePaused = info.pause() && pressedStep;
+ if (pressedPlay || pressedStepWhilePaused)
+ req.mutable_state()->CopyFrom(this->dataPtr->ecm.State());
+
+ std::function cb =
+ [](const ignition::msgs::Boolean &/*_rep*/, const bool _result)
+ {
+ if (!_result)
+ ignerr << "Error sharing WorldControl info with the server.\n";
+ };
+ this->dataPtr->node.Request(this->dataPtr->controlService, req, cb);
+ }
+ }
+
+ // Standard event processing
+ return QObject::eventFilter(_obj, _event);
+}
+
/////////////////////////////////////////////////
void GuiRunner::RequestState()
{
diff --git a/src/gui/GuiRunner.hh b/src/gui/GuiRunner.hh
index b2bceaf0e9..1a06098161 100644
--- a/src/gui/GuiRunner.hh
+++ b/src/gui/GuiRunner.hh
@@ -46,6 +46,9 @@ class IGNITION_GAZEBO_GUI_VISIBLE GuiRunner : public QObject
/// \brief Destructor
public: ~GuiRunner() override;
+ // Documentation inherited
+ protected: bool eventFilter(QObject *_obj, QEvent *_event) override;
+
/// \brief Callback when a plugin has been added.
/// This function has no effect and is left here for ABI compatibility.
/// \param[in] _objectName Plugin's object name.
diff --git a/src/gui/gui.config b/src/gui/gui.config
index 36166061f8..36782b6b9d 100644
--- a/src/gui/gui.config
+++ b/src/gui/gui.config
@@ -136,6 +136,7 @@
true
true
true
+ true
diff --git a/src/gui/playback_gui.config b/src/gui/playback_gui.config
index 50cffa6992..4e6d386447 100644
--- a/src/gui/playback_gui.config
+++ b/src/gui/playback_gui.config
@@ -60,6 +60,7 @@
true
true
true
+ true