Merge pull request #11692 from maribu/fmt_table

sys/fmt: Added submodule fmt_table for printing tables
This commit is contained in:
Kaspar Schleiser 2019-08-20 22:18:54 +02:00 committed by GitHub
commit d6356bdc08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 193 additions and 125 deletions

View File

@ -13,6 +13,7 @@ PSEUDOMODULES += devfs_%
PSEUDOMODULES += ecc_% PSEUDOMODULES += ecc_%
PSEUDOMODULES += emb6_router PSEUDOMODULES += emb6_router
PSEUDOMODULES += event_% PSEUDOMODULES += event_%
PSEUDOMODULES += fmt_%
PSEUDOMODULES += gnrc_ipv6_default PSEUDOMODULES += gnrc_ipv6_default
PSEUDOMODULES += gnrc_ipv6_router PSEUDOMODULES += gnrc_ipv6_router
PSEUDOMODULES += gnrc_ipv6_router_default PSEUDOMODULES += gnrc_ipv6_router_default

View File

@ -2,6 +2,10 @@ ifneq (,$(filter eepreg,$(USEMODULE)))
FEATURES_REQUIRED += periph_eeprom FEATURES_REQUIRED += periph_eeprom
endif endif
ifneq (,$(filter fmt_table,$(USEMODULE)))
USEMODULE += fmt
endif
ifneq (,$(filter i2c_scan,$(USEMODULE))) ifneq (,$(filter i2c_scan,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c FEATURES_REQUIRED += periph_i2c
endif endif

View File

@ -1 +1,7 @@
# exclude submodule sources from *.c wildcard source selection
SRC := $(filter-out table.c,$(wildcard *.c))
# enable submodules
SUBMODULES := 1
include $(RIOTBASE)/Makefile.base include $(RIOTBASE)/Makefile.base

74
sys/fmt/table.c Normal file
View File

@ -0,0 +1,74 @@
/*
* Copyright 2019 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_fmt_table
* @{
*
* @file
* @brief Implementation of the table extensions of the string formatting
* library
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*
* @}
*/
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include "fmt.h"
static const char fmt_table_spaces[16] = " ";
/**
* @brief Prints @p fill_size bytes of the given pattern, repeating the
* pattern if needed
* @param pat Pattern to print
* @param pat_size Size of the pattern in bytes
* @param fill_size Number of bytes to print (if bigger than @p pat_size, the
* pattern will be repeated)
*
* E.g. `print_pattern("ab", 2, 5);` will print `ababa` to the console.
* This can be used to fill table columns with spaces, draw lines, etc.
*/
static void print_pattern(const char *pat, size_t pat_size, size_t fill_size)
{
while (fill_size > pat_size) {
print(pat, pat_size);
}
print(pat, fill_size);
}
void print_col_u32_dec(uint32_t number, size_t width)
{
char sbuf[10]; /* "4294967295" */
size_t slen;
slen = fmt_u32_dec(sbuf, number);
if (width > slen) {
print_pattern(fmt_table_spaces, sizeof(fmt_table_spaces), width - slen);
}
print(sbuf, slen);
}
void print_col_s32_dec(int32_t number, size_t width)
{
char sbuf[11]; /* "-2147483648" */
size_t slen;
slen = fmt_s32_dec(sbuf, number);
if (width > slen) {
print_pattern(fmt_table_spaces, sizeof(fmt_table_spaces), width - slen);
}
print(sbuf, slen);
}

56
sys/include/fmt_table.h Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright 2019 Otto-von-Guericke-Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_fmt_table Table extension of the string formatting API (fmt_table)
* @ingroup sys_fmt
* @brief Provides utilities to print tables.
*
* \note The print functions in this library do not buffer any output.
* Mixing calls to standard @c printf from stdio.h with the @c print_xxx
* functions in fmt, especially on the same output line, may cause garbled
* output.
*
* @{
*
* @file
* @brief Table extension of the string formatting API
*
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
*/
#ifndef FMT_TABLE_H
#define FMT_TABLE_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Print a table column with the given number as decimal
* @param number Number to print in the column
* @param width Width of the column
*/
void print_col_u32_dec(uint32_t number, size_t width);
/**
* @brief Print a table column with the given number as decimal
* @param number Number to print in the column
* @param width Width of the column
*/
void print_col_s32_dec(int32_t number, size_t width);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* FMT_TABLE_H */

View File

@ -4,7 +4,7 @@ BOARD_INSUFFICIENT_MEMORY += arduino-duemilanove arduino-leonardo arduino-nano \
arduino-uno arduino-uno
BOARD ?= msba2 BOARD ?= msba2
USEMODULE += fmt USEMODULE += fmt_table
USEMODULE += ltc4150 USEMODULE += ltc4150
include $(RIOTBASE)/Makefile.include include $(RIOTBASE)/Makefile.include

View File

@ -19,9 +19,9 @@
*/ */
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include "fmt.h" #include "fmt.h"
#include "fmt_table.h"
#include "led.h" #include "led.h"
#include "ltc4150.h" #include "ltc4150.h"
#include "thread.h" #include "thread.h"
@ -62,22 +62,6 @@ static void *recorder_data[] = {
#include "ltc4150_params.h" #include "ltc4150_params.h"
/**
* @brief Like `puts()`, but do not append a newline
*
* Normally I would just use `fputs(str, stdout)` directly, but the msp430
* toolchain lacks `fputs()`. This wrapper allows to add a less efficient
* fallback to printf()
*/
static inline void puts_no_nl(const char *s)
{
#ifndef NO_FPUTS
fputs(s, stdout);
#else
printf("%s", s);
#endif
}
/** /**
* @brief Callback function to reset/initialize the recorder data * @brief Callback function to reset/initialize the recorder data
*/ */
@ -151,62 +135,6 @@ static void *busy_thread(void *arg)
return NULL; return NULL;
} }
/**
* @brief Print the given number of spaces
*/
static void print_spaces(size_t number)
{
static const char *spaces = " ";
while (number > 16) {
puts_no_nl(spaces);
number -= 16;
}
puts_no_nl(spaces + 16 - number);
}
/**
* @brief Print a table column with the given number as decimal
* @param number Number to print in the column
* @param width Width of the column
*/
static void print_col_u32(uint32_t number, size_t width)
{
char sbuf[32];
size_t slen;
slen = fmt_u32_dec(sbuf, number);
sbuf[slen] = '\0';
if (width > slen) {
print_spaces(width - slen);
}
puts_no_nl(sbuf);
}
/**
* @brief Print a table column with the given number as decimal
* @param number Number to print in the column
* @param width Width of the column
*/
static void print_col_i32(int32_t number, size_t width)
{
char sbuf[32];
size_t slen;
char *pos = sbuf;
if (number < 0) {
*pos++ = '-';
number = -number;
width--;
}
slen = fmt_u32_dec(sbuf, (uint32_t)number);
sbuf[slen] = '\0';
if (width > slen) {
print_spaces(width - slen);
}
puts_no_nl(sbuf);
}
/** /**
* @brief Print a table column with the given current as E-01 * @brief Print a table column with the given current as E-01
* @param current Value to print in the column (as E-01) * @param current Value to print in the column (as E-01)
@ -214,13 +142,12 @@ static void print_col_i32(int32_t number, size_t width)
*/ */
static void print_current(int32_t current, size_t width) static void print_current(int32_t current, size_t width)
{ {
char sbuf[3]; char sbuf[2];
print_col_i32(current/10, width - 2); print_col_s32_dec(current/10, width - 2);
sbuf[0] = '.'; sbuf[0] = '.';
sbuf[1] = '0' + current % 10; sbuf[1] = '0' + current % 10;
sbuf[2] = '\0'; print(sbuf, 2);
puts_no_nl(sbuf);
} }
int main(void) int main(void)
@ -236,16 +163,16 @@ int main(void)
ltc4150_pulses2c(&ltc4150, &ten_uc_per_pulse, NULL, 10000, 0); ltc4150_pulses2c(&ltc4150, &ten_uc_per_pulse, NULL, 10000, 0);
if (retval) { if (retval) {
puts_no_nl("Failed to initialize LTC4150 driver:"); print_str("Failed to initialize LTC4150 driver:");
switch (retval) { switch (retval) {
case -EINVAL: case -EINVAL:
puts("Invalid parameter"); print_str("Invalid parameter\n");
break; break;
case -EIO: case -EIO:
puts("GPIO or interrupt configuration failed"); print_str("GPIO or interrupt configuration failed\n");
break; break;
default: default:
puts("Unknown (should no happen, file a bug)"); print_str("Unknown (should no happen, file a bug)\n");
break; break;
} }
return -1; return -1;
@ -256,39 +183,39 @@ int main(void)
THREAD_PRIORITY_MAIN + 1, THREAD_CREATE_STACKTEST, THREAD_PRIORITY_MAIN + 1, THREAD_CREATE_STACKTEST,
busy_thread, NULL, "busy_thread"); busy_thread, NULL, "busy_thread");
puts("This test will put three levels of load on the MCU:\n" print_str("This test will put three levels of load on the MCU:\n"
" 1. One minute of little to no load (LEDs(*) off)\n" " 1. One minute of little to no load (LEDs(*) off)\n"
" 2. One minute of about 50% CPU load (LEDs(*) blinking)\n" " 2. One minute of about 50% CPU load (LEDs(*) blinking)\n"
" 3. One minute of 100% CPU load (LEDs(*) constantly on)\n" " 3. One minute of 100% CPU load (LEDs(*) constantly on)\n"
"\n" "\n"
" (*) LED0 and LED1, if present on your board\n" " (*) LED0 and LED1, if present on your board\n"
"\n" "\n"
"During this time the charge drawn is measured and printed on every\n" "During this time the charge drawn is measured and printed on every\n"
"pulse the LTC4150 generates. A horizontal line in the table\n" "pulse the LTC4150 generates. A horizontal line in the table\n"
"separates values of different load levels. On the MSB-A2 the\n" "separates values of different load levels. On the MSB-A2 the\n"
"expected result per column is:\n" "expected result per column is:\n"
"\n" "\n"
" Charging: Should remain zero\n" " Charging: Should remain zero\n"
" Discharging: Should increase for every pulse\n" " Discharging: Should increase for every pulse\n"
" Average: Should be something between 60mA to 80mA\n" " Average: Should be something between 60mA to 80mA\n"
" Last Minute: Starts with 0 at boot up and is updated every 10s.\n" " Last Minute: Starts with 0 at boot up and is updated every 10s.\n"
" Should be higher for higher system load when looking\n" " Should be higher for higher system load when looking\n"
" at the last update for each load level.\n" " at the last update for each load level.\n"
" (Note: Not synchronized with load levels!)\n" " (Note: Not synchronized with load levels!)\n"
" Currently: Should be higher for higher system load. Might be\n" " Currently: Should be higher for higher system load. Might be\n"
" \"jumpy\" on 50% load, as this implemented by having\n" " \"jumpy\" on 50% load, as this implemented by having\n"
" one second of 100% load and one second of ~0% load\n" " one second of 100% load and one second of ~0% load\n"
" in turns.\n" " in turns.\n"
"\n" "\n"
"Hint: You'll want to look mostly at the rightmost column.\n" "Hint: You'll want to look mostly at the rightmost column.\n"
"Note: The test will repeat endlessly."); "Note: The test will repeat endlessly.\n");
LED0_OFF; LED0_OFF;
puts("+-------------------------------+-----------------------------------+\n" print_str("+-------------------------------+-----------------------------------+\n"
"| Total Transferred Charge [mC] | Current from Power Supply [mA] |\n" "| Total Transferred Charge [mC] | Current from Power Supply [mA] |\n"
"| Charging | Discharging | Average | Last Minute | Currently |\n" "| Charging | Discharging | Average | Last Minute | Currently |\n"
"+---------------+---------------+---------+-------------+-----------+"); "+---------------+---------------+---------+-------------+-----------+\n");
while (1) { while (1) {
/* Wait for the next pulse of the LTC4150 */ /* Wait for the next pulse of the LTC4150 */
@ -298,40 +225,40 @@ int main(void)
int32_t current; int32_t current;
if (change_of_load_level) { if (change_of_load_level) {
puts("+---------------+---------------+---------+-------------+-----------+"); print_str("+---------------+---------------+---------+-------------+-----------+\n");
change_of_load_level = 0; change_of_load_level = 0;
} }
/* Get & print total charge transferred */ /* Get & print total charge transferred */
if (ltc4150_charge(&ltc4150, &charged, &discharged)) { if (ltc4150_charge(&ltc4150, &charged, &discharged)) {
puts("ltc4150_charge() failed!"); print_str("ltc4150_charge() failed!\n");
return -1; return -1;
} }
puts_no_nl("| "); print_str("| ");
print_col_u32(charged, 13); print_col_u32_dec(charged, 13);
puts_no_nl(" | "); print_str(" | ");
print_col_u32(discharged, 13); print_col_u32_dec(discharged, 13);
puts_no_nl(" | "); print_str(" | ");
/* Get & print avg current */ /* Get & print avg current */
if (ltc4150_avg_current(&ltc4150, &avg_current)) { if (ltc4150_avg_current(&ltc4150, &avg_current)) {
puts("ltc4150_avg_current() failed!"); print_str("ltc4150_avg_current() failed!\n");
return -1; return -1;
} }
print_current(avg_current, 7); print_current(avg_current, 7);
puts_no_nl(" | "); print_str(" | ");
/* Get & print last minute current */ /* Get & print last minute current */
if (ltc4150_last_minute_charge(&ltc4150, &last_minute_data, if (ltc4150_last_minute_charge(&ltc4150, &last_minute_data,
&charged, &discharged) &charged, &discharged)
) { ) {
puts("ltc4150_last_minute_charge() failed!"); print_str("ltc4150_last_minute_charge() failed!\n");
return -1; return -1;
} }
current = (int32_t)discharged - (int32_t)charged; current = (int32_t)discharged - (int32_t)charged;
current /= 60; current /= 60;
print_col_i32(current, 11); print_col_s32_dec(current, 11);
puts_no_nl(" | "); print_str(" | ");
/* Calculate & print the current between the last two pulses */ /* Calculate & print the current between the last two pulses */
current = (int32_t)((test_data.now_usec - test_data.last_usec) / MS_PER_SEC); current = (int32_t)((test_data.now_usec - test_data.last_usec) / MS_PER_SEC);
@ -340,7 +267,7 @@ int main(void)
current = -current; current = -current;
} }
print_current(current, 9); print_current(current, 9);
puts(" |"); print_str(" |\n");
} }
return 0; return 0;