ROS2Names.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "ROS2Names.h"
  9. #include <AzCore/std/string/regex.h>
  10. #include <rcl/validate_topic_name.h>
  11. #include <rmw/validate_namespace.h>
  12. namespace ROS2
  13. {
  14. AZStd::string ROS2Names::GetNamespacedName(const AZStd::string& ns, const AZStd::string& name)
  15. {
  16. if (ns.empty())
  17. {
  18. return name;
  19. }
  20. return AZStd::string::format("%s/%s", ns.c_str(), name.c_str());;
  21. }
  22. AZStd::string ROS2Names::RosifyName(const AZStd::string& input)
  23. {
  24. // TODO - add unit tests
  25. // TODO - implement stricter guidelines and differentiate: https://design.ros2.org/articles/topic_and_service_names.html
  26. AZStd::string rosified = input;
  27. if (input.empty())
  28. {
  29. return rosified;
  30. }
  31. const char underscore = '_';
  32. if (input[0] == underscore)
  33. {
  34. AZ_Warning("RosifyName",
  35. false,
  36. "'%s' name starts with an underscore, which makes topic/namespace/parameter hidden by default. Is this intended?",
  37. input.c_str());
  38. }
  39. const AZStd::string stringToReplaceViolations(1, underscore);
  40. const AZStd::regex ros2Disallowedlist("[^0-9|a-z|A-Z|_]");
  41. rosified = AZStd::regex_replace(rosified, ros2Disallowedlist, stringToReplaceViolations);
  42. if (std::isdigit(rosified[0]) || (input[0] != underscore && rosified[0] == underscore))
  43. { // Prepend "o3de_" if it would otherwise start with a number (which would violate ros2 name requirements)
  44. // Also, starting with '_' is not desired unless explicit. Topics/namespaces/parameters starting with "_" are hidden by default.
  45. const AZStd::string prependToNumberStart = "o3de_";
  46. rosified = prependToNumberStart + rosified;
  47. }
  48. if (input != rosified)
  49. {
  50. AZ_TracePrintf("RosifyName", "Name '%s' has been changed to '%s' to conform with ros2 naming restrictions", input.c_str(), rosified.c_str());
  51. }
  52. return rosified;
  53. }
  54. AZ::Outcome<void, AZStd::string> ROS2Names::ValidateNamespace(const AZStd::string& ros2Namespace)
  55. {
  56. auto ros2GlobalizedNamespace = ros2Namespace;
  57. const char namespacePrefix = '/';
  58. if (ros2Namespace.find(namespacePrefix, 0) != 0)
  59. { // Prepend "/" if not included, this is done automatically by rclcpp so "/"-less namespaces are ok.
  60. ros2GlobalizedNamespace = namespacePrefix + ros2Namespace;
  61. }
  62. int validationResult = 0;
  63. auto ret = rmw_validate_namespace(ros2GlobalizedNamespace.c_str(), &validationResult, NULL);
  64. if (ret != RMW_RET_OK)
  65. {
  66. AZ_Error("ValidateNamespace", false, "Call to rmw validation for namespace failed");
  67. return AZ::Failure(AZStd::string("Unable to validate namespace due to rmw error"));
  68. }
  69. if (validationResult != RMW_NAMESPACE_VALID)
  70. {
  71. return AZ::Failure(AZStd::string(rmw_namespace_validation_result_string(validationResult)));
  72. }
  73. return AZ::Success();
  74. }
  75. AZ::Outcome<void, AZStd::string> ROS2Names::ValidateNamespaceField(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  76. {
  77. AZStd::string ros2Namespace(static_cast<const char*>(newValue));
  78. return ValidateNamespace(ros2Namespace);
  79. }
  80. AZ::Outcome<void, AZStd::string> ROS2Names::ValidateTopic(const AZStd::string& topic)
  81. {
  82. int validationResult = 0;
  83. size_t invalidIndex; // Unused. We could choose to display it in information, but it is not necessary.
  84. if (rcl_validate_topic_name(topic.c_str(), &validationResult, &invalidIndex) != RCL_RET_OK)
  85. {
  86. AZ_Error("ValidateTopic", false, "Call to rcl validation for topic failed");
  87. return AZ::Failure(AZStd::string("Unable to validate topic due to rcl error"));
  88. }
  89. if (RCL_TOPIC_NAME_VALID != validationResult)
  90. {
  91. return AZ::Failure(AZStd::string(rcl_topic_name_validation_result_string(validationResult)));
  92. }
  93. return AZ::Success();
  94. }
  95. AZ::Outcome<void, AZStd::string> ROS2Names::ValidateTopicField(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  96. {
  97. AZStd::string topic(static_cast<const char*>(newValue));
  98. return ValidateTopic(topic);
  99. }
  100. } // namespace ROS2