add webserver components, some webserver fices and improvements in templating, add real time history chart

This commit is contained in:
2020-01-07 00:21:27 +01:00
parent ed53611e0f
commit ef7866a29f
50 changed files with 3594 additions and 2 deletions
+8
View File
@@ -0,0 +1,8 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCDIRS
"src")
#set(COMPONENT_REQUIRES)
register_component()
+2
View File
@@ -0,0 +1,2 @@
General purpose, mostly platofrm-idependent utilities
that may be used by other components.
+3
View File
@@ -0,0 +1,3 @@
COMPONENT_SRCDIRS := src
COMPONENT_ADD_INCLUDEDIRS := include
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef BASE16_H_
#define BASE16_H_
#include <stdint.h>
#include <string.h>
/**
* Calculate length of base16-encoded data
* @param raw_len Raw data length
* @return Encoded string length (excluding NUL)
*/
static inline size_t base16_encoded_len(size_t raw_len) {
return (2 * raw_len);
}
/**
* Calculate maximum length of base16-decoded string
* @param encoded Encoded string
* @return Maximum length of raw data
*/
static inline size_t base16_decoded_max_len(const char *encoded) {
return ((strlen(encoded) + 1) / 2);
}
/**
* Base16-encode data
*
* The buffer must be the correct length for the encoded string. Use
* something like
*
* char buf[ base16_encoded_len ( len ) + 1 ];
*
* (the +1 is for the terminating NUL) to provide a buffer of the
* correct size.
*
* @param raw Raw data
* @param len Length of raw data
* @param encoded Buffer for encoded string
*/
extern void base16_encode(uint8_t *raw, size_t len, char *encoded);
/**
* Base16-decode data
*
* The buffer must be large enough to contain the decoded data. Use
* something like
*
* char buf[ base16_decoded_max_len ( encoded ) ];
*
* to provide a buffer of the correct size.
* @param encoded Encoded string
* @param raw Raw data
* @return Length of raw data, or negative error
*/
extern int base16_decode(const char *encoded, uint8_t *raw);
#endif /* BASE16_H_ */
@@ -0,0 +1,131 @@
/**
* TODO file description
*
* Created on 2019/09/13.
*/
#ifndef CSPEMU_DATETIME_H
#define CSPEMU_DATETIME_H
#include <stdbool.h>
#include <stdint.h>
enum weekday {
MONDAY = 1,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
};
_Static_assert(MONDAY==1, "enum weekday numbering Mon");
_Static_assert(SUNDAY==7, "enum weekday numbering Sun");
enum month {
JANUARY = 1,
FEBRUARY,
MARCH,
APRIL,
MAY,
JUNE,
JULY,
AUGUST,
SEPTEMBER,
OCTOBER,
NOVEMBER,
DECEMBER
};
_Static_assert(JANUARY==1, "enum month numbering Jan");
_Static_assert(DECEMBER==12, "enum month numbering Dec");
/** Abbreviated weekday names */
extern const char *DT_WKDAY_NAMES[];
/** Full-length weekday names */
extern const char *DT_WKDAY_NAMES_FULL[];
/** Abbreviated month names */
extern const char *DT_MONTH_NAMES[];
/** Full-length month names */
extern const char *DT_MONTH_NAMES_FULL[];
typedef struct datetime {
uint16_t year;
enum month month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
enum weekday wkday; // 1=monday
} datetime_t;
// Templates for printf
#define DT_FORMAT_DATE "%d/%d/%d"
#define DT_SUBS_DATE(dt) (dt).year, (dt).month, (dt).day
#define DT_FORMAT_TIME "%d:%02d:%02d"
#define DT_SUBS_TIME(dt) (dt).hour, (dt).min, (dt).sec
#define DT_FORMAT_DATE_WK DT_FORMAT_WK " " DT_FORMAT_DATE
#define DT_SUBS_DATE_WK(dt) DT_SUBS_WK(dt), DT_SUBS_DATE(dt)
#define DT_FORMAT_WK "%s"
#define DT_SUBS_WK(dt) DT_WKDAY_NAMES[(dt).wkday]
#define DT_FORMAT_DATE_TIME DT_FORMAT_DATE " " DT_FORMAT_TIME
#define DT_SUBS_DATE_TIME(dt) DT_SUBS_DATE(dt), DT_SUBS_TIME(dt)
#define DT_FORMAT DT_FORMAT_DATE_WK " " DT_FORMAT_TIME
#define DT_SUBS(dt) DT_SUBS_DATE_WK(dt), DT_SUBS_TIME(dt)
// base century for two-digit year conversions
#define DT_CENTURY 2000
// start year for weekday computation
#define DT_START_YEAR 2019
// January 1st weekday of DT_START_YEAR
#define DT_START_WKDAY TUESDAY
// max date supported by 2-digit year RTC counters (it can't check Y%400==0 with only two digits)
#define DT_END_YEAR 2399
typedef union __attribute__((packed)) {
struct __attribute__((packed)) {
uint8_t ones : 4;
uint8_t tens : 4;
};
uint8_t byte;
} bcd_t;
_Static_assert(sizeof(bcd_t) == 1, "Bad bcd_t len");
/** Check if a year is leap */
static inline bool is_leap_year(int year)
{
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
/**
* Check if a datetime could be valid (ignores leap years)
*
* @param[in] dt
* @return basic validations passed
*/
bool datetime_is_valid(const datetime_t *dt);
/**
* Set weekday based on a date in a given datetime
*
* @param[in,out] dt
* @return success
*/
bool datetime_set_weekday(datetime_t *dt);
/**
* Get weekday for given a date
*
* @param year - year number
* @param month - 1-based month number
* @param day - 1-based day number
* @return weekday
*/
enum weekday date_weekday(uint16_t year, enum month month, uint8_t day);
#endif //CSPEMU_DATETIME_H
@@ -0,0 +1,19 @@
/**
* @file
* @brief A simple way of dumping memory to a hex output
*
* \addtogroup Hexdump
*
* @{
*/
#include <stdio.h>
#define HEX_DUMP_LINE_BUFF_SIZ 16
extern void hex_dump(FILE * fp,void *src, int len);
extern void hex_dump_buff_line(FILE *fp, int addr_size, unsigned pos, char *line, unsigned len);
/**
* }@
*/
@@ -0,0 +1,101 @@
/**
* General purpose, platform agnostic, reusable utils
*/
#ifndef COMMON_UTILS_UTILS_H
#define COMMON_UTILS_UTILS_H
#include <stdbool.h>
#include <stdint.h>
#include "base16.h"
#include "datetime.h"
#include "hexdump.h"
/** Convert a value to BCD struct */
static inline bcd_t num2bcd(uint8_t value)
{
return (bcd_t) {.ones=value % 10, .tens=value / 10};
}
/** Convert unpacked BCD to value */
static inline uint8_t bcd2num(uint8_t tens, uint8_t ones)
{
return tens * 10 + ones;
}
/**
* Append to a buffer.
*
* In case the buffer capacity is reached, it is reverted to the previous length (by replacing the NUL byte)
* and NULL is returned.
*
* @param buf - buffer position to append at; if NULL is given, the function immediately returns NULL.
* @param appended - string to append
* @param pcap - pointer to a capacity variable
* @return the new end of the string (null byte); use as 'buf' for following appends
*/
char *append(char *buf, const char *appended, size_t *pcap);
/**
* Append, re-allocating as needed.
*
* @param head - pointer to heap buffer head, may be updated on realloc.
* @param size - string size pointer, may be updated on realloc.
* @param appended - string to append
* @return false on alloc error
*/
bool append_realloc(char **head, size_t *cap, const char *appended);
/**
* Test if a file descriptor is valid (e.g. when cleaning up after a failed select)
*
* @param fd - file descriptor number
* @return is valid
*/
bool fd_is_valid(int fd);
/**
* parse user-provided string as boolean
*
* @param str
* @return is true
*/
bool parse_boolean_arg(const char *str);
/**
* complementary function to parse_boolean_arg() that matches strings
* meaning 'false'. Can be used together with the positive version
* in case there can be other values as well.
*
* @param str
* @return is false
*/
bool parse_boolean_arg_false(const char *str);
/** Check equality of two strings; returns bool */
#define streq(a, b) (strcmp((const char*)(a), (const char*)(b)) == 0)
/** Check prefix equality of two strings; returns bool */
#define strneq(a, b, n) (strncmp((const char*)(a), (const char*)(b), (n)) == 0)
/** Check if a string starts with a substring; returns bool */
#define strstarts(a, b) strneq((a), (b), (int)strlen((b)))
#ifndef MIN
/** Get min of two numbers */
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#endif
#ifndef MAX
/** Get max of two values */
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef STR
#define STR_HELPER(x) #x
/** Stringify a token */
#define STR(x) STR_HELPER(x)
#endif
#endif //COMMON_UTILS_UTILS_H
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <esp_log.h>
static const char *TAG = "base16";
void base16_encode(uint8_t *raw, size_t len, char *encoded) {
uint8_t *raw_bytes = raw;
char *encoded_bytes = encoded;
size_t remaining = len;
for (; remaining--; encoded_bytes += 2)
snprintf(encoded_bytes, 3, "%02X", *(raw_bytes++));
}
int base16_decode(const char *encoded, uint8_t *raw) {
const char *encoded_bytes = encoded;
uint8_t *raw_bytes = raw;
char buf[3];
char *endp;
size_t len;
while (encoded_bytes[0]) {
if (!encoded_bytes[1]) {
ESP_LOGE(TAG, "Base16-encoded string \"%s\" has invalid length\n",
encoded);
return -22;
}
memcpy(buf, encoded_bytes, 2);
buf[2] = '\0';
*(raw_bytes++) = strtoul(buf, &endp, 16);
if (*endp != '\0') {
ESP_LOGE(TAG,"Base16-encoded string \"%s\" has invalid byte \"%s\"\n",
encoded, buf);
return -22;
}
encoded_bytes += 2;
}
len = (raw_bytes - raw);
return (len);
}
@@ -0,0 +1,85 @@
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include "common_utils/utils.h"
char *append(char *buf, const char *appended, size_t *pcap)
{
char c;
char *buf0 = buf;
size_t cap = *pcap;
if (buf0 == NULL) return NULL;
if (appended == NULL || appended[0] == 0) return buf0;
if (*pcap < strlen(appended)+1) {
return NULL;
}
while (cap > 1 && 0 != (c = *appended++)) {
*buf++ = c;
cap--;
}
assert(cap > 0);
*pcap = cap;
*buf = 0;
return buf;
}
bool append_realloc(char **head, size_t *cap, const char *appended) {
if (!head) return NULL;
if (!*head) return NULL;
if (!cap) return NULL;
if (!appended) return NULL;
size_t cursize = strlen(*head);
size_t needed = strlen(appended) + 1;
size_t remains = *cap - cursize;
if (remains < needed) {
size_t need_extra = needed - remains;
size_t newsize = *cap + need_extra;
char *new = realloc(*head, newsize);
if (!new) return false;
*head = new;
*cap = newsize;
}
strcpy(*head + cursize, appended);
return true;
}
bool fd_is_valid(int fd)
{
return fcntl(fd, F_GETFD) != -1 || errno != EBADF;
}
bool parse_boolean_arg(const char *str)
{
if (0 == strcasecmp(str, "on")) return true;
if (0 == strcmp(str, "1")) return true;
if (0 == strcasecmp(str, "yes")) return true;
if (0 == strcasecmp(str, "enable")) return true;
if (0 == strcasecmp(str, "en")) return true;
if (0 == strcasecmp(str, "y")) return true;
if (0 == strcasecmp(str, "a")) return true;
return false;
}
bool parse_boolean_arg_false(const char *str)
{
if (0 == strcasecmp(str, "off")) return true;
if (0 == strcmp(str, "0")) return true;
if (0 == strcasecmp(str, "no")) return true;
if (0 == strcasecmp(str, "disable")) return true;
if (0 == strcasecmp(str, "dis")) return true;
if (0 == strcasecmp(str, "n")) return true;
return false;
}
+110
View File
@@ -0,0 +1,110 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "common_utils/datetime.h"
const char *DT_WKDAY_NAMES[] = {
[MONDAY] = "Mon",
[TUESDAY] = "Tue",
[WEDNESDAY] = "Wed",
[THURSDAY] = "Thu",
[FRIDAY] = "Fri",
[SATURDAY] = "Sat",
[SUNDAY] = "Sun"
};
const char *DT_WKDAY_NAMES_FULL[] = {
[MONDAY] = "Monday",
[TUESDAY] = "Tuesday",
[WEDNESDAY] = "Wednesday",
[THURSDAY] = "Thursday",
[FRIDAY] = "Friday",
[SATURDAY] = "Saturday",
[SUNDAY] = "Sunday"
};
const char *DT_MONTH_NAMES[] = {
[JANUARY] = "Jan",
[FEBRUARY] = "Feb",
[MARCH] = "Mar",
[APRIL] = "Apr",
[MAY] = "May",
[JUNE] = "Jun",
[JULY] = "Jul",
[AUGUST] = "Aug",
[SEPTEMBER] = "Sep",
[OCTOBER] = "Oct",
[NOVEMBER] = "Nov",
[DECEMBER] = "Dec"
};
const char *DT_MONTH_NAMES_FULL[] = {
[JANUARY] = "January",
[FEBRUARY] = "February",
[MARCH] = "March",
[APRIL] = "April",
[MAY] = "May",
[JUNE] = "June",
[JULY] = "July",
[AUGUST] = "August",
[SEPTEMBER] = "September",
[OCTOBER] = "October",
[NOVEMBER] = "November",
[DECEMBER] = "December"
};
static const uint16_t MONTH_LENGTHS[] = { /* 1-based, normal year */
[JANUARY]=31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
_Static_assert(sizeof(MONTH_LENGTHS) / sizeof(uint16_t) == 13, "Months array length");
static const uint16_t MONTH_YEARDAYS[] = { /* 1-based */
[JANUARY]=0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 // // days until 1st of month
};
_Static_assert(sizeof(MONTH_YEARDAYS) / sizeof(uint16_t) == 13, "Months array length");
_Static_assert(MONDAY < SUNDAY, "Weekday ordering");
bool datetime_is_valid(const datetime_t *dt)
{
if (dt == NULL) return false;
// check month first to avoid out-of-bounds read from the MONTH_LENGTHS table
if (!(dt->month >= JANUARY && dt->month <= DECEMBER)) return false;
int monthlen = MONTH_LENGTHS[dt->month];
if (dt->month == FEBRUARY && is_leap_year(dt->year)) {
monthlen = 29;
}
return dt->sec < 60 &&
dt->min < 60 &&
dt->hour < 24 &&
dt->wkday >= MONDAY &&
dt->wkday <= SUNDAY &&
dt->year >= DT_START_YEAR &&
dt->year <= DT_END_YEAR &&
dt->day >= 1 &&
dt->day <= monthlen;
}
bool datetime_set_weekday(datetime_t *dt)
{
dt->wkday = MONDAY; // prevent the validator func erroring out on invalid weekday
if (!datetime_is_valid(dt)) return false;
dt->wkday = date_weekday(dt->year, dt->month, dt->day);
return true;
}
enum weekday date_weekday(uint16_t year, enum month month, uint8_t day)
{
uint16_t days = (DT_START_WKDAY - MONDAY) + (year - DT_START_YEAR) * 365 + MONTH_YEARDAYS[month] + (day - 1);
for (uint16_t i = DT_START_YEAR; i <= year; i++) {
if (is_leap_year(i) && (i < year || month > FEBRUARY)) days++;
}
return MONDAY + days % 7;
}
+72
View File
@@ -0,0 +1,72 @@
/*
* util.c
*
* Created on: Aug 12, 2009
* Author: johan
*/
// adapted from libgomspace
#include <string.h>
#include <stdio.h>
#include "common_utils/hexdump.h"
//! Dump memory to debugging output
/**
* Dumps a chunk of memory to the screen
*/
void hex_dump(FILE * fp, void *src, int len) {
int i, j=0, k;
char text[17];
text[16] = '\0';
//printf("Hex dump:\r\n");
fprintf(fp, "%p : ", src);
for(i=0; i<len; i++) {
j++;
fprintf(fp, "%02X ", ((volatile unsigned char *)src)[i]);
if(j == 8)
fputc(' ', fp);
if(j == 16) {
j = 0;
memcpy(text, &((char *)src)[i-15], 16);
for(k=0; k<16; k++) {
if((text[k] < 32) || (text[k] > 126)) {
text[k] = '.';
}
}
fprintf(fp, " |%s|\n\r", text);
if(i<len-1) {
fprintf(fp, "%p : ", src+i+1);
}
}
}
if (i % 16)
fprintf(fp, "\r\n");
}
void hex_dump_buff_line(FILE *fp, int addr_size, unsigned pos, char *line, unsigned len)
{
unsigned i;
fprintf(fp, "%0*x", addr_size, pos);
for (i = 0; i < HEX_DUMP_LINE_BUFF_SIZ; i++)
{
if (!(i % 8))
fputc(' ', fp);
if (i < len)
fprintf(fp, " %02x", (unsigned char)line[i]);
else
fputs(" ", fp);
}
fputs(" |", fp);
for (i = 0; i < HEX_DUMP_LINE_BUFF_SIZ && i < len; i++)
{
if (line[i] >= 32 && line[i] <= 126)
fprintf(fp, "%c", (unsigned char)line[i]);
else
fputc('.', fp);
}
fputs("|\r\n", fp);
}