sim_leds.cpp
Go to the documentation of this file.
1 #include <led_msgs/SetLEDs.h>
2 #include <led_msgs/LEDStateArray.h>
3 
4 #include <ros/ros.h>
5 #include <ros/callback_queue.h>
6 #include <gazebo/common/Plugin.hh>
7 #include <gazebo/rendering/rendering.hh>
8 
9 #if GAZEBO_MAJOR_VERSION >= 9
10 #include <ignition/math/Color.hh>
11 using GazeboColor = ignition::math::Color;
12 #else
13 #include <gazebo/common/Color.hh>
14 using GazeboColor = gazebo::common::Color;
15 #endif
16 
17 #include <string>
18 #include <unordered_map>
19 #include <mutex>
20 #include <vector>
21 
22 #include <thread>
23 
24 namespace sim_led
25 {
26 // Forward declaration of the plugin for led controller
27 class LedVisualPlugin;
28 } //
29 
30 namespace led_controller
31 {
32 
35 {
36 private:
38  enum class Role
39  {
40  Client, // Client: runs on /gazebo_gui, responsible for "preview window"
41  Server // Server: runs on /gazebo, responsible for renders on Gazebo sensors
42  } role;
43 
44  // Pointers to the LED plugins that we know about
45  std::vector<sim_led::LedVisualPlugin*> registeredLeds;
46  // Mutex protecting the vector
47  std::mutex registryMutex;
48  std::string robotNamespace;
49 
50  std::unique_ptr<ros::NodeHandle> nh;
51 
53  // Note: LED state should only be published by the /gazebo node
54  led_msgs::LEDStateArray ledState;
56  // LED state will be read from the topic to avoid creating more services
58 
59  bool setLeds(led_msgs::SetLEDs::Request& req, led_msgs::SetLEDs::Response& resp);
60  void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds);
61 
62 public:
63  LedController(const std::string& robotNamespace) : robotNamespace(robotNamespace)
64  {
65  // We need "libgazebo_ros_api.so" to be loaded
66  if (!ros::isInitialized())
67  {
68  ROS_FATAL_NAMED("LedController", "Tried to load ROS plugin when ROS Gazebo API is not loaded. Please use gazebo_ros node to"
69  "launch Gazebo.");
70  }
71 
72  role = (ros::this_node::getName() == "/gazebo") ? Role::Server : Role::Client;
73  ROS_INFO_NAMED(("LedController_" + robotNamespace).c_str(), "LedController has started (as %s)", role == Role::Client ? "client" : "server");
74 
75  nh.reset(new ros::NodeHandle(robotNamespace));
76  if (role == Role::Server)
77  {
78  setLedsSrv = nh->advertiseService("led/set_leds", &LedController::setLeds, this);
79  statePublisher = nh->advertise<led_msgs::LEDStateArray>("led/state", 1, true);
80  }
81  else
82  {
83  // LED state should be published to the "led/state" topic, so we grab our data there
84  stateSubscriber = nh->subscribe<led_msgs::LEDStateArray>("led/state", 1, &LedController::handleLedsMsg, this);
85  }
86  };
87 
89  {
90  nh->shutdown();
91  }
92 
93  void registerPlugin(sim_led::LedVisualPlugin* plugin, int ledIdx = 0, int totalLeds = 0)
94  {
95  assert(ledIdx < totalLeds);
96  std::lock_guard<std::mutex> lock(registryMutex);
97  if (totalLeds > 0) {
98  registeredLeds.resize(totalLeds);
99  ledState.leds.resize(totalLeds);
100  }
101  ROS_DEBUG_NAMED(("LedController_" + robotNamespace).c_str(), "Registering LED visual plugin to %s (LED id=%d)", (role == Role::Client) ? "client" : "server", ledIdx);
102  registeredLeds[ledIdx] = plugin;
103  ledState.leds[ledIdx].index = ledIdx;
104  if (role == Role::Server)
105  statePublisher.publish(ledState);
106  }
107 
109  {
110  std::lock_guard<std::mutex> lock(registryMutex);
111  auto it = std::find(registeredLeds.begin(), registeredLeds.end(), plugin);
112  if (it != registeredLeds.end())
113  {
114  ROS_DEBUG_STREAM_NAMED(("LedController_" + robotNamespace).c_str(), "Unregistering LED visual plugin (LED id=" << (it - registeredLeds.begin()) << ")");
115  *it = nullptr;
116  }
117  }
118 };
119 
120 // Guards controllers map (static to led_controller::get())
121 std::mutex controllerMutex;
122 
123 LedController& get(std::string robotNamespace)
124 {
125  static std::unordered_map<std::string, std::unique_ptr<LedController>> controllers;
126  std::lock_guard<std::mutex> lock(controllerMutex);
127  auto it = controllers.find(robotNamespace);
128  if (it == controllers.end()) {
129  gzwarn << "Creating new LED controller for namespace " << robotNamespace << "\n";
130  controllers[robotNamespace].reset(new LedController(robotNamespace));
131  return *controllers[robotNamespace];
132  }
133  return *(it->second);
134 }
135 
136 } // led_controller
137 
138 
139 namespace sim_led
140 {
141 
142 class LedVisualPlugin : public gazebo::VisualPlugin {
143 private:
144  std::string ns;
145  gazebo::rendering::VisualPtr vptr;
146 
147 public:
148  void Load(gazebo::rendering::VisualPtr visual, sdf::ElementPtr sdf) override {
149  // FIXME: This is a fragile way to guess our index
150  // FIXME: This is based on an assumption that the parent will have a mangled name
151  // FIXME: (like led_strip::base_link::base_link_fixed_joint_lump__led_00_link_visual_1)
152  // FIXME: This will obviously break if gazebo decides to go with something else
153  auto parentName = sdf->GetParent()->GetAttribute("name")->GetAsString();
154  auto lastDashPos = parentName.rfind('_');
155  int myIndex = 0;
156  if (lastDashPos != std::string::npos)
157  {
158  auto indexStr = parentName.substr(lastDashPos + 1);
159  try {
160  myIndex = std::stoi(indexStr);
161  } catch(const std::exception &e) {
162  gzwarn << "Failed to convert " << indexStr << " to integer: " << e.what() << ", assuming 0\n";
163  myIndex = 0;
164  }
165  }
166  else
167  {
168  gzerr << "Failed to parse parent name: " << parentName << "; could not determine our index\n";
169  }
170 
171  ns = "";
172  if (sdf->HasElement("robotNamespace")) {
173  ns = sdf->Get<std::string>("robotNamespace");
174  }
175  if (!sdf->HasElement("ledCount")) {
176  gzerr << "ledCount is not set, but is required for the plugin to function correctly\n";
177  return;
178  }
179  int totalLeds = sdf->Get<int>("ledCount");
180 
181  vptr = visual;
182  led_controller::get(ns).registerPlugin(this, myIndex, totalLeds);
183  }
184 
186  {
188  }
189 
190  void SetColor(const GazeboColor& emissive)
191  {
192  vptr->SetEmissive(emissive);
193  }
194 
195 };
196 }
197 
198 // FIXME: These two functions basically do the same thing, maybe they can be merged?
199 bool led_controller::LedController::setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
200 {
201  std::lock_guard<std::mutex> lock(registryMutex);
202  for(const auto& led : req.leds)
203  {
204  if (led.index < registeredLeds.size()) {
205  auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
206  auto ledPlugin = registeredLeds[led.index];
207  if (ledPlugin) ledPlugin->SetColor(color);
208  ledState.leds[led.index].r = led.r;
209  ledState.leds[led.index].g = led.g;
210  ledState.leds[led.index].b = led.b;
211  }
212  }
213  statePublisher.publish(ledState);
214  resp.success = true;
215  return true;
216 }
217 
218 void led_controller::LedController::handleLedsMsg(const led_msgs::LEDStateArrayConstPtr& leds)
219 {
220  std::lock_guard<std::mutex> lock(registryMutex);
221  for(const auto& led : leds->leds)
222  {
223  if (led.index < registeredLeds.size())
224  {
225  auto color = GazeboColor(led.r / 255.0f, led.g / 255.0f, led.b / 255.0f);
226  auto ledPlugin = registeredLeds[led.index];
227  if (ledPlugin) ledPlugin->SetColor(color);
228  }
229  }
230 }
231 
LedController & get(std::string robotNamespace)
Definition: sim_leds.cpp:123
gazebo::rendering::VisualPtr vptr
Definition: sim_leds.cpp:145
#define ROS_INFO_NAMED(name,...)
#define ROS_DEBUG_STREAM_NAMED(name, args)
LedController: a class that provides ROS interface for the LEDs.
Definition: sim_leds.cpp:34
ros::ServiceServer setLedsSrv
Definition: sim_leds.cpp:52
void handleLedsMsg(const led_msgs::LEDStateArrayConstPtr &leds)
Definition: sim_leds.cpp:218
void unregisterPlugin(sim_led::LedVisualPlugin *plugin)
Definition: sim_leds.cpp:108
ROSCPP_DECL bool isInitialized()
Role
Role for the current controller.
Definition: sim_leds.cpp:38
led_msgs::LEDStateArray ledState
Definition: sim_leds.cpp:54
ROSCPP_DECL const std::string & getName()
std::unique_ptr< ros::NodeHandle > nh
Definition: sim_leds.cpp:50
#define ROS_DEBUG_NAMED(name,...)
GZ_REGISTER_VISUAL_PLUGIN(sim_led::LedVisualPlugin)
void publish(const boost::shared_ptr< M > &message) const
void SetColor(const GazeboColor &emissive)
Definition: sim_leds.cpp:190
gazebo::common::Color GazeboColor
Definition: sim_leds.cpp:14
bool setLeds(led_msgs::SetLEDs::Request &req, led_msgs::SetLEDs::Response &resp)
Definition: sim_leds.cpp:199
ros::Subscriber stateSubscriber
Definition: sim_leds.cpp:57
void registerPlugin(sim_led::LedVisualPlugin *plugin, int ledIdx=0, int totalLeds=0)
Definition: sim_leds.cpp:93
std::vector< sim_led::LedVisualPlugin * > registeredLeds
Definition: sim_leds.cpp:45
void Load(gazebo::rendering::VisualPtr visual, sdf::ElementPtr sdf) override
Definition: sim_leds.cpp:148
LedController(const std::string &robotNamespace)
Definition: sim_leds.cpp:63
#define ROS_FATAL_NAMED(name,...)
std::mutex controllerMutex
Definition: sim_leds.cpp:121
ros::Publisher statePublisher
Definition: sim_leds.cpp:55


clover_simulation
Author(s): Alexey Rogachevskiy, Andrey Ryabov, Arthur Golubtsov, Oleg Kalachev, Svyatoslav Zhuravlev
autogenerated on Mon Feb 28 2022 22:08:36