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.
488 lines
16 KiB
488 lines
16 KiB
2 months ago
|
// License: Apache 2.0. See LICENSE file in root directory.
|
||
|
// Copyright(c) 2015 Intel Corporation. All Rights Reserved.
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <fstream>
|
||
|
#include <string>
|
||
|
#include <algorithm>
|
||
|
|
||
|
#include <librealsense2/rs.hpp>
|
||
|
#include <rsutils/string/string-utilities.h>
|
||
|
#include "tclap/CmdLine.h"
|
||
|
#include "parser.hpp"
|
||
|
#include "auto-complete.h"
|
||
|
|
||
|
#include <rsutils/json.h>
|
||
|
#include <thread>
|
||
|
|
||
|
|
||
|
using namespace std;
|
||
|
using namespace TCLAP;
|
||
|
using rsutils::json;
|
||
|
|
||
|
|
||
|
vector<uint8_t> build_raw_command_data(const command& command, const vector<string>& params)
|
||
|
{
|
||
|
if (params.size() > command.parameters.size() && !command.is_cmd_write_data)
|
||
|
throw runtime_error("Input string was not in a correct format!");
|
||
|
|
||
|
vector<parameter> vec_parameters;
|
||
|
for (auto param_index = 0; param_index < params.size(); ++param_index)
|
||
|
{
|
||
|
auto is_there_write_data = param_index >= int(command.parameters.size());
|
||
|
auto name = (is_there_write_data) ? "" : command.parameters[param_index].name;
|
||
|
auto is_reverse_bytes = (is_there_write_data) ? false : command.parameters[param_index].is_reverse_bytes;
|
||
|
auto is_decimal = (is_there_write_data) ? false : command.parameters[param_index].is_decimal;
|
||
|
auto format_length = (is_there_write_data) ? -1 : command.parameters[param_index].format_length;
|
||
|
vec_parameters.push_back(parameter(name, params[param_index], is_decimal, is_reverse_bytes, format_length));
|
||
|
}
|
||
|
|
||
|
vector<uint8_t> raw_data;
|
||
|
encode_raw_data_command(command, vec_parameters, raw_data);
|
||
|
return raw_data;
|
||
|
}
|
||
|
|
||
|
void xml_mode(const string& line, const commands_xml& cmd_xml, rs2::device& dev, map<string, xml_parser_function>& format_type_to_lambda)
|
||
|
{
|
||
|
vector<string> tokens;
|
||
|
stringstream ss(line);
|
||
|
string word;
|
||
|
while (ss >> word)
|
||
|
{
|
||
|
stringstream converter;
|
||
|
converter << hex << word;
|
||
|
tokens.push_back(word);
|
||
|
}
|
||
|
|
||
|
if (tokens.empty())
|
||
|
throw runtime_error("Invalid input! - no arguments provided");
|
||
|
|
||
|
auto command_str = rsutils::string::to_lower(tokens.front());
|
||
|
auto it = cmd_xml.commands.find(command_str);
|
||
|
if (it == cmd_xml.commands.end())
|
||
|
throw runtime_error("Command " + command_str + " was not found!");
|
||
|
|
||
|
auto command = it->second;
|
||
|
vector<string> params;
|
||
|
for (auto i = 1; i < tokens.size(); ++i)
|
||
|
params.push_back(tokens[i]);
|
||
|
|
||
|
// In case of sending data from file, the data will be retrieved and converted into raw format
|
||
|
file_argument_to_blob(params);
|
||
|
|
||
|
auto raw_data = build_raw_command_data(command, params);
|
||
|
|
||
|
for (auto b : raw_data)
|
||
|
{
|
||
|
cout << hex << fixed << setfill('0') << setw(2) << (int)b << " ";
|
||
|
}
|
||
|
cout << endl;
|
||
|
|
||
|
auto result = dev.as<rs2::debug_protocol>().send_and_receive_raw_data(raw_data);
|
||
|
|
||
|
unsigned returned_opcode = *result.data();
|
||
|
// check returned opcode
|
||
|
if (command.op_code != returned_opcode)
|
||
|
{
|
||
|
stringstream msg;
|
||
|
msg << "OpCodes do not match! Sent 0x" << hex << command.op_code << " but received 0x" << hex << (returned_opcode) << "!";
|
||
|
throw runtime_error(msg.str());
|
||
|
}
|
||
|
|
||
|
if (command.is_read_command)
|
||
|
{
|
||
|
string data;
|
||
|
decode_string_from_raw_data(command, cmd_xml.custom_formatters, result.data(), result.size(), data, format_type_to_lambda);
|
||
|
cout << endl << data << endl;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cout << endl << "Done!" << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void hex_mode(const string& line, rs2::device& dev)
|
||
|
{
|
||
|
vector<uint8_t> raw_data;
|
||
|
stringstream ss(line);
|
||
|
string word;
|
||
|
while (ss >> word)
|
||
|
{
|
||
|
stringstream converter;
|
||
|
int temp;
|
||
|
converter << hex << word;
|
||
|
converter >> temp;
|
||
|
raw_data.push_back(temp);
|
||
|
}
|
||
|
if (raw_data.empty())
|
||
|
throw runtime_error("Wrong input!");
|
||
|
|
||
|
auto result = dev.as<rs2::debug_protocol>().send_and_receive_raw_data(raw_data);
|
||
|
|
||
|
cout << endl;
|
||
|
for (auto& elem : result)
|
||
|
cout << setfill('0') << setw(2) << hex << static_cast<int>(elem) << " ";
|
||
|
}
|
||
|
|
||
|
auto_complete get_auto_complete_obj(bool is_application_in_hex_mode, const map<string, command>& commands_map)
|
||
|
{
|
||
|
set<string> commands;
|
||
|
if (!is_application_in_hex_mode)
|
||
|
{
|
||
|
for (auto& elem : commands_map)
|
||
|
commands.insert(elem.first);
|
||
|
}
|
||
|
return auto_complete(commands, is_application_in_hex_mode);
|
||
|
}
|
||
|
|
||
|
void read_script_file(const string& full_file_path, vector<string>& hex_lines)
|
||
|
{
|
||
|
ifstream myfile(full_file_path);
|
||
|
if (myfile.is_open())
|
||
|
{
|
||
|
string line;
|
||
|
while (getline(myfile, line))
|
||
|
hex_lines.push_back(line);
|
||
|
|
||
|
myfile.close();
|
||
|
return;
|
||
|
}
|
||
|
throw runtime_error("Script file not found!");
|
||
|
}
|
||
|
|
||
|
rs2::device wait_for_device(const rs2::device_hub& hub, bool print_info = true)
|
||
|
{
|
||
|
if (print_info)
|
||
|
cout << "\nWaiting for RealSense device to connect...\n";
|
||
|
|
||
|
auto dev = hub.wait_for_device();
|
||
|
|
||
|
if (print_info)
|
||
|
cout << "RealSense device has connected...\n";
|
||
|
|
||
|
return dev;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char** argv) try
|
||
|
{
|
||
|
CmdLine cmd("librealsense rs-terminal tool", ' ', RS2_API_FULL_VERSION_STR);
|
||
|
SwitchArg debug_arg( "", "debug", "Turn on LibRS debug logs" );
|
||
|
ValueArg<string> xml_arg("l", "load", "Full file path of commands XML file", false, "", "Load commands XML file");
|
||
|
ValueArg<int> device_id_arg("d", "deviceId", "Device ID could be obtain from rs-enumerate-devices example", false, 0, "Select a device to work with");
|
||
|
ValueArg<string> specific_SN_arg("n", "serialNum", "Serial Number can be obtain from rs-enumerate-devices example", false, "", "Select a device serial number to work with");
|
||
|
SwitchArg all_devices_arg("a", "allDevices", "Do this command to all attached Realsense Devices", false);
|
||
|
ValueArg<string> hex_cmd_arg("s", "send", "Hexadecimal raw data", false, "", "Send hexadecimal raw data to device");
|
||
|
ValueArg<string> hex_script_arg("r", "raw", "Full file path of hexadecimal raw data script", false, "", "Send raw data line by line from script file");
|
||
|
ValueArg<string> commands_script_arg("c", "cmd", "Full file path of commands script", false, "", "Send commands line by line from script file");
|
||
|
SwitchArg only_sw_arg( "", "sw-only", "Show only software devices (playback, DDS, etc. -- but not USB/HID/etc.)" );
|
||
|
cmd.add(debug_arg);
|
||
|
cmd.add(xml_arg);
|
||
|
cmd.add(device_id_arg);
|
||
|
cmd.add(specific_SN_arg);
|
||
|
cmd.add(all_devices_arg);
|
||
|
cmd.add(hex_cmd_arg);
|
||
|
cmd.add(hex_script_arg);
|
||
|
cmd.add(commands_script_arg);
|
||
|
cmd.add(only_sw_arg);
|
||
|
#ifdef BUILD_WITH_DDS
|
||
|
ValueArg< int > domain_arg( "", "dds-domain", "Set the DDS domain ID (default to 0)", false, 0, "0-232" );
|
||
|
cmd.add( domain_arg );
|
||
|
#endif
|
||
|
cmd.parse(argc, argv);
|
||
|
|
||
|
#ifdef BUILD_EASYLOGGINGPP
|
||
|
bool debugging = debug_arg.getValue();
|
||
|
rs2::log_to_console( debugging ? RS2_LOG_SEVERITY_DEBUG : RS2_LOG_SEVERITY_ERROR );
|
||
|
#endif
|
||
|
|
||
|
// parse command.xml
|
||
|
rs2::log_to_file(RS2_LOG_SEVERITY_WARN, "librealsense.log");
|
||
|
|
||
|
json settings = json::object();
|
||
|
#ifdef BUILD_WITH_DDS
|
||
|
if( domain_arg.isSet() || only_sw_arg.isSet() )
|
||
|
{
|
||
|
json dds = json::object();
|
||
|
if( domain_arg.isSet() )
|
||
|
dds["domain"] = domain_arg.getValue();
|
||
|
dds["enabled"]; // null: remove global dds:false or dds/enabled:false, if any
|
||
|
settings["dds"] = std::move( dds );
|
||
|
}
|
||
|
#endif
|
||
|
if( only_sw_arg.getValue() )
|
||
|
settings["device-mask"] = RS2_PRODUCT_LINE_SW_ONLY | RS2_PRODUCT_LINE_ANY;
|
||
|
|
||
|
// Obtain a list of devices currently present on the system
|
||
|
rs2::context ctx( settings.dump() );
|
||
|
rs2::device_hub hub(ctx);
|
||
|
rs2::device_list all_device_list = ctx.query_devices();
|
||
|
if( only_sw_arg.getValue() )
|
||
|
{
|
||
|
// For SW-only devices, allow some time for DDS devices to connect
|
||
|
int tries = 5;
|
||
|
cout << "No device detected. Waiting..." << flush;
|
||
|
while( ! all_device_list.size() && tries-- )
|
||
|
{
|
||
|
cout << "." << flush;
|
||
|
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
|
||
|
all_device_list = ctx.query_devices();
|
||
|
}
|
||
|
cout << endl;
|
||
|
}
|
||
|
if (all_device_list.size() == 0) {
|
||
|
std::cout << "\nLibrealsense is not detecting any devices" << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
};
|
||
|
|
||
|
std::vector<rs2::device> rs_device_list;
|
||
|
// Ensure that deviceList only has realsense devices in it. tmpList contains webcams as well
|
||
|
if( only_sw_arg.getValue() )
|
||
|
{
|
||
|
rs_device_list = all_device_list;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (uint32_t i = 0; i < all_device_list.size(); i++) {
|
||
|
try {
|
||
|
all_device_list[i].get_info(RS2_CAMERA_INFO_FIRMWARE_VERSION);
|
||
|
rs_device_list.push_back(all_device_list[i]);
|
||
|
}
|
||
|
catch (...) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
auto num_rs_devices = rs_device_list.size();
|
||
|
if (rs_device_list.size() == 0) {
|
||
|
std::cout << "\nLibrealsense is not detecting any Realsense cameras" << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
};
|
||
|
|
||
|
auto xml_full_file_path = xml_arg.getValue();
|
||
|
map<string, xml_parser_function> format_type_to_lambda;
|
||
|
commands_xml cmd_xml;
|
||
|
auto is_application_in_hex_mode = true;
|
||
|
|
||
|
if (!xml_full_file_path.empty())
|
||
|
{
|
||
|
auto sts = parse_xml_from_file(xml_full_file_path, cmd_xml);
|
||
|
if (!sts)
|
||
|
{
|
||
|
cout << "Commands dictionary " << xml_full_file_path << " is invalid, aborting!\n";
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
update_format_type_to_lambda(format_type_to_lambda);
|
||
|
is_application_in_hex_mode = false;
|
||
|
cout << "Commands XML file - " << xml_full_file_path << " was loaded successfully.\n Type commands by name (e.g.'gvd'`).\n";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cout << "Commands XML file not provided.\nyou still can send raw data to device in hexadecimal\nseparated by spaces.\n";
|
||
|
cout << "Example GVD command for the D4XX:\n14 00 ab cd 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n";
|
||
|
}
|
||
|
auto auto_comp = get_auto_complete_obj(is_application_in_hex_mode, cmd_xml.commands);
|
||
|
|
||
|
|
||
|
std::vector<rs2::device> selected_rs_devices;
|
||
|
while (true)
|
||
|
{
|
||
|
if (all_devices_arg.isSet()) {
|
||
|
for (size_t i = 0; i < num_rs_devices; i++) {
|
||
|
std::string sn = std::string(rs_device_list[i].get_info(RS2_CAMERA_INFO_SERIAL_NUMBER));
|
||
|
selected_rs_devices.push_back(rs_device_list[i]);
|
||
|
std::cout << "\nDevice with Serial Number: " << rs_device_list[i].get_info(RS2_CAMERA_INFO_SERIAL_NUMBER) << " has loaded.\n";
|
||
|
}
|
||
|
}
|
||
|
else if (specific_SN_arg.isSet()) {
|
||
|
auto desired_sn = specific_SN_arg.getValue();
|
||
|
bool device_not_found = true;
|
||
|
for (size_t i = 0; i < num_rs_devices; i++) {
|
||
|
std::string device_sn = std::string(rs_device_list[i].get_info(RS2_CAMERA_INFO_SERIAL_NUMBER));
|
||
|
if (device_sn.compare(desired_sn) == 0) { //std::compare returns 0 if the strings are the same
|
||
|
selected_rs_devices.push_back(rs_device_list[i]);
|
||
|
device_not_found = false;
|
||
|
std::cout << "\nDevice with SN: " << device_sn << " has loaded.\n";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (device_not_found) {
|
||
|
std::cout << "\nGiven device serial number doesn't exist! desired serial number=" << desired_sn << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (device_id_arg.isSet())
|
||
|
{
|
||
|
auto dev_id = device_id_arg.getValue();
|
||
|
if (num_rs_devices < (dev_id + 1))
|
||
|
{
|
||
|
std::cout << "\nGiven device_id doesn't exist! device_id=" <<
|
||
|
dev_id << " ; connected devices=" << num_rs_devices << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < (num_rs_devices - 1); ++i)
|
||
|
{
|
||
|
wait_for_device(hub, true);
|
||
|
}
|
||
|
selected_rs_devices.push_back(rs_device_list[dev_id]);
|
||
|
std::cout << "\nDevice ID " << dev_id << " has loaded.\n";
|
||
|
}
|
||
|
else if (rs_device_list.size() == 1)
|
||
|
{
|
||
|
selected_rs_devices.push_back(rs_device_list[0]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
std::cout << "\nEnter a command line option:" << std::endl;
|
||
|
std::cout << "-d to choose by device number" << std::endl;
|
||
|
std::cout << "-n to choose by serial number" << std::endl;
|
||
|
std::cout << "-a to send to all devices" << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (selected_rs_devices.empty()) {
|
||
|
std::cout << "\nNo devices were selected. Recheck input arguments" << std::endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
fflush(nullptr);
|
||
|
if (hex_cmd_arg.isSet())
|
||
|
{
|
||
|
for (auto dev : selected_rs_devices) {
|
||
|
auto line = hex_cmd_arg.getValue();
|
||
|
try
|
||
|
{
|
||
|
hex_mode(line, dev);
|
||
|
}
|
||
|
catch (const exception& ex)
|
||
|
{
|
||
|
cout << endl << ex.what() << endl;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
std::string script_file("");
|
||
|
vector<string> script_lines;
|
||
|
if (hex_script_arg.isSet())
|
||
|
{
|
||
|
script_file = hex_script_arg.getValue();
|
||
|
}
|
||
|
else if (commands_script_arg.isSet())
|
||
|
{
|
||
|
script_file = commands_script_arg.getValue();
|
||
|
}
|
||
|
|
||
|
if (!script_file.empty())
|
||
|
{
|
||
|
read_script_file(script_file, script_lines);
|
||
|
cout << "Executing the following command from script file " << script_file << endl;
|
||
|
for (auto& ln : script_lines)
|
||
|
cout << rsutils::string::to_upper(ln) << endl;
|
||
|
cout << endl;
|
||
|
}
|
||
|
|
||
|
if (hex_script_arg.isSet())
|
||
|
{
|
||
|
for (auto& dev : selected_rs_devices) {
|
||
|
try
|
||
|
{
|
||
|
for (auto& elem : script_lines)
|
||
|
hex_mode(elem, dev);
|
||
|
}
|
||
|
catch (const exception& ex)
|
||
|
{
|
||
|
cout << endl << ex.what() << endl;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
if (commands_script_arg.isSet())
|
||
|
{
|
||
|
for (auto dev : selected_rs_devices) {
|
||
|
try
|
||
|
{
|
||
|
for (auto& elem : script_lines)
|
||
|
xml_mode(elem, cmd_xml, dev, format_type_to_lambda);
|
||
|
}
|
||
|
catch (const exception& ex)
|
||
|
{
|
||
|
cout << endl << ex.what() << endl;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
auto dev = selected_rs_devices[0];
|
||
|
while (hub.is_connected(dev))
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
cout << "\n\n#>";
|
||
|
fflush(nullptr);
|
||
|
string line = "";
|
||
|
line = auto_comp.get_line([&]() {return !hub.is_connected(dev); });
|
||
|
if (!hub.is_connected(dev))
|
||
|
continue;
|
||
|
|
||
|
|
||
|
if (line == "next")
|
||
|
{
|
||
|
dev = wait_for_device(hub);
|
||
|
continue;
|
||
|
}
|
||
|
if (line == "exit")
|
||
|
{
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
for (auto&& dev : selected_rs_devices)
|
||
|
{
|
||
|
if (!hub.is_connected(dev))
|
||
|
continue;
|
||
|
|
||
|
if (is_application_in_hex_mode)
|
||
|
{
|
||
|
hex_mode(line, dev);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xml_mode(line, cmd_xml, dev, format_type_to_lambda);
|
||
|
}
|
||
|
cout << endl;
|
||
|
}
|
||
|
}
|
||
|
catch (const rs2::error & e)
|
||
|
{
|
||
|
cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what() << endl;
|
||
|
}
|
||
|
catch (const exception & e)
|
||
|
{
|
||
|
cerr << e.what() << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
catch( const rs2::error & e )
|
||
|
{
|
||
|
cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what() << endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
catch( const exception & e )
|
||
|
{
|
||
|
cerr << e.what() << endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
catch( ... )
|
||
|
{
|
||
|
cerr << "some error" << endl;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|