From e3e016ace4ae3cdf669390cd0c50887b97eca3f6 Mon Sep 17 00:00:00 2001 From: ob-yalian Date: Fri, 3 Apr 2026 16:09:54 +0800 Subject: [PATCH 1/3] fix: update CMakeLists.txt to include ip_config_tool and remove LLA parameters from ip_config_tool --- orbbec_camera/CMakeLists.txt | 2 +- orbbec_camera/tools/ip_config_tool.cpp | 71 ++++++-------------------- 2 files changed, 16 insertions(+), 57 deletions(-) diff --git a/orbbec_camera/CMakeLists.txt b/orbbec_camera/CMakeLists.txt index 29079dcf..a4b6ba75 100644 --- a/orbbec_camera/CMakeLists.txt +++ b/orbbec_camera/CMakeLists.txt @@ -254,7 +254,7 @@ if(DEFINED ENV{BUILDING_PACKAGE}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/scripts/99-obsensor-libusb.rules DESTINATION /etc/udev/rules.d) endif() -install(TARGETS list_devices_node list_depth_work_mode_node list_camera_profile_mode_node firmware_update_tool topic_statistics_node service_benchmark_node ob_benchmark_node 435le_example_node set_device_ip image_sync_example_node DESTINATION lib/${PROJECT_NAME}/ +install(TARGETS list_devices_node list_depth_work_mode_node list_camera_profile_mode_node firmware_update_tool topic_statistics_node service_benchmark_node ob_benchmark_node 435le_example_node ip_config_tool set_device_ip image_sync_example_node DESTINATION lib/${PROJECT_NAME}/ ) if(BUILD_TESTING) diff --git a/orbbec_camera/tools/ip_config_tool.cpp b/orbbec_camera/tools/ip_config_tool.cpp index eaab8b06..3da708b9 100644 --- a/orbbec_camera/tools/ip_config_tool.cpp +++ b/orbbec_camera/tools/ip_config_tool.cpp @@ -26,17 +26,6 @@ bool parseIpString(const std::string &ip_str, uint8_t ip[4]) { return i == 4; } -bool isParamProvided(int argc, char **argv, const std::string &key) { - const std::string pattern = key + ":="; - for (int i = 1; i < argc; ++i) { - const std::string arg(argv[i]); - if (arg.find(pattern) != std::string::npos) { - return true; - } - } - return false; -} - void printHelp() { std::cout << "Usage:\n" @@ -45,10 +34,6 @@ void printHelp() { << "Parameters:\n" << " -p old_ip:= Current device IP (default: 192.168.1.10)\n" << " -p port:= Device port (default: 8090)\n" - << " -p enable_lla:= Set LLA switch directly (true: enable, false: disable, " - "default: false)\n" - << " Note: LLA is applied only when this parameter is " - "explicitly provided.\n" << " -p enable_set_ip:= Enable set-ip operation (default: false)\n" << " -p dhcp:= DHCP flag for set-ip/force-ip config (default: false)\n" << " -p new_ip:= Static IP for set-ip/force-ip (default: 192.168.1.200)\n" @@ -59,12 +44,6 @@ void printHelp() { "54:14:FD:06:07:DA)\n\n" << "Examples:\n" << "\n" - << " [LLA]\n" - << " enable: ros2 run orbbec_camera ip_config_tool --ros-args -p old_ip:=192.168.1.10 -p " - "enable_lla:=true\n" - << " disable: ros2 run orbbec_camera ip_config_tool --ros-args -p old_ip:=192.168.1.10 -p " - "enable_lla:=false\n" - << "\n" << " [Set IP]\n" << " DHCP: ros2 run orbbec_camera ip_config_tool --ros-args \\\n" << " -p old_ip:=192.168.1.10 -p enable_set_ip:=true -p dhcp:=true\n" @@ -95,9 +74,6 @@ int main(int argc, char **argv) { std::string device_ip_str = node->declare_parameter("old_ip", "192.168.1.10"); int port = node->declare_parameter("port", 8090); - bool enable_lla = node->declare_parameter("enable_lla", false); - bool do_lla = isParamProvided(argc, argv, "enable_lla"); - bool enable_set_ip = node->declare_parameter("enable_set_ip", false); bool dhcp = node->declare_parameter("dhcp", false); std::string new_ip_str = node->declare_parameter("new_ip", "192.168.1.200"); @@ -107,9 +83,9 @@ int main(int argc, char **argv) { bool enable_force_ip = node->declare_parameter("enable_force_ip", false); std::string force_ip_mac = node->declare_parameter("force_ip_mac", ""); - if (!do_lla && !enable_set_ip && !enable_force_ip) { + if (!enable_set_ip && !enable_force_ip) { RCLCPP_ERROR(logger, - "No operation enabled. Please enable at least one of: enable_lla, enable_set_ip, " + "No operation enabled. Please enable at least one of: enable_set_ip, " "enable_force_ip."); rclcpp::shutdown(); return 1; @@ -140,41 +116,24 @@ int main(int argc, char **argv) { ob::Context::setLoggerSeverity(OBLogSeverity::OB_LOG_SEVERITY_OFF); auto context = std::make_shared(); - if (do_lla || enable_set_ip) { + if (enable_set_ip) { RCLCPP_INFO(logger, "Connecting to device %s:%d ...", device_ip_str.c_str(), port); auto device = context->createNetDevice(device_ip_str.c_str(), port); - if (do_lla) { - if (device->isPropertySupported(OB_PROP_DEVICE_NETWORK_LLA_BOOL, - OB_PERMISSION_READ_WRITE)) { - device->setBoolProperty(OB_PROP_DEVICE_NETWORK_LLA_BOOL, enable_lla); - RCLCPP_INFO(logger, "LLA set successfully. target=%s", - enable_lla ? "enabled" : "disabled"); - } else { - RCLCPP_WARN(logger, "LLA property is not supported on this device."); - } - } else { - RCLCPP_INFO(logger, "LLA operation skipped (enable_lla not explicitly provided)."); - } + RCLCPP_INFO(logger, "Applying set-ip configuration..."); + device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG, + reinterpret_cast(&ip_config), sizeof(ip_config)); - if (enable_set_ip) { - RCLCPP_INFO(logger, "Applying set-ip configuration..."); - device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG, - reinterpret_cast(&ip_config), sizeof(ip_config)); - - RCLCPP_INFO(logger, "Set-ip configuration applied successfully."); - if (dhcp) { - RCLCPP_INFO(logger, "Set-ip target mode: DHCP."); - } else { - RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config.address[0], - ip_config.address[1], ip_config.address[2], ip_config.address[3]); - RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], - ip_config.mask[1], ip_config.mask[2], ip_config.mask[3]); - RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config.gateway[0], - ip_config.gateway[1], ip_config.gateway[2], ip_config.gateway[3]); - } + RCLCPP_INFO(logger, "Set-ip configuration applied successfully."); + if (dhcp) { + RCLCPP_INFO(logger, "Set-ip target mode: DHCP."); } else { - RCLCPP_INFO(logger, "Set-ip operation skipped (enable_set_ip=false)."); + RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config.address[0], + ip_config.address[1], ip_config.address[2], ip_config.address[3]); + RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], + ip_config.mask[1], ip_config.mask[2], ip_config.mask[3]); + RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config.gateway[0], + ip_config.gateway[1], ip_config.gateway[2], ip_config.gateway[3]); } } From 7cb6ab3e4aeb2e9e3d8a34a5a0e03a04577f1a71 Mon Sep 17 00:00:00 2001 From: ob-yalian Date: Fri, 3 Apr 2026 16:35:54 +0800 Subject: [PATCH 2/3] feat: enhance ip_config_tool with CLI argument parsing and improved help documentation --- orbbec_camera/tools/ip_config_tool.cpp | 325 +++++++++++++++++++------ 1 file changed, 249 insertions(+), 76 deletions(-) diff --git a/orbbec_camera/tools/ip_config_tool.cpp b/orbbec_camera/tools/ip_config_tool.cpp index 3da708b9..3f273a99 100644 --- a/orbbec_camera/tools/ip_config_tool.cpp +++ b/orbbec_camera/tools/ip_config_tool.cpp @@ -1,14 +1,33 @@ #include "rclcpp/rclcpp.hpp" #include #include + +#include +#include #include #include #include -#include #include using namespace ob; +struct CliArgs { + enum class Operation { + NONE, + SET_IP, + FORCE_IP, + }; + bool help = false; + Operation operation = Operation::NONE; + bool dhcp = false; + std::string force_ip_mac; + std::string old_ip = "192.168.1.10"; + int port = 8090; + std::string new_ip = "192.168.1.200"; + std::string mask = "255.255.255.0"; + std::string gateway = "192.168.1.1"; +}; + bool parseIpString(const std::string &ip_str, uint8_t ip[4]) { std::stringstream ss(ip_str); std::string item; @@ -26,87 +45,247 @@ bool parseIpString(const std::string &ip_str, uint8_t ip[4]) { return i == 4; } +bool parseBool(const std::string &value, bool &out) { + if (value == "true" || value == "1") { + out = true; + return true; + } + if (value == "false" || value == "0") { + out = false; + return true; + } + return false; +} + +bool parseInt(const std::string &value, int &out) { + try { + size_t consumed = 0; + const int parsed = std::stoi(value, &consumed); + if (consumed != value.size()) { + return false; + } + out = parsed; + return true; + } catch (...) { + return false; + } +} + void printHelp() { std::cout << "Usage:\n" - << " ros2 run orbbec_camera ip_config_tool --ros-args [params]\n" + << " ros2 run orbbec_camera ip_config_tool --\\\n" + << " [options]\n" + << " ros2 run orbbec_camera ip_config_tool -- set_ip [--enable_dhcp ]\\\n" + << " [--old_ip ] [--port ] [--new_ip ] [--mask ] [--gateway ]\n" + << " ros2 run orbbec_camera ip_config_tool -- force_ip --force_ip_mac \\\n" + << " [--enable_dhcp ] [--new_ip ] [--mask ] [--gateway ]\n" << " (legacy alias: set_device_ip)\n\n" + << "Subcommands:\n" + << " set_ip Configure IP on device by current device address.\n" + << " force_ip Force IP by MAC address.\n\n" << "Parameters:\n" - << " -p old_ip:= Current device IP (default: 192.168.1.10)\n" - << " -p port:= Device port (default: 8090)\n" - << " -p enable_set_ip:= Enable set-ip operation (default: false)\n" - << " -p dhcp:= DHCP flag for set-ip/force-ip config (default: false)\n" - << " -p new_ip:= Static IP for set-ip/force-ip (default: 192.168.1.200)\n" - << " -p mask:= Subnet mask for set-ip/force-ip (default: 255.255.255.0)\n" - << " -p gateway:= Gateway for set-ip/force-ip (default: 192.168.1.1)\n" - << " -p enable_force_ip:= Enable force-ip operation (default: false)\n" - << " -p force_ip_mac:= Target MAC for force-ip (required, e.g. " - "54:14:FD:06:07:DA)\n\n" + << " --enable_dhcp DHCP flag for set-ip/force-ip (default: false).\n" + << " --old_ip Current device IP for set-ip (default: 192.168.1.10).\n" + << " --port Device port for set-ip (default: 8090).\n" + << " --new_ip Static IP for set-ip/force-ip (default: 192.168.1.200).\n" + << " --mask Subnet mask for set-ip/force-ip (default: 255.255.255.0).\n" + << " --gateway Gateway for set-ip/force-ip (default: 192.168.1.1).\n" + << " --force_ip_mac Target MAC for force-ip (required, e.g. " + "54:14:FD:06:07:DA).\n\n" << "Examples:\n" << "\n" << " [Set IP]\n" - << " DHCP: ros2 run orbbec_camera ip_config_tool --ros-args \\\n" - << " -p old_ip:=192.168.1.10 -p enable_set_ip:=true -p dhcp:=true\n" - << " Static: ros2 run orbbec_camera ip_config_tool --ros-args \\\n" - << " -p old_ip:=192.168.1.10 -p enable_set_ip:=true -p dhcp:=false \\\n" - << " -p new_ip:=192.168.1.200 -p mask:=255.255.255.0 -p gateway:=192.168.1.1\n" + << " DHCP: ros2 run orbbec_camera ip_config_tool -- \\\n" + << " set_ip \\\n" + << " --old_ip 192.168.1.10 \\\n" + << " --enable_dhcp true\n" + << " Static: ros2 run orbbec_camera ip_config_tool -- \\\n" + << " set_ip \\\n" + << " --old_ip 192.168.1.10 \\\n" + << " --enable_dhcp false \\\n" + << " --new_ip 192.168.1.200 \\\n" + << " --mask 255.255.255.0 \\\n" + << " --gateway 192.168.1.1\n" << "\n" << " [Force IP]\n" - << " by MAC: ros2 run orbbec_camera ip_config_tool --ros-args \\\n" - << " -p enable_force_ip:=true \\\n" - << " -p force_ip_mac:=54:14:FD:06:07:DA -p dhcp:=false \\\n" - << " -p new_ip:=192.168.1.200 -p mask:=255.255.255.0 -p gateway:=192.168.1.1\n"; + << " by MAC: ros2 run orbbec_camera ip_config_tool -- \\\n" + << " force_ip \\\n" + << " --force_ip_mac 54:14:FD:06:07:DA \\\n" + << " --enable_dhcp false \\\n" + << " --new_ip 192.168.1.200 \\\n" + << " --mask 255.255.255.0 \\\n" + << " --gateway 192.168.1.1\n"; +} + +bool parseArgs(int argc, char **argv, CliArgs &args, std::string &error) { + for (int i = 1; i < argc; ++i) { + const std::string current = argv[i]; + + if (current == "-h" || current == "--help") { + args.help = true; + return true; + } + if (current == "set_ip" || current == "set-ip") { + if (args.operation != CliArgs::Operation::NONE) { + error = "Only one subcommand is allowed: set_ip or force_ip"; + return false; + } + args.operation = CliArgs::Operation::SET_IP; + continue; + } + if (current == "force_ip" || current == "force-ip") { + if (args.operation != CliArgs::Operation::NONE) { + error = "Only one subcommand is allowed: set_ip or force_ip"; + return false; + } + args.operation = CliArgs::Operation::FORCE_IP; + continue; + } + + if (current.rfind("--enable_dhcp=", 0) == 0) { + if (!parseBool(current.substr(std::strlen("--enable_dhcp=")), args.dhcp)) { + error = "--enable_dhcp expects true/false"; + return false; + } + continue; + } + if (current == "--enable_dhcp") { + if (++i >= argc || !parseBool(argv[i], args.dhcp)) { + error = "--enable_dhcp expects true/false"; + return false; + } + continue; + } + + if (current.rfind("--old_ip=", 0) == 0) { + args.old_ip = current.substr(std::strlen("--old_ip=")); + continue; + } + if (current == "--old_ip") { + if (++i >= argc) { + error = "--old_ip requires a value"; + return false; + } + args.old_ip = argv[i]; + continue; + } + + if (current.rfind("--port=", 0) == 0) { + if (!parseInt(current.substr(std::strlen("--port=")), args.port)) { + error = "--port expects an integer"; + return false; + } + continue; + } + if (current == "--port") { + if (++i >= argc || !parseInt(argv[i], args.port)) { + error = "--port expects an integer"; + return false; + } + continue; + } + + if (current.rfind("--new_ip=", 0) == 0) { + args.new_ip = current.substr(std::strlen("--new_ip=")); + continue; + } + if (current == "--new_ip") { + if (++i >= argc) { + error = "--new_ip requires a value"; + return false; + } + args.new_ip = argv[i]; + continue; + } + + if (current.rfind("--mask=", 0) == 0) { + args.mask = current.substr(std::strlen("--mask=")); + continue; + } + if (current == "--mask") { + if (++i >= argc) { + error = "--mask requires a value"; + return false; + } + args.mask = argv[i]; + continue; + } + + if (current.rfind("--gateway=", 0) == 0) { + args.gateway = current.substr(std::strlen("--gateway=")); + continue; + } + if (current == "--gateway") { + if (++i >= argc) { + error = "--gateway requires a value"; + return false; + } + args.gateway = argv[i]; + continue; + } + + if (current.rfind("--force_ip_mac=", 0) == 0) { + args.force_ip_mac = current.substr(std::strlen("--force_ip_mac=")); + continue; + } + if (current == "--force_ip_mac") { + if (++i >= argc) { + error = "--force_ip_mac requires a value"; + return false; + } + args.force_ip_mac = argv[i]; + continue; + } + + error = "Unknown argument: " + current; + return false; + } + + if (args.operation == CliArgs::Operation::NONE) { + error = "Missing subcommand. Use one of: set_ip, force_ip"; + return false; + } + if (args.operation == CliArgs::Operation::FORCE_IP && args.force_ip_mac.empty()) { + error = "force_ip requires --force_ip_mac "; + return false; + } + + return true; } int main(int argc, char **argv) { - for (int i = 1; i < argc; ++i) { - const std::string arg(argv[i]); - if (arg == "-h" || arg == "--help") { - printHelp(); - return 0; - } + CliArgs args; + std::string parse_error; + if (!parseArgs(argc, argv, args, parse_error)) { + std::cerr << "Argument error: " << parse_error << std::endl; + printHelp(); + return 1; + } + if (args.help) { + printHelp(); + return 0; } rclcpp::init(argc, argv); - auto node = std::make_shared("ip_config_tool"); - auto logger = node->get_logger(); - - std::string device_ip_str = node->declare_parameter("old_ip", "192.168.1.10"); - int port = node->declare_parameter("port", 8090); - - bool enable_set_ip = node->declare_parameter("enable_set_ip", false); - bool dhcp = node->declare_parameter("dhcp", false); - std::string new_ip_str = node->declare_parameter("new_ip", "192.168.1.200"); - std::string mask_str = node->declare_parameter("mask", "255.255.255.0"); - std::string gateway_str = node->declare_parameter("gateway", "192.168.1.1"); - - bool enable_force_ip = node->declare_parameter("enable_force_ip", false); - std::string force_ip_mac = node->declare_parameter("force_ip_mac", ""); - - if (!enable_set_ip && !enable_force_ip) { - RCLCPP_ERROR(logger, - "No operation enabled. Please enable at least one of: enable_set_ip, " - "enable_force_ip."); - rclcpp::shutdown(); - return 1; - } + auto logger = rclcpp::get_logger("ip_config_tool"); OBNetIpConfig ip_config{}; - ip_config.dhcp = dhcp ? 1 : 0; + ip_config.dhcp = args.dhcp ? 1 : 0; - if ((enable_set_ip || enable_force_ip) && !dhcp) { - if (!parseIpString(new_ip_str, ip_config.address)) { - RCLCPP_ERROR(logger, "Invalid new_ip format: %s", new_ip_str.c_str()); + 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; } - if (!parseIpString(mask_str, ip_config.mask)) { - RCLCPP_ERROR(logger, "Invalid mask format: %s", mask_str.c_str()); + if (!parseIpString(args.mask, ip_config.mask)) { + RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); rclcpp::shutdown(); return 1; } - if (!parseIpString(gateway_str, ip_config.gateway)) { - RCLCPP_ERROR(logger, "Invalid gateway format: %s", gateway_str.c_str()); + if (!parseIpString(args.gateway, ip_config.gateway)) { + RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); rclcpp::shutdown(); return 1; } @@ -116,44 +295,38 @@ int main(int argc, char **argv) { ob::Context::setLoggerSeverity(OBLogSeverity::OB_LOG_SEVERITY_OFF); auto context = std::make_shared(); - if (enable_set_ip) { - RCLCPP_INFO(logger, "Connecting to device %s:%d ...", device_ip_str.c_str(), port); - auto device = context->createNetDevice(device_ip_str.c_str(), port); + if (args.operation == CliArgs::Operation::SET_IP) { + RCLCPP_INFO(logger, "Connecting to device %s:%d ...", args.old_ip.c_str(), args.port); + auto device = context->createNetDevice(args.old_ip.c_str(), args.port); RCLCPP_INFO(logger, "Applying set-ip configuration..."); device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG, reinterpret_cast(&ip_config), sizeof(ip_config)); RCLCPP_INFO(logger, "Set-ip configuration applied successfully."); - if (dhcp) { + if (args.dhcp) { RCLCPP_INFO(logger, "Set-ip target mode: DHCP."); } else { RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config.address[0], ip_config.address[1], ip_config.address[2], ip_config.address[3]); - RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], - ip_config.mask[1], ip_config.mask[2], ip_config.mask[3]); + RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], ip_config.mask[1], + ip_config.mask[2], ip_config.mask[3]); RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config.gateway[0], ip_config.gateway[1], ip_config.gateway[2], ip_config.gateway[3]); } } - if (enable_force_ip) { - if (force_ip_mac.empty()) { - RCLCPP_ERROR(logger, "force_ip_mac is required when enable_force_ip=true."); - rclcpp::shutdown(); - return 1; - } - - RCLCPP_INFO(logger, "Applying force-ip to MAC %s ...", force_ip_mac.c_str()); - if (context->forceIp(force_ip_mac.c_str(), ip_config)) { + if (args.operation == CliArgs::Operation::FORCE_IP) { + RCLCPP_INFO(logger, "Applying force-ip to MAC %s ...", args.force_ip_mac.c_str()); + if (context->forceIp(args.force_ip_mac.c_str(), ip_config)) { RCLCPP_INFO(logger, "Force-ip operation applied successfully."); std::this_thread::sleep_for(std::chrono::seconds(5)); - if (dhcp) { + if (args.dhcp) { RCLCPP_INFO(logger, "Force-ip target mode: DHCP."); } else { - RCLCPP_INFO(logger, "Force-ip target static IP: %s", new_ip_str.c_str()); - RCLCPP_INFO(logger, "Force-ip target mask: %s", mask_str.c_str()); - RCLCPP_INFO(logger, "Force-ip target gateway: %s", gateway_str.c_str()); + RCLCPP_INFO(logger, "Force-ip target static IP: %s", args.new_ip.c_str()); + RCLCPP_INFO(logger, "Force-ip target mask: %s", args.mask.c_str()); + RCLCPP_INFO(logger, "Force-ip target gateway: %s", args.gateway.c_str()); } } else { RCLCPP_ERROR(logger, "Force-ip failed (SDK returned false)."); From 24591deac64096564fd8e07bcb0167a62914e01d Mon Sep 17 00:00:00 2001 From: ob-yalian Date: Fri, 3 Apr 2026 16:54:33 +0800 Subject: [PATCH 3/3] feat: implement support for IP configuration V2 in ip_config_tool --- orbbec_camera/tools/ip_config_tool.cpp | 149 +++++++++++++++++++------ 1 file changed, 116 insertions(+), 33 deletions(-) diff --git a/orbbec_camera/tools/ip_config_tool.cpp b/orbbec_camera/tools/ip_config_tool.cpp index 3f273a99..607172e9 100644 --- a/orbbec_camera/tools/ip_config_tool.cpp +++ b/orbbec_camera/tools/ip_config_tool.cpp @@ -270,27 +270,6 @@ int main(int argc, char **argv) { rclcpp::init(argc, argv); auto logger = rclcpp::get_logger("ip_config_tool"); - OBNetIpConfig ip_config{}; - ip_config.dhcp = args.dhcp ? 1 : 0; - - 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; - } - if (!parseIpString(args.mask, ip_config.mask)) { - RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); - rclcpp::shutdown(); - return 1; - } - if (!parseIpString(args.gateway, ip_config.gateway)) { - RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); - rclcpp::shutdown(); - return 1; - } - } - try { ob::Context::setLoggerSeverity(OBLogSeverity::OB_LOG_SEVERITY_OFF); auto context = std::make_shared(); @@ -299,24 +278,128 @@ int main(int argc, char **argv) { RCLCPP_INFO(logger, "Connecting to device %s:%d ...", args.old_ip.c_str(), args.port); auto device = context->createNetDevice(args.old_ip.c_str(), args.port); - RCLCPP_INFO(logger, "Applying set-ip configuration..."); - device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG, - reinterpret_cast(&ip_config), sizeof(ip_config)); + const bool v2_supported = + device->isPropertySupported(OB_STRUCT_DEVICE_IP_ADDR_CONFIG_V2, OB_PERMISSION_READ_WRITE); - RCLCPP_INFO(logger, "Set-ip configuration applied successfully."); - if (args.dhcp) { - RCLCPP_INFO(logger, "Set-ip target mode: DHCP."); + if (v2_supported) { + // V2 supports enabling DHCP and persistent(static) independently. + uint8_t address[4] = {0}; + uint8_t mask[4] = {0}; + uint8_t gateway[4] = {0}; + if (!parseIpString(args.new_ip, address)) { + RCLCPP_ERROR(logger, "Invalid new_ip format: %s", args.new_ip.c_str()); + rclcpp::shutdown(); + return 1; + } + if (!parseIpString(args.mask, mask)) { + RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); + rclcpp::shutdown(); + return 1; + } + if (!parseIpString(args.gateway, gateway)) { + RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); + rclcpp::shutdown(); + return 1; + } + + OBNetIpConfigV2 ip_config_v2{}; + ip_config_v2.flags = OB_NET_IP_FLAG_PERSISTENT; + if (args.dhcp) { + ip_config_v2.flags = static_cast(ip_config_v2.flags | OB_NET_IP_FLAG_DHCP); + } + std::memcpy(ip_config_v2.address, address, sizeof(address)); + std::memcpy(ip_config_v2.mask, mask, sizeof(mask)); + std::memcpy(ip_config_v2.gateway, gateway, sizeof(gateway)); + + RCLCPP_INFO(logger, "Applying set-ip configuration with V2 property (1088)..."); + device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG_V2, + reinterpret_cast(&ip_config_v2), + sizeof(ip_config_v2)); + + RCLCPP_INFO(logger, "Set-ip configuration applied successfully (V2)."); + RCLCPP_INFO(logger, "Set-ip target DHCP: %s", args.dhcp ? "enabled" : "disabled"); + RCLCPP_INFO(logger, "Set-ip target persistent(static): enabled"); + RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config_v2.address[0], + ip_config_v2.address[1], ip_config_v2.address[2], ip_config_v2.address[3]); + RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config_v2.mask[0], + ip_config_v2.mask[1], ip_config_v2.mask[2], ip_config_v2.mask[3]); + RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config_v2.gateway[0], + ip_config_v2.gateway[1], ip_config_v2.gateway[2], ip_config_v2.gateway[3]); } else { - RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config.address[0], - ip_config.address[1], ip_config.address[2], ip_config.address[3]); - RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], ip_config.mask[1], - ip_config.mask[2], ip_config.mask[3]); - RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config.gateway[0], - ip_config.gateway[1], ip_config.gateway[2], ip_config.gateway[3]); + OBNetIpConfig ip_config{}; + ip_config.dhcp = args.dhcp ? 1 : 0; + + if (!args.dhcp) { + uint8_t address[4] = {0}; + uint8_t mask[4] = {0}; + uint8_t gateway[4] = {0}; + if (!parseIpString(args.new_ip, address)) { + RCLCPP_ERROR(logger, "Invalid new_ip format: %s", args.new_ip.c_str()); + rclcpp::shutdown(); + return 1; + } + if (!parseIpString(args.mask, mask)) { + RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); + rclcpp::shutdown(); + return 1; + } + if (!parseIpString(args.gateway, gateway)) { + RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); + rclcpp::shutdown(); + return 1; + } + std::memcpy(ip_config.address, address, sizeof(address)); + std::memcpy(ip_config.mask, mask, sizeof(mask)); + std::memcpy(ip_config.gateway, gateway, sizeof(gateway)); + } + + RCLCPP_WARN(logger, + "Device does not support IP config V2 (1088), fallback to legacy property (1041)."); + if (args.dhcp) { + RCLCPP_WARN(logger, + "Legacy IP config does not support DHCP and static IP simultaneously; static fields will be ignored."); + } + + RCLCPP_INFO(logger, "Applying set-ip configuration..."); + device->setStructuredData(OB_STRUCT_DEVICE_IP_ADDR_CONFIG, + reinterpret_cast(&ip_config), sizeof(ip_config)); + + RCLCPP_INFO(logger, "Set-ip configuration applied successfully."); + if (args.dhcp) { + RCLCPP_INFO(logger, "Set-ip target mode: DHCP."); + } else { + RCLCPP_INFO(logger, "Set-ip target static IP: %d.%d.%d.%d", ip_config.address[0], + ip_config.address[1], ip_config.address[2], ip_config.address[3]); + RCLCPP_INFO(logger, "Set-ip target mask: %d.%d.%d.%d", ip_config.mask[0], ip_config.mask[1], + ip_config.mask[2], ip_config.mask[3]); + RCLCPP_INFO(logger, "Set-ip target gateway: %d.%d.%d.%d", ip_config.gateway[0], + ip_config.gateway[1], ip_config.gateway[2], ip_config.gateway[3]); + } } } if (args.operation == CliArgs::Operation::FORCE_IP) { + OBNetIpConfig ip_config{}; + ip_config.dhcp = args.dhcp ? 1 : 0; + + 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; + } + if (!parseIpString(args.mask, ip_config.mask)) { + RCLCPP_ERROR(logger, "Invalid mask format: %s", args.mask.c_str()); + rclcpp::shutdown(); + return 1; + } + if (!parseIpString(args.gateway, ip_config.gateway)) { + RCLCPP_ERROR(logger, "Invalid gateway format: %s", args.gateway.c_str()); + rclcpp::shutdown(); + return 1; + } + } + RCLCPP_INFO(logger, "Applying force-ip to MAC %s ...", args.force_ip_mac.c_str()); if (context->forceIp(args.force_ip_mac.c_str(), ip_config)) { RCLCPP_INFO(logger, "Force-ip operation applied successfully.");