Skip to content

Commit 4e49444

Browse files
author
Avi SZYCHTER
committed
Better table for PrintOne frame
1 parent 47ac2f7 commit 4e49444

File tree

1 file changed

+215
-45
lines changed

1 file changed

+215
-45
lines changed

utils/can-parse/print-single-frame.cpp

Lines changed: 215 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,198 @@
22
#include "CANDatabase.h"
33
#include <iostream>
44
#include <iomanip>
5+
#include <algorithm>
6+
#include <numeric>
57

6-
void print_info_separator(int level = 0) {
7-
static const char* level_0_separator = "##############";
8-
static const char* level_1_separator = "--------------";
9-
static const char* level_2_separator = "~~~~~~~~~~~~~~";
10-
const char** separator = nullptr;
11-
12-
switch(level) {
13-
case 0:
14-
default:
15-
separator = &level_0_separator;
16-
break;
17-
18-
case 1:
19-
separator = &level_1_separator;
20-
break;
8+
class ConsoleTable {
9+
public:
10+
struct TableData {
11+
struct TableCell {
12+
enum Type {
13+
UnsignedHex, Unsigned, Signed, String, Float
14+
};
15+
16+
Type type;
17+
uint64_t u;
18+
int64_t i;
19+
std::string s;
20+
double d;
21+
};
22+
23+
std::vector<TableCell> data;
24+
bool is_full_line;
25+
};
26+
27+
static const char LINE_SEPARATOR = '-';
28+
static const char COLUMN_SEPARATOR = '|';
29+
static const unsigned SPACE_AMOUNT = 2;
30+
31+
public:
32+
ConsoleTable(std::initializer_list<std::string> headers, bool add_final_line_separator = true)
33+
: column_headers(headers), add_final_ls(add_final_line_separator) {
34+
std::transform(column_headers.begin(), column_headers.end(), std::back_inserter(columns_width),
35+
[](const std::string& s) { return s.size() + 2; });
36+
}
37+
38+
ConsoleTable(unsigned headers_num, bool add_final_line_separator = true) {
39+
columns_width.resize(headers_num);
40+
add_final_ls = add_final_line_separator;
41+
}
42+
43+
void add_row(std::initializer_list<TableData::TableCell> row_data, bool is_full_line) {
44+
data.push_back({ row_data, is_full_line });
45+
46+
if(is_full_line)
47+
return;
48+
49+
size_t i = 0;
50+
for(const TableData::TableCell& data: row_data) {
51+
if(data.type == TableData::TableCell::String) {
52+
columns_width[i] = std::max(data.s.size() + SPACE_AMOUNT,
53+
columns_width[i]);
54+
}
55+
i++;
56+
}
57+
}
58+
59+
void render() const {
60+
// Some columns headers have been defined, we can create the table normally
61+
if(column_headers.size() > 0) {
62+
unsigned total_width = std::accumulate(columns_width.begin(), columns_width.end(), 0);
63+
total_width += columns_width.size(); // for the + " "
64+
65+
std::string line_separator(total_width, LINE_SEPARATOR);
66+
67+
// Render the table header
68+
renderNormalHeader(line_separator);
69+
70+
// Render the data
71+
for(size_t i = 0; i < data.size(); i++) {
72+
std::cout << COLUMN_SEPARATOR;
73+
for(size_t j = 0; j < data[i].data.size(); j++) {
74+
renderCell(data[i].data[j], columns_width[j]);
75+
std::cout << COLUMN_SEPARATOR;
76+
}
77+
78+
if(i < data.size() - 1 || add_final_ls) {
79+
std::cout << std::endl << line_separator << std::endl;
80+
}
81+
}
82+
}
83+
else {
84+
std::vector<std::string> computed_string;
85+
std::transform(data[0].data.begin(), data[0].data.end(),
86+
std::back_inserter(computed_string), [this](const TableData::TableCell& cell) {
87+
std::stringstream ss;
88+
ss << " ";
89+
parseCell(ss, cell);
90+
ss << " ";
91+
return ss.str();
92+
});
93+
94+
size_t i = 0;
95+
unsigned total_width = std::accumulate(computed_string.begin(), computed_string.end(), 0,
96+
[&i, this](unsigned prev, const std::string& s){
97+
return prev + s.size() + 1;
98+
}
99+
);
100+
101+
std::string line_separator(total_width, LINE_SEPARATOR);
102+
103+
std::cout << line_separator << std::endl << COLUMN_SEPARATOR;
104+
105+
for(size_t j = 0; j < data[0].data.size(); j++) {
106+
std::cout << computed_string[j] << COLUMN_SEPARATOR;
107+
}
108+
109+
std::cout << std::endl;
110+
if(add_final_ls) {
111+
std::cout << line_separator << std::endl;
112+
}
113+
}
114+
}
115+
116+
private:
117+
void renderNormalHeader(const std::string& line_separator) const {
118+
std::cout << line_separator << std::endl << COLUMN_SEPARATOR;
119+
for(size_t i = 0; i < column_headers.size(); i++) {
120+
std::cout << std::left << std::setw(columns_width[i]) << (" " + column_headers[i]) << COLUMN_SEPARATOR;
121+
}
122+
std::cout << std::endl << line_separator << std::endl;
21123
}
22-
std::cout << *separator << std::endl;
124+
125+
void renderCell(const TableData::TableCell& cell, unsigned col_size) const {
126+
std::cout << std::left << " ";
127+
std::cout << std::setw(col_size - 1);
128+
129+
parseCell(std::cout, cell);
130+
}
131+
132+
void parseCell(std::ostream& is, const TableData::TableCell& cell) const {
133+
switch(cell.type) {
134+
case TableData::TableCell::Unsigned:
135+
is << cell.u;
136+
break;
137+
138+
case TableData::TableCell::UnsignedHex:
139+
is << std::hex << std::showbase << cell.u
140+
<< std::dec << std::noshowbase;
141+
break;
142+
143+
case TableData::TableCell::Signed:
144+
is << cell.i;
145+
break;
146+
147+
case TableData::TableCell::String:
148+
is << cell.s;
149+
break;
150+
151+
case TableData::TableCell::Float:
152+
{
153+
std::streamsize precision = is.precision();
154+
is << std::setprecision(3) << cell.d
155+
<< std::setprecision(precision);
156+
}
157+
break;
158+
}
159+
}
160+
private:
161+
std::vector<std::string> column_headers;
162+
std::vector<size_t> columns_width;
163+
std::vector<TableData> data;
164+
bool add_final_ls;
165+
};
166+
167+
ConsoleTable::TableData::TableCell createUnsigned(uint64_t val) {
168+
ConsoleTable::TableData::TableCell result;
169+
result.type = ConsoleTable::TableData::TableCell::Unsigned;
170+
result.u = val;
171+
172+
return result;
173+
}
174+
175+
ConsoleTable::TableData::TableCell createHex(uint64_t val) {
176+
ConsoleTable::TableData::TableCell result;
177+
result.type = ConsoleTable::TableData::TableCell::UnsignedHex;
178+
result.u = val;
179+
180+
return result;
181+
}
182+
183+
ConsoleTable::TableData::TableCell createStr(const std::string& val) {
184+
ConsoleTable::TableData::TableCell result;
185+
result.type = ConsoleTable::TableData::TableCell::String;
186+
result.s = val;
187+
188+
return result;
189+
}
190+
191+
ConsoleTable::TableData::TableCell createFloat(double val) {
192+
ConsoleTable::TableData::TableCell result;
193+
result.type = ConsoleTable::TableData::TableCell::Float;
194+
result.d = val;
195+
196+
return result;
23197
}
24198

25199
void print_frame_impl(const CANFrame& frame) {
@@ -60,38 +234,34 @@ void print_signal_impl(const CANSignal& sig) {
60234
void CppCAN::can_parse::print_single_frame(CANDatabase& db, uint32_t can_id) {
61235
const CANFrame& frame = db[can_id];
62236

63-
print_frame_impl(frame);
64-
65-
print_info_separator(1);
66-
67-
// First, explore the database to find "pretty-printing" parameters
68-
int sig_name_maxsize = 15; // At least a reasonable column size
69-
for(const auto& sig : frame) {
70-
if(sig.second.name().size() > sig_name_maxsize)
71-
sig_name_maxsize = sig.second.name().size() + 1;
72-
}
237+
std::stringstream can_id_ss;
238+
can_id_ss << "CAN ID: " << std::hex << std::showbase << frame.can_id();
73239

74-
std::cout << std::left << std::setw(sig_name_maxsize) << "Signal name"
75-
<< std::setw(10) << "Start bit"
76-
<< std::setw(9) << "Length"
77-
<< std::setw(9) << "Scale"
78-
<< std::setw(10) << "Offset"
79-
<< std::setw(12) << "Signedness"
80-
<< std::setw(15) << "Endianness"
81-
<< std::setw(10) << "Range"
82-
<< std::endl;
240+
ConsoleTable summary_header(4, false);
241+
summary_header.add_row({
242+
createStr(frame.name()), createStr(can_id_ss.str()),
243+
createStr("DLC: " + std::to_string(frame.dlc())), createStr("Period: " + std::to_string(frame.period()) + "ms")
244+
}, false);
83245

246+
ConsoleTable console_table = {
247+
"Signal name", "Start bit", "Length", "Scale",
248+
"Offset", "Signedness", "Endianness", "Range"
249+
};
250+
84251
for(const auto& sig : frame) {
85252
const CANSignal& signal = sig.second;
86-
std::cout << std::left << std::setw(sig_name_maxsize) << signal.name()
87-
<< std::setw(10) << signal.start_bit()
88-
<< std::setw(9) << signal.length()
89-
<< std::setw(9) << std::setprecision(3) << signal.scale()
90-
<< std::setw(10) << std::setprecision(3) << signal.offset()
91-
<< std::setw(12) << ((signal.signedness() == CANSignal::Signed) ? "Signed" : "Unsigned")
92-
<< std::setw(15) << ((signal.endianness() == CANSignal::BigEndian) ? "BigEndian" : "LittleEndian")
93-
<< std::setw(10) << ("[" + std::to_string(signal.range().min) + ", " + std::to_string(signal.range().max) + "]")
94-
<< std::endl;
253+
console_table.add_row({
254+
createStr(signal.name()),
255+
createUnsigned(signal.start_bit()),
256+
createUnsigned(signal.length()),
257+
createFloat(signal.scale()),
258+
createFloat(signal.offset()),
259+
signal.signedness() == CANSignal::Signed ? createStr("Signed") : createStr("Unsigned"),
260+
signal.endianness() == CANSignal::BigEndian ? createStr("BigEndian") : createStr("LittleEndian"),
261+
createStr("[" + std::to_string(signal.range().min) + ", " + std::to_string(signal.range().max) + "]")
262+
}, false);
95263
}
96-
264+
265+
summary_header.render();
266+
console_table.render();
97267
}

0 commit comments

Comments
 (0)