sick_scan_test.cpp
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2018, Ing.-Buero Dr. Michael Lehning, Hildesheim
3 * Copyright (C) 2018, SICK AG, Waldkirch
4 * All rights reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 *
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions are met:
23 *
24 * * Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * * Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 * * Neither the name of Osnabrueck University nor the names of its
30 * contributors may be used to endorse or promote products derived from
31 * this software without specific prior written permission.
32 * * Neither the name of SICK AG nor the names of its
33 * contributors may be used to endorse or promote products derived from
34 * this software without specific prior written permission
35 * * Neither the name of Ing.-Buero Dr. Michael Lehning nor the names of its
36 * contributors may be used to endorse or promote products derived from
37 * this software without specific prior written permission
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
40 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
43 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 * POSSIBILITY OF SUCH DAMAGE.
50 *
51 * Last modified: 27th Mar 2018
52 *
53 * Authors:
54 * Michael Lehning <michael.lehning@lehning.de>
55 *
56 */
57 
58 #define WIN32_LEAN_AND_MEAN
59 //#include "boost/filesystem.hpp"
60 //#include <boost/regex.hpp>
61 //#include <boost/algorithm/string.hpp>
62 #include <algorithm> // for std::min
63 
64 
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 
69 #include <iostream>
70 #ifndef _MSC_VER
71 #include <sys/wait.h>
72 #endif
73 #include <unistd.h>
74 #include <cstdio>
75 #include <cstdlib>
76 
77 
79 
80 #include <ros/console.h>
81 
82 #ifdef _MSC_VER
84 #endif
85 
86 #include "tinystr.h"
87 #include "tinyxml.h"
88 
89 #define MAX_NAME_LEN (1024)
90 
91 // 001.000.000 Initial Version
92 #define SICK_SCAN_TEST_MAJOR_VER "001"
93 #define SICK_SCAN_TEST_MINOR_VER "000"
94 #define SICK_SCAN_TEST_PATCH_LEVEL "000"
95 
96 
97 
99 {
100 public:
101  paramEntryAscii(std::string _nameVal, std::string _typeVal, std::string _valueVal)
102  {
103  nameVal = _nameVal;
104  typeVal = _typeVal;
105  valueVal = _valueVal;
106  setCheckStatus(999,"untested");
107  minMaxGiven = false;
108  };
109 
110  void setPointerToXmlNode(TiXmlElement *paramEntryPtr)
111  {
112  this->nodePtr = paramEntryPtr;
113  }
114 
116  {
117  return( this->nodePtr);
118  }
119  void setValues(std::string _nameVal, std::string _typeVal, std::string _valueVal)
120  {
121  nameVal = _nameVal;
122  typeVal = _typeVal;
123  valueVal = _valueVal;
124  };
125 
126 
128  {
129  return(minMaxGiven);
130  }
131 
132  void setMinMaxValues(std::string _valueMinVal, std::string _valueMaxVal)
133  {
134 
135  valueMinVal = _valueMinVal;
136  valueMaxVal = _valueMaxVal;
137  minMaxGiven = true;
138 
139  };
140 
141  std::string getName()
142  {
143  return(nameVal);
144  }
145 
146  std::string getType()
147  {
148  return(typeVal);
149  }
150 
151  std::string getValue()
152  {
153  return(valueVal);
154  }
155 
156  std::string getMinValue()
157  {
158  return(valueMinVal);
159  }
160 
161  std::string getMaxValue()
162  {
163  return(valueMaxVal);
164  }
165 
166  void setCheckStatus(int errCode, std::string errMsg)
167  {
168  errorCode = errCode;
169  errorMsg = errMsg;
170  };
171 
173  {
174  return(errorCode);
175  }
176 
177  std::string getErrorMsg()
178  {
179  return(errorMsg);
180  }
181 
182 private:
183  std::string nameVal;
184  std::string typeVal;
185  std::string valueVal;
186  std::string valueMinVal;
187  std::string valueMaxVal;
188  bool minMaxGiven;
189  int errorCode;
190  std::string errorMsg;
191  TiXmlElement *nodePtr;
192 };
193 
194 
195 
196 
197 
198 void sudokill(pid_t tokill)
199 {
200 #ifndef _MSC_VER
201  kill(tokill, SIGTERM);
202 #endif
203  sleep(5);
204 }
205 
206 std::vector<paramEntryAscii> getParamList(TiXmlNode *paramList)
207 {
208  std::vector<paramEntryAscii> tmpList;
209 
210 
211  TiXmlElement *paramEntry = (TiXmlElement *)paramList->FirstChild("param"); // first child
212  while (paramEntry)
213  {
214  std::string nameVal = "";
215  std::string typeVal = "";
216  std::string valueVal = "";
217  std::string minValueVal = "";
218  std::string maxValueVal = "";
219 
220  bool minValFnd = false;
221  bool maxValFnd = false;
222  // is this a param-node?
223  // if this is valid than process attributes
224  const char *val = paramEntry->Value();
225  bool searchAttributes = true;
226  if (strcmp(val,"param") == 0)
227  {
228  // expected value
229  }
230  else
231  {
232  searchAttributes = false;
233  }
234  if (paramEntry->Type() == TiXmlNode::TINYXML_ELEMENT)
235  {
236  // expected value
237  }
238  else
239  {
240  searchAttributes = false;
241  }
242  if (searchAttributes)
243  {
244  for (TiXmlAttribute* node = paramEntry->FirstAttribute(); ; node = node->Next())
245  {
246  const char *tag = node->Name();
247  const char *val = node->Value();
248 
249  if (strcmp(tag, "name") == 0)
250  {
251  nameVal = val;
252  }
253  if (strcmp(tag, "type") == 0)
254  {
255  typeVal = val;
256  }
257  if (strcmp(tag, "value") == 0)
258  {
259  valueVal = val;
260  }
261  if (strcmp(tag, "valueMin") == 0)
262  {
263  minValFnd = true;
264  minValueVal = val;
265 
266  }
267  if (strcmp(tag, "valueMax") == 0)
268  {
269  maxValFnd = true;
270  maxValueVal = val;
271  }
272  if (node == paramEntry->LastAttribute())
273  {
274 
275  break;
276  }
277  }
278 
279  paramEntryAscii tmpEntry(nameVal, typeVal, valueVal);
280  if (maxValFnd && minValFnd)
281  {
282  tmpEntry.setMinMaxValues(minValueVal, maxValueVal);
283  }
284 
285  tmpEntry.setPointerToXmlNode(paramEntry);
286  tmpList.push_back(tmpEntry);
287  }
288  paramEntry = (TiXmlElement *)paramEntry->NextSibling(); // go to next sibling
289  }
290 
291  return(tmpList);
292 }
293 
294 
295 
296 void replaceParamList(TiXmlNode * node, std::vector<paramEntryAscii> paramOrgList)
297 {
298  bool fndParam = false;
299  do
300  {
301  fndParam = false;
302  TiXmlElement *paramEntry = (TiXmlElement *)node->FirstChild("param");
303  if (paramEntry != NULL)
304  {
305  fndParam = true;
306  node->RemoveChild(paramEntry);
307  }
308  } while (fndParam == true);
309 
310  for (int i = 0; i < paramOrgList.size(); i++)
311  {
312  paramEntryAscii tmp = paramOrgList[i];
313  TiXmlElement *paramTmp = new TiXmlElement("param");
314  paramTmp->SetAttribute("name", tmp.getName().c_str());
315  paramTmp->SetAttribute("type", tmp.getType().c_str());
316  paramTmp->SetAttribute("value", tmp.getValue().c_str());
317  node->LinkEndChild(paramTmp);
318  }
319 }
320 
321 pid_t pid;
322 
323 pid_t launchRosFile(std::string cmdLine)
324 {
325  std::string cmd = cmdLine;
326  typedef std::vector<std::string > split_vector_type;
327 
328  split_vector_type splitVec; // #2: Search for tokens
329  boost::split(splitVec, cmdLine, boost::is_any_of(" "), boost::token_compress_on); // SplitVec == { "hello abc","ABC","aBc goodbye" }
330 
331 
332 
333  pid_t pid = fork(); // create child process
334  int status;
335 
336  switch (pid)
337  {
338  case -1: // error
339  perror("fork");
340  exit(1);
341 
342  case 0: // child process
343  execl("/opt/ros/kinetic/bin/roslaunch", splitVec[0].c_str(), splitVec[1].c_str(), splitVec[2].c_str(), NULL); // run the command
344  perror("execl"); // execl doesn't return unless there is a problem
345  exit(1);
346 
347  default: // parent process, pid now contains the child pid
348  /*
349  while (-1 == waitpid(pid, &status, 0)); // wait for child to complete
350  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
351  {
352  // handle error
353  std::cerr << "process " << cmd << " (pid=" << pid << ") failed" << std::endl;
354  }
355  */
356  break;
357 
358  }
359 
360  return(pid);
361 }
362 
363 
364 int createTestLaunchFile(std::string launchFileFullName, std::vector<paramEntryAscii> entryList, std::string& testLaunchFile)
365 {
366  printf("Try loading launchfile :%s",launchFileFullName.c_str());
367  TiXmlDocument doc;
368  doc.LoadFile(launchFileFullName.c_str());
369 
370  if (doc.Error() == true)
371  {
372  ROS_ERROR("ERROR parsing launch file %s\nRow: %d\nCol: %d", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
373  return(-1);
374  }
375  TiXmlNode *node = doc.FirstChild("launch");
376  if (node != NULL)
377  {
378  node = node->FirstChild("node");
379  std::vector<paramEntryAscii> paramOrgList = getParamList(node);
380  for (int i = 0; i < entryList.size(); i++)
381  {
382  bool replaceEntry = false;
383  for (int j = 0; j < paramOrgList.size(); j++)
384  {
385  if (entryList[i].getName().compare(paramOrgList[j].getName()) == 0)
386  {
387  paramOrgList[j].setValues(entryList[i].getName(), entryList[i].getType(), entryList[i].getValue());
388  replaceEntry = true;
389  }
390  }
391  if (replaceEntry == false) // add new one to list
392  {
393  paramEntryAscii tmp(entryList[i].getName(), entryList[i].getType(), entryList[i].getValue());
394  paramOrgList.push_back(tmp);
395  }
396  }
397  // delete all parameters and replace with new one
398  replaceParamList(node, paramOrgList);
399  }
400 
401  // append source extension if any
402  size_t pos = launchFileFullName.rfind('.');
403  std::string resultFileName = "";
404  testLaunchFile = "";
405  if (pos != std::string::npos)
406  {
407  resultFileName = launchFileFullName.substr(0, pos);
408  resultFileName += "_test.launch";
409  doc.SaveFile(resultFileName.c_str());
410  testLaunchFile = resultFileName;
411  }
412  return(0);
413 }
414 
415 int cloud_width = 0; // hacky
416 int cloud_height = 0;
417 int callbackCnt = 0;
418 double intensityStdDev = 0.0;
419 double rangeStdDev = 0.0;
420 
421 
422 //
424 
425 
427 {
428  static int cnt = 0;
429  callbackCnt++;
430  cloud_width = cloud_msg->width;
431  cloud_height = cloud_msg->height;
432  int intensity_idx = -1;
433  int cartesianIdxArr[3];
434  for (int i = 0; i < 3; i++)
435  {
436  cartesianIdxArr[i] = -1;
437  }
438 
439 
440  for (int i = 0; i < cloud_msg->fields.size(); i++)
441  {
442  std::string fieldName = cloud_msg->fields[i].name;
443  if (fieldName.compare("intensity") == 0)
444  {
445  intensity_idx = i;
446  }
447  if (fieldName.compare("x") == 0)
448  {
449  cartesianIdxArr[0] = i;
450  }
451  if (fieldName.compare("y") == 0)
452  {
453  cartesianIdxArr[1] = i;
454  }
455  if (fieldName.compare("z") == 0)
456  {
457  cartesianIdxArr[2] = i;
458  }
459 
460  }
461  if (intensity_idx != -1)
462  {
463  float intensitySum = 0.0;
464  float intensity2Sum = 0.0;
465  int intensityNum = cloud_width * cloud_height;
466  for (int i = 0; i < cloud_height; i++)
467  for (int j = 0; j < cloud_width; j++)
468  {
469  int idx = i * cloud_width + j;
470  idx *= cloud_msg->point_step; // calculate byte address offset
471  int relOff = cloud_msg->fields[intensity_idx].offset;
472  float *intensityPtr = (float *)(&(cloud_msg->data[idx+relOff]));
473  float intensity = *intensityPtr;
474  intensitySum += intensity;
475  intensity2Sum += intensity * intensity;
476  }
477  if (intensityNum > 1)
478  {
479  intensityStdDev = (intensity2Sum - 1.0 / intensityNum * (intensitySum * intensitySum));
480  {
481  intensityStdDev /= (intensityNum - 1);
483  }
484  }
485  }
486 
487  if (cartesianIdxArr[0] != -1 &&cartesianIdxArr[1] != -1 &&cartesianIdxArr[2] != -1)
488  {
489  float rangeSum = 0.0;
490  float range2Sum = 0.0;
491  int rangeNum = cloud_width * cloud_height;
492  for (int i = 0; i < cloud_height; i++)
493  for (int j = 0; j < cloud_width; j++)
494  {
495  int idx = i * cloud_width + j;
496  idx *= cloud_msg->point_step; // calculate byte address offset
497  int relOffX = cloud_msg->fields[cartesianIdxArr[0]].offset;
498  float *XPtr = (float *)(&(cloud_msg->data[idx+relOffX]));
499  float X = *XPtr;
500  int relOffY = cloud_msg->fields[cartesianIdxArr[1]].offset;
501  float *YPtr = (float *)(&(cloud_msg->data[idx+relOffY]));
502  float Y = *YPtr;
503  int relOffZ = cloud_msg->fields[cartesianIdxArr[2]].offset;
504  float *ZPtr = (float *)(&(cloud_msg->data[idx+relOffZ]));
505  float Z = *ZPtr;
506  float range =sqrt(X*X+Y*Y+Z*Z);
507  rangeSum += range;
508  range2Sum += range * range;
509  }
510  if (rangeNum > 1)
511  {
512  rangeStdDev = (range2Sum - 1.0 / rangeNum * (rangeSum * rangeSum));
513  {
514  rangeStdDev /= (rangeNum - 1);
516  }
517  }
518  }
519  ROS_INFO("valid PointCloud2 message received");
520 
521 }
522 
523 float ERR_CONST = -1E3;
524 float scan_angle_min = -1E3;
525 float scan_angle_max = -1E3;
526 float scan_time_increment = -1E3;
527 float scan_angle_increment = -1E3;
528 float range_min = -1E3;
529 float range_max = -1E3;
530 
531 
533 {
534  if (callbackScanCnt == 0)
535  {
536  scan_angle_min = scan->angle_min;
537  scan_angle_max = scan->angle_max;
538  scan_time_increment = scan->time_increment;
539  scan_angle_increment = scan->angle_increment;
540  range_min = scan->range_min;
541  range_max = scan->range_max;
542  }
543  callbackScanCnt++;
544 }
545 
546 
547 int startCallbackTest(int argc, char** argv)
548 {
549 
550  ros::NodeHandle nh;
551  ros::Rate loop_rate(10);
552  ros::Subscriber sub;
553  ros::Subscriber scanSub;
554  ROS_INFO("Cloudtester started."
555  );
556  sub = nh.subscribe("cloud", 1, cloud_callback);
557  scanSub=nh.subscribe<sensor_msgs::LaserScan>("/scan",10,processLaserScan);
558 
559  while (callbackCnt < 10)
560  {
561  ros::spinOnce();
562  }
563  sub.shutdown();
564  ros::shutdown();
565 
567  return(0);
568 
569 }
570 
571 
572 void createPrefixFromExeName(std::string exeName, std::string& prefix)
573 {
574 #ifdef _MSC_VER
575 #else
576 #endif
577  std::string searchPattern = "(.*)cmake\\-build\\-debug.*";
578  boost::regex expr(searchPattern);
579  boost::smatch what;
580 
581  if (boost::regex_search(exeName, what, expr))
582  {
583  std::string prefixPath = what[1]; // started from CLION IDE - build complete path
584  prefix = prefixPath;
585  prefix += "test";
586  }
587  else
588  {
589  prefix = ".";
590  }
591 
592 }
593 
594 
595 void searchForXmlFileInPathList(std::string pathList, std::string& packagePath, std::string xmlFileName)
596 {
597  typedef std::vector<std::string > split_vector_type;
598  packagePath = "???";
599  split_vector_type splitVec; // #2: Search for tokens
600  boost::split(splitVec, pathList, boost::is_any_of(":"), boost::token_compress_on); // SplitVec == { "hello abc","ABC","aBc goodbye" }
601  if (splitVec.size() > 0)
602  {
603  packagePath = splitVec[0];
604  }
605 
606  for (int i = 0; i < splitVec.size(); i++)
607  {
608  FILE *fin;
609  std::string tmpFileName = splitVec[i] + "/" + xmlFileName;
610  fin = fopen(tmpFileName.c_str(),"r");
611  if (fin != NULL)
612  {
613  packagePath = splitVec[i];
614  fclose(fin);
615  }
616  }
617 }
618 
619 bool getPackageRootFolder(std::string exePath, std::string& packageRootFolder)
620 {
621  packageRootFolder = "";
622  // try to retrive package name from exePat
623  typedef std::vector<std::string > split_vector_type;
624  split_vector_type splitVec; // #2: Search for tokens
625  boost::split(splitVec, exePath, boost::is_any_of("/"), boost::token_compress_on); // SplitVec == { "hello abc","ABC","aBc goodbye" }
626  bool bFnd = false;
627  for (int i = splitVec.size() - 1; i >= 0; i--) // last part is exe-Name - so forget this
628  {
629  std::string tmpPath = "";
630  for (int j = 1; j < i; j++)
631  {
632  tmpPath += '/';
633  tmpPath += splitVec[j];
634  }
635 
636  std::string fileTmpPath = tmpPath;
637  fileTmpPath += "/package.xml";
638  if (boost::filesystem::exists(fileTmpPath))
639  {
640  packageRootFolder = tmpPath;
641  bFnd = true;
642  break;
643  }
644  }
645 
646  return(bFnd);
647 
648 }
649 
656 int main(int argc, char **argv)
657 {
658  int result = 0;
659  char sep = boost::filesystem::path::preferred_separator;
660  std::string prefix = ".";
661  std::string packageRootFolder;
662 
663  std::string exeName = argv[0];
664 
665  if (argc == 1)
666  {
667  printf("Usage: %s <XML-Test-Ctrl-File>\n", exeName.c_str());
668  printf("\n");
669  printf("The XML-Test-Ctrl-File controls the parameter checking of various scanner types.");
670  printf("The XML-File sick_scan_test.xml in the test-Directory shows an example of this file type.\n");
671  exit(-1);
672  }
673  else
674  {
675  printf("Given program arguments:\n");
676  for (int i = 0; i < argc; i++)
677  {
678  printf("%d: %s\n", i, argv[i]);
679  }
680  }
681 #if 0
682  createPrefixFromExeName(exeName, prefix);
683 #ifdef _MSC_VER
684  prefix = std::string("..") + sep + std::string("..") + sep + "test";
685 #endif
687 #endif
688 
689  ros::init(argc, argv, "cloudtester");
690 
691  std::string nodeName = ros::this_node::getName();
692 
693  printf("Nodename: %s\n", nodeName.c_str());
694  bool bFnd = getPackageRootFolder(argv[0], packageRootFolder);
695 
696  printf("Package Path: %s\n", packageRootFolder.c_str());
697 
698  char* pPath;
699  pPath = getenv("ROS_PACKAGE_PATH");
700 #ifdef _MSC_VER
701  pPath = "..\\..:\tmp";
702 #endif
703  if (pPath != NULL)
704  {
705  ROS_INFO("Current root path entries for launch file search: %s\n", pPath);
706  }
707  else
708  {
709  ROS_FATAL("Cannot find ROS_PACKAGE_PATH - please run <Workspace>/devel/setup.bash");
710  }
711 
712  std::string packagePath;
713  // try to find xml-file in path list ... and give the result back in the variable packagePath
714  std::string testCtrlXmlFileName = argv[1];
715  searchForXmlFileInPathList(pPath, packagePath, testCtrlXmlFileName);
716 
717 // std::string testCtrlXmlFileName = packagePath + std::string(1, sep) + "sick_scan" + std::string(1, sep) + "test" + std::string(1, sep) + "sick_scan_test.xml";
718 // $ROS_PACKAGE_PATH/sick_scan/test/sick_scan_test.xml
719  boost::replace_all(testCtrlXmlFileName, "$ROS_PACKAGE_PATH", packagePath);
720 
721 
722  testCtrlXmlFileName = packagePath + std::string(1,'/') + testCtrlXmlFileName;
723  TiXmlDocument doc;
724  doc.LoadFile(testCtrlXmlFileName.c_str());
725  if (doc.Error())
726  {
727  ROS_ERROR("Package root folder %s", packageRootFolder.c_str());
728  ROS_ERROR("Cannot load/parse %s", testCtrlXmlFileName.c_str());
729  ROS_ERROR("Details: %s\n", doc.ErrorDesc());
730  exit(-1);
731  }
732 
733  bool launch_only = false;
734  boost::filesystem::path p(testCtrlXmlFileName);
735  boost::filesystem::path parentPath = p.parent_path();
736  std::string pathName = parentPath.string();
737  std::string launchFilePrefix = parentPath.parent_path().string() + std::string(1, sep) + "launch" + std::string(1, sep);
738  TiXmlNode *node = doc.FirstChild("launch");
739  if (node == NULL)
740  {
741  ROS_ERROR("Cannot find tag <launch>\n");
742  exit(-1);
743  }
744 
745  if (node)
746  {
747  TiXmlElement *fileNameEntry;
748  fileNameEntry = (TiXmlElement *)node->FirstChild("filename");
749  if (fileNameEntry == NULL)
750  {
751  ROS_ERROR("Cannot find tag <filename>\n");
752  exit(-1);
753  }
754  TiXmlText *fileNameVal = (TiXmlText *)fileNameEntry->FirstChild();
755  if (fileNameVal != NULL)
756  {
757  ROS_INFO("Processing launch file %s\n", fileNameVal->Value());
758  std::string launchFileFullName = launchFilePrefix + fileNameVal->Value();
759  ROS_INFO("Processing %s\n", launchFileFullName.c_str());
760  TiXmlNode *paramListNode = (TiXmlNode *)node->FirstChild("paramList");
761  if (paramListNode != NULL)
762  {
763  std::vector<paramEntryAscii> paramList = getParamList(paramListNode);
764  for (int i = 0; i < paramList.size(); i++)
765  {
766  if (paramList[i].getName().compare("launch_only") == 0)
767  {
768  if (paramList[i].getValue().compare("true") == 0)
769  {
770  printf("launch_only set to true. Just modifying launch file and launch (without testing).");
771  launch_only = true;
772  }
773  }
774 
775 
776  }
777 
778  std::string testLaunchFile;
779  createTestLaunchFile(launchFileFullName, paramList, testLaunchFile);
780  std::string commandLine;
781  boost::filesystem::path p(testLaunchFile);
782  std::string testOnlyLaunchFileName = p.filename().string();
783  commandLine = "roslaunch sick_scan " + testOnlyLaunchFileName;
784  ROS_INFO("launch ROS test ... [%s]", commandLine.c_str());
785 
786  // int result = std::system(commandLine.c_str());
787 
788  pid_t pidId = launchRosFile(commandLine.c_str());
789 
790  if (launch_only)
791  {
792  printf("Launch file [ %s ] started ...\n", commandLine.c_str());
793  printf("No further testing...\n");
794  exit(0);
795  }
796  startCallbackTest(argc, argv); // get 10 pointcloud messages
797 
799  ROS_INFO("If you receive an error message like \"... is neither a launch file ... \""
800  "please source ROS env. via source ./devel/setup.bash");
801 
802 
803  TiXmlNode *resultListNode = (TiXmlNode *)node->FirstChild("resultList");
804  if (resultListNode != NULL)
805  {
806  std::vector<paramEntryAscii> resultList = getParamList(resultListNode);
807 
808  enum KeyWord_Idx {MIN_ANG_KEYWORD_IDX, MAX_ANG_KEYWORD_IDX,
809  SCAN_TIME_INCREMENT_KEYWORD_IDX,
810  SCAN_ANGLE_INCREMENT_KEYWORD_IDX,
811  MIN_RANGE_KEYWORD_IDX,
812  MAX_RANGE_KEYWORD_IDX,
813  KEYWORD_IDX_NUM};
814 
815  std::vector<std::string> keyWordList;
816  keyWordList.resize(KEYWORD_IDX_NUM);
817 
818  keyWordList[MIN_ANG_KEYWORD_IDX] = "min_ang";
819  keyWordList[MAX_ANG_KEYWORD_IDX] = "max_ang";
820  keyWordList[MIN_RANGE_KEYWORD_IDX] = "range_min";
821  keyWordList[MAX_RANGE_KEYWORD_IDX] = "range_max";
822  keyWordList[SCAN_ANGLE_INCREMENT_KEYWORD_IDX] = "angle_increment";
823  keyWordList[SCAN_TIME_INCREMENT_KEYWORD_IDX] = "time_increment";
824 
825  for (int i = 0; i < resultList.size(); i++)
826  {
827 
828  for (int ki = 0; ki < keyWordList.size(); ki++)
829  {
830 
831  float measurementVal = .0F;
832  std::string keyWordTag = keyWordList[ki];
833 
834  switch(ki)
835  {
836  case MIN_ANG_KEYWORD_IDX: measurementVal = scan_angle_min; break;
837  case MAX_ANG_KEYWORD_IDX: measurementVal = scan_angle_max; break;
838  case SCAN_ANGLE_INCREMENT_KEYWORD_IDX: measurementVal = scan_angle_increment; break;
839  case SCAN_TIME_INCREMENT_KEYWORD_IDX: measurementVal = scan_time_increment; break;
840  case MIN_RANGE_KEYWORD_IDX: measurementVal = range_min; break;
841  case MAX_RANGE_KEYWORD_IDX: measurementVal = range_max; break;
842  default: printf("Did not find a correspondence for index %d\n", ki); break;
843  }
844  char errMsg[255] = {0};
845  if (keyWordTag.compare("min_ang") == 0)
846  {
847  measurementVal = scan_angle_min;
848  }
849  if (resultList[i].getName().compare(keyWordTag) == 0)
850  {
851 
852  enum Expected_Idx {MIN_EXP_IDX, DEF_EXP_IDX, MAX_EXP_IDX, EXP_IDX_NUM};
853  float expectedValues[EXP_IDX_NUM];
854  std::string valString;
855 
856  for (int j = 0; j < EXP_IDX_NUM; j++)
857  {
858  switch(j)
859  {
860  case MIN_EXP_IDX: valString = resultList[i].getMinValue(); break;
861  case DEF_EXP_IDX: valString = resultList[i].getValue(); break;
862  case MAX_EXP_IDX: valString = resultList[i].getMaxValue(); break;
863  default: ROS_ERROR("Check index"); break;
864 
865  }
866 
867  const char *valPtr = valString.c_str();
868  if (valPtr != NULL)
869  {
870  float tmpVal = .0F;
871  sscanf(valPtr, "%f", &tmpVal);
872  expectedValues[j] = tmpVal;
873  }
874  }
875  valString = resultList[i].getMinValue();
876 
877  int errorCode = 0;
878  std::string resultStr = "FAILED";
879  std::string appendResultStr = " <-----";
880  if ( (measurementVal >= expectedValues[MIN_EXP_IDX]) && (measurementVal <= expectedValues[MAX_EXP_IDX]))
881  {
882  appendResultStr = "";
883  resultStr = "PASSED";
884  errorCode = 0;
885  }
886  else
887  {
888  errorCode = 1;
889  }
890  sprintf(errMsg, "CHECK %s! Key: %-20s %14.9f in [%14.9f,%14.9f] %s", resultStr.c_str(), keyWordTag.c_str(), measurementVal,
891  expectedValues[MIN_EXP_IDX], expectedValues[MAX_EXP_IDX], appendResultStr.c_str());
892 
893  printf("%s\n", errMsg);
894  resultList[i].setCheckStatus(errorCode, (errorCode == 0) ? "OK" : errMsg);
895 
896  }
897  }
898 
899 
900  if (resultList[i].getName().compare("shotsPerLayer") == 0)
901  {
902  int expectedWidth = -1;
903  std::string valString = resultList[i].getValue();
904  const char *valPtr = valString.c_str();
905  if (valPtr != NULL)
906  {
907  sscanf(valPtr, "%d", &expectedWidth);
908  }
909 
910 
911  ROS_INFO("Test\n");
912  int errorCode = 0;
913  if (expectedWidth == cloud_width)
914  {
915  printf("CHECK PASSED! WIDTH %d == %d\n", expectedWidth, cloud_width);
916  errorCode = 0;
917  }
918  else
919  {
920  printf("CHECK FAILED! WIDTH %d <> %d\n", expectedWidth, cloud_width);
921  errorCode = 1;
922 
923  }
924  resultList[i].setCheckStatus(errorCode, (errorCode == 0) ? "OK" : "Unexpected number of shots");
925 
926  }
927  if (resultList[i].getName().compare("pointCloud2Height") == 0)
928  {
929  int expectedHeight = -1;
930  std::string valString = resultList[i].getValue();
931  const char *valPtr = valString.c_str();
932  if (valPtr != NULL)
933  {
934  sscanf(valPtr, "%d", &expectedHeight);
935  }
936  ROS_INFO("Test\n");
937  int errorCode = 0;
938  if (expectedHeight == cloud_height)
939  {
940  printf("CHECK PASSED! HEIGHT %d == %d\n", expectedHeight, cloud_height);
941  errorCode = 0;
942  }
943  else
944  {
945  printf("CHECK FAILED! HEIGHT %d <> %d\n", expectedHeight, cloud_height);
946  errorCode = 1;
947  }
948  resultList[i].setCheckStatus(errorCode, (errorCode == 0) ? "OK" : "Unexpected pointCloud2 height");
949  cloud_height = 0;
950  }
951 
952  if (resultList[i].getName().compare("RSSIEnabled") == 0)
953  {
954  int expectedWidth = -1;
955  std::string valString = resultList[i].getValue();
956  bool rssiEnabled = false;
957  if (boost::iequals(valString, "true"))
958  {
959  rssiEnabled = true;
960  } else if (boost::iequals(valString, "false"))
961  {
962  rssiEnabled = false;
963  } else
964  {
965  ROS_WARN("RSSIEnabled parameter wrong. True or False are valid parameters!\n");
966  }
967 
968  ROS_INFO("Test\n");
969  int errorCode = 0;
970  if (rssiEnabled == true)
971  {
972  if (intensityStdDev >= 1e-5)
973  {
974  printf("CHECK PASSED! RSSI Standard deviation %.3e is not 0.\n",intensityStdDev);
975  errorCode = 0;
976  } else
977  {
978  printf("CHECK FAILED! RSSI standard deviation is samller than %.10e even though RSSI is enabled\n",intensityStdDev);
979  errorCode = 1;
980 
981  }
982  }
983 
984  if (rssiEnabled == false)
985  {
986  if (intensityStdDev <= 1e-5)
987  {
988  printf("CHECK FAILED! RSSI standard deviation %.3e is bigger than 1e-5 even though RSSI is disabled\n",intensityStdDev);
989  errorCode = 1;
990  } else
991  {
992  printf("CHECK PASSED! RSSI Standard deviation %.3e is smaller than 1e-5.\n",intensityStdDev);
993  errorCode = 0;
994 
995  }
996  }
997  }
998  if (resultList[i].getName().compare("ranges") == 0)
999  {
1000  int expectedWidth = -1;
1001  std::string valString = resultList[i].getValue();
1002  bool rangeTest=false;
1003  if (boost::iequals(valString, "true"))
1004  {
1005  rangeTest=true;
1006  }
1007  else
1008  {
1009  ROS_WARN("ranges parameter wrong. True or False are valid parameters!\n");
1010  }
1011 
1012  ROS_INFO("Test\n");
1013  int errorCode = 0;
1014  if (rangeTest==true)
1015  {
1016  if (rangeStdDev >= 1e-5)
1017  {
1018  printf("CHECK PASSED! Range Standard deviation %.3e is not 0.\n", rangeStdDev);
1019  errorCode = 0;
1020  }
1021  else
1022  {
1023  printf("CHECK FAILED! Range standard deviation %.3e is smaller than 1e-5!\n", rangeStdDev);
1024  errorCode = 1;
1025 
1026  }
1027  resultList[i].setCheckStatus(errorCode, (errorCode == 0) ? "OK" : "Range standard deviation is 0 !\n");
1028  }
1029 
1030 
1031  }
1032  TiXmlElement *paramSet = resultList[i].getPointerToXmlNode();
1033  paramSet->SetAttribute("errorCode", resultList[i].getErrorCode());
1034  paramSet->SetAttribute("errorMsg", resultList[i].getErrorMsg().c_str());
1035  }
1036 
1037  }
1038 
1039  printf("Killing process %d\n", pidId);
1040  sudokill(pidId);
1041 
1042  std::string testCtrlResultXmlFileName = "";
1043  size_t pos = testCtrlXmlFileName.rfind('.');
1044  if (pos != std::string::npos)
1045  {
1046  boost::filesystem::path tmpFilePath =testCtrlXmlFileName.substr(0,pos);
1047  boost::filesystem::path xmlDir= tmpFilePath.parent_path();
1048  std::string xmlDirName = xmlDir.string();
1049  xmlDirName.append("/results");
1050  if (boost::filesystem::exists(xmlDirName))
1051  {
1052 
1053  }
1054  else
1055  {
1056  boost::filesystem::create_directory(xmlDirName);
1057  }
1058  std::string tmpFilNamewoExtension= tmpFilePath.stem().string();
1059  std::string resultFileName=xmlDir.string();
1060  resultFileName.append("/");
1061  resultFileName.append(tmpFilNamewoExtension);
1062  resultFileName.append(".res");
1063  printf("Save to %s\n", resultFileName.c_str());
1064  doc.SaveFile(resultFileName.c_str());
1065  }
1066 
1067  }
1068  }
1069  else
1070  {
1071  ROS_WARN("Cannot find filename entry");
1072  }
1073 
1074 
1075  }
1076  return result;
1077 
1078 }
1079 
sensor_msgs::LaserScan_
Definition: LaserScan.h:24
getenv
char * getenv()
TiXmlElement
Definition: tinyxml.h:1074
replaceParamList
void replaceParamList(TiXmlNode *node, std::vector< paramEntryAscii > paramOrgList)
Definition: sick_scan_test.cpp:296
paramEntryAscii::setPointerToXmlNode
void setPointerToXmlNode(TiXmlElement *paramEntryPtr)
Definition: sick_scan_test.cpp:110
TiXmlDocument
Definition: tinyxml.h:1593
NULL
#define NULL
paramEntryAscii::isMinMaxGiven
bool isMinMaxGiven()
Definition: sick_scan_test.cpp:127
sick_generic_laser.h
sensor_msgs::LaserScan_::ConstPtr
std::shared_ptr< ::sensor_msgs::LaserScan_< ContainerAllocator > const > ConstPtr
Definition: LaserScan.h:90
processLaserScan
void processLaserScan(const sensor_msgs::LaserScan::ConstPtr &scan)
Definition: sick_scan_test.cpp:532
paramEntryAscii
Definition: launchparser.cpp:12
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
alternate ROS initialization function.
scan_angle_min
float scan_angle_min
Definition: sick_scan_test.cpp:524
TiXmlElement::LastAttribute
TiXmlAttribute * LastAttribute()
Definition: tinyxml.h:1232
scan_angle_increment
float scan_angle_increment
Definition: sick_scan_test.cpp:527
createTestLaunchFile
int createTestLaunchFile(std::string launchFileFullName, std::vector< paramEntryAscii > entryList, std::string &testLaunchFile)
Definition: sick_scan_test.cpp:364
TiXmlNode::FirstChild
TiXmlNode * FirstChild()
Definition: tinyxml.h:581
TiXmlNode::Type
int Type() const
Definition: tinyxml.h:773
TiXmlNode::TINYXML_ELEMENT
@ TINYXML_ELEMENT
Definition: tinyxml.h:516
TiXmlNode::NextSibling
const TiXmlNode * NextSibling() const
Navigate to a sibling node.
Definition: tinyxml.h:707
ros::spinOnce
ROSCPP_DECL void spinOnce()
Process a single round of callbacks.
Definition: rossimu.cpp:255
tinyxml.h
ros::shutdown
ROSCPP_DECL void shutdown()
Disconnects everything and unregisters from the master. It is generally not necessary to call this fu...
Definition: rossimu.cpp:269
paramEntryAscii::getMinValue
std::string getMinValue()
Definition: sick_scan_test.cpp:156
paramEntryAscii::getValue
std::string getValue()
Definition: sick_scan_test.cpp:151
main
int main(int argc, char **argv)
Startup routine.
Definition: sick_scan_test.cpp:656
ros::Subscriber::shutdown
void shutdown()
ROS_WARN
#define ROS_WARN(...)
Definition: sick_scan_logging.h:122
TiXmlDocument::Error
bool Error() const
Definition: tinyxml.h:1671
ros::console::set_logger_level
ROSCONSOLE_DECL bool set_logger_level(const std::string &name, levels::Level level)
searchForXmlFileInPathList
void searchForXmlFileInPathList(std::string pathList, std::string &packagePath, std::string xmlFileName)
Definition: sick_scan_test.cpp:595
paramEntryAscii::paramEntryAscii
paramEntryAscii(std::string _nameVal, std::string _typeVal, std::string _valueVal)
Definition: sick_scan_test.cpp:101
TiXmlDocument::ErrorRow
int ErrorRow() const
Definition: tinyxml.h:1691
callbackScanCnt
int callbackScanCnt
Definition: sick_scan_test.cpp:423
ros::console::levels::Debug
Debug
rangeStdDev
double rangeStdDev
Definition: sick_scan_test.cpp:419
tinystr.h
TiXmlDocument::SaveFile
bool SaveFile() const
Save a file using the current document value. Returns true if successful.
Definition: tinyxml.cpp:960
SICK_SCAN_TEST_MAJOR_VER
#define SICK_SCAN_TEST_MAJOR_VER
Definition: sick_scan_test.cpp:92
cloud_callback
void cloud_callback(const sensor_msgs::PointCloud2ConstPtr &cloud_msg)
Definition: sick_scan_test.cpp:426
cloud_height
int cloud_height
Definition: sick_scan_test.cpp:416
TiXmlAttribute
Definition: tinyxml.h:884
polar_to_cartesian_pointcloud_ros1.node
node
Definition: polar_to_cartesian_pointcloud_ros1.py:81
ROS_FATAL
#define ROS_FATAL(...)
Definition: sick_scan_logging.h:132
scan_time_increment
float scan_time_increment
Definition: sick_scan_test.cpp:526
paramEntryAscii::getName
std::string getName()
Definition: sick_scan_test.cpp:141
callbackCnt
int callbackCnt
Definition: sick_scan_test.cpp:417
TiXmlElement::SetAttribute
void SetAttribute(const char *name, const char *_value)
Definition: tinyxml.cpp:780
paramEntryAscii::getPointerToXmlNode
TiXmlElement * getPointerToXmlNode(void)
Definition: sick_scan_test.cpp:115
range_max
float range_max
Definition: sick_scan_test.cpp:529
paramEntryAscii::getErrorCode
int getErrorCode()
Definition: sick_scan_test.cpp:172
paramEntryAscii::setValues
void setValues(std::string _nameVal, std::string _typeVal, std::string _valueVal)
Definition: sick_scan_test.cpp:119
getParamList
std::vector< paramEntryAscii > getParamList(TiXmlNode *paramList)
Definition: sick_scan_test.cpp:206
paramEntryAscii::setCheckStatus
void setCheckStatus(int errCode, std::string errMsg)
Definition: sick_scan_test.cpp:166
launchRosFile
pid_t launchRosFile(std::string cmdLine)
Definition: sick_scan_test.cpp:323
ros::NodeHandle::subscribe
Subscriber subscribe(const std::string &topic, uint32_t queue_size, const boost::function< void(C)> &callback, const VoidConstPtr &tracked_object=VoidConstPtr(), const TransportHints &transport_hints=TransportHints())
TiXmlNode
Definition: tinyxml.h:473
TINYXML_EXPORT_ATTR
#define TINYXML_EXPORT_ATTR
Definition: tinystr.h:42
sudokill
void sudokill(pid_t tokill)
Definition: sick_scan_test.cpp:198
fork
int fork()
Definition: rossimu.cpp:492
ROS_INFO
#define ROS_INFO(...)
Definition: sick_scan_logging.h:117
paramEntryAscii::getErrorMsg
std::string getErrorMsg()
Definition: sick_scan_test.cpp:177
TiXmlDocument::ErrorDesc
const char * ErrorDesc() const
Contains a textual (english) description of the error if one occurs.
Definition: tinyxml.h:1675
paramEntryAscii::getType
std::string getType()
Definition: sick_scan_test.cpp:146
getPackageRootFolder
bool getPackageRootFolder(std::string exePath, std::string &packageRootFolder)
Definition: sick_scan_test.cpp:619
ros::this_node::getName
const ROSCPP_DECL std::string & getName()
Returns the name of the current node.
Definition: rossimu.cpp:343
scan_angle_max
float scan_angle_max
Definition: sick_scan_test.cpp:525
paramEntryAscii::setMinMaxValues
void setMinMaxValues(std::string _valueMinVal, std::string _valueMaxVal)
Definition: sick_scan_test.cpp:132
startCallbackTest
int startCallbackTest(int argc, char **argv)
Definition: sick_scan_test.cpp:547
SICK_SCAN_TEST_MINOR_VER
#define SICK_SCAN_TEST_MINOR_VER
Definition: sick_scan_test.cpp:93
range_min
float range_min
Definition: sick_scan_test.cpp:528
unistd.h
ROS_ERROR
#define ROS_ERROR(...)
Definition: sick_scan_logging.h:127
TiXmlNode::Value
const char * Value() const
Definition: tinyxml.h:538
SICK_SCAN_TEST_PATCH_LEVEL
#define SICK_SCAN_TEST_PATCH_LEVEL
Definition: sick_scan_test.cpp:94
cloud_width
int cloud_width
Definition: sick_scan_test.cpp:415
TiXmlDocument::LoadFile
bool LoadFile(TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
Definition: tinyxml.cpp:954
roswrap::this_node::getName
const ROSCPP_DECL std::string & getName()
Returns the name of the current node.
Definition: rossimu.cpp:343
ERR_CONST
float ERR_CONST
Definition: sick_scan_test.cpp:523
TiXmlElement::FirstAttribute
TiXmlAttribute * FirstAttribute()
Definition: tinyxml.h:1227
pid
pid_t pid
Definition: sick_scan_test.cpp:321
ros::Rate
ROSCONSOLE_DEFAULT_NAME
#define ROSCONSOLE_DEFAULT_NAME
Definition: console.h:300
createPrefixFromExeName
void createPrefixFromExeName(std::string exeName, std::string &prefix)
Definition: sick_scan_test.cpp:572
cmd
string cmd
TiXmlText
Definition: tinyxml.h:1372
sleep
void sleep(int secs)
rosconsole_simu.hpp
paramEntryAscii::getMaxValue
std::string getMaxValue()
Definition: sick_scan_test.cpp:161
sick_scan_xd_simu.status
def status
Definition: sick_scan_xd_simu.py:139
intensityStdDev
double intensityStdDev
Definition: sick_scan_test.cpp:418
sensor_msgs::PointCloud2ConstPtr
std::shared_ptr< ::sensor_msgs::PointCloud2 const > PointCloud2ConstPtr
Definition: PointCloud2.h:93
ros::NodeHandle
ros::Subscriber


sick_scan_xd
Author(s): Michael Lehning , Jochen Sprickerhof , Martin Günther
autogenerated on Fri Oct 25 2024 02:47:11