From e179c0ed3f017ef171176231d71dbc579deca3d1 Mon Sep 17 00:00:00 2001 From: "Saurkar, Nikita" Date: Tue, 19 Nov 2024 07:38:57 +0000 Subject: [PATCH] Add new command "set-temporary-ip" to set temporary IP --- ChangeLog.md | 2 + modules/device/include/ifm3d/device/device.h | 7 + modules/device/src/libifm3d_device/device.cpp | 11 + .../device/src/libifm3d_device/discovery.hpp | 196 ++++++++++++++++-- modules/tools/include/ifm3d/tools.h | 9 +- modules/tools/include/ifm3d/tools/command.hpp | 6 + .../include/ifm3d/tools/common/discover_app.h | 1 + .../ifm3d/tools/common/set_temp_ip_app.h | 36 ++++ .../libifm3d_tools/common/discover_app.cpp | 127 +++++++----- .../libifm3d_tools/common/set_temp_ip_app.cpp | 42 ++++ 10 files changed, 368 insertions(+), 69 deletions(-) create mode 100644 modules/tools/include/ifm3d/tools/common/set_temp_ip_app.h create mode 100644 modules/tools/src/libifm3d_tools/common/set_temp_ip_app.cpp diff --git a/ChangeLog.md b/ChangeLog.md index 8a5bade8..b7f7542c 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Add `set-temporary-ip` subcommand under `discover` command to set temporary IP to device which is in wrong sub-net ### Added - Add new subcommands `app` and `device` under import and export subcommands diff --git a/modules/device/include/ifm3d/device/device.h b/modules/device/include/ifm3d/device/device.h index 8e6a307a..8702052d 100644 --- a/modules/device/include/ifm3d/device/device.h +++ b/modules/device/include/ifm3d/device/device.h @@ -226,6 +226,13 @@ namespace ifm3d */ static std::vector DeviceDiscovery(); + /** + * @brief This function Provides a way to set temporary IP. + * @param[in] mac The MAC address of the device + * @param[in] temp_ip The temporary IP to be set to device + */ + static void SetTempIPAddress(std::string mac, std::string temp_ip); + /** * Factory function for instantiating the proper subclass based on h/w * probing. diff --git a/modules/device/src/libifm3d_device/device.cpp b/modules/device/src/libifm3d_device/device.cpp index 7840d94c..07c78701 100644 --- a/modules/device/src/libifm3d_device/device.cpp +++ b/modules/device/src/libifm3d_device/device.cpp @@ -118,6 +118,17 @@ ifm3d::Device::DeviceDiscovery() return devices; } +//================================================ +// Function for Setting Temporary IP Address +//================================================ + +void +ifm3d::Device::SetTempIPAddress(std::string mac, std::string temp_ip) +{ + ifm3d::IFMDeviceDiscovery ifm_discovery; + ifm_discovery.SetTemporaryIP(mac, temp_ip); +} + //================================================ // Factory function for making cameras //================================================ diff --git a/modules/device/src/libifm3d_device/discovery.hpp b/modules/device/src/libifm3d_device/discovery.hpp index 3abc93f4..cb629c37 100644 --- a/modules/device/src/libifm3d_device/discovery.hpp +++ b/modules/device/src/libifm3d_device/discovery.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef __unix__ # include # include @@ -33,6 +34,9 @@ namespace ifm3d /** Broadcast request magic number. */ constexpr uint32_t BCAST_MAGIC_REQUEST = 0x1020efcf; + /** Broadcast ipchange magic number. */ + constexpr uint32_t BCAST_MAGIC_IPCHANGE = 0x1020efce; + /** It signalize that network device is not in same subnet. */ constexpr uint32_t BCAST_FLAG_WRONGSUBNET = 0x0001; @@ -111,6 +115,28 @@ namespace ifm3d char devicename[256]; }; + /** Broadcast ipchange packet structure. */ + struct BcastIPChange + { + /** Default magic number 0x1020efce. */ + uint32_t magic; + + /** Port to send reply to. */ + uint16_t replyPort; + + /** Unused. */ + uint16_t reserved1; + + /** Temporary ip address that should be set. */ + uint32_t tempIP; + + /** Unused. */ + uint16_t reserved2; + + /** Mac address of interface to change. */ + uint8_t mac[6]; + }; + namespace { inline const std::string @@ -122,6 +148,24 @@ namespace ifm3d return ss.str(); } + inline const uint32_t + str2Address(const std::string addr) + { + uint32_t str_addr = 0; + std::stringstream ss(addr); + std::string octet; + int shift = 24; + + while (std::getline(ss, octet, '.')) + { + uint32_t num = std::stoi(octet); + str_addr |= (num << shift); + shift -= 8; + } + + return str_addr; + } + inline const std::string uint8Mac2macStr(uint8_t* mac, size_t size = 6) { @@ -133,11 +177,27 @@ namespace ifm3d for (size_t i = 0; i + 1 < size; i++) { converttoHexandAppend(mac[i]); - ss << "::"; + ss << ":"; } converttoHexandAppend(mac[size - 1]); return ss.str(); } + + inline std::array + macStr2MacUint8(const std::string& mac_str) + { + std::array mac; + std::stringstream ss(mac_str); + std::string byte_str; + int i = 0; + + while (std::getline(ss, byte_str, ':') && i < 6) + { + mac[i] = static_cast(std::stoul(byte_str, nullptr, 16)); + ++i; + } + return mac; + } } IFMNetworkDevice::IFMNetworkDevice( @@ -262,6 +322,30 @@ namespace ifm3d std::placeholders::_2)); } + /*Sends the data on the endpoint + *@param data Data to be send on endpoint + *@param Endpoint to send the data. + **/ + void + SendIPChangeCall(Data& data, asio::ip::udp::endpoint& remote_endpoint) + { + int offsetPortInData = offsetof(BcastIPChange, replyPort); + + if (offsetPortInData > 0 && offsetPortInData < data.size()) + { + unsigned short local_port = ntohs(socket_.local_endpoint().port()); + data[offsetPortInData] = (local_port & 0xFF); + data[offsetPortInData + 1] = ((local_port >> 8) & 0xFF); + } + + socket_.async_send_to(asio::buffer((char*)data.data(), data.size()), + remote_endpoint, + std::bind(&UDPConnection::_HandleSend, + this, + std::placeholders::_1, + std::placeholders::_2)); + } + /*Grab data on the local endpoint till the timeout occur*/ void GrabData() @@ -393,7 +477,9 @@ namespace ifm3d {BCAST_MAGIC_REQUEST, [](size_t size) -> size_t { return sizeof(BcastRequest) - size; }}, {BCAST_MAGIC_REPLY, - [](size_t size) -> size_t { return sizeof(BcastReply) - size; }}}; + [](size_t size) -> size_t { return sizeof(BcastReply) - size; }}, + {BCAST_MAGIC_IPCHANGE, + [](size_t size) -> size_t { return sizeof(BcastIPChange) - size; }}}; const unsigned int THREADS_FOR_IO_OPERATIONS = 3; @@ -408,6 +494,22 @@ namespace ifm3d std::thread(std::bind([&] { io_context_.run(); }))); } } + + ~IFMDeviceDiscovery() + { + /* stop the io_context */ + io_context_.stop(); + + // wait for all threads to complete + for (std::thread& thread : thread_pool_) + { + if (thread.joinable()) + { + thread.join(); + } + } + } + IFMDeviceDiscovery(IFMDeviceDiscovery&&) = delete; IFMDeviceDiscovery& operator=(IFMDeviceDiscovery&&) = delete; IFMDeviceDiscovery(IFMDeviceDiscovery&) = delete; @@ -449,18 +551,6 @@ namespace ifm3d /* get the device list this will block the thread till all NIC are scanned for devices */ auto devices = _GetDeviceList(); - - /* stop the io_context after getting list */ - io_context_.stop(); - - // wait for all threads to complete - for (std::thread& thread : thread_pool_) - { - if (thread.joinable()) - { - thread.join(); - } - } } catch (std::exception& e) { @@ -469,6 +559,18 @@ namespace ifm3d return device_list_; } + void + SetTemporaryIP(std::string mac_address, std::string temp_ip) + { + auto addresses = _GetAllInterfaceAddress(); + + /* broadcast IP change request on all interfaces */ + for (const auto& address : addresses) + { + _SendIPChangeBCast(mac_address, temp_ip, address); + } + } + private: std::vector _GetAllInterfaceAddress() @@ -597,6 +699,10 @@ namespace ifm3d std::lock_guard lock(device_list_lock_); device_list_.push_back(ifm_device); } + if (magic_value == BCAST_MAGIC_IPCHANGE) + { + std::cout << std::endl << "BCAST_IPCHANGE Packet received"; + } } } /* return the discovered device list*/ @@ -633,6 +739,68 @@ namespace ifm3d cv_.notify_one(); } + /* Broadcast the IP change request */ + void + _SendIPChangeBCast(std::string mac_address, + std::string temp_ip, + std::string interface_ip) + { + try + { + BcastIPChange ipc; + Data data; + data.resize(sizeof(BcastIPChange)); + ipc.magic = htonl(BCAST_MAGIC_IPCHANGE); + ipc.tempIP = htonl(str2Address(temp_ip)); + std::array mac_array = macStr2MacUint8(mac_address); + uint8_t* mac = mac_array.data(); + memcpy(ipc.mac, mac, sizeof(ipc.mac)); + std::memcpy(data.data(), &ipc, sizeof(BcastIPChange)); + + /** getting the netmask of the interface */ + auto netmask = asio::ip::address_v4::netmask( + asio::ip::address_v4::from_string(interface_ip)); + + /**broadcast of the interface */ + auto broadcast_address_of_interface = + asio::ip::address_v4::broadcast( + asio::ip::address_v4::from_string(interface_ip), + netmask) + .to_string(); + + /* create a local broadcast endpoint */ + asio::ip::udp::endpoint broadcast_endpoint = asio::ip::udp::endpoint( + asio::ip::address::from_string(broadcast_address_of_interface), + BCAST_DEFAULT_PORT); + + /* create a local endpoint */ + asio::ip::udp::endpoint local_endpoint = + asio::ip::udp::endpoint(asio::ip::udp::v4(), BCAST_DEFAULT_PORT); + + auto con = + std::make_shared(io_context_, local_endpoint); + con->RegisterOnReceive(std::bind(&IFMDeviceDiscovery::_OnReceive, + this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3)); + con->RegisterOnClose( + std::bind(&IFMDeviceDiscovery::_RemoveConnection, + this, + std::placeholders::_1)); + + /* send BcastIPChange to device */ + con->SendIPChangeCall(data, broadcast_endpoint); + + connection_list_.push_back(con); + } + catch (std::exception& e) + { + std::cerr << std::endl + << "ifm3d exception: " << e.what() << std::endl; + } + } + asio::io_context io_context_; asio::executor_work_guard work_guard_; std::mutex con_mutex_; diff --git a/modules/tools/include/ifm3d/tools.h b/modules/tools/include/ifm3d/tools.h index b1e97672..e877a4ee 100644 --- a/modules/tools/include/ifm3d/tools.h +++ b/modules/tools/include/ifm3d/tools.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -34,12 +38,9 @@ #include #include #include +#include #include #include -#include -#include -#include -#include #if defined(BUILD_MODULE_FRAMEGRABBER) # include diff --git a/modules/tools/include/ifm3d/tools/command.hpp b/modules/tools/include/ifm3d/tools/command.hpp index 347b0673..68fd1f4d 100644 --- a/modules/tools/include/ifm3d/tools/command.hpp +++ b/modules/tools/include/ifm3d/tools/command.hpp @@ -87,6 +87,12 @@ namespace ifm3d return parent ? parent->CheckCompatibility() : true; } + CLI::App* + GetSubcommandApp() + { + return this->_context; + } + private: Command* _parent = nullptr; std::vector> _subcommands; diff --git a/modules/tools/include/ifm3d/tools/common/discover_app.h b/modules/tools/include/ifm3d/tools/common/discover_app.h index 87c0a79d..21b82f06 100644 --- a/modules/tools/include/ifm3d/tools/common/discover_app.h +++ b/modules/tools/include/ifm3d/tools/common/discover_app.h @@ -27,6 +27,7 @@ namespace ifm3d std::string GetDeviceType(const ifm3d::Device::Ptr& cam); virtual CLI::App* CreateCommand(CLI::App* parent) override; + CLI::App* subcmd_set_temp_ip; }; // end: class } // end: namespace ifm3d diff --git a/modules/tools/include/ifm3d/tools/common/set_temp_ip_app.h b/modules/tools/include/ifm3d/tools/common/set_temp_ip_app.h new file mode 100644 index 00000000..1eb4216a --- /dev/null +++ b/modules/tools/include/ifm3d/tools/common/set_temp_ip_app.h @@ -0,0 +1,36 @@ +// -*- c++ -*- +/* + * Copyright (C) 2020 ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IFM3D_TOOLS_SET_TEMP_IP_APP_H +#define IFM3D_TOOLS_SET_TEMP_IP_APP_H + +#include +#include +#include +#include +#include + +namespace ifm3d +{ + /** + * Concrete implementation of the `discover set-temporary-ip` subcommand to + * the `ifm3d` command-line utility. + */ + class SetTemporaryIPApp : public Command + { + public: + ~SetTemporaryIPApp(); + virtual void Execute(CLI::App* app) override; + virtual CLI::App* CreateCommand(CLI::App* parent) override; + + std::string mac{""}; + std::string temp_ip{""}; + + }; // end: class SetTemporaryIPApp + +} // end: namespace ifm3d + +#endif // IFM3D_TOOLS_SET_TEMP_IP_APP_H diff --git a/modules/tools/src/libifm3d_tools/common/discover_app.cpp b/modules/tools/src/libifm3d_tools/common/discover_app.cpp index 0047cb85..e1dea49f 100644 --- a/modules/tools/src/libifm3d_tools/common/discover_app.cpp +++ b/modules/tools/src/libifm3d_tools/common/discover_app.cpp @@ -6,6 +6,8 @@ #include #include +constexpr uint32_t BCAST_FLAG_WRONGSUBNET = 0x0001; + ifm3d::DiscoverApp::~DiscoverApp() {} std::string @@ -23,69 +25,89 @@ ifm3d::DiscoverApp::GetDeviceType(const ifm3d::Device::Ptr& cam) void ifm3d::DiscoverApp::Execute(CLI::App* app) { - auto devices = ifm3d::Device::DeviceDiscovery(); - std::string parent_app = app->get_parent()->get_name(); - std::stringstream ss; - - if (!devices.empty()) + if (!(this->subcmd_set_temp_ip->parsed())) { - for (const auto& device : devices) + auto devices = ifm3d::Device::DeviceDiscovery(); + std::stringstream ss; + + if (!devices.empty()) { - auto ip_address = device.GetIPAddress(); - try + for (const auto& device : devices) { - auto cam = ifm3d::Device::MakeShared(ip_address); - auto device_type = GetDeviceType(cam); - bool display_device = false; - - if (device_type.empty()) + auto ip_address = device.GetIPAddress(); + try { - ss << ip_address << " (Unsupported device)" << std::endl; - } - else - { - if ((device_type == "O3D") && (Parent())) + if (device.GetFlag() == BCAST_FLAG_WRONGSUBNET) { - display_device = true; + ss << ip_address << " [" << device.GetMACAddress() + << "] (Device is in different " + "subnet, set temporary IP to connect. Use " + "'set-temporary-ip' subcommand) " + << std::endl; } - else if ((device_type == "O3X") && - (Parent())) + else { - display_device = true; - } - else if ((device_type == "O3R") && (Parent())) - { - display_device = true; - } - else if (Parent() && - !(Parent()) && - !(Parent()) && - !(Parent())) - { - display_device = true; - } + auto cam = ifm3d::Device::MakeShared(ip_address); + auto device_type = GetDeviceType(cam); + bool display_device = false; + if (device_type.empty()) + { + ss << ip_address << " (Unsupported device)" + << std::endl; + } + else + { + if ((device_type == "O3D") && + (Parent())) + { + display_device = true; + } + else if ((device_type == "O3X") && + (Parent())) + { + display_device = true; + } + else if ((device_type == "O3R") && + (Parent())) + { + display_device = true; + } + else if (Parent() && + !(Parent()) && + !(Parent()) && + !(Parent())) + { + display_device = true; + } - if (display_device) + if (display_device) + { + ss << ip_address << " (" << device_type << ")" + << " [" << device.GetMACAddress() << "]" + << std::endl; + } + } + } + } + catch (ifm3d::Error& e) + { + if (e.code() == ifm3d::Error(IFM3D_XMLRPC_TIMEOUT).code()) { - ss << ip_address << " (" << device_type << ")" - << " [" << device.GetMACAddress() << "]" << std::endl; + ss << ip_address << " (Unable to identify) " + << std::endl; } } + catch (std::exception& exp) + { + // ignore this device and search for next devices.. + } } - catch (ifm3d::Error& e) - { - ss << ip_address << " (Unable to identify)" << std::endl; - } - catch (std::exception& exp) - { - // ignore this device and search for next devices.. - } + std::cout << ss.str(); + } + if (devices.empty() || ss.str().empty()) + { + std::cout << "Info: No devices available" << std::endl; } - std::cout << ss.str(); - } - if (devices.empty() || ss.str().empty()) - { - std::cout << "Info: No devices available" << std::endl; } } @@ -94,7 +116,10 @@ ifm3d::DiscoverApp::CreateCommand(CLI::App* parent) { CLI::App* command = parent->add_subcommand("discover", "Discover ifm devices on the network.") - ->require_subcommand(0, 0); + ->require_subcommand(0, 1); + + subcmd_set_temp_ip = + RegisterSubcommand(command)->GetSubcommandApp(); return command; } diff --git a/modules/tools/src/libifm3d_tools/common/set_temp_ip_app.cpp b/modules/tools/src/libifm3d_tools/common/set_temp_ip_app.cpp new file mode 100644 index 00000000..2add636b --- /dev/null +++ b/modules/tools/src/libifm3d_tools/common/set_temp_ip_app.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 ifm electronic, gmbh + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +ifm3d::SetTemporaryIPApp::~SetTemporaryIPApp() {} + +void +ifm3d::SetTemporaryIPApp::Execute(CLI::App* app) +{ + ifm3d::Device::SetTempIPAddress(this->mac, this->temp_ip); +} + +CLI::App* +ifm3d::SetTemporaryIPApp::CreateCommand(CLI::App* parent) +{ + CLI::App* command = + parent->add_subcommand("set-temporary-ip", "Set temporary IP to device") + ->require_subcommand(0, 0); + + command->add_option("--mac", this->mac, "MAC address of Device") + ->required() + ->check([](const std::string& mac) { + const std::regex mac_regex("^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$"); + if (std::regex_match(mac, mac_regex)) + { + return std::string(); + } + return std::string( + "Invalid MAC Address, expected format [xx::xx::xx::xx::xx::xx]"); + }); + + command->add_option("--ip", this->temp_ip, "Temporary IP address to be set") + ->check(CLI::ValidIPV4) + ->required(); + + return command; +}