diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b46dd01701..18648fa655 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -57,6 +57,8 @@ set(COMMON_SRC "${CMAKE_CURRENT_LIST_DIR}/stream-model.cpp" "${CMAKE_CURRENT_LIST_DIR}/post-processing-filters.h" "${CMAKE_CURRENT_LIST_DIR}/post-processing-filters.cpp" + "${CMAKE_CURRENT_LIST_DIR}/dds-model.h" + "${CMAKE_CURRENT_LIST_DIR}/dds-model.cpp" ) set(SW_UPDATE_FILES diff --git a/common/dds-model.cpp b/common/dds-model.cpp new file mode 100644 index 0000000000..50d95c75e6 --- /dev/null +++ b/common/dds-model.cpp @@ -0,0 +1,349 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2024 Intel Corporation. All Rights Reserved. + +#include "dds-model.h" +#include "device-model.h" +#include "ux-window.h" +#include +#include +#include + +#include +#include + +using namespace rs2; +using rsutils::json; +using rsutils::type::ip_address; + +dds_model::dds_model( rs2::device dev ) + : _device( dev ) + , _window_open( false ) + , _no_reset( false ) + , _set_defult( false ) + , _dds_supported( false ) +{ + if( check_DDS_support() ) + { + _defult_config = get_eth_config( _device, DEFULT_VALUES ); + _current_config = get_eth_config( _device, ACTUAL_VALUES ); + _changed_config = _current_config; + _dds_supported = true; + } +} + +eth_config dds_model::get_eth_config( rs2::debug_protocol dev, bool defult_val ) +{ + auto cmd = dev.build_command( GET_ETH_CONFIG, defult_val ? 0 : 1 ); + auto data = dev.send_and_receive_raw_data( cmd ); + int32_t const & code = *reinterpret_cast< int32_t const * >( data.data() ); + data.erase( data.begin(), data.begin() + sizeof( code ) ); + return eth_config( data ); +} + +void rs2::dds_model::set_eth_config( eth_config & new_config, std::string & error_message ) +{ + rs2::debug_protocol hwm( _device ); + auto cmd = hwm.build_command( SET_ETH_CONFIG, 0, 0, 0, 0, new_config.build_command() ); + auto data = hwm.send_and_receive_raw_data( cmd ); + int32_t const & code = *reinterpret_cast< int32_t const * >( data.data() ); + if( data.size() != sizeof( code ) ) + { + error_message = rsutils::string::from() << "Failed to change: bad response size " << data.size() << ' ' + << rsutils::string::hexdump( data.data(), data.size() ); + close_window(); + } + if( code != SET_ETH_CONFIG ) + { + error_message = rsutils::string::from() << "Failed to change: bad response " << code; + close_window(); + } + if( ! _no_reset ) + { + close_window(); + _device.hardware_reset(); + } +} + +bool rs2::dds_model::supports_DDS() +{ + return _dds_supported; +} + +priority rs2::dds_model::classifyPriority( link_priority & pr ) +{ + if( pr == link_priority::usb_only || pr == link_priority::usb_first ) + { + return priority::USB_FIRST; + } + else if( pr == link_priority::eth_first || pr == link_priority::eth_only ) + { + return priority::ETH_FIRST; + } + return priority::DYNAMIC; +} + +bool dds_model::check_DDS_support() +{ + auto dev = debug_protocol( _device ); + auto cmd = dev.build_command( GET_ETH_CONFIG, ACTUAL_VALUES ); + auto data = dev.send_and_receive_raw_data( cmd ); + int32_t const & code = *reinterpret_cast< int32_t const * >( data.data() ); + if( code != GET_ETH_CONFIG ) + return false; + return true; +} + +void rs2::dds_model::ipInputText( std::string label, ip_address & ip ) +{ + char buffer[16]; + std::string ip_str = ip.to_string(); + std::snprintf( buffer, sizeof( buffer ), "%s", ip_str.c_str() ); + std::string label_name = "##" + label; + + if( ImGui::InputText( label_name.c_str(), buffer, sizeof( buffer ) ) ) + { + std::string new_ip_str( buffer ); + ip_address new_ip = ip_address( new_ip_str ); + if( new_ip.is_valid() ) + { + ip = new_ip; + } + else + { + std::snprintf( buffer, sizeof( buffer ), "%s", ip.to_string().c_str() ); + } + } +} + +void dds_model::render_dds_config_window( ux_window & window, std::string & error_message ) +{ + const auto window_name = "DDS Configuration"; + if( _window_open ) + { + try + { + _current_config = get_eth_config( _device, ACTUAL_VALUES ); + _changed_config = _current_config; + ImGui::OpenPopup( window_name ); + } + catch( std::exception e ) + { + error_message = e.what(); + } + _window_open = false; + } + + // Calculate window position and size + const float w = 620; + const float h = 500; + const float x0 = std::max( window.width() - w, 0.f ) / 2; + const float y0 = std::max( window.height() - h, 0.f ) / 2; + ImGui::SetNextWindowPos( { x0, y0 } ); + ImGui::SetNextWindowSize( { w, h } ); + + auto flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse + | ImGuiWindowFlags_NoSavedSettings; + + ImGui::PushStyleColor( ImGuiCol_PopupBg, sensor_bg ); + ImGui::PushStyleColor( ImGuiCol_TextSelectedBg, light_grey ); + ImGui::PushStyleColor( ImGuiCol_Text, light_grey ); + ImGui::PushStyleVar( ImGuiStyleVar_WindowPadding, ImVec2( 5, 5 ) ); + ImGui::PushStyleVar( ImGuiStyleVar_WindowRounding, 1 ); + ImGui::PushStyleColor( ImGuiCol_Button, button_color ); + ImGui::PushStyleColor( ImGuiCol_ButtonHovered, button_color + 0.1f ); + ImGui::PushStyleColor( ImGuiCol_ButtonActive, button_color + 0.1f ); + + + if( ImGui::BeginPopupModal( window_name, nullptr, flags ) ) + { + if( error_message != "" ) + ImGui::CloseCurrentPopup(); + + // Title + const char * title_message = window_name; + ImVec2 title_size = ImGui::CalcTextSize( title_message ); + float title_x = ( w - title_size.x - 10 ) / 2.0f; + ImGui::SetCursorPos( { title_x, 10.0f } ); + ImGui::PushFont( window.get_large_font() ); + ImGui::PushStyleColor( ImGuiCol_Text, white ); + ImGui::Text( "%s", title_message ); + ImGui::PopStyleColor(); + ImGui::PopFont(); + ImGui::Separator(); + ImGui::SetCursorPosY( ImGui::GetCursorPosY() + 15 ); + + // Main Scrollable Section + ImGui::BeginChild( "MainContent", ImVec2( w - 10, h - 100 ), true ); + ImGui::PushItemWidth( 150.0f ); + + // Connection Priority Section + priority connection_priority = classifyPriority( _changed_config.link.priority ); + if( ImGui::CollapsingHeader( "Connection Priority" ) ) + { + ImGui::Text( "Select connection priority:" ); + ImGui::RadioButton( "Ethernet First", reinterpret_cast< int * >( &connection_priority ), 0 ); + if( static_cast< int >( connection_priority ) == 0 ) + { + ImGui::SameLine(); + ImGui::SetCursorPosX( ImGui::GetCursorPosX() + 50 ); + ImGui::Text( "Link Timeout (seconds)" ); + ImGui::SameLine(); + int tempTimeout = static_cast< int >( _changed_config.link.timeout ); + if( ImGui::InputInt( "##Link Timeout (seconds)", &tempTimeout ) ) + { + _changed_config.link.timeout = static_cast< uint16_t >( std::max( 0, tempTimeout ) ); + } + } + ImGui::RadioButton( "USB First", reinterpret_cast< int * >( &connection_priority ), 1 ); + ImGui::RadioButton( "Dynamic Priority", reinterpret_cast< int * >( &connection_priority ), 2 ); + switch( connection_priority ) + { + case ETH_FIRST: + _changed_config.link.priority = link_priority::eth_first; + break; + case USB_FIRST: + _changed_config.link.priority = link_priority::usb_first; + break; + case DYNAMIC: + _changed_config.link.priority + = _current_config.link.speed ? link_priority::dynamic_eth_first : link_priority::dynamic_usb_first; + break; + } + } + + // Network Configuration Section + if( ImGui::CollapsingHeader( "Network Configuration" ) ) + { + ImGui::Checkbox( "Enable DHCP", &_changed_config.dhcp.on ); + if( ! _changed_config.dhcp.on ) + { + ImGui::Text( "Static IP Address" ); + ImGui::SameLine(); + float textbox_align = ImGui::GetCursorPosX(); + ipInputText( "Static IP Address", _changed_config.configured.ip ); + ImGui::Text( "Subnet Mask" ); + ImGui::SameLine(); + ImGui::SetCursorPosX( textbox_align ); + bool maskStylePushed = false; + ipInputText( "Subnet Mask", _changed_config.configured.netmask ); + ImGui::Text( "Gateway" ); + ImGui::SameLine(); + ImGui::SetCursorPosX( textbox_align ); + ipInputText( "Gateway", _changed_config.configured.gateway ); + } + else + { + ImGui::Text( "DHCP Timeout (seconds)" ); + ImGui::SameLine(); + int tempTimeout = static_cast< int >( _changed_config.dhcp.timeout ); + if( ImGui::InputInt( "##DHCP Timeout (seconds)", &tempTimeout ) ) + { + _changed_config.dhcp.timeout = static_cast< uint16_t >( std::max( 0, tempTimeout ) ); + } + } + } + + ImGui::Text( "Domain ID" ); + ImGui::SameLine(); + if( ImGui::InputInt( "##Domain ID", &_changed_config.dds.domain_id ) ) + { + if( _changed_config.dds.domain_id < 0 ) + _changed_config.dds.domain_id = 0; + else if( _changed_config.dds.domain_id > 232 ) + _changed_config.dds.domain_id = 232; + } + ImGui::Checkbox( "No Reset after changes", &_no_reset ); + + if( ImGui::Checkbox( "Load to defult values", &_set_defult ) ) + { + if( _set_defult ) + _changed_config = _defult_config; + else + _changed_config = _current_config; + } + + ImGui::PopItemWidth(); + ImGui::EndChild(); + + // window buttons + float button_width = 115.0f; + float spacing = 10.0f; + float total_buttons_width = button_width * 4 + spacing * 2; + float start_x = ( w - total_buttons_width ) / 2.0f; + bool hasChanges = ( _changed_config != _current_config ); + + ImGui::SetCursorPosY( ImGui::GetCursorPosY() + 8 ); + + ImGui::SetCursorPosX( start_x ); + + if( ImGui::Button( "Cancel", ImVec2( button_width, 25 ) ) ) + { + close_window(); + } + if( ImGui::IsItemHovered() ) + { + window.link_hovered(); + ImGui::SetTooltip( "%s", "Close without saving any changes" ); + } + ImGui::SameLine(); + if( ImGui::Button( "Factory Reset", ImVec2( button_width, 25 ) ) ) + { + set_eth_config( _defult_config, error_message ); + close_window(); + } + if( ImGui::IsItemHovered() ) + { + window.link_hovered(); + ImGui::SetTooltip( "%s", "Reset settings back to defult values" ); + } + ImGui::SameLine(); + RsImGui::RsImButton( + [&]() + { + if( ImGui::ButtonEx( "Revert changes", ImVec2( button_width, 25 ) ) ) + { + _changed_config = _current_config; + }; + }, + ! hasChanges ); + if( ImGui::IsItemHovered() ) + { + window.link_hovered(); + ImGui::SetTooltip( "%s", "Revert to current configuration values" ); + } + ImGui::SameLine(); + RsImGui::RsImButton( + [&]() + { + if( ImGui::ButtonEx( "Apply", ImVec2( button_width, 25 ) ) ) + { + set_eth_config( _changed_config, error_message ); + close_window(); + }; + }, + ! hasChanges ); + if( ImGui::IsItemHovered() ) + { + window.link_hovered(); + ImGui::SetTooltip( "%s", "Apply changes" ); + } + if( ImGui::BeginPopupModal( "No Changes Needed", NULL, ImGuiWindowFlags_AlwaysAutoResize ) ) + { + ImGui::Text( "No changes were made to the configuration." ); + + if( ImGui::Button( "OK", ImVec2( 100, 25 ) ) ) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::EndPopup(); + } + ImGui::PopStyleColor( 6 ); + ImGui::PopStyleVar( 2 ); +} + +void rs2::dds_model::open_dds_tool_window() +{ + _window_open = true; +} diff --git a/common/dds-model.h b/common/dds-model.h new file mode 100644 index 0000000000..2a804a4edf --- /dev/null +++ b/common/dds-model.h @@ -0,0 +1,66 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2024 Intel Corporation. All Rights Reserved. +#pragma once + +#include +#include +#include +#include +#include +#include +#include <../third-party/rsutils/include/rsutils/type/eth-config.h> +#include +#include + +uint32_t const GET_ETH_CONFIG = 0xBB; +uint32_t const SET_ETH_CONFIG = 0xBA; + +bool const ACTUAL_VALUES = 0; +bool const DEFULT_VALUES = 1; + +enum priority { + ETH_FIRST, + USB_FIRST, + DYNAMIC +}; + +namespace rs2 +{ + class ux_window; + + class dds_model + { + + public: + dds_model(rs2::device dev); + + void render_dds_config_window(ux_window& window, std::string& error_message); + + void open_dds_tool_window(); + + void close_window() { ImGui::CloseCurrentPopup(); } + + eth_config get_eth_config(rs2::debug_protocol dev, bool defult_val); + + void set_eth_config(eth_config &new_config , std::string& error_message); + + bool supports_DDS(); + + + private: + rs2::device _device; + + eth_config _defult_config; + eth_config _current_config; + eth_config _changed_config; + + bool _window_open; + bool _no_reset; + bool _set_defult; + bool _dds_supported; + + void ipInputText(std::string label, rsutils::type::ip_address &ip); + priority classifyPriority(link_priority &pr); + bool check_DDS_support(); + }; +} diff --git a/common/device-model.cpp b/common/device-model.cpp index f06677a7f4..7681c47183 100644 --- a/common/device-model.cpp +++ b/common/device-model.cpp @@ -371,7 +371,8 @@ namespace rs2 _detected_objects(std::make_shared< atomic_objects_in_frame >()), _updates(viewer.updates), _updates_profile(std::make_shared()), - _allow_remove(remove) + _allow_remove(remove), + _dds_model(dev) { auto name = get_device_name(dev); id = rsutils::string::from() << name.first << ", " << name.second; @@ -1435,6 +1436,21 @@ namespace rs2 } } } + ImGuiSelectableFlags is_streaming_flag = (is_streaming) ? ImGuiSelectableFlags_Disabled : ImGuiSelectableFlags_None; + if( _dds_model.supports_DDS() ) + { + if( ImGui::Selectable( "DDS Configuration", false, is_streaming_flag ) ) + { + _dds_model.open_dds_tool_window(); + } + if( ImGui::IsItemHovered() ) + { + std::string tooltip = rsutils::string::from() + << "Change the configuration of Ethernet based devices" + << ( is_streaming ? " (Disabled while streaming)" : "" ); + ImGui::SetTooltip( "%s", tooltip.c_str() ); + } + } } draw_device_panel_auto_calib(viewer, something_to_show, error_message); @@ -1457,6 +1473,10 @@ namespace rs2 } _calib_model.update(window, error_message); + if( _dds_model.supports_DDS() ) + { + _dds_model.render_dds_config_window( window, error_message ); + } //////////////////////////////////////// diff --git a/common/device-model.h b/common/device-model.h index 11401f8267..eadf32e548 100644 --- a/common/device-model.h +++ b/common/device-model.h @@ -11,6 +11,7 @@ #include "updates-model.h" #include "calibration-model.h" #include "objects-in-frame.h" +#include "dds-model.h" ImVec4 from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a, bool consistent_color = false); ImVec4 operator+(const ImVec4& c, float v); @@ -108,6 +109,11 @@ namespace rs2 { static const char* enable_writing{ "calibration.enable_writing" }; } + namespace dds + { + static const char* enable_dds{ "context.dds.enabled" }; + static const char* domain_id{ "context.dds.domain" }; + } namespace viewer { static const char* is_3d_view{ "viewer_model.is_3d_view" }; @@ -451,6 +457,7 @@ namespace rs2 std::shared_ptr _updates; std::shared_ptr_updates_profile; calibration_model _calib_model; + dds_model _dds_model; }; std::pair get_device_name(const device& dev); diff --git a/common/ux-window.cpp b/common/ux-window.cpp index 95a16e6dec..bf4258edf1 100644 --- a/common/ux-window.cpp +++ b/common/ux-window.cpp @@ -102,6 +102,13 @@ namespace rs2 config_file::instance().set_default(configurations::viewer::log_filename, path + "librealsense.log"); config_file::instance().set_default(configurations::record::default_path, path); + auto const filename + = rsutils::os::get_special_folder( rsutils::os::special_folder::app_data ) + RS2_CONFIG_FILENAME; + auto config = rsutils::json_config::load_from_file( filename ); + bool enable_dds = config.nested( "context", "dds", "enabled" ).get_ex( enable_dds ); + int domain_id = config.nested("context", "dds", "domain").get_ex(domain_id); + config_file::instance().set_default( configurations::dds::enable_dds, enable_dds ); + config_file::instance().set_default( configurations::dds::domain_id, domain_id ); #ifdef __APPLE__ config_file::instance().set_default(configurations::performance::font_oversample, 2); diff --git a/common/viewer.cpp b/common/viewer.cpp index 7b7ea21fa4..cb00668571 100644 --- a/common/viewer.cpp +++ b/common/viewer.cpp @@ -2813,6 +2813,34 @@ namespace rs2 catch (...){} } } + + ImGui::Separator(); + bool enable_dds = temp_cfg.get( configurations::dds::enable_dds ); + int domain_id = temp_cfg.get( configurations::dds::domain_id ); + if( ImGui::Checkbox( "Enable DDS", &enable_dds ) ) + { + temp_cfg.set( configurations::dds::enable_dds, enable_dds ); + } + if( enable_dds ) + { + ImGui::SameLine(); + ImGui::SetCursorPosX( ImGui::GetCursorPosX() + 50 ); + ImGui::PushItemWidth( 150.0f ); + ImGui::Text( "Domain ID" ); + ImGui::SameLine(); + if( ImGui::InputInt( "##Domain ID", &domain_id ) ) + { + if( domain_id < 0 ) + domain_id = 0; + else if( domain_id > 232 ) + domain_id = 232; + temp_cfg.set( configurations::dds::domain_id, domain_id ); + } + } + ImGui::SameLine(); + ImGui::SetCursorPosX( w - 700 ); + ImGui::Text(u8"\uf071 Changes will take effect only after restarting the application "); + } if (tab == 3) diff --git a/tools/dds/dds-config/eth-config-header.h b/third-party/rsutils/include/rsutils/type/eth-config-header.h similarity index 100% rename from tools/dds/dds-config/eth-config-header.h rename to third-party/rsutils/include/rsutils/type/eth-config-header.h diff --git a/tools/dds/dds-config/eth-config-v3.h b/third-party/rsutils/include/rsutils/type/eth-config-v3.h similarity index 100% rename from tools/dds/dds-config/eth-config-v3.h rename to third-party/rsutils/include/rsutils/type/eth-config-v3.h diff --git a/tools/dds/dds-config/eth-config.cpp b/third-party/rsutils/include/rsutils/type/eth-config.cpp similarity index 96% rename from tools/dds/dds-config/eth-config.cpp rename to third-party/rsutils/include/rsutils/type/eth-config.cpp index a3096972b0..5ad564d5db 100644 --- a/tools/dds/dds-config/eth-config.cpp +++ b/third-party/rsutils/include/rsutils/type/eth-config.cpp @@ -105,7 +105,7 @@ bool eth_config::operator==( eth_config const & other ) const noexcept // Only compare those items that are configurable return configured.ip == other.configured.ip && configured.netmask == other.configured.netmask && configured.gateway == other.configured.gateway && dds.domain_id == other.dds.domain_id - && dhcp.on == other.dhcp.on && link.priority == other.link.priority && link.timeout == other.link.timeout; + && dhcp.on == other.dhcp.on && link.priority == other.link.priority && link.timeout == other.link.timeout && dhcp.timeout != other.dhcp.timeout; } @@ -114,7 +114,7 @@ bool eth_config::operator!=( eth_config const & other ) const noexcept // Only compare those items that are configurable return configured.ip != other.configured.ip || configured.netmask != other.configured.netmask || configured.gateway != other.configured.gateway || dds.domain_id != other.dds.domain_id - || dhcp.on != other.dhcp.on || link.priority != other.link.priority || link.timeout != other.link.timeout; + || dhcp.on != other.dhcp.on || link.priority != other.link.priority || link.timeout != other.link.timeout || dhcp.timeout != other.dhcp.timeout; } diff --git a/tools/dds/dds-config/eth-config.h b/third-party/rsutils/include/rsutils/type/eth-config.h similarity index 100% rename from tools/dds/dds-config/eth-config.h rename to third-party/rsutils/include/rsutils/type/eth-config.h diff --git a/tools/dds/dds-config/CMakeLists.txt b/tools/dds/dds-config/CMakeLists.txt index 805d2e3ef9..200e8e7717 100644 --- a/tools/dds/dds-config/CMakeLists.txt +++ b/tools/dds/dds-config/CMakeLists.txt @@ -9,6 +9,7 @@ file( GLOB_RECURSE RS_DDS_CONFIG_SOURCE_FILES LIST_DIRECTORIES false RELATIVE ${PROJECT_SOURCE_DIR} "${CMAKE_CURRENT_LIST_DIR}/*" + "${CMAKE_SOURCE_DIR}/third-party/rsutils/include/rsutils/type/*" ) target_sources( ${PROJECT_NAME} PRIVATE ${RS_DDS_CONFIG_SOURCE_FILES} ) diff --git a/tools/dds/dds-config/rs-dds-config.cpp b/tools/dds/dds-config/rs-dds-config.cpp index 22c898bd4c..53bee0aa89 100644 --- a/tools/dds/dds-config/rs-dds-config.cpp +++ b/tools/dds/dds-config/rs-dds-config.cpp @@ -1,7 +1,7 @@ // License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2024 Intel Corporation. All Rights Reserved. -#include "eth-config.h" +#include #include diff --git a/tools/depth-quality/CMakeLists.txt b/tools/depth-quality/CMakeLists.txt index 47a4060dc2..1a360eb87d 100644 --- a/tools/depth-quality/CMakeLists.txt +++ b/tools/depth-quality/CMakeLists.txt @@ -40,6 +40,8 @@ if(BUILD_GRAPHICAL_EXAMPLES) ../../third-party/glad/glad.c ../../third-party/tinyfiledialogs/tinyfiledialogs.c ../../third-party/tinyfiledialogs/tinyfiledialogs.h + ../../third-party/rsutils/include/rsutils/type/eth-config.h + ../../third-party/rsutils/include/rsutils/type/eth-config.cpp ) if(WIN32) diff --git a/tools/realsense-viewer/CMakeLists.txt b/tools/realsense-viewer/CMakeLists.txt index c3bf108e4e..c9637ac6aa 100644 --- a/tools/realsense-viewer/CMakeLists.txt +++ b/tools/realsense-viewer/CMakeLists.txt @@ -49,6 +49,8 @@ if(BUILD_GRAPHICAL_EXAMPLES) ../../common/rs-config.cpp ../../common/os.h ../../common/os.cpp + ../../third-party/rsutils/include/rsutils/type/eth-config.h + ../../third-party/rsutils/include/rsutils/type/eth-config.cpp ) SET(DELAYED