12 #include <sys/resource.h>
19 if (FILE *
ff = fopen(path.c_str(),
"rb"))
28 template<
typename T>
void print_element(T t,
const int& width,
bool align)
32 std::cout << std::left << std::setw(width) << std::setfill(
' ') << t <<
" | ";
36 std::cout << std::right << std::setw(width) << std::setfill(
' ') << t <<
" | ";
41 bool check_size(
bool necessary,
char op,
int size_val,
int compare_val)
43 if (!necessary)
return true;
47 return (compare_val > size_val);
49 return (compare_val < size_val);
51 return (compare_val == size_val);
56 bool check_ids(
bool necessary, std::unordered_set<int> id_set,
int id_to_check)
58 return ((id_set.count(id_to_check)) || (!necessary));
64 std::unordered_set<int> id_set;
65 std::stringstream ss(list_of_ids);
66 std::vector<std::string> splited_ids;
70 getline(ss, substr,
',');
71 splited_ids.push_back(substr);
74 for (std::string id_entry : splited_ids)
76 if (id_entry.find(
'-') != std::string::npos)
78 std::stringstream range_stream(id_entry);
79 std::vector<std::string> range;
80 while (range_stream.good())
83 getline(range_stream, substr,
'-');
84 range.push_back(substr);
86 int tmp_id = std::stoi(range.front());
87 while (tmp_id <= std::stoi(range.back()))
89 id_set.insert(tmp_id);
95 id_set.insert(std::stoi(id_entry));
104 size_t del_pos = timerange.find(
',');
105 int time_1 = std::stoi(timerange.substr(0, del_pos));
106 int time_2 = std::stoi(timerange.substr(del_pos + 1, std::string::npos));
107 return std::make_tuple(time_1, time_2);
115 auto i = std::lower_bound(
121 if (i == time_vec.begin())
123 closest_time = cur_time;
127 int s_time = *(i - 1);
129 if (abs(cur_time - s_time) <= abs(cur_time - g_time))
131 closest_time = time_vec[i - time_vec.begin() - 1];
135 closest_time = time_vec[i - time_vec.begin()];
140 int suitable_time = -1;
141 if (abs(cur_time - closest_time) <= tolerance)
143 suitable_time = closest_time;
146 return suitable_time;
151 void saleae_ls(std::string p_path, std::string
size, std::string ids,
bool validate)
154 std::string path = (p_path ==
"") ?
"./saleae.json" : p_path +
"/saleae.json";
157 std::cout <<
"Cannot open file: " << path << std::endl;
162 bool size_necessary =
false;
167 size_necessary =
true;
168 if (
size[0] ==
'+' ||
size[0] ==
'-')
171 size_val = std::stoi(
size.substr(1));
175 size_val = std::stoi(
size);
183 bool ids_necessary =
false;
184 std::unordered_set<int> id_set;
187 ids_necessary =
true;
192 std::vector<SaleaeDirectoryNetEntry> net_entries = sd->
dump();
195 int format_length [6] = {7, 8, 19, 11, 10, 15};
197 format_length[0] = 6;
202 if (
check_size(size_necessary, size_op, size_val, sdfi.numberValues()) &&
check_ids(ids_necessary, id_set, sdne.id()))
204 format_length[0] = (format_length[0] < std::to_string(sdne.id()).length()) ? std::to_string(sdne.id()).length() : format_length[0];
205 format_length[1] = (format_length[1] < sdne.name().length()) ? sdne.name().length() : format_length[1];
206 format_length[2] = (format_length[2] < std::to_string(sdfi.numberValues()).length()) ? std::to_string(sdfi.numberValues()).length() : format_length[2];
207 format_length[3] = (format_length[3] < std::to_string(sdfi.beginTime()).length()) ? std::to_string(sdfi.beginTime()).length() : format_length[3];
208 format_length[4] = (format_length[4] < std::to_string(sdfi.endTime()).length()) ? std::to_string(sdfi.endTime()).length() : format_length[4];
209 format_length[5] = (format_length[5] < std::to_string(sdfi.index()).length() + 12) ? std::to_string(sdfi.index()).length() + 12: format_length[5];
213 int abs_length = format_length[0] + format_length[1] + format_length[2] + format_length[3] + format_length[4] + format_length[5] + 16;
218 std::cout << std::string(abs_length + 2,
'-') << std::endl;
230 print_element(
"Total Number Values", format_length[2],
true);
234 std::cout << std::endl;
235 std::cout <<
'|' << std::string(abs_length,
'-') <<
'|' << std::endl;
240 if (
check_size(size_necessary, size_op, size_val, sdfi.numberValues()) &&
check_ids(ids_necessary, id_set, sdne.id()))
245 std::string valid_char =
" ";
247 p_path = (p_path ==
"") ?
"" : p_path +
"/";
248 std::string bin_path = p_path +
"digital_" + std::to_string(sdfi.index()) +
".bin";
272 print_element(
"digital_" + std::to_string(sdfi.index()) +
".bin", format_length[5],
true);
273 std::cout << std::endl;
277 std::cout << std::string(abs_length + 2,
'-') << std::endl;
282 std::cout <<
"Number of non-matching directory entries: " << val_cnt << std::endl;
287 std::cout <<
"validation OK" << std::endl;
296 void saleae_cat(std::string path, std::string file_name,
bool dump_header,
bool dump_data)
299 path = (path ==
"") ?
"./" + file_name : path +
"/" + file_name;
302 std::cout <<
"Cannot open file: " << path << std::endl;
307 if (!dump_header && !dump_data)
323 std::string data_format;
327 data_format =
"Double";
330 data_format =
"Uint64";
333 data_format =
"Coded";
338 int format_length [5] = {12, 10, 8, 11, 21};
339 format_length[0] = (format_length[0] < data_format.length()) ? data_format.length() : format_length[0];
340 format_length[1] = (format_length[1] < std::to_string(begin_time).length()) ? std::to_string(begin_time).length() : format_length[1];
341 format_length[1] = (format_length[1] < std::to_string(end_time).length()) ? std::to_string(end_time).length() : format_length[1];
342 format_length[2] = (format_length[2] < std::to_string(start_val).length()) ? std::to_string(start_val).length() : format_length[2];
343 format_length[3] = (format_length[3] < std::to_string(num_transitions).length()) ? std::to_string(num_transitions).length() : format_length[3];
344 int abs_length = format_length[0] + format_length[1] + format_length[2] + format_length[3] + format_length[4] + 13;
347 std::cout << std::string(abs_length + 2,
'-') << std::endl;
353 print_element(
"Number of Transitions", format_length[4],
true);
354 std::cout << std::endl;
355 std::cout <<
'|' << std::string(abs_length,
'-') <<
'|' << std::endl;
362 std::cout << std::endl;
363 std::cout << std::string(abs_length + 2,
'-') << std::endl;
367 if (dump_data && (num_transitions > 0))
376 int format_length [3] = {4, 4, 5};
377 for (
int i = 0; i <= num_transitions; i++)
379 format_length[0] = (format_length[0] < std::to_string(i).length()) ? std::to_string(i).length() : format_length[0];
380 format_length[1] = (format_length[1] < std::to_string(time_array[i]).length()) ? std::to_string(time_array[i]).length() : format_length[1];
381 format_length[2] = (format_length[2] < std::to_string(value_array[i]).length()) ? std::to_string(time_array[i]).length() : format_length[2];
383 int abs_length = format_length[0] + format_length[1] + format_length[2] + 7;
386 std::cout << std::string(abs_length + 2,
'-') << std::endl;
391 std::cout << std::endl;
392 std::cout <<
'|' << std::string(abs_length,
'-') <<
'|' << std::endl;
393 for (
int i = 0; i <= num_transitions; i++)
399 std::cout << std::endl;
401 std::cout << std::string(abs_length + 2,
'-') << std::endl;
409 void saleae_diff(std::string path_1, std::string path_2, std::string ids,
bool only_diff,
int tolerance)
412 std::string path_1_json = (path_1 ==
"") ?
"./saleae.json" : path_1 +
"/saleae.json";
415 std::cout <<
"Cannot open file: " << path_1_json << std::endl;
418 std::string path_2_json = path_2 +
"/saleae.json";
421 std::cout <<
"Cannot open file: " << path_2_json << std::endl;
426 bool ids_necessary =
false;
427 std::unordered_set<int> id_set;
430 ids_necessary =
true;
451 std::map<uint64_t, row_t> net_data;
452 int format_length[4];
455 std::vector<int> ids_not_in_2;
456 std::vector<int> ids_not_in_1;
457 std::vector<net_t> diff_vec;
458 bool diff_found =
false;
461 std::vector<SaleaeDirectoryNetEntry> net_entries_1 = sd_1->
dump();
463 std::vector<SaleaeDirectoryNetEntry> net_entries_2 = sd_2->
dump();
466 if (!
check_ids(ids_necessary, id_set, sdne_1.id()))
470 bool id_found =
false;
473 if (sdne_1.id() != sdne_2.id())
480 struct net_t cur_net;
481 cur_net.id = sdne_1.id();
482 cur_net.name_1 = sdne_1.name();
483 cur_net.name_2 = sdne_2.name();
486 cur_net.format_length[0] = 2;
487 cur_net.format_length[1] = 4;
488 cur_net.format_length[2] = cur_net.name_1.length();
489 cur_net.format_length[3] = cur_net.name_2.length();
492 cur_net.name_diff = cur_net.name_1 != cur_net.name_2;
496 std::map<uint64_t, row_t> net_data;
497 std::vector<uint64_t> time_vec;
500 path_1 = (path_1 ==
"") ?
"." : path_1;
501 std::string bin_path = path_1 +
"/digital_" + std::to_string(sdfi.index()) +
".bin";
504 std::cout <<
"Error in database: " << path_1 <<
"\nCannot open file: " << bin_path << std::endl;
511 std::cout <<
"db nullptr: <" << bin_path <<
">" << std::endl;
515 for (
int i = 0; i < db->
mCount; i++)
518 time_vec.push_back(t);
519 net_data[t] = row_t{.val_1 = db->
mValueArray[i], .val_1_avail =
true, .val_2_avail =
false, .diff =
true};
522 cur_net.format_length[1] = (cur_net.format_length[1] < std::to_string(t).length()) ? std::to_string(t).length() : cur_net.format_length[1];
523 cur_net.format_length[2] = (cur_net.format_length[2] < std::to_string(net_data[t].val_1).length()) ? std::to_string(net_data[t].val_1).length() : cur_net.format_length[2];
530 std::string bin_path = path_2 +
"/digital_" + std::to_string(sdfi.index()) +
".bin";
533 std::cout <<
"Error in database: " << path_2 <<
"\nCannot open file: " << bin_path << std::endl;
540 std::cout <<
"db nullptr: <" << bin_path <<
">" << std::endl;
544 for (
int i = 0; i < db->
mCount; i++)
551 net_data[twt].alt_time = t;
553 net_data[twt].val_2_avail =
true;
554 net_data[twt].diff = (net_data[twt].val_1 != net_data[twt].val_2);
555 diff_cnt = net_data[twt].diff ? diff_cnt : diff_cnt - 1;
557 cur_net.format_length[3] = (cur_net.format_length[3] < std::to_string(net_data[twt].val_2).length()) ? std::to_string(net_data[twt].val_2).length() : cur_net.format_length[3];
561 net_data[t] = row_t{.val_1_avail =
false, .val_2 = db->
mValueArray[i], .val_2_avail =
true, .diff =
true};
564 cur_net.format_length[1] = (cur_net.format_length[1] < std::to_string(t).length()) ? std::to_string(t).length() : cur_net.format_length[1];
565 cur_net.format_length[3] = (cur_net.format_length[3] < std::to_string(net_data[t].val_2).length()) ? std::to_string(net_data[t].val_2).length() : cur_net.format_length[3];
569 cur_net.data_diff = diff_cnt > 0;
570 if (cur_net.data_diff)
572 cur_net.net_data = net_data;
575 if (cur_net.data_diff || cur_net.name_diff)
577 diff_vec.push_back(cur_net);
586 ids_not_in_2.push_back(sdne_1.id());
592 if (!
check_ids(ids_necessary, id_set, sdne_2.id()))
596 bool id_found =
false;
599 if (sdne_2.id() != sdne_1.id())
607 ids_not_in_1.push_back(sdne_2.id());
614 std::cout <<
"- Content of waveform database is the same" << std::endl;
617 std::cout <<
"=> Database 1: " << path_1 <<
"\n=> Database 2: " << path_2 <<
"\n\n" << std::endl;
619 for (
int id : ids_not_in_2)
621 std::cout <<
"- Waveform ID " <<
id <<
" found in database 1 but not in database 2\n" << std::endl;
623 for (
int id : ids_not_in_1)
625 std::cout <<
"- Waveform ID " <<
id <<
" found in database 2 but not in database 1\n" << std::endl;
627 for (net_t cur_net : diff_vec)
629 bool name_diff_bool = cur_net.name_diff && !cur_net.data_diff;
633 std::cout <<
"- Waveform ID " << cur_net.id <<
" is named \"" << cur_net.name_1 <<
"\" in database 1 but \"" << cur_net.name_2 <<
"\" in database 2\n" << std::endl;
639 if (name_diff_bool) {
640 std::cout <<
"\n- Waveform ID " << cur_net.id <<
" has a data difference" << std::endl;
643 std::cout <<
"\n- Waveform ID " <<
" ("<< cur_net.name_1 <<
") " << cur_net.id <<
" has a data difference" << std::endl;
645 int abs_length = cur_net.format_length[0] + cur_net.format_length[1] + cur_net.format_length[2] + cur_net.format_length[3] + 10;
646 std::cout << std::string(abs_length + 2,
'-') << std::endl;
647 std::string diff_char = cur_net.name_diff ?
"*" :
" ";
649 print_element(
"| " + diff_char, cur_net.format_length[0],
true);
651 print_element(cur_net.name_1, cur_net.format_length[2],
true);
652 print_element(cur_net.name_2, cur_net.format_length[3],
true);
653 std::cout << std::endl;
654 std::cout <<
'|' << std::string(abs_length,
'-') <<
'|' << std::endl;
657 for (
auto &item : cur_net.net_data)
659 struct row_t cur_row = item.second;
661 if (only_diff && !cur_row.diff)
665 std::string diff_char = cur_row.diff ?
"*" :
" ";
666 std::string val_1 = cur_row.val_1_avail ? std::to_string(cur_row.val_1) :
"-";
667 std::string val_2 = cur_row.val_2_avail ? std::to_string(cur_row.val_2) :
"-";
668 int main_time = item.first;
669 int alt_time = (cur_row.val_1_avail && cur_row.val_2_avail) ? cur_row.alt_time : main_time;
674 if (alt_time < main_time)
686 std::cout << std::endl;
688 std::cout << std::string(abs_length + 2,
'-') << std::endl;
689 std::cout <<
"\n" << std::endl;
697 void saleae_export(std::string path_1, std::string path_2, std::string ids, std::string timerange)
700 path_2 = (path_2 ==
"") ?
"." : path_2;
703 bool ids_necessary =
false;
704 std::unordered_set<int> id_set;
707 ids_necessary =
true;
712 bool tr_necessary =
false;
713 int time_shift, last_time;
720 if (time_shift > last_time) {
721 std::cout <<
"Invalid timerange. First time must be smaller then second time!" << std::endl;
727 std::string saleae_fp= path_2 +
"/saleae.json";
733 for (
int id : id_set) {
736 wave_data_qlist.
append(wd);
741 for (
const WaveData* wd : *wave_data_list) {
742 wave_data_qlist.
append(wd);
753 getrlimit(RLIMIT_NOFILE, &rlim);
755 unsigned int required = wave_data_qlist.
size() + 256;
756 Q_ASSERT(rlim.rlim_max >= required);
757 if (rlim.rlim_cur < required)
759 rlim.rlim_cur = required;
760 setrlimit(RLIMIT_NOFILE, &rlim);
769 else if (ext ==
"csv")
772 std::cout <<
"Export file format not supported, must be either vcd or csv." << std::endl;
783 int main(
int argc,
const char* argv[])
785 std::cout << std::endl;
793 generic_options.
add(
"--help",
"print help messages");
796 tool_options.
add(
"ls",
"Lists content of saleae directory file saleae.json");
797 tool_options.
add(
"cat",
"Dump content of binary file <arg> into console", {
""});
798 tool_options.
add(
"diff",
"Compares content of database in current directory with other saleae database at <arg>", {
""});
799 tool_options.
add(
"export",
"Exports waveforms from database in current SALEAE directory to .VCD or .CSV file <arg>", {
""});
804 ls_options.
add({
"-i",
"--id"},
"list only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {
ProgramOptions::A_REQUIRED_PARAMETER});
805 ls_options.
add({
"-v",
"--validate"},
"validates time stamps and number of waveform events by comparing directory information with binary file header and binary file sizes.");
809 cat_options.
add({
"-h",
"--only-header"},
"dump only header");
810 cat_options.
add({
"-b",
"--only-data"},
"dump only data including start value");
814 diff_options.
add({
"-i",
"--id"},
"compares only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {
ProgramOptions::A_REQUIRED_PARAMETER});
815 diff_options.
add({
"-x",
"--only-differences"},
"when dumping waveform data values all rows without differences are suppressed (except header row)");
816 diff_options.
add({
"-t",
"--max-tolerance"},
"the integer value <ARG> sets the maximum tolerance when comparing waveform data. On default (zero tolerance) two waveforms A,B with transition values A=[0,12000,16000] B=[0,12010,16010] are considered to be different. However, when tolerance is set to 10 or higher the comparison will not find any differences", {
ProgramOptions::A_REQUIRED_PARAMETER});
820 export_options.
add({
"-i",
"--id"},
"export only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {
ProgramOptions::A_REQUIRED_PARAMETER});
821 export_options.
add({
"-r",
"--time-range"},
"exports only events within given time range <ARG>. The value in <ARG> gets subtracted from every exported time stamp so that exported time starts at zero. A start value (last value before entering the time range) must be provided for each exported waveform.", {
ProgramOptions::A_REQUIRED_PARAMETER});
827 ls_options.
add(generic_options);
830 bool unknown_option_exists =
false;
833 unknown_option_exists = (opt !=
"ls") ?
true : unknown_option_exists;
848 cat_options.
add(generic_options);
851 bool unknown_option_exists =
false;
854 unknown_option_exists = ((opt !=
"cat") && (opt != filename)) ?
true : unknown_option_exists;
864 else if (args.
is_option_set(
"--help") || unknown_option_exists)
876 diff_options.
add(generic_options);
879 bool unknown_option_exists =
false;
882 unknown_option_exists = ((opt !=
"diff") && (opt != diff_path)) ?
true : unknown_option_exists;
887 tolerance = std::stoi(args.
get_parameter(
"--max-tolerance"));
890 unknown_option_exists =
true;
900 else if (args.
is_option_set(
"--help") || unknown_option_exists)
912 export_options.
add(generic_options);
915 bool unknown_option_exists =
false;
918 unknown_option_exists = ((opt !=
"export") && (opt != export_path)) ?
true : unknown_option_exists;
920 if (export_path ==
"")
928 else if (args.
is_option_set(
"--help") || unknown_option_exists)
939 tool_options.
add(generic_options);
std::shared_ptr< spdlog::logger > add_channel(const std::string &channel_name, const std::vector< std::shared_ptr< log_sink >> &sinks, const std::string &level="info")
void deactivate_all_channels()
static std::shared_ptr< log_sink > create_gui_sink()
static std::shared_ptr< log_sink > create_file_sink(const std::filesystem::path &file_name="", const bool truncate=false)
static std::shared_ptr< log_sink > create_stdout_sink(const bool colored=true)
static LogManager * get_instance(const std::filesystem::path &file_name="")
std::string get_parameter(const std::string &flag) const
bool is_option_set(const std::string &flag) const
std::string get_options_string() const
ProgramArguments parse(int argc, const char *argv[])
bool add(const std::string &flag, const std::string &description, const std::initializer_list< std::string > ¶meters={})
std::vector< std::string > get_unknown_arguments()
static const std::string A_REQUIRED_PARAMETER
constant to specify that a parameter is required and does not have a default value.
uint64_t mCount
Number of elements in buffer.
uint64_t * mTimeArray
Buffer for mCount transition time values.
int * mValueArray
Buffer for mCount data values.
The SaleaeDirectoryFileIndex class represents a single SALEAE data file. The class comprises the inde...
The SaleaeDirectory class provides the API for the SALEAE directory file. The SALEAE directory file w...
std::vector< SaleaeDirectoryNetEntry > dump() const
Dump content of class instance to console for debugging purpose.
The SaleaeDirectoryNetEntry class represents a regular waveform for a single simulated net....
bool exportVcd(const QString &filename, const QList< const WaveData * > &waves, u32 startTime, u32 endTime, u32 timeShift=0)
bool exportCsv(const QString &filename, const QList< const WaveData * > &waves)
WaveData * waveDataById(const int id)
const WaveDataTimeframe & timeFrame() const
QString suffix() const const
void append(const T &value)
QString fromStdString(const std::string &str)
QString toLower() const const
int time_within_tolerance(const std::vector< uint64_t > &time_vec, int cur_time, int tolerance)
void saleae_export(std::string path_1, std::string path_2, std::string ids, std::string timerange)
void print_element(T t, const int &width, bool align)
bool check_size(bool necessary, char op, int size_val, int compare_val)
std::unordered_set< int > parse_list_of_ids(std::string list_of_ids)
void saleae_diff(std::string path_1, std::string path_2, std::string ids, bool only_diff, int tolerance)
bool check_ids(bool necessary, std::unordered_set< int > id_set, int id_to_check)
std::tuple< int, int > parse_timerange(std::string timerange)
bool file_exists(const std::string &path)
void saleae_cat(std::string path, std::string file_name, bool dump_header, bool dump_data)
int main(int argc, const char *argv[])
void saleae_ls(std::string p_path, std::string size, std::string ids, bool validate)