device_nodelet.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Roboception GmbH
3  * All rights reserved
4  *
5  * Author: Heiko Hirschmueller, Christian Emmerich
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its contributors
18  * may be used to endorse or promote products derived from this software without
19  * specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "device_nodelet.h"
44 
45 #include <rc_genicam_api/device.h>
46 #include <rc_genicam_api/stream.h>
47 #include <rc_genicam_api/buffer.h>
48 #include <rc_genicam_api/config.h>
50 
52 
54 #include <exception>
55 
56 #include <sstream>
57 #include <stdexcept>
58 
59 #include <ros/ros.h>
60 #include <geometry_msgs/PoseWithCovarianceStamped.h>
62 
63 namespace {
64 
66  bool isRcVisardDevice(const std::string& vendor, const std::string& model)
67  {
68  bool isKuka3DPerception = vendor.find("KUKA") != std::string::npos && model.find("3d_perception") != std::string::npos;
69  bool isRcVisard = vendor.find("Roboception") != std::string::npos && model.find("rc_visard") != std::string::npos;
70  return isKuka3DPerception || isRcVisard;
71  }
72 
74  std::vector<std::string> discoverRcVisardDevices()
75  {
76  std::vector<std::string> device_ids;
77  for (auto system : rcg::System::getSystems())
78  {
79  system->open();
80  for (auto interf : system->getInterfaces())
81  {
82  interf->open();
83  for (auto dev : interf->getDevices())
84  {
85  if (isRcVisardDevice(dev->getVendor(), dev->getModel()))
86  {
87  device_ids.push_back(dev->getID());
88  }
89  }
90  interf->close();
91  }
92  system->close();
93  }
94  return device_ids;
95  }
96 }
97 
98 namespace rc
99 {
100 namespace rcd = dynamics;
101 
102 #define ROS_HAS_STEADYTIME (ROS_VERSION_MINIMUM(1, 13, 1) || ((ROS_VERSION_MINOR == 12) && ROS_VERSION_PATCH >= 8))
103 
105  const std::string& stream, ros::NodeHandle& nh,
106  const std::string& frame_id_prefix, bool tfEnabled)
107 {
108  if (stream == "pose")
109  {
110  return ThreadedStream::Ptr(new PoseStream(rcdIface, stream, nh, frame_id_prefix, tfEnabled));
111  }
112  if (stream == "pose_ins" || stream == "pose_rt" || stream == "pose_rt_ins" || stream == "imu")
113  {
114  return ThreadedStream::Ptr(new Protobuf2RosStream(rcdIface, stream, nh, frame_id_prefix));
115  }
116  if (stream == "dynamics" || stream == "dynamics_ins")
117  {
118  return ThreadedStream::Ptr(new DynamicsStream(rcdIface, stream, nh, frame_id_prefix));
119  }
120 
121  throw std::runtime_error(std::string("Not yet implemented! Stream type: ") + stream);
122 }
123 
125 {
126  reconfig = 0;
127  dev_supports_gain = false;
128  dev_supports_wb = false;
131  iocontrol_avail = false;
132  level = 0;
133 
135 
137 
138  stopRecoverThread = false;
139  recoveryRequested = true;
140  cntConsecutiveRecoveryFails = -1; // first time not giving any warnings
144 }
145 
147 {
148  std::cout << "rc_visard_driver: Shutting down" << std::endl;
149 
150  // signal running threads and wait until they finish
151 
152  stopImageThread = true;
153  dynamicsStreams->stop_all();
154  stopRecoverThread = true;
155 
156  if (imageThread.joinable())
157  imageThread.join();
158  dynamicsStreams->join_all();
159  if (recoverThread.joinable())
160  recoverThread.join();
161 
162  delete reconfig;
163 
165 }
166 
168 {
169  // run initialization and recover routine in separate thread
171 
172  // add callbacks for diagnostics publishing
175 }
176 
178 {
179  // get parameter configuration
180 
182 
183  // device defaults to empty string which serves as a wildcard to connect to any
184  // rc_visard as long as only one sensor is available in the network
185  std::string device = "";
186  std::string access = "control";
188 
189  tfEnabled = false;
192  tfPrefix = (ns != "") ? ns + "_" : "";
193 
194  pnh.param("device", device, device);
195  pnh.param("gev_access", access, access);
196  pnh.param("max_reconnects", maxNumRecoveryTrials, maxNumRecoveryTrials);
197  pnh.param("enable_tf", tfEnabled, tfEnabled);
198  pnh.param("autostart_dynamics", autostartDynamics, autostartDynamics);
199  pnh.param("autostart_dynamics_with_slam", autostartSlam, autostartSlam);
200  pnh.param("autostop_dynamics", autostopDynamics, autostopDynamics);
201  pnh.param("autopublish_trajectory", autopublishTrajectory, autopublishTrajectory);
202 
203  rcg::Device::ACCESS access_id;
204  if (access == "exclusive")
205  {
206  access_id = rcg::Device::EXCLUSIVE;
207  }
208  else if (access == "control")
209  {
210  access_id = rcg::Device::CONTROL;
211  }
212  else if (access == "off")
213  {
214  access_id = rcg::Device::READONLY;
215  }
216  else
217  {
218  ROS_FATAL_STREAM("rc_visard_driver: Access must be 'control', 'exclusive' or 'off': " << access);
219  return;
220  }
221 
222  // setup service for depth acquisition trigger
223 
225 
226  // setup services for starting and stopping rcdynamics module
227 
234 
237  slamSaveMapService = pnh.advertiseService("slam_save_map", &DeviceNodelet::saveSlamMap, this);
238  slamLoadMapService = pnh.advertiseService("slam_load_map", &DeviceNodelet::loadSlamMap, this);
239  slamRemoveMapService = pnh.advertiseService("slam_remove_map", &DeviceNodelet::removeSlamMap, this);
240 
242  {
243  trajPublisher = getNodeHandle().advertise<nav_msgs::Path>("trajectory", 10);
244  }
245 
246  // run start-keep-alive-and-recover loop
247 
248  while (!stopRecoverThread && (
249  (maxNumRecoveryTrials < 0) ||
251  ))
252  {
253  // check if everything is running smoothly
254 
255  if (!recoveryRequested)
256  {
257  bool allSucceeded = (!imageRequested || imageSuccess);
258  if ((cntConsecutiveRecoveryFails > 0) && allSucceeded)
259  {
261  ROS_INFO("rc_visard_driver: Device successfully recovered from previous fail(s)!");
262  }
263 
264  updater.update(); // regularly update the status for publishing diagnostics (rate limited by parameter '~diagnostic_period')
265  usleep(1000 * 100);
266  continue;
267  }
268 
269  // it's not running smoothly, we need recovery
270 
274  }
275  updater.force_update(); // immediately update the diagnostics status
276 
277  // stop image and dynamics threads
278 
279  stopImageThread = true;
280  dynamicsStreams->stop_all();
281 
282  if (imageThread.joinable())
283  imageThread.join();
284  dynamicsStreams->join_all();
285 
286  // try discover, open and bring up device
287 
288  bool successfullyOpened = false;
289  while (!stopRecoverThread && !successfullyOpened && (
290  (maxNumRecoveryTrials < 0) ||
292  ))
293  {
294  // if we are recovering, put warning and wait before retrying
295 
297  {
298  ROS_ERROR_STREAM("rc_visard_driver: Failed or lost connection. Trying to recover"
299  " rc_visard_driver from failed state ("
301  << ((maxNumRecoveryTrials>=0) ? std::string("/") + std::to_string(maxNumRecoveryTrials) : "" )
302  << ")...");
303  usleep(1000 * 500);
304  }
305 
306  try
307  {
308  if (rcgdev)
309  {
310  rcgdev->close();
311  }
312 
313  if (device.size() == 0)
314  {
315  ROS_INFO("No device ID given in the private parameter 'device'! Trying to auto-detect a single rc_visard device in the network...");
316  auto device_ids = discoverRcVisardDevices();
317  if (device_ids.size() != 1)
318  {
320  throw std::runtime_error("Auto-connection with rc_visard device failed because none or multiple devices were found!");
321  }
322  ROS_INFO_STREAM("Found rc_visard device '" << device_ids[0] << "'");
323  rcgdev = rcg::getDevice(device_ids[0].c_str());
324  } else {
325  rcgdev = rcg::getDevice(device.c_str());
326  }
327 
328  if (!rcgdev)
329  {
331  throw std::invalid_argument("Unknown or non-unique device '" + device + "'");
332  }
333 
334  ROS_INFO_STREAM("rc_visard_driver: Opening connection to '" << rcgdev->getID() << "'");
335  rcgdev->open(access_id);
336  rcgnodemap = rcgdev->getRemoteNodeMap();
337 
338  // extract some diagnostics data from device
339  dev_serialno = rcg::getString(rcgnodemap, "DeviceID", true);
340  dev_macaddr = rcg::getString(rcgnodemap, "GevMACAddress", true);
341  dev_ipaddr = rcg::getString(rcgnodemap, "GevCurrentIPAddress", true);
342  dev_version = rcg::getString(rcgnodemap, "DeviceVersion", true);
343  gev_userid = rcg::getString(rcgnodemap, "DeviceUserID", true);
344  gev_packet_size = rcg::getString(rcgnodemap, "GevSCPSPacketSize", true);
345 
348 
349  // instantiating dynamics interface and autostart dynamics on sensor if desired
350 
351  std::string currentIPAddress = rcg::getString(rcgnodemap, "GevCurrentIPAddress", true);
352  dynamicsInterface = rcd::RemoteInterface::create(currentIPAddress);
354  {
355  std_srvs::Trigger::Request dummyreq;
356  std_srvs::Trigger::Response dummyresp;
357  if (!((autostartSlam && this->dynamicsStartSlam(dummyreq, dummyresp)) ||
358  (autostartDynamics && this->dynamicsStart(dummyreq, dummyresp))))
359  { // autostart failed!
360  ROS_WARN("rc_visard_driver: Could not auto-start dynamics module!");
362  continue; // to next trial!
363  }
364  }
365 
366  // add streaming thread for each available stream on rc_visard device
367 
368  auto availStreams = dynamicsInterface->getAvailableStreams();
370  for (const auto& streamName : availStreams)
371  {
372  try
373  {
374  auto newStream =
376  dynamicsStreams->add(newStream);
377  }
378  catch (const std::exception& e)
379  {
380  ROS_WARN_STREAM("rc_visard_driver: Unable to create dynamics stream of type " << streamName << ": "
381  << e.what());
382  }
383  }
384 
385  successfullyOpened = true;
386  }
387  catch (std::exception& ex)
388  {
390  ROS_ERROR_STREAM("rc_visard_driver: " << ex.what());
391  }
392  }
393  if (stopRecoverThread)
394  break;
395 
397  {
398  ROS_FATAL_STREAM("rc_visard_driver: could not recover from failed state!");
399  break;
400  }
401 
402  // start grabbing threads
403 
404  recoveryRequested = false;
405  if (access_id != rcg::Device::READONLY)
406  {
407  imageThread = std::thread(&DeviceNodelet::grab, this, device, access_id);
408  }
409  dynamicsStreams->start_all();
410  }
411 
412  if (autostopDynamics)
413  {
414  std_srvs::Trigger::Request dummyreq;
415  std_srvs::Trigger::Response dummyresp;
416  ROS_INFO("rc_visard_driver: Autostop dynamics ...");
417  if (!this->dynamicsStop(dummyreq, dummyresp))
418  { // autostop failed!
419  ROS_WARN("rc_visard_driver: Could not auto-stop dynamics module!");
420  }
421  }
422  std::cout << "rc_visard_driver: stopped." << std::endl;
423 }
424 
425 void DeviceNodelet::initConfiguration(const std::shared_ptr<GenApi::CNodeMapRef>& nodemap,
426  rc_visard_driver::rc_visard_driverConfig& cfg, rcg::Device::ACCESS access)
427 {
429 
430  // get current camera configuration
431 
432  cfg.camera_fps = rcg::getFloat(nodemap, "AcquisitionFrameRate", 0, 0, true);
433 
434  std::string v = rcg::getEnum(nodemap, "ExposureAuto", true);
435  cfg.camera_exp_auto = (v != "Off");
436 
437  cfg.camera_exp_value = rcg::getFloat(nodemap, "ExposureTime", 0, 0, true) / 1000000;
438  cfg.camera_exp_max = rcg::getFloat(nodemap, "ExposureTimeAutoMax", 0, 0, true) / 1000000;
439 
440  // get optional gain value
441 
442  v = rcg::getEnum(nodemap, "GainSelector", false);
443  if (v.size() > 0)
444  {
445  dev_supports_gain = true;
446 
447  if (v != "All")
448  {
449  dev_supports_gain = rcg::setEnum(nodemap, "GainSelector", "All", true);
450  }
451 
452  if (dev_supports_gain)
453  {
454  cfg.camera_gain_value = rcg::getFloat(nodemap, "Gain", 0, 0, true);
455  }
456  }
457  else
458  {
459  ROS_WARN("rc_visard_driver: Device does not support setting gain. gain_value is without function.");
460 
461  dev_supports_gain = false;
462  cfg.camera_gain_value = 0;
463  }
464 
465  // get optional white balancing values (only for color camera)
466 
467  v = rcg::getEnum(nodemap, "BalanceWhiteAuto", false);
468  if (v.size() > 0)
469  {
470  dev_supports_wb = true;
471  cfg.camera_wb_auto = (v != "Off");
472  rcg::setEnum(nodemap, "BalanceRatioSelector", "Red", true);
473  cfg.camera_wb_ratio_red = rcg::getFloat(nodemap, "BalanceRatio", 0, 0, true);
474  rcg::setEnum(nodemap, "BalanceRatioSelector", "Blue", true);
475  cfg.camera_wb_ratio_blue = rcg::getFloat(nodemap, "BalanceRatio", 0, 0, true);
476  }
477  else
478  {
479  ROS_WARN("rc_visard_driver: Not a color camera. wb_auto, wb_ratio_red and wb_ratio_blue are without function.");
480 
481  dev_supports_wb = false;
482  cfg.camera_wb_auto = true;
483  cfg.camera_wb_ratio_red = 1;
484  cfg.camera_wb_ratio_blue = 1;
485  }
486 
487  // get current depth image configuration
488 
489  v = rcg::getEnum(nodemap, "DepthAcquisitionMode", false);
490  if (v.size() > 0)
491  {
493  cfg.depth_acquisition_mode = v;
494  }
495  else
496  {
497  ROS_WARN("rc_visard_driver: Device does not support triggering depth images. depth_acquisition_mode is without function.");
498 
500  cfg.depth_acquisition_mode = "Continuous";
501  }
502 
503  v = rcg::getEnum(nodemap, "DepthQuality", true);
504  cfg.depth_quality = v;
505 
506  cfg.depth_static_scene = rcg::getBoolean(nodemap, "DepthStaticScene", false);
507  cfg.depth_disprange = rcg::getInteger(nodemap, "DepthDispRange", 0, 0, true);
508  cfg.depth_seg = rcg::getInteger(nodemap, "DepthSeg", 0, 0, true);
509  cfg.depth_median = rcg::getInteger(nodemap, "DepthMedian", 0, 0, true);
510  cfg.depth_fill = rcg::getInteger(nodemap, "DepthFill", 0, 0, true);
511  cfg.depth_minconf = rcg::getFloat(nodemap, "DepthMinConf", 0, 0, true);
512  cfg.depth_mindepth = rcg::getFloat(nodemap, "DepthMinDepth", 0, 0, true);
513  cfg.depth_maxdepth = rcg::getFloat(nodemap, "DepthMaxDepth", 0, 0, true);
514  cfg.depth_maxdeptherr = rcg::getFloat(nodemap, "DepthMaxDepthErr", 0, 0, true);
515 
516  cfg.ptp_enabled = rcg::getBoolean(nodemap, "GevIEEE1588", false);
517 
518  // fix for rc_visard < 1.5
519 
520  if (cfg.depth_quality[0] == 'S')
521  {
522  cfg.depth_quality = "High";
523  cfg.depth_static_scene = true;
524  }
525 
526  // check for stereo_plus license
527 
528  try
529  {
530  cfg.depth_smooth = rcg::getBoolean(nodemap, "DepthSmooth", true);
531  stereo_plus_avail = nodemap->_GetNode("DepthSmooth")->GetAccessMode() == GenApi::RW;
532 
533  if (!stereo_plus_avail)
534  {
535  ROS_INFO("rc_visard_driver: License for stereo_plus not available, disabling depth_quality=Full and depth_smooth.");
536  }
537  }
538  catch (const std::exception&)
539  {
540  ROS_WARN("rc_visard_driver: rc_visard has an older firmware, disabling depth_quality=Full and depth_smooth.");
541  stereo_plus_avail = false;
542  }
543 
544  // check if io-control is available and get values
545 
546  try
547  {
548  // disable filtering images on the sensor
549 
550  rcg::setEnum(nodemap, "AcquisitionAlternateFilter", "Off", false);
551 
552  // get current values
553 
554  rcg::setEnum(nodemap, "LineSelector", "Out1", true);
555  cfg.out1_mode = rcg::getString(nodemap, "LineSource", true);
556 
557  rcg::setEnum(nodemap, "LineSelector", "Out2", true);
558  cfg.out2_mode = rcg::getString(nodemap, "LineSource", true);
559 
560  // check if license for IO control functions is available
561 
562  iocontrol_avail = nodemap->_GetNode("LineSource")->GetAccessMode() == GenApi::RW;
563 
564  if (!iocontrol_avail)
565  {
566  ROS_INFO("rc_visard_driver: License for iocontrol module not available, disabling out1_mode and out2_mode.");
567  }
568 
569  // enabling chunk data for getting the live line status
570 
571  rcg::setBoolean(nodemap, "ChunkModeActive", iocontrol_avail, false);
572  }
573  catch (const std::exception&)
574  {
575  ROS_WARN("rc_visard_driver: rc_visard has an older firmware, IO control functions are not available.");
576 
577  cfg.out1_mode = "ExposureActive";
578  cfg.out2_mode = "Low";
579 
580  iocontrol_avail = false;
581  }
582 
583 
584  // try to get ROS parameters: if parameter is not set in parameter server, default to current sensor configuration
585 
586  pnh.param("camera_fps", cfg.camera_fps, cfg.camera_fps);
587  pnh.param("camera_exp_auto", cfg.camera_exp_auto, cfg.camera_exp_auto);
588  pnh.param("camera_exp_value", cfg.camera_exp_value, cfg.camera_exp_value);
589  pnh.param("camera_gain_value", cfg.camera_gain_value, cfg.camera_gain_value);
590  pnh.param("camera_exp_max", cfg.camera_exp_max, cfg.camera_exp_max);
591  pnh.param("camera_wb_auto", cfg.camera_wb_auto, cfg.camera_wb_auto);
592  pnh.param("camera_wb_ratio_red", cfg.camera_wb_ratio_red, cfg.camera_wb_ratio_red);
593  pnh.param("camera_wb_ratio_blue", cfg.camera_wb_ratio_blue, cfg.camera_wb_ratio_blue);
594  pnh.param("depth_acquisition_mode", cfg.depth_acquisition_mode, cfg.depth_acquisition_mode);
595  pnh.param("depth_quality", cfg.depth_quality, cfg.depth_quality);
596  pnh.param("depth_static_scene", cfg.depth_static_scene, cfg.depth_static_scene);
597  pnh.param("depth_disprange", cfg.depth_disprange, cfg.depth_disprange);
598  pnh.param("depth_seg", cfg.depth_seg, cfg.depth_seg);
599  pnh.param("depth_smooth", cfg.depth_smooth, cfg.depth_smooth);
600  pnh.param("depth_median", cfg.depth_median, cfg.depth_median);
601  pnh.param("depth_fill", cfg.depth_fill, cfg.depth_fill);
602  pnh.param("depth_minconf", cfg.depth_minconf, cfg.depth_minconf);
603  pnh.param("depth_mindepth", cfg.depth_mindepth, cfg.depth_mindepth);
604  pnh.param("depth_maxdepth", cfg.depth_maxdepth, cfg.depth_maxdepth);
605  pnh.param("depth_maxdeptherr", cfg.depth_maxdeptherr, cfg.depth_maxdeptherr);
606  pnh.param("ptp_enabled", cfg.ptp_enabled, cfg.ptp_enabled);
607  pnh.param("out1_mode", cfg.out1_mode, cfg.out1_mode);
608  pnh.param("out2_mode", cfg.out2_mode, cfg.out2_mode);
609 
610  // set parameters on parameter server so that dynamic reconfigure picks them up
611 
612  pnh.setParam("camera_fps", cfg.camera_fps);
613  pnh.setParam("camera_exp_auto", cfg.camera_exp_auto);
614  pnh.setParam("camera_exp_value", cfg.camera_exp_value);
615  pnh.setParam("camera_gain_value", cfg.camera_gain_value);
616  pnh.setParam("camera_exp_max", cfg.camera_exp_max);
617  pnh.setParam("camera_wb_auto", cfg.camera_wb_auto);
618  pnh.setParam("camera_wb_ratio_red", cfg.camera_wb_ratio_red);
619  pnh.setParam("camera_wb_ratio_blue", cfg.camera_wb_ratio_blue);
620  pnh.setParam("depth_acquisition_mode", cfg.depth_acquisition_mode);
621  pnh.setParam("depth_quality", cfg.depth_quality);
622  pnh.setParam("depth_static_scene", cfg.depth_static_scene);
623  pnh.setParam("depth_disprange", cfg.depth_disprange);
624  pnh.setParam("depth_seg", cfg.depth_seg);
625  pnh.setParam("depth_smooth", cfg.depth_smooth);
626  pnh.setParam("depth_median", cfg.depth_median);
627  pnh.setParam("depth_fill", cfg.depth_fill);
628  pnh.setParam("depth_minconf", cfg.depth_minconf);
629  pnh.setParam("depth_mindepth", cfg.depth_mindepth);
630  pnh.setParam("depth_maxdepth", cfg.depth_maxdepth);
631  pnh.setParam("depth_maxdeptherr", cfg.depth_maxdeptherr);
632  pnh.setParam("ptp_enabled", cfg.ptp_enabled);
633  pnh.setParam("out1_mode", cfg.out1_mode);
634  pnh.setParam("out2_mode", cfg.out2_mode);
635 
636  if (reconfig == 0)
637  {
638  // TODO: we need to dismangle initialization of dynreconfserver from not-READONLY-access-condition
639  reconfig = new dynamic_reconfigure::Server<rc_visard_driver::rc_visard_driverConfig>(pnh);
640  }
641  // always set callback to (re)load configuration even after recovery
642  dynamic_reconfigure::Server<rc_visard_driver::rc_visard_driverConfig>::CallbackType cb;
643  cb = boost::bind(&DeviceNodelet::reconfigure, this, _1, _2);
644  reconfig->setCallback(cb);
645 }
646 
647 void DeviceNodelet::reconfigure(rc_visard_driver::rc_visard_driverConfig& c, uint32_t l)
648 {
649  std::lock_guard<std::mutex> lock(mtx);
650 
651  // check and correct parameters
652 
653  if (!dev_supports_gain)
654  {
655  c.camera_gain_value = 0;
656  l &= ~8192;
657  }
658 
659  c.camera_gain_value = round(c.camera_gain_value / 6) * 6;
660 
661  if (!dev_supports_wb)
662  {
663  c.camera_wb_auto = true;
664  c.camera_wb_ratio_red = 1;
665  c.camera_wb_ratio_blue = 1;
666  l &= ~(16384 | 32768 | 65536);
667  }
668 
670  {
671  c.depth_acquisition_mode = c.depth_acquisition_mode.substr(0, 1);
672 
673  if (c.depth_acquisition_mode[0] == 'S')
674  {
675  c.depth_acquisition_mode = "SingleFrame";
676  }
677  else
678  {
679  c.depth_acquisition_mode = "Continuous";
680  }
681  }
682  else
683  {
684  c.depth_acquisition_mode = "Continuous";
685  l &= ~1048576;
686  }
687 
688  if (c.depth_quality[0] == 'L')
689  {
690  c.depth_quality = "Low";
691  }
692  else if (c.depth_quality[0] == 'M')
693  {
694  c.depth_quality = "Medium";
695  }
696  else if (c.depth_quality[0] == 'F' && stereo_plus_avail)
697  {
698  c.depth_quality = "Full";
699  }
700  else
701  {
702  c.depth_quality = "High";
703  }
704 
705  if (!stereo_plus_avail)
706  {
707  c.depth_smooth=false;
708  l &= ~4194304;
709  }
710 
711  if (iocontrol_avail)
712  {
713  if (c.out1_mode != "Low" && c.out1_mode != "High" && c.out1_mode != "ExposureActive" &&
714  c.out1_mode != "ExposureAlternateActive")
715  {
716  c.out1_mode = "ExposureActive";
717  }
718 
719  if (c.out2_mode != "Low" && c.out2_mode != "High" && c.out2_mode != "ExposureActive" &&
720  c.out2_mode != "ExposureAlternateActive")
721  {
722  c.out2_mode = "Low";
723  }
724  }
725  else
726  {
727  c.out1_mode = "ExposureActive";
728  c.out2_mode = "Low";
729  }
730 
731  // copy config for using it in the grabbing thread
732 
733  config = c;
734  level |= l;
735 }
736 
737 namespace
738 {
739 /*
740  Set changed values of the given configuration.
741 */
742 
743 void setConfiguration(const std::shared_ptr<GenApi::CNodeMapRef>& nodemap,
744  const rc_visard_driver::rc_visard_driverConfig& cfg, uint32_t lvl, bool iocontrol_avail)
745 {
746  uint32_t prev_lvl = 0;
747 
748  while (lvl != 0 && prev_lvl != lvl)
749  {
750  prev_lvl = lvl; // used to avoid endless loops
751 
752  try
753  {
754  // NOTE: The flags used in lvl are defined in cfg/rc_visard_driver.cfg
755 
756  // set changed values via genicam
757 
758  if (lvl & 1)
759  {
760  lvl &= ~1;
761  rcg::setFloat(nodemap, "AcquisitionFrameRate", cfg.camera_fps, true);
762  }
763 
764  if (lvl & 2)
765  {
766  lvl &= ~2;
767 
768  if (cfg.camera_exp_auto)
769  {
770  rcg::setEnum(nodemap, "ExposureAuto", "Continuous", true);
771  }
772  else
773  {
774  rcg::setEnum(nodemap, "ExposureAuto", "Off", true);
775  }
776  }
777 
778  if (lvl & 4)
779  {
780  lvl &= ~4;
781  rcg::setFloat(nodemap, "ExposureTime", 1000000 * cfg.camera_exp_value, true);
782  }
783 
784  if (lvl & 8192)
785  {
786  lvl &= ~8192;
787  rcg::setFloat(nodemap, "Gain", cfg.camera_gain_value, true);
788  }
789 
790  if (lvl & 8)
791  {
792  lvl &= ~8;
793  rcg::setFloat(nodemap, "ExposureTimeAutoMax", 1000000 * cfg.camera_exp_max, true);
794  }
795 
796  if (lvl & 16384)
797  {
798  lvl &= ~16384;
799 
800  if (cfg.camera_wb_auto)
801  {
802  rcg::setEnum(nodemap, "BalanceWhiteAuto", "Continuous", false);
803  }
804  else
805  {
806  rcg::setEnum(nodemap, "BalanceWhiteAuto", "Off", false);
807  }
808  }
809 
810  if (lvl & 32768)
811  {
812  lvl &= ~32768;
813 
814  rcg::setEnum(nodemap, "BalanceRatioSelector", "Red", false);
815  rcg::setFloat(nodemap, "BalanceRatio", cfg.camera_wb_ratio_red, false);
816  }
817 
818  if (lvl & 65536)
819  {
820  lvl &= ~65536;
821 
822  rcg::setEnum(nodemap, "BalanceRatioSelector", "Blue", false);
823  rcg::setFloat(nodemap, "BalanceRatio", cfg.camera_wb_ratio_blue, false);
824  }
825 
826  if (lvl & 1048576)
827  {
828  lvl &= ~1048576;
829 
830  std::vector<std::string> list;
831  rcg::getEnum(nodemap, "DepthAcquisitionMode", list, true);
832 
833  std::string val;
834  for (size_t i = 0; i < list.size(); i++)
835  {
836  if (list[i].compare(0, 1, cfg.depth_acquisition_mode, 0, 1) == 0)
837  {
838  val = list[i];
839  }
840  }
841 
842  if (val.size() > 0)
843  {
844  rcg::setEnum(nodemap, "DepthAcquisitionMode", val.c_str(), true);
845  }
846  }
847 
848  if (lvl & 16)
849  {
850  lvl &= ~16;
851 
852  std::vector<std::string> list;
853  rcg::getEnum(nodemap, "DepthQuality", list, true);
854 
855  std::string val;
856 
857  if (cfg.depth_quality == "High" && cfg.depth_static_scene)
858  {
859  // support for rc_visard < 1.5
860 
861  for (size_t i = 0; i < list.size() && val.size() == 0; i++)
862  {
863  if (list[i].compare(0, 1, "StaticHigh", 0, 1) == 0)
864  {
865  val = "StaticHigh";
866  }
867  }
868  }
869 
870  for (size_t i = 0; i < list.size() && val.size() == 0; i++)
871  {
872  if (list[i].compare(0, 1, cfg.depth_quality, 0, 1) == 0)
873  {
874  val = list[i];
875  }
876  }
877 
878  if (val.size() > 0)
879  {
880  rcg::setEnum(nodemap, "DepthQuality", val.c_str(), true);
881  }
882  }
883 
884  if (lvl & 2097152)
885  {
886  lvl &= ~2097152;
887 
888  if (!rcg::setBoolean(nodemap, "DepthStaticScene", cfg.depth_static_scene, false))
889  {
890  // support for rc_visard < 1.5
891 
892  std::string quality = cfg.depth_quality;
893 
894  if (cfg.depth_static_scene && quality == "High")
895  {
896  quality = "StaticHigh";
897  }
898 
899  std::vector<std::string> list;
900  rcg::getEnum(nodemap, "DepthQuality", list, true);
901 
902  std::string val;
903  for (size_t i = 0; i < list.size() && val.size() == 0; i++)
904  {
905  if (list[i].compare(0, 1, quality, 0, 1) == 0)
906  {
907  val = list[i];
908  }
909  }
910 
911  if (val.size() > 0)
912  {
913  rcg::setEnum(nodemap, "DepthQuality", val.c_str(), true);
914  }
915  }
916  }
917 
918  if (lvl & 32)
919  {
920  lvl &= ~32;
921  rcg::setInteger(nodemap, "DepthDispRange", cfg.depth_disprange, true);
922  }
923 
924  if (lvl & 64)
925  {
926  lvl &= ~64;
927  rcg::setInteger(nodemap, "DepthSeg", cfg.depth_seg, true);
928  }
929 
930  if (lvl & 4194304)
931  {
932  lvl &= ~4194304;
933  rcg::setBoolean(nodemap, "DepthSmooth", cfg.depth_smooth, false);
934  }
935 
936  if (lvl & 128)
937  {
938  lvl &= ~128;
939  rcg::setInteger(nodemap, "DepthMedian", cfg.depth_median, true);
940  }
941 
942  if (lvl & 256)
943  {
944  lvl &= ~256;
945  rcg::setInteger(nodemap, "DepthFill", cfg.depth_fill, true);
946  }
947 
948  if (lvl & 512)
949  {
950  lvl &= ~512;
951  rcg::setFloat(nodemap, "DepthMinConf", cfg.depth_minconf, true);
952  }
953 
954  if (lvl & 1024)
955  {
956  lvl &= ~1024;
957  rcg::setFloat(nodemap, "DepthMinDepth", cfg.depth_mindepth, true);
958  }
959 
960  if (lvl & 2048)
961  {
962  lvl &= ~2048;
963  rcg::setFloat(nodemap, "DepthMaxDepth", cfg.depth_maxdepth, true);
964  }
965 
966  if (lvl & 4096)
967  {
968  lvl &= ~4096;
969  rcg::setFloat(nodemap, "DepthMaxDepthErr", cfg.depth_maxdeptherr, true);
970  }
971 
972  if (lvl & 131072)
973  {
974  lvl &= ~131072;
975  rcg::setBoolean(nodemap, "GevIEEE1588", cfg.ptp_enabled, true);
976  }
977 
978  if (lvl & 262144)
979  {
980  lvl &= ~262144;
981 
982  if (iocontrol_avail)
983  {
984  rcg::setEnum(nodemap, "LineSelector", "Out1", true);
985  rcg::setEnum(nodemap, "LineSource", cfg.out1_mode.c_str(), true);
986  }
987  }
988 
989  if (lvl & 524288)
990  {
991  lvl &= ~524288;
992 
993  if (iocontrol_avail)
994  {
995  rcg::setEnum(nodemap, "LineSelector", "Out2", true);
996  rcg::setEnum(nodemap, "LineSource", cfg.out2_mode.c_str(), true);
997  }
998  }
999  }
1000  catch (const std::exception& ex)
1001  {
1002  ROS_ERROR_STREAM("rc_visard_driver: " << ex.what());
1003  }
1004  }
1005 }
1006 
1007 /*
1008  Disable all available components.
1009 
1010  @param nodemap Feature nodemap.
1011 */
1012 
1013 void disableAll(const std::shared_ptr<GenApi::CNodeMapRef>& nodemap)
1014 {
1015  std::vector<std::string> component;
1016 
1017  rcg::getEnum(nodemap, "ComponentSelector", component, true);
1018 
1019  for (size_t i = 0; i < component.size(); i++)
1020  {
1021  rcg::setEnum(nodemap, "ComponentSelector", component[i].c_str(), true);
1022  rcg::setBoolean(nodemap, "ComponentEnable", false, true);
1023  }
1024 
1025  rcg::setEnum(nodemap, "ComponentSelector", "Intensity");
1026  rcg::setEnum(nodemap, "PixelFormat", "Mono8");
1027 }
1028 
1029 /*
1030  Conditionally enables a component.
1031 
1032  @param nodemap Feature nodemap.
1033  @param en_curr Current selection status, which will be changed.
1034  @param en_new New selection status.
1035  @return 1 if status changed, 0 otherwise.
1036 */
1037 
1038 int enable(const std::shared_ptr<GenApi::CNodeMapRef>& nodemap, const char* component, bool& en_curr, bool en_new)
1039 {
1040  if (en_new != en_curr)
1041  {
1042  if (en_new)
1043  ROS_INFO_STREAM("rc_visard_driver: Enabled image stream: " << component);
1044  else
1045  ROS_INFO_STREAM("rc_visard_driver: Disabled image stream: " << component);
1046 
1047  rcg::setEnum(nodemap, "ComponentSelector", component, true);
1048  rcg::setBoolean(nodemap, "ComponentEnable", en_new);
1049  en_curr = en_new;
1050 
1051  return 1;
1052  }
1053 
1054  return 0;
1055 }
1056 }
1057 
1058 void DeviceNodelet::grab(std::string device, rcg::Device::ACCESS access)
1059 {
1060  unsigned int maxNumConsecutiveFails = 5;
1061 
1062  stopImageThread = false;
1063 
1064  imageRequested = true;
1065  imageSuccess = false;
1066 
1067  // at most 5 consecutive failures are permitted
1068 
1069  unsigned int cntConsecutiveFails = 0;
1070  while (!stopImageThread && cntConsecutiveFails < maxNumConsecutiveFails)
1071  {
1072  imageSuccess = false;
1073  try
1074  {
1075  // mark all dynamic parameters as changed
1076 
1077  level=0xffffffff;
1078 
1079  // initially switch off all components
1080 
1081  disableAll(rcgnodemap);
1082 
1083  bool ccolor = false;
1084  bool cintensity = false;
1085  bool cintensitycombined = false;
1086  bool cdisparity = false;
1087  bool cconfidence = false;
1088  bool cerror = false;
1089 
1090  bool firstTime = true;
1091 
1092  // get focal length factor of the camera
1093 
1094  double f = rcg::getFloat(rcgnodemap, "FocalLengthFactor", 0, 0, false);
1095  if (!f) // backward compatibility: also check for old name 'FocalLength'
1096  {
1097  f = rcg::getFloat(rcgnodemap, "FocalLength", 0, 0, false);
1098  if (!f)
1099  {
1100  throw std::runtime_error("Focal length not found: Neither 'FocalLength' nor 'FocalLengthFactor'!");
1101  }
1102  }
1103 
1104  // get baseline of the camera (for backward capability check unit)
1105 
1106  double t = rcg::getFloat(rcgnodemap, "Baseline", 0, 0, true);
1107  GenApi::INode* node = rcgnodemap->_GetNode("Baseline");
1108  if (!node || !GenApi::IsReadable(node))
1109  {
1110  throw std::invalid_argument("Feature not found or not readable: Baseline");
1111  }
1112  GenApi::IFloat* val = dynamic_cast<GenApi::IFloat*>(node);
1113  if (!val)
1114  {
1115  throw std::invalid_argument("Feature not float: Baseline");
1116  }
1117  if (val->GetUnit() == "mm") // in previous versions rc_visard reported as mm
1118  {
1119  t /= 1000;
1120  }
1121 
1122  // get disparity scale
1123 
1124  double scale = rcg::getFloat(rcgnodemap, "Scan3dCoordinateScale", 0, 0, true);
1125 
1126  // check certain values
1127 
1128  rcg::checkFeature(rcgnodemap, "Scan3dOutputMode", "DisparityC");
1129  rcg::checkFeature(rcgnodemap, "Scan3dCoordinateOffset", "0");
1130  rcg::checkFeature(rcgnodemap, "Scan3dInvalidDataFlag", "1");
1131  rcg::checkFeature(rcgnodemap, "Scan3dInvalidDataValue", "0");
1132 
1133  // get current configuration and start dynamic reconfigure server
1134 
1136 
1137  int disprange = config.depth_disprange;
1138  bool is_depth_acquisition_continuous = (config.depth_acquisition_mode[0] == 'C');
1139 
1140  // prepare chunk adapter for getting chunk data, if iocontrol is available
1141 
1142  std::shared_ptr<GenApi::CChunkAdapter> chunkadapter;
1143 
1144  if (iocontrol_avail)
1145  {
1146  chunkadapter = rcg::getChunkAdapter(rcgnodemap, rcgdev->getTLType());
1147  }
1148 
1149  // initialize all publishers
1150 
1151  ros::NodeHandle nh(getNodeHandle(), "stereo");
1153 
1154  CameraInfoPublisher lcaminfo(nh, tfPrefix, f, t, true);
1155  CameraInfoPublisher rcaminfo(nh, tfPrefix, f, t, false);
1156 
1157  ImagePublisher limage(it, tfPrefix, true, false, iocontrol_avail);
1158  ImagePublisher rimage(it, tfPrefix, false, false, iocontrol_avail);
1159 
1160  DisparityPublisher disp(nh, tfPrefix, f, t, scale);
1161  DisparityColorPublisher cdisp(it, tfPrefix, scale);
1162  DepthPublisher depth(nh, tfPrefix, f, t, scale);
1163 
1164  ConfidencePublisher confidence(nh, tfPrefix);
1165  ErrorDisparityPublisher error_disp(nh, tfPrefix, scale);
1166  ErrorDepthPublisher error_depth(nh, tfPrefix, f, t, scale);
1167 
1168  Points2Publisher points2(nh, tfPrefix, f, t, scale);
1169 
1170  // add color image publishers if the camera supports color
1171 
1172  std::shared_ptr<ImagePublisher> limage_color;
1173  std::shared_ptr<ImagePublisher> rimage_color;
1174 
1175  {
1176  std::vector<std::string> format;
1177  rcg::setEnum(rcgnodemap, "ComponentSelector", "Intensity", true);
1178  rcg::getEnum(rcgnodemap, "PixelFormat", format, true);
1179 
1180  for (size_t i = 0; i < format.size(); i++)
1181  {
1182  if (format[i] == "YCbCr411_8")
1183  {
1184  limage_color = std::make_shared<ImagePublisher>(it, tfPrefix, true, true, iocontrol_avail);
1185  rimage_color = std::make_shared<ImagePublisher>(it, tfPrefix, false, true, iocontrol_avail);
1186  break;
1187  }
1188  }
1189  }
1190 
1191  // start streaming of first stream
1192 
1193  std::vector<std::shared_ptr<rcg::Stream> > stream = rcgdev->getStreams();
1194 
1195  gev_packet_size="";
1196 
1197  if (stream.size() > 0)
1198  {
1199  stream[0]->open();
1200  stream[0]->startStreaming();
1201 
1202  // enter grabbing loop
1203 #if ROS_HAS_STEADYTIME
1204  ros::SteadyTime tlastimage = ros::SteadyTime::now();
1205 #else
1206  ros::WallTime tlastimage = ros::WallTime::now();
1207 #endif
1208 
1209  while (!stopImageThread)
1210  {
1211  const rcg::Buffer* buffer = stream[0]->grab(40);
1212 
1213  if (buffer != 0 && !buffer->getIsIncomplete())
1214  {
1215  if (gev_packet_size.size() == 0)
1216  {
1217  gev_packet_size = rcg::getString(rcgnodemap, "GevSCPSPacketSize", true, true);
1218  ROS_INFO_STREAM("rc_visard_driver: Image streams ready (Packet size "
1219  << gev_packet_size << ")");
1220  }
1221 
1222  // get out1 line status from chunk data if possible
1223 
1224  bool out1 = false;
1225  if (iocontrol_avail && chunkadapter && buffer->getContainsChunkdata())
1226  {
1227  chunkadapter->AttachBuffer(reinterpret_cast<std::uint8_t*>(buffer->getGlobalBase()),
1228  buffer->getSizeFilled());
1229  out1 = (rcg::getInteger(rcgnodemap, "ChunkLineStatusAll", 0, 0, false) & 0x1);
1230  }
1231 
1232  uint32_t npart=buffer->getNumberOfParts();
1233  for (uint32_t part=0; part<npart; part++)
1234  {
1235  if (buffer->getImagePresent(part))
1236  {
1237  // reset counter of consecutive missing images and failures
1238 #if ROS_HAS_STEADYTIME
1239  tlastimage = ros::SteadyTime::now();
1240 #else
1241  tlastimage = ros::WallTime::now();
1242 #endif
1243  cntConsecutiveFails = 0;
1244  imageSuccess = true;
1245 
1246  // the buffer is offered to all publishers
1247 
1248  disp.setDisprange(disprange);
1249  cdisp.setDisprange(disprange);
1250 
1251  uint64_t pixelformat = buffer->getPixelFormat(part);
1252 
1253  lcaminfo.publish(buffer, part, pixelformat);
1254  rcaminfo.publish(buffer, part, pixelformat);
1255 
1256  limage.publish(buffer, part, pixelformat, out1);
1257  rimage.publish(buffer, part, pixelformat, out1);
1258 
1259  if (limage_color && rimage_color)
1260  {
1261  limage_color->publish(buffer, part, pixelformat, out1);
1262  rimage_color->publish(buffer, part, pixelformat, out1);
1263  }
1264 
1265  disp.publish(buffer, part, pixelformat);
1266  cdisp.publish(buffer, part, pixelformat);
1267  depth.publish(buffer, part, pixelformat);
1268 
1269  confidence.publish(buffer, part, pixelformat);
1270  error_disp.publish(buffer, part, pixelformat);
1271  error_depth.publish(buffer, part, pixelformat);
1272 
1273  points2.publish(buffer, part, pixelformat, out1);
1274  }
1275  }
1276 
1277  // detach buffer from nodemap
1278 
1279  if (chunkadapter) chunkadapter->DetachBuffer();
1280  }
1281  else if (buffer != 0 && buffer->getIsIncomplete())
1282  {
1283 #if ROS_HAS_STEADYTIME
1284  tlastimage = ros::SteadyTime::now();
1285 #else
1286  tlastimage = ros::WallTime::now();
1287 #endif
1289  ROS_WARN("rc_visard_driver: Received incomplete image buffer");
1290  }
1291  else if (buffer == 0)
1292  {
1293  // throw an expection if data from enabled components is expected,
1294  // but not comming for more than 3 seconds
1295 
1296  if (cintensity || cintensitycombined ||
1297  (is_depth_acquisition_continuous && (cdisparity || cconfidence || cerror)))
1298  {
1299 #if ROS_HAS_STEADYTIME
1300  double t = (ros::SteadyTime::now() - tlastimage).toSec();
1301 #else
1302  double t = (ros::WallTime::now() - tlastimage).toSec();
1303 #endif
1304 
1305  if (t > 3) // report error
1306  {
1308  std::ostringstream out;
1309  out << "No images received for " << t << " seconds!";
1310  throw std::underflow_error(out.str());
1311  }
1312  }
1313  else
1314  {
1315  // if nothing is expected then store current time as last time
1316  // to avoid possible timeout after resubscription
1317 
1318 #if ROS_HAS_STEADYTIME
1319  tlastimage = ros::SteadyTime::now();
1320 #else
1321  tlastimage = ros::WallTime::now();
1322 #endif
1323  }
1324  }
1325 
1326  // trigger depth
1327 
1329  {
1331  rcg::callCommand(rcgnodemap, "DepthAcquisitionTrigger", true);
1332  }
1333 
1334  // determine what should be streamed, according to subscriptions to
1335  // topics
1336 
1337  // switch color on or off
1338 
1339  if (limage_color && rimage_color && (limage_color->used() || rimage_color->used() || points2.used()))
1340  {
1341  if (!ccolor)
1342  {
1343  rcg::setEnum(rcgnodemap, "ComponentSelector", "Intensity", true);
1344  rcg::setEnum(rcgnodemap, "PixelFormat", "YCbCr411_8", true);
1345  ccolor = true;
1346  }
1347  }
1348  else
1349  {
1350  if (ccolor)
1351  {
1352  rcg::setEnum(rcgnodemap, "ComponentSelector", "Intensity", true);
1353  rcg::setEnum(rcgnodemap, "PixelFormat", "Mono8", true);
1354  ccolor = false;
1355  }
1356  }
1357 
1358  // enable or disable components
1359 
1360  int changed = enable(rcgnodemap, "IntensityCombined", cintensitycombined,
1361  rimage.used() || (rimage_color && rimage_color->used()));
1362 
1363  changed += enable(rcgnodemap, "Intensity", cintensity,
1364  !cintensitycombined && (lcaminfo.used() || rcaminfo.used() || limage.used() ||
1365  (limage_color && limage_color->used()) || points2.used()));
1366 
1367  changed += enable(rcgnodemap, "Disparity", cdisparity,
1368  disp.used() || cdisp.used() || depth.used() || error_depth.used() || points2.used());
1369 
1370  changed += enable(rcgnodemap, "Confidence", cconfidence, confidence.used());
1371 
1372  changed += enable(rcgnodemap, "Error", cerror, error_disp.used() || error_depth.used());
1373 
1374  // report activation of all components, everytime when a component is
1375  // enabled or disabled
1376 
1377  if (changed > 0 || firstTime)
1378  {
1379  if (cintensity || cintensitycombined || cdisparity || cconfidence || cerror)
1380  imageRequested = true;
1381  else
1382  imageRequested = false;
1383  firstTime = false;
1384  }
1385 
1386  // check if dynamic configuration hast changed
1387 
1388  if (level != 0)
1389  {
1390  // set all changed values via genicam
1391 
1392  mtx.lock();
1393  rc_visard_driver::rc_visard_driverConfig cfg = config;
1394  uint32_t lvl = level;
1395  level = 0;
1396  mtx.unlock();
1397 
1398  setConfiguration(rcgnodemap, cfg, lvl, iocontrol_avail);
1399 
1400  disprange = cfg.depth_disprange;
1401 
1402  // if in alternate mode, then make publishers aware of it
1403 
1404  if (lvl & 262144)
1405  {
1406  bool alternate = (cfg.out1_mode == "ExposureAlternateActive");
1407 
1408  limage.setOut1Alternate(alternate);
1409  rimage.setOut1Alternate(alternate);
1410  points2.setOut1Alternate(alternate);
1411 
1412  if (limage_color && rimage_color)
1413  {
1414  limage_color->setOut1Alternate(alternate);
1415  rimage_color->setOut1Alternate(alternate);
1416  }
1417  }
1418 
1419  // if depth acquisition changed to continuous mode, reset last
1420  // grabbing time to avoid triggering timout if only disparity,
1421  // confidence and/or error components are enabled
1422 
1423  is_depth_acquisition_continuous = (cfg.depth_acquisition_mode[0] == 'C');
1424 
1425  if (lvl & 1048576)
1426  {
1427  if (is_depth_acquisition_continuous)
1428  {
1429 #if ROS_HAS_STEADYTIME
1430  tlastimage = ros::SteadyTime::now();
1431 #else
1432  tlastimage = ros::WallTime::now();
1433 #endif
1434  }
1435  }
1436  }
1437  }
1438 
1439  stream[0]->stopStreaming();
1440  stream[0]->close();
1441  }
1442  else
1443  {
1444  ROS_ERROR("rc_visard_driver: No stream");
1445  cntConsecutiveFails++;
1446  }
1447  }
1448  catch (const std::exception& ex)
1449  {
1450  ROS_ERROR_STREAM("rc_visard_driver: " << ex.what());
1451  cntConsecutiveFails++;
1452  }
1453  }
1454 
1455  // report failures, if any
1456 
1457  if (cntConsecutiveFails > 0)
1458  {
1459  ROS_ERROR_STREAM("rc_visard_driver: Number of consecutive failures: " << cntConsecutiveFails);
1460  }
1461  if (cntConsecutiveFails >= maxNumConsecutiveFails)
1462  {
1463  ROS_ERROR_STREAM("rc_visard_driver: Image grabbing failed.");
1464  recoveryRequested = true;
1466  }
1467 }
1468 
1469 bool DeviceNodelet::depthAcquisitionTrigger(std_srvs::Trigger::Request& req,
1470  std_srvs::Trigger::Response& resp)
1471 {
1473 
1474  resp.success = true;
1475  resp.message = "";
1476 
1477  return true;
1478 }
1479 
1480 // Anonymous namespace for local linkage
1481 namespace
1482 {
1484 enum class DynamicsCmd
1485 {
1486  START = 0,
1487  START_SLAM,
1488  STOP,
1489  STOP_SLAM,
1490  RESTART,
1491  RESTART_SLAM,
1492  RESET_SLAM
1493 };
1494 
1496 void handleDynamicsStateChangeRequest(rcd::RemoteInterface::Ptr dynIF, DynamicsCmd state,
1497  std_srvs::Trigger::Response& resp)
1498 {
1499  resp.success = true;
1500  resp.message = "";
1501 
1502  std::string new_state;
1503 
1504  if (dynIF)
1505  {
1506  try
1507  {
1508  switch (state)
1509  {
1510  case DynamicsCmd::STOP:
1511  new_state = dynIF->stop();
1512  break;
1513  case DynamicsCmd::STOP_SLAM:
1514  new_state = dynIF->stopSlam();
1515  break;
1516  case DynamicsCmd::START:
1517  new_state = dynIF->start();
1518  break;
1519  case DynamicsCmd::START_SLAM:
1520  new_state = dynIF->startSlam();
1521  break;
1522  case DynamicsCmd::RESTART_SLAM:
1523  new_state = dynIF->restartSlam();
1524  break;
1525  case DynamicsCmd::RESTART:
1526  new_state = dynIF->restart();
1527  break;
1528  case DynamicsCmd::RESET_SLAM:
1529  new_state = dynIF->resetSlam();
1530  break;
1531  default:
1532  throw std::runtime_error("handleDynamicsStateChangeRequest: unrecognized state change request");
1533  }
1534  if (new_state == rcd::RemoteInterface::State::FATAL)
1535  {
1536  resp.success = false;
1537  resp.message = "rc_dynamics module is in " + new_state + " state. Check the log files.";
1538  }
1539  }
1540  catch (std::exception& e)
1541  {
1542  resp.success = false;
1543  resp.message = std::string("Failed to change state of rcdynamics module: ") + e.what();
1544  }
1545  }
1546  else
1547  {
1548  resp.success = false;
1549  resp.message = "rcdynamics remote interface not yet initialized!";
1550  }
1551 
1552  if (!resp.success)
1553  ROS_ERROR_STREAM(resp.message);
1554 }
1555 }
1556 
1557 bool DeviceNodelet::dynamicsStart(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1558 {
1559  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::START, resp);
1560  return true;
1561 }
1562 
1563 bool DeviceNodelet::dynamicsStartSlam(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1564 {
1565  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::START_SLAM, resp);
1566  return true;
1567 }
1568 
1569 bool DeviceNodelet::dynamicsRestart(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1570 {
1571  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::RESTART, resp);
1572  return true;
1573 }
1574 
1575 bool DeviceNodelet::dynamicsRestartSlam(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1576 {
1577  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::RESTART_SLAM, resp);
1578  return true;
1579 }
1580 
1581 bool DeviceNodelet::dynamicsStop(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1582 {
1583  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::STOP, resp);
1584  return true;
1585 }
1586 
1587 bool DeviceNodelet::dynamicsStopSlam(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1588 {
1589  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::STOP_SLAM, resp);
1590  return true;
1591 }
1592 
1593 bool DeviceNodelet::dynamicsResetSlam(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1594 {
1595  handleDynamicsStateChangeRequest(dynamicsInterface, DynamicsCmd::RESET_SLAM, resp);
1596  return true;
1597 }
1598 
1599 bool DeviceNodelet::getSlamTrajectory(rc_visard_driver::GetTrajectory::Request& req,
1600  rc_visard_driver::GetTrajectory::Response& resp)
1601 {
1602  TrajectoryTime start(req.start_time.sec, req.start_time.nsec, req.start_time_relative);
1603  TrajectoryTime end(req.end_time.sec, req.end_time.nsec, req.end_time_relative);
1604 
1605  auto pbTraj = dynamicsInterface->getSlamTrajectory(start, end);
1606  resp.trajectory.header.frame_id = pbTraj.parent();
1607  resp.trajectory.header.stamp.sec = pbTraj.timestamp().sec();
1608  resp.trajectory.header.stamp.nsec = pbTraj.timestamp().nsec();
1609 
1610  for (auto pbPose : pbTraj.poses())
1611  {
1612  geometry_msgs::PoseStamped rosPose;
1613  rosPose.header.frame_id = pbTraj.parent();
1614  rosPose.header.stamp.sec = pbPose.timestamp().sec();
1615  rosPose.header.stamp.nsec = pbPose.timestamp().nsec();
1616  rosPose.pose.position.x = pbPose.pose().position().x();
1617  rosPose.pose.position.y = pbPose.pose().position().y();
1618  rosPose.pose.position.z = pbPose.pose().position().z();
1619  rosPose.pose.orientation.x = pbPose.pose().orientation().x();
1620  rosPose.pose.orientation.y = pbPose.pose().orientation().y();
1621  rosPose.pose.orientation.z = pbPose.pose().orientation().z();
1622  rosPose.pose.orientation.w = pbPose.pose().orientation().w();
1623  resp.trajectory.poses.push_back(rosPose);
1624  }
1625 
1626  // additionally publish extracted trajectory on topic
1628  {
1629  trajPublisher.publish(resp.trajectory);
1630  }
1631 
1632  return true;
1633 }
1634 
1635 bool DeviceNodelet::saveSlamMap(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1636 {
1637  resp.success = false;
1638 
1639  if (dynamicsInterface)
1640  {
1641  try
1642  {
1643  rcd::RemoteInterface::ReturnCode rc = dynamicsInterface->saveSlamMap();
1644  resp.success = (rc.value >= 0);
1645  resp.message = rc.message;
1646  }
1647  catch (std::exception& e)
1648  {
1649  resp.message = std::string("Failed to save SLAM map: ") + e.what();
1650  }
1651  }
1652  else
1653  {
1654  resp.message = "rcdynamics remote interface not yet initialized!";
1655  }
1656 
1657  if (!resp.success)
1658  ROS_ERROR_STREAM(resp.message);
1659 
1660  return true;
1661 }
1662 
1663 bool DeviceNodelet::loadSlamMap(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1664 {
1665  resp.success = false;
1666 
1667  if (dynamicsInterface)
1668  {
1669  try
1670  {
1671  rcd::RemoteInterface::ReturnCode rc = dynamicsInterface->loadSlamMap();
1672  resp.success = (rc.value >= 0);
1673  resp.message = rc.message;
1674  }
1675  catch (std::exception& e)
1676  {
1677  resp.message = std::string("Failed to load SLAM map: ") + e.what();
1678  }
1679  }
1680  else
1681  {
1682  resp.message = "rcdynamics remote interface not yet initialized!";
1683  }
1684 
1685  if (!resp.success)
1686  ROS_ERROR_STREAM(resp.message);
1687 
1688  return true;
1689 }
1690 
1691 bool DeviceNodelet::removeSlamMap(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& resp)
1692 {
1693  resp.success = false;
1694 
1695  if (dynamicsInterface)
1696  {
1697  try
1698  {
1699  rcd::RemoteInterface::ReturnCode rc = dynamicsInterface->removeSlamMap();
1700  resp.success = (rc.value >= 0);
1701  resp.message = rc.message;
1702  }
1703  catch (std::exception& e)
1704  {
1705  resp.message = std::string("Failed to remove SLAM map: ") + e.what();
1706  }
1707  }
1708  else
1709  {
1710  resp.message = "rcdynamics remote interface not yet initialized!";
1711  }
1712 
1713  if (!resp.success)
1714  ROS_ERROR_STREAM(resp.message);
1715 
1716  return true;
1717 }
1718 
1720 {
1721  stat.add("connection_loss_total", totalConnectionLosses);
1722  stat.add("incomplete_buffers_total", totalIncompleteBuffers);
1723  stat.add("image_receive_timeouts_total", totalImageReceiveTimeouts);
1724  stat.add("current_reconnect_trial", cntConsecutiveRecoveryFails);
1725 
1726  // general connection status is supervised by the recoveryRequested variable
1727 
1728  if (recoveryRequested) {
1729  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Disconnected");
1730  return;
1731  }
1732 
1733  // at least we are connected to gev server
1734 
1735  stat.add("ip_address", dev_ipaddr);
1736  stat.add("gev_packet_size", gev_packet_size);
1737 
1738  if (imageRequested) {
1739  if (imageSuccess) {
1740  // someone subscribed to images, and we actually receive data via GigE vision
1741  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Streaming");
1742  } else {
1743  // someone subscribed to images, but we do not receive any data via GigE vision (yet)
1744  stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "No data");
1745  }
1746  } else {
1747  // no one requested images -> node is ok but stale
1748  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Idle");
1749  }
1750 
1751 }
1752 
1754  if (dev_serialno.empty()) {
1755  stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Unknown");
1756  } else {
1757  stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Info");
1758  stat.add("serial", dev_serialno);
1759  stat.add("mac", dev_macaddr);
1760  stat.add("user_id", gev_userid);
1761  stat.add("image_version", dev_version);
1762  }
1763 }
1764 
1765 }
1766 
bool getBoolean(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, bool exception=false, bool igncache=false)
ThreadedStream::Manager::Ptr dynamicsStreams
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
std::shared_ptr< Device > getDevice(const char *devid)
bool used() override
Returns true if there are subscribers to the topic.
bool used() override
Returns true if there are subscribers to the topic.
void publish(const boost::shared_ptr< M > &message) const
f
size_t getSizeFilled() const
unsigned int totalIncompleteBuffers
void summary(unsigned char lvl, const std::string s)
void reconfigure(rc_visard_driver::rc_visard_driverConfig &config, uint32_t level)
bool used() override
Returns true if there are subscribers to the topic.
int64_t round(double x)
std::shared_ptr< GenApi::CChunkAdapter > getChunkAdapter(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const std::string &tltype)
bool used() override
Returns true if there are subscribers to the topic.
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
void produce_connection_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
void setHardwareID(const std::string &hwid)
static std::vector< std::shared_ptr< System > > getSystems()
bool used() override
Returns true if there are subscribers to the topic.
bool dynamicsRestart(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Restart Stereo INS.
std::shared_ptr< ThreadedStream > Ptr
bool depthAcquisitionTrigger(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Trigger stereo matching in mode &#39;SingleFrame&#39;.
diagnostic_updater::Updater updater
diagnostics publishing
void add(const std::string &name, TaskFunction f)
bool used() override
Returns true if there are subscribers to the topic.
bool getSlamTrajectory(rc_visard_driver::GetTrajectory::Request &req, rc_visard_driver::GetTrajectory::Response &resp)
Get the Slam trajectory.
int64_t getInteger(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, int64_t *vmin=0, int64_t *vmax=0, bool exception=false, bool igncache=false)
ros::ServiceServer slamLoadMapService
void grab(std::string device, rcg::Device::ACCESS access)
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
std::string dev_version
bool dynamicsStartSlam(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Start Stereo INS+SLAM.
std::string str()
void produce_device_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
#define ROS_WARN(...)
Implementation of a ThreadedStream that receives rc_visard&#39;s dynamics protobuf messages and re-publis...
bool setFloat(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, double value, bool exception=false)
bool dynamicsStart(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Start Stereo INS.
ros::ServiceServer depthAcquisitionTriggerService
wrapper for REST-API calls relating to rc_visard&#39;s dynamics interface
bool setEnum(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, const char *value, bool exception=false)
ros::ServiceServer dynamicsStartSlamService
ros::NodeHandle & getPrivateNodeHandle() const
ros::ServiceServer getSlamTrajectoryService
interface GENAPI_DECL_ABSTRACT IFloat
static void clearSystems()
bool perform_depth_acquisition_trigger
bool tfEnabled
should poses published also via tf?
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
bool dev_supports_depth_acquisition_trigger
virtual void onInit()
void initConfiguration(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, rc_visard_driver::rc_visard_driverConfig &cfg, rcg::Device::ACCESS access)
static ThreadedStream::Ptr CreateDynamicsStreamOfType(rc::dynamics::RemoteInterface::Ptr rcdIface, const std::string &stream, ros::NodeHandle &nh, const std::string &frame_id_prefix, bool tfEnabled)
std::atomic_bool imageSuccess
bool used() override
Returns true if there are subscribers to the topic.
void * getGlobalBase() const
std::thread imageThread
ROSCPP_DECL const std::string & getNamespace()
static SteadyTime now()
ros::ServiceServer dynamicsStopSlamService
void checkFeature(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, const char *value, bool igncache=false)
unsigned int totalImageReceiveTimeouts
#define ROS_FATAL_STREAM(args)
std::string getEnum(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, bool exception=false)
ros::ServiceServer dynamicsRestartSlamService
uint64_t getPixelFormat(std::uint32_t part) const
#define ROS_INFO(...)
bool dynamicsStopSlam(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Stop SLAM (keep Stereo INS running)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
std::atomic_uint_least32_t level
rc_visard_driver::rc_visard_driverConfig config
void keepAliveAndRecoverFromFails()
bool used() override
Returns true if there are subscribers to the topic.
ros::ServiceServer slamSaveMapService
bool dynamicsRestartSlam(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Restart Stereo INS+SLAM.
bool IsReadable(const CPointer< T, B > &ptr)
ros::ServiceServer dynamicsStopService
void setOut1Alternate(bool alternate)
DynamicsCmd
Commands taken by handleDynamicsStateChangeRequest()
std::atomic_bool imageRequested
bool saveSlamMap(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Save the onboard SLAM map.
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
std::string gev_packet_size
ros::NodeHandle & getNodeHandle() const
#define ROS_WARN_STREAM(args)
ros::Publisher trajPublisher
bool loadSlamMap(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Load the onboard SLAM map.
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
bool setBoolean(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, bool value, bool exception=false)
std::shared_ptr< rcg::Device > rcgdev
ros::ServiceServer dynamicsStartService
std::string strip_leading_slash(const std::string &frame_name)
std::atomic_bool stopImageThread
ros::ServiceServer dynamicsRestartService
dynamic_reconfigure::Server< rc_visard_driver::rc_visard_driverConfig > * reconfig
bool getContainsChunkdata() const
double getFloat(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, double *vmin=0, double *vmax=0, bool exception=false, bool igncache=false)
#define ROS_INFO_STREAM(args)
static WallTime now()
std::atomic_bool stopRecoverThread
void setDisprange(int disprange)
Set the disparity range for scaling of images.
rc::dynamics::RemoteInterface::Ptr dynamicsInterface
std::string dev_macaddr
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
bool callCommand(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, bool exception=false)
std::string dev_serialno
std::string gev_userid
bool getImagePresent(std::uint32_t part) const
bool getIsIncomplete() const
bool dynamicsStop(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Stop Stereo INS(+SLAM if running)
Specific implementation for roboception::msgs::Dynamics messages.
bool setInteger(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, int64_t value, bool exception=false)
std::thread recoverThread
void setOut1Alternate(bool alternate)
void add(const std::string &key, const T &val)
#define ROS_ERROR_STREAM(args)
RW
unsigned int totalConnectionLosses
interface GENAPI_DECL_ABSTRACT INode
bool removeSlamMap(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Remove the onboard SLAM map.
std::shared_ptr< GenApi::CNodeMapRef > rcgnodemap
bool dynamicsResetSlam(std_srvs::Trigger::Request &req, std_srvs::Trigger::Response &resp)
Reset SLAM (keep Stereo INS running)
ros::ServiceServer dynamicsResetSlamService
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define ROS_ERROR(...)
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
bool used() override
Returns true if there are subscribers to the topic.
std::string getString(const std::shared_ptr< GenApi::CNodeMapRef > &nodemap, const char *name, bool exception=false, bool igncache=false)
std::string dev_ipaddr
virtual ~DeviceNodelet()
Specific implementation for roboception::msgs::Frame messages.
void setDisprange(int disprange)
Set the disparity range for scaling of images.
void publish(const rcg::Buffer *buffer, uint32_t part, uint64_t pixelformat) override
Offers a buffer for publication.
std::string tfPrefix
all frame names must be prefixed when using more than one rc_visard
ros::ServiceServer slamRemoveMapService
std::uint32_t getNumberOfParts() const


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Wed Mar 20 2019 07:55:49