You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

467 lines
21 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2023 Intel Corporation. All Rights Reserved.
#pragma once
#include <set>
#include "notifications.h"
#include "realsense-ui-advanced-mode.h"
#include <rsutils/json.h>
#include "sw-update/dev-updates-profile.h"
#include <rsutils/time/periodic-timer.h>
#include "updates-model.h"
#include "calibration-model.h"
#include "objects-in-frame.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);
static const ImVec4 light_blue = from_rgba(0, 174, 239, 255, true); // Light blue color for selected elements such as play button glyph when paused
static const ImVec4 regular_blue = from_rgba(0, 115, 200, 255, true); // Checkbox mark, slider grabber
static const ImVec4 light_grey = from_rgba(0xc3, 0xd5, 0xe5, 0xff, true); // Text
static const ImVec4 dark_window_background = from_rgba(9, 11, 13, 255);
static const ImVec4 almost_white_bg = from_rgba(230, 230, 230, 255, true);
static const ImVec4 black = from_rgba(0, 0, 0, 255, true);
static const ImVec4 transparent = from_rgba(0, 0, 0, 0, true);
static const ImVec4 white = from_rgba(0xff, 0xff, 0xff, 0xff, true);
static const ImVec4 scrollbar_bg = from_rgba(14, 17, 20, 255);
static const ImVec4 scrollbar_grab = from_rgba(54, 66, 67, 255);
static const ImVec4 grey{ 0.5f,0.5f,0.5f,1.f };
static const ImVec4 dark_grey = from_rgba(30, 30, 30, 255);
static const ImVec4 sensor_header_light_blue = from_rgba(80, 99, 115, 0xff);
static const ImVec4 sensor_bg = from_rgba(36, 44, 51, 0xff);
static const ImVec4 redish = from_rgba(255, 46, 54, 255, true);
static const ImVec4 light_red = from_rgba(255, 146, 154, 255, true);
static const ImVec4 dark_red = from_rgba(200, 46, 54, 255, true);
static const ImVec4 button_color = from_rgba(62, 77, 89, 0xff);
static const ImVec4 header_window_bg = from_rgba(36, 44, 54, 0xff);
static const ImVec4 header_color = from_rgba(62, 77, 89, 255);
static const ImVec4 title_color = from_rgba(27, 33, 38, 255);
static const ImVec4 device_info_color = from_rgba(33, 40, 46, 255);
static const ImVec4 yellow = from_rgba(229, 195, 101, 255, true);
static const ImVec4 yellowish = from_rgba(255, 253, 191, 255, true);
static const ImVec4 green = from_rgba(0x20, 0xe0, 0x20, 0xff, true);
static const ImVec4 dark_sensor_bg = from_rgba(0x1b, 0x21, 0x25, 170);
static const ImVec4 red = from_rgba(233, 0, 0, 255, true);
static const ImVec4 greenish = from_rgba(67, 163, 97, 255);
static const ImVec4 orange = from_rgba(255, 157, 0, 255, true);
inline ImVec4 operator*(const ImVec4& color, float t)
{
return ImVec4(color.x * t, color.y * t, color.z * t, color.w * t);
}
inline ImVec4 operator+(const ImVec4& a, const ImVec4& b)
{
return ImVec4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
inline ImVec4 blend(const ImVec4& c, float a)
{
return{ c.x, c.y, c.z, a * c.w };
}
namespace rs2
{
void imgui_easy_theming(ImFont*& font_dynamic, ImFont*& font_18, ImFont*& monofont, int& font_size);
constexpr const char* server_versions_db_url = "https://librealsense.intel.com/Releases/rs_versions_db.json";
typedef std::vector<std::unique_ptr<device_model>> device_models_list;
void open_issue(const device_models_list& devices);
struct textual_icon
{
explicit constexpr textual_icon(const char(&unicode_icon)[4]) :
_icon{ unicode_icon[0], unicode_icon[1], unicode_icon[2], unicode_icon[3] }
{
}
operator const char* () const
{
return _icon.data();
}
private:
std::array<char, 5> _icon;
};
inline std::ostream& operator<<(std::ostream& os, const textual_icon& i)
{
return os << static_cast<const char*>(i);
}
namespace configurations
{
namespace record
{
static const char* file_save_mode{ "record.file_save_mode" };
static const char* default_path{ "record.default_path" };
static const char* compression_mode{ "record.compression" };
}
namespace update
{
static const char* allow_rc_firmware{ "update.allow_rc_firmware" };
static const char* recommend_updates{ "update.recommend_updates" };
static const char* recommend_calibration{ "update.recommend_calibration" };
static const char* sw_updates_url{ "update.sw_update_url" };
static const char* sw_updates_official_server{ "update.sw_update_official_server" };
}
namespace calibration
{
static const char* enable_writing{ "calibration.enable_writing" };
}
namespace viewer
{
static const char* is_3d_view{ "viewer_model.is_3d_view" };
static const char* ground_truth_r{ "viewer_model.ground_truth_r" };
static const char* target_width_r{ "viewer_model.target_width_r" };
static const char* target_height_r{ "viewer_model.target_height_r" };
static const char* continue_with_ui_not_aligned{ "viewer_model.continue_with_ui_not_aligned" };
static const char* continue_with_current_fw{ "viewer_model.continue_with_current_fw" };
static const char* settings_tab{ "viewer_model.settings_tab" };
static const char* sdk_version{ "viewer_model.sdk_version" };
static const char* last_calib_notice{ "viewer_model.last_calib_notice" };
static const char* is_measuring{ "viewer_model.is_measuring" };
static const char* output_open{ "viewer_model.output_open" };
static const char* dashboard_open{ "viewer_model.dashboard_open" };
static const char* search_term{ "viewer_model.search_term" };
static const char* log_to_console{ "viewer_model.log_to_console" };
static const char* log_to_file{ "viewer_model.log_to_file" };
static const char* log_filename{ "viewer_model.log_filename" };
static const char* log_severity{ "viewer_model.log_severity" };
static const char* post_processing{ "viewer_model.post_processing" };
static const char* show_map_ruler{ "viewer_model.show_map_ruler" };
static const char* show_stream_details{ "viewer_model.show_stream_details" };
static const char* metric_system{ "viewer_model.metric_system" };
static const char* shading_mode{ "viewer_model.shading_mode" };
static const char* commands_xml{ "viewer_model.commands_xml" };
static const char* hwlogger_xml{ "viewer_model.hwlogger_xml" };
static const char* last_ip{ "viewer_model.last_ip" };
}
namespace window
{
static const char* is_fullscreen{ "window.is_fullscreen" };
static const char* saved_pos{ "window.saved_pos" };
static const char* position_x{ "window.position_x" };
static const char* position_y{ "window.position_y" };
static const char* saved_size{ "window.saved_size" };
static const char* width{ "window.width" };
static const char* height{ "window.height" };
static const char* maximized{ "window.maximized" };
static const char* font_size{ "window.font_size" };
}
namespace performance
{
static const char* glsl_for_rendering{ "performance.glsl_for_rendering.v2" };
static const char* glsl_for_processing{ "performance.glsl_for_processing.v2" };
static const char* enable_msaa{ "performance.msaa" };
static const char* msaa_samples{ "performance.msaa_samples" };
static const char* show_fps{ "performance.show_fps" };
static const char* vsync{ "performance.vsync" };
static const char* font_oversample{ "performance.font_oversample.v2" };
static const char* show_skybox{ "performance.show_skybox" };
static const char* occlusion_invalidation{ "performance.occlusion_invalidation" };
}
namespace ply
{
static const char* mesh{ "ply.mesh" };
static const char* use_normals{ "ply.normals" };
static const char* encoding{ "ply.encoding" };
enum encoding_types
{
textual = 0,
binary = 1
};
}
}
namespace textual_icons
{
// A note to a maintainer - preserve order when adding values to avoid duplicates
static const textual_icon file_movie{ u8"\uf008" };
static const textual_icon times{ u8"\uf00d" };
static const textual_icon download{ u8"\uf019" };
static const textual_icon refresh{ u8"\uf021" };
static const textual_icon lock{ u8"\uf023" };
static const textual_icon camera{ u8"\uf030" };
static const textual_icon video_camera{ u8"\uf03d" };
static const textual_icon edit{ u8"\uf044" };
static const textual_icon step_backward{ u8"\uf048" };
static const textual_icon play{ u8"\uf04b" };
static const textual_icon pause{ u8"\uf04c" };
static const textual_icon stop{ u8"\uf04d" };
static const textual_icon step_forward{ u8"\uf051" };
static const textual_icon plus_circle{ u8"\uf055" };
static const textual_icon question_mark{ u8"\uf059" };
static const textual_icon info_circle{ u8"\uf05a" };
static const textual_icon fix_up{ u8"\uf062" };
static const textual_icon minus{ u8"\uf068" };
static const textual_icon exclamation_triangle{ u8"\uf071" };
static const textual_icon shopping_cart{ u8"\uf07a" };
static const textual_icon bar_chart{ u8"\uf080" };
static const textual_icon upload{ u8"\uf093" };
static const textual_icon square_o{ u8"\uf096" };
static const textual_icon unlock{ u8"\uf09c" };
static const textual_icon floppy{ u8"\uf0c7" };
static const textual_icon square{ u8"\uf0c8" };
static const textual_icon bars{ u8"\uf0c9" };
static const textual_icon caret_down{ u8"\uf0d7" };
static const textual_icon repeat{ u8"\uf0e2" };
static const textual_icon circle{ u8"\uf111" };
static const textual_icon check_square_o{ u8"\uf14a" };
static const textual_icon cubes{ u8"\uf1b3" };
static const textual_icon toggle_off{ u8"\uf204" };
static const textual_icon toggle_on{ u8"\uf205" };
static const textual_icon connectdevelop{ u8"\uf20e" };
static const textual_icon usb_type{ u8"\uf287" };
static const textual_icon braille{ u8"\uf2a1" };
static const textual_icon window_maximize{ u8"\uf2d0" };
static const textual_icon window_restore{ u8"\uf2d2" };
static const textual_icon grid{ u8"\uf1cb" };
static const textual_icon exit{ u8"\uf011" };
static const textual_icon see_less{ u8"\uf070" };
static const textual_icon dotdotdot{ u8"\uf141" };
static const textual_icon link{ u8"\uf08e" };
static const textual_icon throphy{ u8"\uF091" };
static const textual_icon metadata{ u8"\uF0AE" };
static const textual_icon check{ u8"\uF00C" };
static const textual_icon mail{ u8"\uF01C" };
static const textual_icon cube{ u8"\uf1b2" };
static const textual_icon measure{ u8"\uf545" };
static const textual_icon wifi{ u8"\uf1eb" };
}
class viewer_model;
class ux_window;
class subdevice_model;
class syncer_model
{
public:
syncer_model() :
_active(true) {}
std::shared_ptr<rs2::asynchronous_syncer> create_syncer()
{
stop();
std::lock_guard<std::mutex> lock(_mutex);
auto shared_syncer = std::make_shared<rs2::asynchronous_syncer>();
// This queue stores the output from the syncer, and has to be large enough to deal with
// slowdowns on the processing/rendering threads of the Viewer to avoid frame drops. Frame
// drops can be pretty noticeable to the user!
//
// The syncer may also give out frames in bursts. This commonly happens, for example, when
// streams have different FPS, and is a known issue. Even with same-FPS streams, the actual
// FPS may change depending on a number of variables (e.g., exposure). When FPS is not the
// same between the streams, a latency is introduced and bursts from the syncer are the
// result.
//
// Bursts are so fast the other threads will never have a chance to pull them in time. For
// example, the syncer output can be the following, all one after the other:
//
// [Color: timestamp 100 (arrived @ 105), Infrared: timestamp 100 (arrived at 150)]
// [Color: timestamp 116 (arrived @ 120)]
// [Color: timestamp 132 (arrived @ 145)]
//
// They are received one at a time & pushed into the syncer, which will wait and keep all of
// them inside until a match with Infrared is possible. Once that happens, it will output
// everything it has as a burst.
//
// The queue size must therefore be big enough to deal with expected latency: the more
// latency, the bigger the burst.
//
// Another option is to use an aggregator, similar to the behavior inside the pipeline.
// But this still doesn't solve the issue of the bursts: we'll get frame drops in the fast
// stream instead of the slow.
//
rs2::frame_queue q(10);
_syncers.push_back({ shared_syncer,q });
shared_syncer->start([this, q](rs2::frame f)
{
q.enqueue(f);
on_frame();
});
start();
return shared_syncer;
}
void remove_syncer(std::shared_ptr<rs2::asynchronous_syncer> s)
{
stop();
std::lock_guard<std::mutex> lock(_mutex);
_syncers.erase(std::remove_if(_syncers.begin(), _syncers.end(),
[s](std::pair<std::shared_ptr<rs2::asynchronous_syncer>, rs2::frame_queue> pair)
{
return pair.first.get() == s.get();
}), _syncers.end());
start();
}
std::vector<rs2::frameset> try_wait_for_frames()
{
std::lock_guard<std::mutex> lock(_mutex);
std::vector<rs2::frameset> result;
for (auto&& s = _syncers.begin(); s != _syncers.end() && _active; s++)
{
rs2::frameset f;
if (s->second.try_wait_for_frame(&f, 1))
{
result.push_back(f);
}
}
return result;
}
void stop()
{
_active.exchange(false);
}
void start()
{
_active.exchange(true);
}
std::function<void()> on_frame = [] {};
private:
std::vector<std::pair<std::shared_ptr<rs2::asynchronous_syncer>, rs2::frame_queue>> _syncers;
std::mutex _mutex;
std::atomic<bool> _active;
};
class device_model
{
public:
typedef std::function<void(std::function<void()> load)> json_loading_func;
void reset();
explicit device_model(device& dev, std::string& error_message, viewer_model& viewer, bool new_device_connected = true, bool allow_remove=true);
~device_model();
void start_recording(const std::string& path, std::string& error_message);
void stop_recording(viewer_model& viewer);
void pause_record();
void resume_record();
void refresh_notifications(viewer_model& viewer);
bool check_for_bundled_fw_update( const rs2::context & ctx,
std::shared_ptr< notifications_model > not_model,
bool reset_delay = false );
int draw_playback_panel(ux_window& window, ImFont* font, viewer_model& view);
bool draw_advanced_controls(viewer_model& view, ux_window& window, std::string& error_message);
void draw_controls(float panel_width, float panel_height,
ux_window& window,
std::string& error_message,
device_model*& device_to_remove,
viewer_model& viewer, float windows_width,
std::vector<std::function<void()>>& draw_later,
bool load_json_if_streaming = false,
json_loading_func json_loading = [](std::function<void()> load) {load(); },
bool draw_device_outline = true);
void handle_hardware_events(const std::string& serialized_data);
void begin_update(std::vector<uint8_t> data,
viewer_model& viewer, std::string& error_message);
void begin_update_unsigned(viewer_model& viewer, std::string& error_message);
void check_for_device_updates(viewer_model& viewer, bool activated_by_user = false);
bool disable_record_button_logic(bool is_streaming, bool is_playback_device);
std::string get_record_button_hover_text(bool is_streaming);
std::shared_ptr< atomic_objects_in_frame > get_detected_objects() const { return _detected_objects; }
std::vector<std::shared_ptr<subdevice_model>> subdevices;
std::shared_ptr<syncer_model> syncer;
std::shared_ptr<rs2::asynchronous_syncer> dev_syncer;
bool is_streaming() const;
bool metadata_supported = false;
bool get_curr_advanced_controls = true;
device dev;
std::string id;
bool is_recording = false;
int seek_pos = 0;
int playback_speed_index = 2;
bool _playback_repeat = true;
bool _should_replay = false;
bool show_device_info = false;
bool _allow_remove = true;
bool show_depth_only = false;
bool show_stream_selection = true;
std::vector<std::pair<std::string, std::string>> infos;
std::vector<std::string> restarting_device_info;
std::set<std::string> advanced_mode_settings_file_names;
std::string selected_file_preset;
std::vector<std::shared_ptr<notification_model>> related_notifications;
private:
// This class is in charge of camera accuracy health window parameters,
// Needed as a member for reseting the window memory on device disconnection.
void draw_info_icon(ux_window& window, ImFont* font, const ImVec2& size);
int draw_seek_bar();
int draw_playback_controls(ux_window& window, ImFont* font, viewer_model& view);
advanced_mode_control amc;
std::string pretty_time(std::chrono::nanoseconds duration);
float draw_device_panel(float panel_width,
ux_window& window,
std::string& error_message,
viewer_model& viewer);
void play_defaults(viewer_model& view);
float draw_preset_panel(float panel_width,
ux_window& window,
std::string& error_message,
viewer_model& viewer,
bool update_read_only_options,
bool load_json_if_streaming,
json_loading_func json_loading);
bool prompt_toggle_advanced_mode(bool enable_advanced_mode, const std::string& message_text,
std::vector<std::string>& restarting_device_info,
viewer_model& view,
ux_window& window,
const std::string& error_message);
void load_viewer_configurations(const std::string& json_str);
void save_viewer_configurations(std::ofstream& outfile, rsutils::json& j);
void handle_online_sw_update(
std::shared_ptr< notifications_model > nm,
std::shared_ptr< sw_update::dev_updates_profile::update_profile > update_profile,
bool reset_delay = false );
bool handle_online_fw_update(
const context & ctx,
std::shared_ptr< notifications_model > nm,
std::shared_ptr< sw_update::dev_updates_profile::update_profile > update_profile,
bool reset_delay = false );
std::shared_ptr<recorder> _recorder;
std::vector<std::shared_ptr<subdevice_model>> live_subdevices;
rsutils::time::periodic_timer _update_readonly_options_timer;
bool pause_required = false;
std::shared_ptr< atomic_objects_in_frame > _detected_objects;
std::shared_ptr<updates_model> _updates;
std::shared_ptr<sw_update::dev_updates_profile::update_profile >_updates_profile;
calibration_model _calib_model;
};
std::pair<std::string, std::string> get_device_name(const device& dev);
std::vector<std::pair<std::string, std::string>> get_devices_names(const device_list& list);
class device_changes
{
public:
explicit device_changes(rs2::context& ctx);
bool try_get_next_changes(event_information& removed_and_connected);
private:
void add_changes(const event_information& c);
std::queue<event_information> _changes;
std::mutex _mtx;
};
}