From 89f7cf6da969297854a250377c81e50f9aac3cc1 Mon Sep 17 00:00:00 2001 From: ob-yalian Date: Wed, 19 Aug 2026 17:10:31 +0800 Subject: [PATCH] feat: enhance error handling and documentation links in benchmark tools --- .../scripts/common_benchmark_node.py | 27 +++++++- .../scripts/service_benchmark_node.py | 28 ++++++++- orbbec_camera/tools/firmware_update_tool.cpp | 8 ++- orbbec_camera/tools/ip_config_tool.cpp | 61 +++++++++---------- orbbec_camera/tools/list_camera_profile.cpp | 10 ++- orbbec_camera/tools/list_devices_node.cpp | 13 +++- 6 files changed, 105 insertions(+), 42 deletions(-) diff --git a/orbbec_camera/scripts/common_benchmark_node.py b/orbbec_camera/scripts/common_benchmark_node.py index 97f4e169..a012d5a6 100644 --- a/orbbec_camera/scripts/common_benchmark_node.py +++ b/orbbec_camera/scripts/common_benchmark_node.py @@ -12,6 +12,7 @@ usage: """ import argparse +import sys import rclpy from rclpy.node import Node import psutil @@ -21,11 +22,24 @@ import os from collections import defaultdict from orbbec_camera_msgs.msg import DeviceStatus from sensor_msgs.msg import Image -import sys from tabulate import tabulate CAMERA_NODE_NAMES = ["component_container", "orbbec_camera_node", "nodelet"] +DOCUMENTATION_URL = ( + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/" + "6_benchmark/benchmark_tools.html" +) + + +class DocumentationArgumentParser(argparse.ArgumentParser): + def error(self, message): + self.print_usage(sys.stderr) + self.exit( + 2, + f"{self.prog}: error: {message}\n" + f"For usage and troubleshooting, see: {DOCUMENTATION_URL}\n", + ) # ----------------tool functions---------------- def parse_duration(s): @@ -471,7 +485,10 @@ class CameraMonitorNode(Node): def main(argv=None): - parser = argparse.ArgumentParser() + parser = DocumentationArgumentParser( + epilog=f"Documentation: {DOCUMENTATION_URL}", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) parser.add_argument("--run_time", type=str, default="10s", help="Total run time for monitoring, e.g., 10s, 5m, 1h.") parser.add_argument("--csv_file", type=str, default="camera_monitor_log.csv") parser.add_argument("--ideal_fps", type=float, default=0.0, help="Optional ideal frame rate to use for drop detection (overrides reported avg).") @@ -494,4 +511,8 @@ def main(argv=None): node.destroy_node() if __name__ == "__main__": - main() + try: + main() + except Exception: + print(f"For usage and troubleshooting, see: {DOCUMENTATION_URL}", file=sys.stderr) + raise diff --git a/orbbec_camera/scripts/service_benchmark_node.py b/orbbec_camera/scripts/service_benchmark_node.py index 3aaed950..e79dd93e 100644 --- a/orbbec_camera/scripts/service_benchmark_node.py +++ b/orbbec_camera/scripts/service_benchmark_node.py @@ -23,6 +23,23 @@ from statistics import mean from tabulate import tabulate import importlib import csv +import sys + + +DOCUMENTATION_URL = ( + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/" + "6_benchmark/benchmark_tools.html" +) + + +class DocumentationArgumentParser(argparse.ArgumentParser): + def error(self, message): + self.print_usage(sys.stderr) + self.exit( + 2, + f"{self.prog}: error: {message}\n" + f"For usage and troubleshooting, see: {DOCUMENTATION_URL}\n", + ) class ServiceBenchmark: @@ -180,7 +197,10 @@ class BenchmarkRunner: def main(): - parser = argparse.ArgumentParser() + parser = DocumentationArgumentParser( + epilog=f"Documentation: {DOCUMENTATION_URL}", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) parser.add_argument("--service", help="Service name") parser.add_argument("--count", type=int, default=10, help="Number of calls") parser.add_argument("--yaml_file", help="YAML config file for batch testing") @@ -207,4 +227,8 @@ def main(): if __name__ == "__main__": - main() + try: + main() + except Exception: + print(f"For usage and troubleshooting, see: {DOCUMENTATION_URL}", file=sys.stderr) + raise diff --git a/orbbec_camera/tools/firmware_update_tool.cpp b/orbbec_camera/tools/firmware_update_tool.cpp index 01943fe5..3fdb97b5 100644 --- a/orbbec_camera/tools/firmware_update_tool.cpp +++ b/orbbec_camera/tools/firmware_update_tool.cpp @@ -33,6 +33,9 @@ #include "orbbec_camera/utils.h" namespace { +constexpr char kDocumentationUrl[] = + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/" + "firmware_update_tool.html"; constexpr int kProgressLogBucketPercent = 25; constexpr int kFirmwareLogDrainDelaySec = 5; @@ -131,7 +134,9 @@ void printUsage() { << " 2) If multiple devices are connected, specify target by serial/usb/ip to avoid wrong " "updates.\n" << " 3) Repeating --serial_number or passing comma-separated values enables sequential " - "batch update.\n"; + "batch update.\n\n" + << "Documentation:\n" + << " " << kDocumentationUrl << "\n"; } bool parseArgs(int argc, char **argv, CliArgs &args, std::string &error) { @@ -862,6 +867,7 @@ int main(int argc, char **argv) { RCLCPP_ERROR(logger, "Unknown error"); } + RCLCPP_ERROR(logger, "For usage and troubleshooting, see: %s", kDocumentationUrl); rclcpp::shutdown(); return 1; } diff --git a/orbbec_camera/tools/ip_config_tool.cpp b/orbbec_camera/tools/ip_config_tool.cpp index 2a1fef60..6f36ecde 100644 --- a/orbbec_camera/tools/ip_config_tool.cpp +++ b/orbbec_camera/tools/ip_config_tool.cpp @@ -14,6 +14,9 @@ using namespace ob; namespace { constexpr int kFirmwareLogDrainDelaySec = 5; +constexpr char kDocumentationUrl[] = + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/" + "network_config_tools.html"; void waitForFirmwareLogDrain(const rclcpp::Logger &logger) { RCLCPP_INFO(logger, "Waiting %d seconds to keep firmware log alive...", @@ -25,6 +28,12 @@ bool isSdkLogEnabled(const std::string &log_level) { return orbbec_camera::obLogSeverityFromString(log_level) != OBLogSeverity::OB_LOG_SEVERITY_OFF; } +int reportFailure(const rclcpp::Logger &logger) { + RCLCPP_ERROR(logger, "For usage and troubleshooting, see: %s", kDocumentationUrl); + rclcpp::shutdown(); + return 1; +} + } // namespace struct CliArgs { @@ -177,7 +186,9 @@ void printHelp() { << " Debug: ros2 run orbbec_camera ip_config_tool -- \\\n" << " set_ip --current_ip 192.168.1.10 --enable_dhcp true " "--enable_persistent_ip false \\\n" - << " --sdk_log_level debug\n"; + << " --sdk_log_level debug\n\n" + << "Documentation:\n" + << " " << kDocumentationUrl << "\n"; } bool parseArgs(int argc, char **argv, CliArgs &args, std::string &error) { @@ -461,18 +472,15 @@ int main(int argc, char **argv) { uint8_t gateway[4] = {0}; if (args.persistent_ip && !parseIpString(args.new_ip, address)) { RCLCPP_ERROR(logger, "Invalid new_ip format: %s", args.new_ip.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (args.persistent_ip && !parseIpString(args.mask, mask)) { RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (args.persistent_ip && !parseIpString(args.gateway, gateway)) { RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } OBNetIpConfigV2 ip_config_v2{}; @@ -521,8 +529,7 @@ int main(int argc, char **argv) { "Legacy IP config (1041) supports mutually exclusive DHCP and persistent " "IP only. Use --enable_dhcp true --enable_persistent_ip false or " "--enable_dhcp false --enable_persistent_ip true."); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } ip_config.dhcp = args.dhcp ? 1 : 0; uint8_t address[4] = {0}; @@ -530,18 +537,15 @@ int main(int argc, char **argv) { uint8_t gateway[4] = {0}; if (args.persistent_ip && !parseIpString(args.new_ip, address)) { RCLCPP_ERROR(logger, "Invalid new_ip format: %s", args.new_ip.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (args.persistent_ip && !parseIpString(args.mask, mask)) { RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (args.persistent_ip && !parseIpString(args.gateway, gateway)) { RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (args.persistent_ip) { std::memcpy(ip_config.address, address, sizeof(address)); @@ -579,18 +583,15 @@ int main(int argc, char **argv) { if (!args.dhcp) { if (!parseIpString(args.new_ip, ip_config.address)) { RCLCPP_ERROR(logger, "Invalid new_ip format: %s", args.new_ip.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (!parseIpString(args.mask, ip_config.mask)) { RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } if (!parseIpString(args.gateway, ip_config.gateway)) { RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } } @@ -607,8 +608,7 @@ int main(int argc, char **argv) { } } else { RCLCPP_ERROR(logger, "Force-ip failed (SDK returned false)."); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } } @@ -621,16 +621,14 @@ int main(int argc, char **argv) { if (!device->isPropertySupported(OB_PROP_DHCP_ASSIGN_IP_TIMEOUT_INT, OB_PERMISSION_WRITE)) { RCLCPP_ERROR(logger, "Current device or firmware does not support DHCP assign IP timeout"); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } auto range = device->getIntPropertyRange(OB_PROP_DHCP_ASSIGN_IP_TIMEOUT_INT); if (args.dhcp_assign_ip_timeout < range.min || args.dhcp_assign_ip_timeout > range.max) { RCLCPP_ERROR(logger, "Timeout %d is out of range [%d, %d]", args.dhcp_assign_ip_timeout, range.min, range.max); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } device->setIntProperty(OB_PROP_DHCP_ASSIGN_IP_TIMEOUT_INT, args.dhcp_assign_ip_timeout); @@ -645,16 +643,13 @@ int main(int argc, char **argv) { } catch (ob::Error &e) { RCLCPP_ERROR(logger, "ip_config_tool: %s", orbbec_camera::formatObErrorWithStatus(e).c_str()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } catch (const std::exception &e) { RCLCPP_ERROR(logger, "ip_config_tool: %s", e.what()); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } catch (...) { RCLCPP_ERROR(logger, "ip_config_tool: unknown error"); - rclcpp::shutdown(); - return 1; + return reportFailure(logger); } rclcpp::shutdown(); diff --git a/orbbec_camera/tools/list_camera_profile.cpp b/orbbec_camera/tools/list_camera_profile.cpp index 364e8825..78c03a6f 100644 --- a/orbbec_camera/tools/list_camera_profile.cpp +++ b/orbbec_camera/tools/list_camera_profile.cpp @@ -15,6 +15,9 @@ using namespace orbbec_camera; namespace { constexpr int kFirmwareLogDrainDelaySec = 5; +constexpr char kDocumentationUrl[] = + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/" + "device_query_tools.html"; struct CliArgs { bool help = false; @@ -33,7 +36,9 @@ void printUsage() { "(default: off).\n" << " -h, --help Show this help message.\n" << "Examples:\n" - << " ros2 run orbbec_camera list_camera_profile_mode_node -- --sdk_log_level debug\n"; + << " ros2 run orbbec_camera list_camera_profile_mode_node -- --sdk_log_level debug\n\n" + << "Documentation:\n" + << " " << kDocumentationUrl << "\n"; } bool parseArgs(int argc, char** argv, CliArgs& args, std::string& error) { @@ -116,7 +121,8 @@ std::shared_ptr initializeDevice(const std::string& serial_number) { auto context = std::make_shared(); auto device_list = context->queryDeviceList(); if (!device_list || device_list->getCount() == 0) { - std::cout << "No device found" << std::endl; + std::cout << "No device found\nFor usage and troubleshooting, see: " << kDocumentationUrl + << std::endl; return nullptr; } diff --git a/orbbec_camera/tools/list_devices_node.cpp b/orbbec_camera/tools/list_devices_node.cpp index aeca24a3..6cdfcb36 100644 --- a/orbbec_camera/tools/list_devices_node.cpp +++ b/orbbec_camera/tools/list_devices_node.cpp @@ -29,6 +29,9 @@ namespace { constexpr int kFirmwareLogDrainDelaySec = 5; +constexpr char kDocumentationUrl[] = + "https://orbbec.github.io/OrbbecSDK_ROS2/en/source/camera_devices/6_benchmark/" + "device_query_tools.html"; struct CliArgs { bool help = false; @@ -42,7 +45,9 @@ void printUsage() { << " --sdk_log_level LEVEL SDK file log level: debug/info/warn/error/fatal/off " "(default: off).\n\n" << "Examples:\n" - << " ros2 run orbbec_camera list_devices_node -- --sdk_log_level debug\n"; + << " ros2 run orbbec_camera list_devices_node -- --sdk_log_level debug\n\n" + << "Documentation:\n" + << " " << kDocumentationUrl << "\n"; } bool parseArgs(int argc, char **argv, CliArgs &args, std::string &error) { @@ -339,10 +344,16 @@ int main(int argc, char **argv) { } catch (ob::Error &e) { RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), orbbec_camera::formatObErrorWithStatus(e)); + RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), + "For usage and troubleshooting, see: " << kDocumentationUrl); } catch (const std::exception &e) { RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), e.what()); + RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), + "For usage and troubleshooting, see: " << kDocumentationUrl); } catch (...) { RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), "unknown error"); + RCLCPP_ERROR_STREAM(rclcpp::get_logger("list_device_node"), + "For usage and troubleshooting, see: " << kDocumentationUrl); } return 0; }