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
25199void print_frame_impl (const CANFrame& frame) {
@@ -60,38 +234,34 @@ void print_signal_impl(const CANSignal& sig) {
60234void 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