From d16edaa5ef73e9139ac9c9d3b71981bf21a5de17 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 2 Jun 2021 15:22:45 -0700 Subject: [PATCH] [cli] add helper `Interpreter::OutputTableHeader()` (#6696) This commit adds a helper method `OutputTableHeader()` that can be used to print a table header from a given array of column widths and title strings. This is then used in CLI module for different tables (child/neighbor/router table, active/energy scan result, etc). --- src/cli/README.md | 2 +- src/cli/cli.cpp | 108 +++++++++++++++++++++++---- src/cli/cli.hpp | 9 +++ tests/scripts/expect/tun-netstat.exp | 6 +- 4 files changed, 105 insertions(+), 20 deletions(-) diff --git a/src/cli/README.md b/src/cli/README.md index dba1ba509..0f459db42 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -1767,7 +1767,7 @@ List all UDP sockets. ```bash > netstat -| Local Address | Peer Address | +| Local Address | Peer Address | +-------------------------------------------------+-------------------------------------------------+ | [0:0:0:0:0:0:0:0]:49153 | [0:0:0:0:0:0:0:0]:0 | | [0:0:0:0:0:0:0:0]:49152 | [0:0:0:0:0:0:0:0]:0 | diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index db4a52f89..925710e7d 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -168,6 +168,46 @@ int Interpreter::OutputIp6Address(const otIp6Address &aAddress) return OutputFormat("%s", string); } +void Interpreter::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]) +{ + for (uint8_t index = 0; index < aNumColumns; index++) + { + const char *title = aTitles[index]; + uint8_t width = aWidths[index]; + size_t titleLength = strlen(title); + + if (titleLength + 2 <= width) + { + // `title` fits in column width so we write it with extra space + // at beginning and end ("| Title |"). + + OutputFormat("| %*s", -static_cast(width - 1), title); + } + else + { + // Use narrow style (no space at beginning) and write as many + // chars from `title` as it can fit in the given column width + // ("|Title|"). + + OutputFormat("|%*.*s", -static_cast(width), width, title); + } + } + + OutputLine("|"); + + for (uint8_t index = 0; index < aNumColumns; index++) + { + OutputFormat("+"); + + for (uint8_t width = aWidths[index]; width != 0; width--) + { + OutputFormat("-"); + } + } + + OutputLine("+"); +} + otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable) { otError error = OT_ERROR_NONE; @@ -871,10 +911,16 @@ otError Interpreter::ProcessChild(uint8_t aArgsLength, Arg aArgs[]) if (isTable) { - OutputLine( - "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |"); - OutputLine( - "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+"); + static const char *const kChildTableTitles[] = { + "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R", + "D", "N", "Ver", "CSL", "QMsgCnt", "Extended MAC", + }; + + static const uint8_t kChildTableColumnWidths[] = { + 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 18, + }; + + OutputTableHeader(kChildTableTitles, kChildTableColumnWidths); } maxChildren = otThreadGetMaxAllowedChildren(mInstance); @@ -1432,8 +1478,8 @@ otError Interpreter::ProcessDiscover(uint8_t aArgsLength, Arg aArgs[]) SuccessOrExit(error = otThreadDiscover(mInstance, scanChannels, OT_PANID_BROADCAST, false, false, &Interpreter::HandleActiveScanResult, this)); - OutputLine("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |"); - OutputLine("+---+------------------+------------------+------+------------------+----+-----+-----+"); + + OutputScanTableHeader(); error = OT_ERROR_PENDING; @@ -2985,8 +3031,15 @@ otError Interpreter::ProcessNeighbor(uint8_t aArgsLength, Arg aArgs[]) { if (isTable) { - OutputLine("| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|D|N| Extended MAC |"); - OutputLine("+------+--------+-----+----------+-----------+-+-+-+------------------+"); + static const char *const kNeighborTableTitles[] = { + "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC", + }; + + static const uint8_t kNeighborTableColumnWidths[] = { + 6, 8, 5, 10, 11, 1, 1, 1, 18, + }; + + OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths); } while (otThreadGetNextNeighborInfo(mInstance, &iterator, &neighborInfo) == OT_ERROR_NONE) @@ -3028,10 +3081,12 @@ otError Interpreter::ProcessNetstat(uint8_t aArgsLength, Arg aArgs[]) OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); + static const char *const kNetstatTableTitles[] = {"Local Address", "Peer Address"}; + static const uint8_t kNetstatTableColumnWidths[] = {49, 49}; + char string[OT_IP6_SOCK_ADDR_STRING_SIZE]; - OutputLine("| Local Address | Peer Address |"); - OutputLine("+-------------------------------------------------+-------------------------------------------------+"); + OutputTableHeader(kNetstatTableTitles, kNetstatTableColumnWidths); for (const otUdpSocket *socket = otUdpGetSockets(mInstance); socket != nullptr; socket = socket->mNext) { @@ -3890,8 +3945,15 @@ otError Interpreter::ProcessRouter(uint8_t aArgsLength, Arg aArgs[]) if (isTable) { - OutputLine("| ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC | Link |"); - OutputLine("+----+--------+----------+-----------+-------+--------+-----+------------------+------+"); + static const char *const kRouterTableTitles[] = { + "ID", "RLOC16", "Next Hop", "Path Cost", "LQ In", "LQ Out", "Age", "Extended MAC", "Link", + }; + + static const uint8_t kRouterTableColumnWidths[] = { + 4, 8, 10, 11, 7, 8, 5, 18, 6, + }; + + OutputTableHeader(kRouterTableTitles, kRouterTableColumnWidths); } maxRouterId = otThreadGetMaxRouterId(mInstance); @@ -4068,15 +4130,16 @@ otError Interpreter::ProcessScan(uint8_t aArgsLength, Arg aArgs[]) if (energyScan) { - OutputLine("| Ch | RSSI |"); - OutputLine("+----+------+"); + static const char *const kEnergyScanTableTitles[] = {"Ch", "RSSI"}; + static const uint8_t kEnergyScanTableColumnWidths[] = {4, 6}; + + OutputTableHeader(kEnergyScanTableTitles, kEnergyScanTableColumnWidths); SuccessOrExit(error = otLinkEnergyScan(mInstance, scanChannels, scanDuration, &Interpreter::HandleEnergyScanResult, this)); } else { - OutputLine("| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |"); - OutputLine("+---+------------------+------------------+------+------------------+----+-----+-----+"); + OutputScanTableHeader(); SuccessOrExit(error = otLinkActiveScan(mInstance, scanChannels, scanDuration, &Interpreter::HandleActiveScanResult, this)); } @@ -4087,6 +4150,19 @@ exit: return error; } +void Interpreter::OutputScanTableHeader(void) +{ + static const char *const kScanTableTitles[] = { + "J", "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI", + }; + + static const uint8_t kScanTableColumnWidths[] = { + 3, 18, 18, 6, 18, 4, 5, 5, + }; + + OutputTableHeader(kScanTableTitles, kScanTableColumnWidths); +} + void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext) { static_cast(aContext)->HandleActiveScanResult(aResult); diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 914a7a2e4..961bd9a5b 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -313,6 +313,14 @@ private: otError (Interpreter::*mHandler)(uint8_t aArgsLength, Arg aArgs[]); }; + void OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]); + + template + void OutputTableHeader(const char *const (&aTitles)[kTableNumColumns], const uint8_t (&aWidths)[kTableNumColumns]) + { + OutputTableHeader(kTableNumColumns, &aTitles[0], aWidths); + } + #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE otError ParsePingInterval(const Arg &aArg, uint32_t &aInterval); #endif @@ -545,6 +553,7 @@ private: static void HandlePingReply(const otPingSenderReply *aReply, void *aContext); static void HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext); #endif + void OutputScanTableHeader(void); static void HandleActiveScanResult(otActiveScanResult *aResult, void *aContext); static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext); static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext); diff --git a/tests/scripts/expect/tun-netstat.exp b/tests/scripts/expect/tun-netstat.exp index 33a082bce..0a6cee979 100755 --- a/tests/scripts/expect/tun-netstat.exp +++ b/tests/scripts/expect/tun-netstat.exp @@ -35,14 +35,14 @@ setup_leader send "udp open\n" expect_line "Done" send "netstat\n" -expect "| Local Address | Peer Address |" +expect "| Local Address | Peer Address |" expect "+-------------------------------------------------+-------------------------------------------------+" expect "| [0:0:0:0:0:0:0:0]:0 | [0:0:0:0:0:0:0:0]:0 |" expect_line "Done" send "udp bind :: 10001\n" expect_line "Done" send "netstat\n" -expect "| Local Address | Peer Address |" +expect "| Local Address | Peer Address |" expect "+-------------------------------------------------+-------------------------------------------------+" expect "| [0:0:0:0:0:0:0:0]:1001 | [0:0:0:0:0:0:0:0]:0 |" expect_line "Done" @@ -50,7 +50,7 @@ set addr [get_ipaddr mleid] send "udp connect $addr 10001\n" expect_line "Done" send "netstat\n" -expect "| Local Address | Peer Address |" +expect "| Local Address | Peer Address |" expect "+-------------------------------------------------+-------------------------------------------------+" expect -re "\\| \\[0:0:0:0:0:0:0:0\\]:10001 +\\| \\[$addr\\]:10001 +\\|" expect_line "Done"