mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
updated uuid libs
This commit is contained in:
@@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* EMS-ESP - https://github.com/emsesp/EMS-ESP
|
|
||||||
* Copyright 2020 Paul Derbyshire
|
|
||||||
*
|
|
||||||
* 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 3 of the License, or
|
|
||||||
* (at your option) 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <uuid/common.h>
|
|
||||||
|
|
||||||
// #ifdef ESP8266
|
|
||||||
// #include <pgmspace.h>
|
|
||||||
// #else
|
|
||||||
// #include <avr/pgmspace.h>
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
namespace uuid {
|
|
||||||
|
|
||||||
// On ESP8266, pgm_read_byte() already takes care of 4-byte alignment, and
|
|
||||||
// memcpy_P(s, p, 4) makes 4 calls to pgm_read_byte() anyway, so don't bother
|
|
||||||
// optimizing for 4-byte alignment here.
|
|
||||||
|
|
||||||
// class __FlashStringHelper;
|
|
||||||
|
|
||||||
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b) {
|
|
||||||
const char * aa = reinterpret_cast<const char *>(a);
|
|
||||||
const char * bb = reinterpret_cast<const char *>(b);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
uint8_t ca = pgm_read_byte(aa);
|
|
||||||
uint8_t cb = pgm_read_byte(bb);
|
|
||||||
if (ca != cb)
|
|
||||||
return (int)ca - (int)cb;
|
|
||||||
if (ca == 0)
|
|
||||||
return 0;
|
|
||||||
aa++;
|
|
||||||
bb++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b, size_t n) {
|
|
||||||
const char * aa = reinterpret_cast<const char *>(a);
|
|
||||||
const char * bb = reinterpret_cast<const char *>(b);
|
|
||||||
|
|
||||||
while (n > 0) {
|
|
||||||
uint8_t ca = pgm_read_byte(aa);
|
|
||||||
uint8_t cb = pgm_read_byte(bb);
|
|
||||||
if (ca != cb)
|
|
||||||
return (int)ca - (int)cb;
|
|
||||||
if (ca == 0)
|
|
||||||
return 0;
|
|
||||||
aa++;
|
|
||||||
bb++;
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uuid
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
// added by proddy, modified
|
// added by proddy for EMS-ESP
|
||||||
static uint64_t now_millis = 0;
|
static uint64_t now_millis = 0;
|
||||||
|
|
||||||
// returns system uptime in seconds
|
// returns system uptime in seconds
|
||||||
|
|||||||
@@ -26,6 +26,20 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#if __has_include(<mutex>) && (!defined(_GLIBCXX_MUTEX) || defined(_GLIBCXX_HAS_GTHREADS))
|
||||||
|
#define UUID_COMMON_STD_MUTEX_AVAILABLE 1
|
||||||
|
#else
|
||||||
|
#define UUID_COMMON_STD_MUTEX_AVAILABLE 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) || UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#define UUID_COMMON_THREAD_SAFE 1
|
||||||
|
#else
|
||||||
|
#define UUID_COMMON_THREAD_SAFE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common utilities.
|
* Common utilities.
|
||||||
*
|
*
|
||||||
@@ -35,31 +49,15 @@
|
|||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String compare two flash strings
|
* Thread-safe status of the library.
|
||||||
*
|
*
|
||||||
* The flash string must be stored with appropriate alignment for
|
* @since 1.1.2
|
||||||
* reading it on the platform.
|
|
||||||
*
|
|
||||||
* @param[in] a Pointer to string stored in flash.
|
|
||||||
* @param[in] b Pointer to string stored in flash.
|
|
||||||
* @param[in] n optional max length
|
|
||||||
* @return 0 for match, otherwise diff
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
*/
|
||||||
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b);
|
#if UUID_COMMON_THREAD_SAFE
|
||||||
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b, size_t n);
|
static constexpr bool thread_safe = true;
|
||||||
|
#else
|
||||||
/**
|
static constexpr bool thread_safe = false;
|
||||||
* Read a string from flash and convert it to a std::string.
|
#endif
|
||||||
*
|
|
||||||
* The flash string must be stored with appropriate alignment for
|
|
||||||
* reading it on the platform.
|
|
||||||
*
|
|
||||||
* @param[in] flash_str Pointer to string stored in flash.
|
|
||||||
* @return A string copy of the flash string.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
std::string read_flash_string(const __FlashStringHelper * flash_str);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append to a std::string by printing a Printable object.
|
* Append to a std::string by printing a Printable object.
|
||||||
@@ -104,8 +102,8 @@ void loop();
|
|||||||
*/
|
*/
|
||||||
uint64_t get_uptime_ms();
|
uint64_t get_uptime_ms();
|
||||||
|
|
||||||
uint32_t get_uptime(); // added by proddy
|
uint32_t get_uptime(); // added by proddy for EMS-ESP
|
||||||
uint32_t get_uptime_sec(); // added by proddy
|
uint32_t get_uptime_sec(); // added by proddy for EMS-ESP
|
||||||
|
|
||||||
void set_uptime();
|
void set_uptime();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-console - Microcontroller console shell
|
* uuid-console - Microcontroller console shell
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2022 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a std::copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifndef __cpp_lib_make_unique
|
#ifndef __cpp_lib_make_unique
|
||||||
@@ -43,23 +44,39 @@ namespace uuid {
|
|||||||
namespace console {
|
namespace console {
|
||||||
|
|
||||||
void Commands::add_command(const string_vector & name, command_function function) {
|
void Commands::add_command(const string_vector & name, command_function function) {
|
||||||
add_command(0, 0, name, string_vector{}, function, nullptr);
|
add_command(0, 0, 0, name, string_vector{}, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function) {
|
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function) {
|
||||||
add_command(0, 0, name, arguments, function, nullptr);
|
add_command(0, 0, 0, name, arguments, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function, argument_completion_function arg_function) {
|
void Commands::add_command(const string_vector & name, const string_vector & arguments, command_function function, argument_completion_function arg_function) {
|
||||||
add_command(0, 0, name, arguments, function, arg_function);
|
add_command(0, 0, 0, name, arguments, function, arg_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Commands::add_command(unsigned int context, const string_vector & name, command_function function) {
|
||||||
|
add_command(context, 0, 0, name, string_vector{}, function, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Commands::add_command(unsigned int context, const string_vector & name, const string_vector & arguments, command_function function) {
|
||||||
|
add_command(context, 0, 0, name, arguments, function, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Commands::add_command(unsigned int context,
|
||||||
|
const string_vector & name,
|
||||||
|
const string_vector & arguments,
|
||||||
|
command_function function,
|
||||||
|
argument_completion_function arg_function) {
|
||||||
|
add_command(context, 0, 0, name, arguments, function, arg_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, command_function function) {
|
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, command_function function) {
|
||||||
add_command(context, flags, name, string_vector{}, function, nullptr);
|
add_command(context, flags, 0, name, string_vector{}, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, const string_vector & arguments, command_function function) {
|
void Commands::add_command(unsigned int context, unsigned int flags, const string_vector & name, const string_vector & arguments, command_function function) {
|
||||||
add_command(context, flags, name, arguments, function, nullptr);
|
add_command(context, flags, 0, name, arguments, function, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::add_command(unsigned int context,
|
void Commands::add_command(unsigned int context,
|
||||||
@@ -68,34 +85,30 @@ void Commands::add_command(unsigned int context,
|
|||||||
const string_vector & arguments,
|
const string_vector & arguments,
|
||||||
command_function function,
|
command_function function,
|
||||||
argument_completion_function arg_function) {
|
argument_completion_function arg_function) {
|
||||||
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, name, arguments, function, arg_function));
|
add_command(context, flags, 0, name, arguments, function, arg_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Commands::add_command(unsigned int context, unsigned int flags, unsigned int not_flags, const string_vector & name, command_function function) {
|
||||||
// added by proddy
|
add_command(context, flags, not_flags, name, string_vector{}, function, nullptr);
|
||||||
// note we should really iterate and free up the lambda code and any flashstrings
|
|
||||||
void Commands::remove_all_commands() {
|
|
||||||
commands_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// added by proddy
|
void Commands::add_command(unsigned int context,
|
||||||
// note we should really iterate and free up the lambda code and any flashstrings
|
unsigned int flags,
|
||||||
void Commands::remove_context_commands(unsigned int context) {
|
unsigned int not_flags,
|
||||||
commands_.erase(context);
|
const string_vector & name,
|
||||||
|
const string_vector & arguments,
|
||||||
|
command_function function) {
|
||||||
|
add_command(context, flags, not_flags, name, arguments, function, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
void Commands::add_command(unsigned int context,
|
||||||
auto commands = commands_.equal_range(context);
|
unsigned int flags,
|
||||||
for (auto command_it = commands.first; command_it != commands.second; command_it++) {
|
unsigned int not_flags,
|
||||||
shell.printf("Got: ");
|
const string_vector & name,
|
||||||
for (auto flash_name : command_it->second.name_) {
|
const string_vector & arguments,
|
||||||
shell.printf("%s ", read_flash_string(flash_name).c_str());
|
command_function function,
|
||||||
}
|
argument_completion_function arg_function) {
|
||||||
shell.println();
|
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, not_flags, name, arguments, function, arg_function));
|
||||||
}
|
|
||||||
|
|
||||||
size_t nun = commands_.erase(context);
|
|
||||||
shell.printfln("Erased %d commands", nun);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Execution Commands::execute_command(Shell & shell, CommandLine && command_line) {
|
Commands::Execution Commands::execute_command(Shell & shell, CommandLine && command_line) {
|
||||||
@@ -106,7 +119,7 @@ Commands::Execution Commands::execute_command(Shell & shell, CommandLine && comm
|
|||||||
result.error = nullptr;
|
result.error = nullptr;
|
||||||
|
|
||||||
if (commands.exact.empty()) {
|
if (commands.exact.empty()) {
|
||||||
result.error = F("Command not found");
|
result.error = "Command not found";
|
||||||
} else if (commands.exact.count(longest->first) == 1) {
|
} else if (commands.exact.count(longest->first) == 1) {
|
||||||
auto & command = longest->second;
|
auto & command = longest->second;
|
||||||
std::vector<std::string> arguments;
|
std::vector<std::string> arguments;
|
||||||
@@ -117,16 +130,16 @@ Commands::Execution Commands::execute_command(Shell & shell, CommandLine && comm
|
|||||||
command_line.reset();
|
command_line.reset();
|
||||||
|
|
||||||
if (commands.partial.upper_bound(longest->first) != commands.partial.end() && !arguments.empty()) {
|
if (commands.partial.upper_bound(longest->first) != commands.partial.end() && !arguments.empty()) {
|
||||||
result.error = F("Command not found");
|
result.error = "Command not found";
|
||||||
} else if (arguments.size() < command->minimum_arguments()) {
|
} else if (arguments.size() < command->minimum_arguments()) {
|
||||||
result.error = F("Not enough arguments for command");
|
result.error = "Not enough arguments for command";
|
||||||
} else if (arguments.size() > command->maximum_arguments()) {
|
} else if (arguments.size() > command->maximum_arguments()) {
|
||||||
result.error = F("Too many arguments for command");
|
result.error = "Too many arguments for command";
|
||||||
} else {
|
} else {
|
||||||
command->function_(shell, arguments);
|
command->function_(shell, arguments);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.error = F("Fatal error (multiple commands found)");
|
result.error = "Fatal error (multiple commands found)";
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -145,7 +158,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
|
|||||||
|
|
||||||
for (size_t length = 0; all_match && length < shortest_match; length++) {
|
for (size_t length = 0; all_match && length < shortest_match; length++) {
|
||||||
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
||||||
if ((*std::next(first.begin(), length)) != (*std::next(command_it->second->name_.begin(), length))) {
|
if (*std::next(first.begin(), length) != *std::next(command_it->second->name_.begin(), length)) {
|
||||||
all_match = false;
|
all_match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -158,7 +171,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
|
|||||||
|
|
||||||
auto name_it = first.begin();
|
auto name_it = first.begin();
|
||||||
for (size_t i = 0; i < component_prefix; i++) {
|
for (size_t i = 0; i < component_prefix; i++) {
|
||||||
longest_name.push_back(std::move((*name_it)));
|
longest_name.push_back(std::move(*name_it));
|
||||||
name_it++;
|
name_it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,11 +186,17 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
|
|||||||
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
for (auto command_it = std::next(commands.begin()); command_it != commands.end(); command_it++) {
|
||||||
// This relies on the null terminator character limiting the
|
// This relies on the null terminator character limiting the
|
||||||
// length before it becomes longer than any of the strings
|
// length before it becomes longer than any of the strings
|
||||||
if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length)
|
|
||||||
!= pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
|
if (pgm_read_byte(first + length) != pgm_read_byte((*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
|
||||||
all_match = false;
|
all_match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (pgm_read_byte(reinterpret_cast<PGM_P>(first) + length)
|
||||||
|
// != pgm_read_byte(reinterpret_cast<PGM_P>(*std::next(command_it->second->name_.begin(), component_prefix)) + length)) {
|
||||||
|
// all_match = false;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_match) {
|
if (all_match) {
|
||||||
@@ -223,12 +242,16 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
|
|
||||||
auto match = commands.partial.begin();
|
auto match = commands.partial.begin();
|
||||||
size_t count;
|
size_t count;
|
||||||
|
bool multiple_matches;
|
||||||
if (match != commands.partial.end()) {
|
if (match != commands.partial.end()) {
|
||||||
count = commands.partial.count(match->first);
|
count = commands.partial.count(match->first);
|
||||||
|
multiple_matches =
|
||||||
|
count > 1 || commands.partial.size() > count || (!commands.exact.empty() && commands.exact.rbegin()->first >= command_line.total_size());
|
||||||
} else if (!commands.exact.empty()) {
|
} else if (!commands.exact.empty()) {
|
||||||
// Use prev() because both iterators must be forwards
|
// Use prev() because both iterators must be forwards
|
||||||
match = std::prev(commands.exact.end());
|
match = std::prev(commands.exact.end());
|
||||||
count = commands.exact.count(match->first);
|
count = commands.exact.count(match->first);
|
||||||
|
multiple_matches = false;
|
||||||
} else {
|
} else {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -237,20 +260,13 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
std::vector<std::string> temp_command_name;
|
std::vector<std::string> temp_command_name;
|
||||||
std::multimap<size_t, const Command *>::iterator temp_command_it;
|
std::multimap<size_t, const Command *>::iterator temp_command_it;
|
||||||
|
|
||||||
if (commands.partial.size() > 1 && (commands.exact.empty() || command_line.total_size() > commands.exact.begin()->second->name_.size())) {
|
if (multiple_matches && (commands.exact.empty() || command_line.total_size() > commands.exact.begin()->second->name_.size())) {
|
||||||
// There are multiple partial matching commands, find the longest common prefix
|
// There are multiple matching commands, find the longest common prefix
|
||||||
bool whole_components = find_longest_common_prefix(commands.partial, temp_command_name);
|
bool whole_components = find_longest_common_prefix(commands.partial, temp_command_name);
|
||||||
|
|
||||||
if (count == 1 && whole_components && temp_command_name.size() == match->first) {
|
// Construct a temporary command with the longest common prefix to use as the replacement
|
||||||
// If the longest common prefix is the same as the single shortest matching command
|
|
||||||
// then there's no need for a temporary command, but add a trailing space because
|
|
||||||
// there are longer commands that matched.
|
|
||||||
temp_command_name.clear();
|
|
||||||
result.replacement.trailing_space = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!temp_command_name.empty() && command_line.total_size() <= temp_command_name.size()) {
|
if (!temp_command_name.empty() && command_line.total_size() <= temp_command_name.size()) {
|
||||||
temp_command = std::make_unique<Command>(0, string_vector{}, string_vector{}, nullptr, nullptr);
|
temp_command = std::make_unique<Command>(0, 0, string_vector{}, string_vector{}, nullptr, nullptr);
|
||||||
count = 1;
|
count = 1;
|
||||||
match = commands.partial.end();
|
match = commands.partial.end();
|
||||||
result.replacement.trailing_space = whole_components;
|
result.replacement.trailing_space = whole_components;
|
||||||
@@ -261,12 +277,12 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count == 1 && !temp_command) {
|
if (count == 1 && !multiple_matches) {
|
||||||
// Construct a replacement string for a single matching command
|
// Construct a replacement string for a single matching command
|
||||||
auto & matching_command = match->second;
|
auto & matching_command = match->second;
|
||||||
|
|
||||||
for (auto & name : matching_command->name_) {
|
for (auto & name : matching_command->name_) {
|
||||||
result.replacement->push_back(std::move((name)));
|
result.replacement->push_back(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command_line.total_size() > result.replacement->size()
|
if (command_line.total_size() > result.replacement->size()
|
||||||
@@ -290,7 +306,8 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto potential_arguments = matching_command->arg_function_ ? matching_command->arg_function_(shell, arguments) : std::vector<std::string>{};
|
auto potential_arguments =
|
||||||
|
matching_command->arg_function_ ? matching_command->arg_function_(shell, arguments, last_argument) : std::vector<std::string>{};
|
||||||
|
|
||||||
// Remove arguments that can't match
|
// Remove arguments that can't match
|
||||||
if (!command_line.trailing_space) {
|
if (!command_line.trailing_space) {
|
||||||
@@ -340,7 +357,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
remaining_help.escape_initial_parameters();
|
remaining_help.escape_initial_parameters();
|
||||||
|
|
||||||
for (auto it = std::next(matching_command->arguments_.cbegin(), current_args_count); it != matching_command->arguments_.cend(); it++) {
|
for (auto it = std::next(matching_command->arguments_.cbegin(), current_args_count); it != matching_command->arguments_.cend(); it++) {
|
||||||
remaining_help->push_back(std::move((*it)));
|
remaining_help->push_back(std::move(*it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,13 +383,13 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
// Add a space because there are more arguments for this command
|
// Add a space because there are more arguments for this command
|
||||||
result.replacement.trailing_space = true;
|
result.replacement.trailing_space = true;
|
||||||
}
|
}
|
||||||
} else if (count > 1 || temp_command) {
|
} else if (count != 0) {
|
||||||
// Provide help for all of the potential commands
|
// Provide help for all of the potential commands
|
||||||
for (auto command_it = commands.partial.begin(); command_it != commands.partial.end(); command_it++) {
|
for (auto command_it = commands.all.begin(); command_it != commands.all.end(); command_it++) {
|
||||||
CommandLine help;
|
CommandLine help;
|
||||||
|
|
||||||
auto line_it = command_line->cbegin();
|
auto line_it = command_line->cbegin();
|
||||||
auto flash_name_it = command_it->second->name_.cbegin();
|
auto flash_name_it = (*command_it)->name_.cbegin();
|
||||||
|
|
||||||
if (temp_command) {
|
if (temp_command) {
|
||||||
// Skip parts of the command name/line when the longest common prefix was used
|
// Skip parts of the command name/line when the longest common prefix was used
|
||||||
@@ -381,14 +398,19 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
skip--;
|
skip--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exact match that is shorter than the replacement
|
||||||
|
if (flash_name_it + skip > (*command_it)->name_.cend()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
flash_name_it += skip;
|
flash_name_it += skip;
|
||||||
while (line_it != command_line->cend()) {
|
while (line_it != command_line->cend()) {
|
||||||
line_it++;
|
line_it++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; flash_name_it != command_it->second->name_.cend(); flash_name_it++) {
|
for (; flash_name_it != (*command_it)->name_.cend(); flash_name_it++) {
|
||||||
std::string name = (*flash_name_it);
|
std::string name = *flash_name_it;
|
||||||
|
|
||||||
// Skip parts of the command name that match the command line
|
// Skip parts of the command name that match the command line
|
||||||
if (line_it != command_line->cend()) {
|
if (line_it != command_line->cend()) {
|
||||||
@@ -404,22 +426,22 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
|
|||||||
|
|
||||||
help.escape_initial_parameters();
|
help.escape_initial_parameters();
|
||||||
|
|
||||||
for (auto argument : command_it->second->arguments_) {
|
for (auto argument : (*command_it)->arguments_) {
|
||||||
// Skip parts of the command arguments that exist in the command line
|
// Skip parts of the command arguments that exist in the command line
|
||||||
if (line_it != command_line->cend()) {
|
if (line_it != command_line->cend()) {
|
||||||
line_it++;
|
line_it++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
help->push_back(std::move((argument)));
|
help->push_back(argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.help.push_back(std::move(help));
|
result.help.push_back(std::move(help));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count > 1 && !commands.exact.empty()) {
|
if (multiple_matches && !commands.exact.empty() && result.replacement.total_size() == 0) {
|
||||||
// Try to add a space to exact matches
|
// Try to add a space to exact matches if there's no other partial match replacement
|
||||||
auto longest = commands.exact.crbegin();
|
auto longest = commands.exact.crbegin();
|
||||||
|
|
||||||
if (commands.exact.count(longest->first) == 1) {
|
if (commands.exact.count(longest->first) == 1) {
|
||||||
@@ -449,7 +471,7 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
|
|||||||
bool match = true;
|
bool match = true;
|
||||||
bool exact = true;
|
bool exact = true;
|
||||||
|
|
||||||
if (!shell.has_flags(command.flags_)) {
|
if (!shell.has_flags(command.flags_, command.not_flags_)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +479,7 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
|
|||||||
auto line_it = command_line->cbegin();
|
auto line_it = command_line->cbegin();
|
||||||
|
|
||||||
for (; name_it != command.name_.cend() && line_it != command_line->cend(); name_it++, line_it++) {
|
for (; name_it != command.name_.cend() && line_it != command_line->cend(); name_it++, line_it++) {
|
||||||
std::string name = (*name_it);
|
std::string name = *name_it;
|
||||||
size_t found = name.rfind(*line_it, 0);
|
size_t found = name.rfind(*line_it, 0);
|
||||||
|
|
||||||
if (found == std::string::npos) {
|
if (found == std::string::npos) {
|
||||||
@@ -491,41 +513,21 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
|
|||||||
} else {
|
} else {
|
||||||
commands.partial.emplace(command.name_.size(), &command);
|
commands.partial.emplace(command.name_.size(), &command);
|
||||||
}
|
}
|
||||||
|
commands.all.push_back(&command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Commands::for_each_available_command(Shell & shell, apply_function f) const {
|
|
||||||
auto commands = commands_.equal_range(shell.context());
|
|
||||||
|
|
||||||
for (auto command_it = commands.first; command_it != commands.second; command_it++) {
|
|
||||||
if (shell.has_flags(command_it->second.flags_)) {
|
|
||||||
std::vector<std::string> name;
|
|
||||||
std::vector<std::string> arguments;
|
|
||||||
|
|
||||||
name.reserve(command_it->second.name_.size());
|
|
||||||
for (auto flash_name : command_it->second.name_) {
|
|
||||||
name.push_back(std::move((flash_name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
arguments.reserve(command_it->second.arguments_.size());
|
|
||||||
for (auto flash_argument : command_it->second.arguments_) {
|
|
||||||
arguments.push_back(std::move((flash_argument)));
|
|
||||||
}
|
|
||||||
|
|
||||||
f(name, arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Commands::Command::Command(unsigned int flags,
|
Commands::Command::Command(unsigned int flags,
|
||||||
|
unsigned int not_flags,
|
||||||
const string_vector name,
|
const string_vector name,
|
||||||
const string_vector arguments,
|
const string_vector arguments,
|
||||||
command_function function,
|
command_function function,
|
||||||
argument_completion_function arg_function)
|
argument_completion_function arg_function)
|
||||||
: flags_(flags)
|
: flags_(flags)
|
||||||
|
, not_flags_(not_flags)
|
||||||
, name_(name)
|
, name_(name)
|
||||||
, arguments_(arguments)
|
, arguments_(arguments)
|
||||||
, function_(function)
|
, function_(function)
|
||||||
|
|||||||
103
lib/uuid-console/src/commands_iterable.cpp
Normal file
103
lib/uuid-console/src/commands_iterable.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* uuid-console - Microcontroller console shell
|
||||||
|
* Copyright 2022 Simon Arlott
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or
|
||||||
|
* (at your option) 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <uuid/console.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace uuid {
|
||||||
|
|
||||||
|
namespace console {
|
||||||
|
|
||||||
|
Commands::AvailableCommands Commands::available_commands(const Shell & shell) const {
|
||||||
|
auto range = commands_.equal_range(shell.context());
|
||||||
|
return AvailableCommands(shell, range.first, range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommand::AvailableCommand(const Command & command)
|
||||||
|
: command_(command) {
|
||||||
|
name_.reserve(command_.name_.size());
|
||||||
|
for (auto name : command_.name_) {
|
||||||
|
name_.push_back(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments_.reserve(command_.arguments_.size());
|
||||||
|
for (auto argument : command_.arguments_) {
|
||||||
|
arguments_.push_back(argument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::AvailableCommands(const Shell & shell, const command_iterator & begin, const command_iterator & end)
|
||||||
|
: shell_(shell)
|
||||||
|
, begin_(begin)
|
||||||
|
, end_(end) {
|
||||||
|
// Skip over unavailable commands at the beginning
|
||||||
|
while (begin_ != end_ && !shell_.has_flags(begin_->second.flags_, begin_->second.not_flags_)) {
|
||||||
|
++begin_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::const_iterator Commands::AvailableCommands::cbegin() const {
|
||||||
|
return AvailableCommands::const_iterator(shell_, begin_, begin_, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::const_iterator Commands::AvailableCommands::cend() const {
|
||||||
|
return AvailableCommands::const_iterator(shell_, begin_, end_, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::const_iterator::const_iterator(const Shell & shell,
|
||||||
|
const command_iterator & begin,
|
||||||
|
const command_iterator & command,
|
||||||
|
const command_iterator & end)
|
||||||
|
: shell_(std::move(shell))
|
||||||
|
, begin_(begin)
|
||||||
|
, command_(command)
|
||||||
|
, end_(end) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Commands::AvailableCommands::const_iterator::update() {
|
||||||
|
if (command_ != end_) {
|
||||||
|
available_command_ = std::make_shared<AvailableCommand>(command_->second);
|
||||||
|
} else {
|
||||||
|
available_command_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::const_iterator & Commands::AvailableCommands::const_iterator::operator++() {
|
||||||
|
do {
|
||||||
|
++command_;
|
||||||
|
} while (command_ != end_ && !shell_.has_flags(command_->second.flags_, command_->second.not_flags_));
|
||||||
|
|
||||||
|
update();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands::const_iterator & Commands::AvailableCommands::const_iterator::operator--() {
|
||||||
|
do {
|
||||||
|
--command_;
|
||||||
|
} while (command_ != begin_ && !shell_.has_flags(command_->second.flags_, command_->second.not_flags_));
|
||||||
|
|
||||||
|
update();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace console
|
||||||
|
|
||||||
|
} // namespace uuid
|
||||||
@@ -45,21 +45,23 @@ namespace uuid {
|
|||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
|
|
||||||
// cppcheck-suppress passedByValue
|
Shell::Shell(Stream & stream, std::shared_ptr<Commands> commands, unsigned int context, unsigned int flags)
|
||||||
Shell::Shell(std::shared_ptr<Commands> commands, unsigned int context, unsigned int flags)
|
: stream_(stream)
|
||||||
: commands_(std::move(commands))
|
, commands_(std::move(commands))
|
||||||
, flags_(flags) {
|
, flags_(flags) {
|
||||||
enter_context(context);
|
enter_context(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shell::~Shell() {
|
void Shell::started() {
|
||||||
uuid::log::Logger::unregister_handler(this);
|
}
|
||||||
|
|
||||||
|
bool Shell::running() const {
|
||||||
|
return !stopped_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::start() {
|
void Shell::start() {
|
||||||
#ifdef EMSESP_DEBUG
|
#ifdef EMSESP_DEBUG
|
||||||
uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added by proddy
|
uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added for EMS-ESP
|
||||||
//uuid::log::Logger::register_handler(this, uuid::log::Level::INFO); // added by proddy
|
|
||||||
#else
|
#else
|
||||||
uuid::log::Logger::register_handler(this, uuid::log::Level::INFO);
|
uuid::log::Logger::register_handler(this, uuid::log::Level::INFO);
|
||||||
#endif
|
#endif
|
||||||
@@ -71,18 +73,11 @@ void Shell::start() {
|
|||||||
}
|
}
|
||||||
display_banner();
|
display_banner();
|
||||||
display_prompt();
|
display_prompt();
|
||||||
shells_.insert(shared_from_this());
|
registered_shells().insert(shared_from_this());
|
||||||
idle_time_ = uuid::get_uptime_ms();
|
idle_time_ = uuid::get_uptime_ms();
|
||||||
started();
|
started();
|
||||||
};
|
};
|
||||||
|
|
||||||
void Shell::started() {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Shell::running() const {
|
|
||||||
return !stopped_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Shell::stop() {
|
void Shell::stop() {
|
||||||
if (mode_ == Mode::BLOCKING) {
|
if (mode_ == Mode::BLOCKING) {
|
||||||
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
||||||
@@ -108,6 +103,10 @@ bool Shell::exit_context() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Commands::AvailableCommands Shell::available_commands() const {
|
||||||
|
return commands_->available_commands(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void Shell::loop_one() {
|
void Shell::loop_one() {
|
||||||
if (!running()) {
|
if (!running()) {
|
||||||
return;
|
return;
|
||||||
@@ -144,7 +143,7 @@ void Shell::set_command_str(const char * str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Shell::loop_normal() {
|
void Shell::loop_normal() {
|
||||||
const int input = read_one_char();
|
const int input = stream_.read();
|
||||||
|
|
||||||
if (input < 0) {
|
if (input < 0) {
|
||||||
check_idle_timeout();
|
check_idle_timeout();
|
||||||
@@ -158,8 +157,10 @@ void Shell::loop_normal() {
|
|||||||
// Interrupt (^C)
|
// Interrupt (^C)
|
||||||
line_buffer_.clear();
|
line_buffer_.clear();
|
||||||
println();
|
println();
|
||||||
cursor_ = 0;
|
cursor_ = 0;
|
||||||
line_no_ = 0;
|
line_no_ = 0;
|
||||||
|
// prompt_displayed_ = false;
|
||||||
|
// display_prompt();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\x04':
|
case '\x04':
|
||||||
@@ -304,18 +305,20 @@ void Shell::loop_normal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// common for all, display the complete line
|
// common for all, display the complete line
|
||||||
|
// added for EMS-ESP
|
||||||
erase_current_line();
|
erase_current_line();
|
||||||
prompt_displayed_ = false;
|
prompt_displayed_ = false;
|
||||||
display_prompt();
|
display_prompt();
|
||||||
|
|
||||||
if (cursor_) {
|
if (cursor_) {
|
||||||
printf(F("\033[%dD"), cursor_);
|
printf("\033[%dD", cursor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
previous_ = c;
|
previous_ = c;
|
||||||
|
|
||||||
// This is a hack to let TelnetStream know that command
|
// This is a hack to let TelnetStream know that command
|
||||||
// execution is complete and that output can be flushed.
|
// execution is complete and that output can be flushed.
|
||||||
available_char();
|
stream_.available();
|
||||||
|
|
||||||
idle_time_ = uuid::get_uptime_ms();
|
idle_time_ = uuid::get_uptime_ms();
|
||||||
}
|
}
|
||||||
@@ -326,7 +329,7 @@ Shell::PasswordData::PasswordData(const char * password_prompt, password_functio
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Shell::loop_password() {
|
void Shell::loop_password() {
|
||||||
const int input = read_one_char();
|
const int input = stream_.read();
|
||||||
|
|
||||||
if (input < 0) {
|
if (input < 0) {
|
||||||
check_idle_timeout();
|
check_idle_timeout();
|
||||||
@@ -393,7 +396,7 @@ void Shell::loop_password() {
|
|||||||
|
|
||||||
// This is a hack to let TelnetStream know that command
|
// This is a hack to let TelnetStream know that command
|
||||||
// execution is complete and that output can be flushed.
|
// execution is complete and that output can be flushed.
|
||||||
available_char();
|
stream_.available();
|
||||||
|
|
||||||
idle_time_ = uuid::get_uptime_ms();
|
idle_time_ = uuid::get_uptime_ms();
|
||||||
}
|
}
|
||||||
@@ -414,9 +417,10 @@ void Shell::loop_delay() {
|
|||||||
|
|
||||||
function_copy(*this);
|
function_copy(*this);
|
||||||
|
|
||||||
// if (running()) {
|
// TODO comment this like in v3.5? display_prompt
|
||||||
// display_prompt();
|
if (running()) {
|
||||||
// }
|
display_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
idle_time_ = uuid::get_uptime_ms();
|
idle_time_ = uuid::get_uptime_ms();
|
||||||
}
|
}
|
||||||
@@ -444,9 +448,10 @@ void Shell::loop_blocking() {
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (running()) {
|
// TODO comment this like in v3.5? display_prompt
|
||||||
// display_prompt();
|
if (running()) {
|
||||||
// }
|
display_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
idle_time_ = uuid::get_uptime_ms();
|
idle_time_ = uuid::get_uptime_ms();
|
||||||
}
|
}
|
||||||
@@ -513,14 +518,17 @@ void Shell::maximum_command_line_length(size_t length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Shell::process_command() {
|
void Shell::process_command() {
|
||||||
|
// added for EMS-ESP
|
||||||
if (line_buffer_.empty()) {
|
if (line_buffer_.empty()) {
|
||||||
println();
|
println();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t no = line_no_ ? line_no_ : MAX_LINES;
|
uint8_t no = line_no_ ? line_no_ : MAX_LINES;
|
||||||
while (--no) {
|
while (--no) {
|
||||||
line_old_[no] = line_old_[no - 1];
|
line_old_[no] = line_old_[no - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
line_no_ = 0;
|
line_no_ = 0;
|
||||||
line_old_[0] = line_buffer_;
|
line_old_[0] = line_buffer_;
|
||||||
while (!line_buffer_.empty()) {
|
while (!line_buffer_.empty()) {
|
||||||
@@ -542,18 +550,21 @@ void Shell::process_command() {
|
|||||||
if (!command_line->empty()) {
|
if (!command_line->empty()) {
|
||||||
if (commands_) {
|
if (commands_) {
|
||||||
auto execution = commands_->execute_command(*this, std::move(command_line));
|
auto execution = commands_->execute_command(*this, std::move(command_line));
|
||||||
|
|
||||||
if (execution.error != nullptr) {
|
if (execution.error != nullptr) {
|
||||||
println(execution.error);
|
println(execution.error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println(F("No commands configured"));
|
println("No commands configured");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::yield();
|
|
||||||
}
|
}
|
||||||
// if (running()) {
|
|
||||||
// display_prompt();
|
// TODO comment this like in v3.5? display_prompt
|
||||||
// }
|
if (running()) {
|
||||||
|
display_prompt();
|
||||||
|
}
|
||||||
|
::yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::process_completion() {
|
void Shell::process_completion() {
|
||||||
@@ -561,9 +572,11 @@ void Shell::process_completion() {
|
|||||||
|
|
||||||
if (!command_line->empty() && commands_) {
|
if (!command_line->empty() && commands_) {
|
||||||
auto completion = commands_->complete_command(*this, command_line);
|
auto completion = commands_->complete_command(*this, command_line);
|
||||||
|
bool redisplay = false;
|
||||||
|
|
||||||
if (!completion.help.empty()) {
|
if (!completion.help.empty()) {
|
||||||
println();
|
println();
|
||||||
|
redisplay = true;
|
||||||
|
|
||||||
for (auto & help : completion.help) {
|
for (auto & help : completion.help) {
|
||||||
std::string help_line = help.to_string(maximum_command_line_length_);
|
std::string help_line = help.to_string(maximum_command_line_length_);
|
||||||
@@ -573,8 +586,18 @@ void Shell::process_completion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!completion.replacement->empty()) {
|
if (!completion.replacement->empty()) {
|
||||||
|
if (!redisplay) {
|
||||||
|
erase_current_line();
|
||||||
|
prompt_displayed_ = false;
|
||||||
|
redisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
line_buffer_ = completion.replacement.to_string(maximum_command_line_length_);
|
line_buffer_ = completion.replacement.to_string(maximum_command_line_length_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (redisplay) {
|
||||||
|
display_prompt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::yield();
|
::yield();
|
||||||
@@ -599,9 +622,15 @@ void Shell::process_password(bool completed) {
|
|||||||
|
|
||||||
void Shell::invoke_command(const std::string & line) {
|
void Shell::invoke_command(const std::string & line) {
|
||||||
erase_current_line();
|
erase_current_line();
|
||||||
prompt_displayed_ = false;
|
if (!line_buffer_.empty()) {
|
||||||
line_buffer_ = line;
|
println();
|
||||||
display_prompt();
|
prompt_displayed_ = false;
|
||||||
|
}
|
||||||
|
if (!prompt_displayed_) {
|
||||||
|
display_prompt();
|
||||||
|
}
|
||||||
|
line_buffer_ = line;
|
||||||
|
print(line_buffer_);
|
||||||
process_command();
|
process_command();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <uuid/log.h>
|
#include <uuid/log.h>
|
||||||
@@ -30,8 +33,13 @@ namespace uuid {
|
|||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
|
|
||||||
static const char __pstr__logger_name[] = "shell";
|
static const char __pstr__logger_name[] = "shell";
|
||||||
const uuid::log::Logger Shell::logger_{(__pstr__logger_name), uuid::log::Facility::LPR};
|
|
||||||
|
const uuid::log::Logger & Shell::logger() {
|
||||||
|
static const uuid::log::Logger logger_instance{reinterpret_cast<const char *>(__pstr__logger_name), uuid::log::Facility::LPR};
|
||||||
|
|
||||||
|
return logger_instance;
|
||||||
|
}
|
||||||
|
|
||||||
Shell::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
|
Shell::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
|
||||||
: id_(id)
|
: id_(id)
|
||||||
@@ -39,6 +47,9 @@ Shell::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Shell::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
void Shell::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
if (log_messages_.size() >= maximum_log_messages_) {
|
if (log_messages_.size() >= maximum_log_messages_) {
|
||||||
log_messages_.pop_front();
|
log_messages_.pop_front();
|
||||||
}
|
}
|
||||||
@@ -55,10 +66,18 @@ void Shell::log_level(uuid::log::Level level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t Shell::maximum_log_messages() const {
|
size_t Shell::maximum_log_messages() const {
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
return maximum_log_messages_;
|
return maximum_log_messages_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::maximum_log_messages(size_t count) {
|
void Shell::maximum_log_messages(size_t count) {
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
maximum_log_messages_ = std::max((size_t)1, count);
|
maximum_log_messages_ = std::max((size_t)1, count);
|
||||||
while (log_messages_.size() > maximum_log_messages_) {
|
while (log_messages_.size() > maximum_log_messages_) {
|
||||||
log_messages_.pop_front();
|
log_messages_.pop_front();
|
||||||
@@ -66,39 +85,71 @@ void Shell::maximum_log_messages(size_t count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Shell::output_logs() {
|
void Shell::output_logs() {
|
||||||
if (!log_messages_.empty()) {
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
if (mode_ != Mode::DELAY) {
|
std::unique_lock<std::mutex> lock{mutex_};
|
||||||
erase_current_line();
|
#endif
|
||||||
prompt_displayed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!log_messages_.empty()) {
|
if (log_messages_.empty())
|
||||||
auto message = std::move(log_messages_.front());
|
return;
|
||||||
log_messages_.pop_front();
|
|
||||||
|
|
||||||
print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3));
|
size_t count = std::max((size_t)1, MAX_LOG_MESSAGES);
|
||||||
printf(F(" %c %lu: [%S] "), uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name);
|
auto message = log_messages_.front();
|
||||||
|
|
||||||
if ((message.content_->level == uuid::log::Level::ERR) || (message.content_->level == uuid::log::Level::WARNING)) {
|
log_messages_.pop_front();
|
||||||
print(COLOR_RED);
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
println(message.content_->text);
|
lock.unlock();
|
||||||
print(COLOR_RESET);
|
#endif
|
||||||
} else if (message.content_->level == uuid::log::Level::INFO) {
|
|
||||||
print(COLOR_YELLOW);
|
|
||||||
println(message.content_->text);
|
|
||||||
print(COLOR_RESET);
|
|
||||||
} else if (message.content_->level == uuid::log::Level::DEBUG) {
|
|
||||||
print(COLOR_CYAN);
|
|
||||||
println(message.content_->text);
|
|
||||||
print(COLOR_RESET);
|
|
||||||
} else {
|
|
||||||
println(message.content_->text);
|
|
||||||
}
|
|
||||||
|
|
||||||
::yield();
|
if (mode_ != Mode::DELAY) {
|
||||||
}
|
erase_current_line();
|
||||||
display_prompt();
|
prompt_displayed_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3));
|
||||||
|
printf(" %c %lu: [%s] ", uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name);
|
||||||
|
|
||||||
|
if ((message.content_->level == uuid::log::Level::ERR) || (message.content_->level == uuid::log::Level::WARNING)) {
|
||||||
|
print(COLOR_RED);
|
||||||
|
println(message.content_->text);
|
||||||
|
print(COLOR_RESET);
|
||||||
|
} else if (message.content_->level == uuid::log::Level::INFO) {
|
||||||
|
print(COLOR_YELLOW);
|
||||||
|
println(message.content_->text);
|
||||||
|
print(COLOR_RESET);
|
||||||
|
} else if (message.content_->level == uuid::log::Level::DEBUG) {
|
||||||
|
print(COLOR_CYAN);
|
||||||
|
println(message.content_->text);
|
||||||
|
print(COLOR_RESET);
|
||||||
|
} else {
|
||||||
|
println(message.content_->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
::yield();
|
||||||
|
|
||||||
|
count--;
|
||||||
|
if (count == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
lock.lock();
|
||||||
|
#endif
|
||||||
|
if (log_messages_.empty()) {
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = log_messages_.front();
|
||||||
|
log_messages_.pop_front();
|
||||||
|
#if UUID_CONSOLE_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
display_prompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace console
|
} // namespace console
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-console - Microcontroller console shell
|
* uuid-console - Microcontroller console shell
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2021 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,15 +25,21 @@ namespace uuid {
|
|||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
|
|
||||||
std::set<std::shared_ptr<Shell>> Shell::shells_;
|
std::set<std::shared_ptr<Shell>> & Shell::registered_shells() {
|
||||||
|
static std::set<std::shared_ptr<Shell>> shells;
|
||||||
|
|
||||||
|
return shells;
|
||||||
|
}
|
||||||
|
|
||||||
void Shell::loop_all() {
|
void Shell::loop_all() {
|
||||||
for (auto shell = shells_.begin(); shell != shells_.end();) {
|
auto & shells = registered_shells();
|
||||||
|
|
||||||
|
for (auto shell = shells.begin(); shell != shells.end();) {
|
||||||
shell->get()->loop_one();
|
shell->get()->loop_one();
|
||||||
|
|
||||||
// This avoids copying the shared_ptr every time loop_one() is called
|
// This avoids copying the shared_ptr every time loop_one() is called
|
||||||
if (!shell->get()->running()) {
|
if (!shell->get()->running()) {
|
||||||
shell = shells_.erase(shell);
|
shell = shells.erase(shell);
|
||||||
} else {
|
} else {
|
||||||
shell++;
|
shell++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,16 +51,6 @@ size_t Shell::printf(const char * format, ...) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Shell::printf(const __FlashStringHelper * format, ...) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
size_t len = vprintf(format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Shell::printfln(const char * format, ...) {
|
size_t Shell::printfln(const char * format, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
@@ -72,17 +62,6 @@ size_t Shell::printfln(const char * format, ...) {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Shell::printfln(const __FlashStringHelper * format, ...) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
size_t len = vprintf(format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
len += println();
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Shell::vprintf(const char * format, va_list ap) {
|
size_t Shell::vprintf(const char * format, va_list ap) {
|
||||||
size_t print_len = 0;
|
size_t print_len = 0;
|
||||||
va_list copy_ap;
|
va_list copy_ap;
|
||||||
@@ -101,64 +80,26 @@ size_t Shell::vprintf(const char * format, va_list ap) {
|
|||||||
return print_len;
|
return print_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Shell::vprintf(const __FlashStringHelper * format, va_list ap) {
|
|
||||||
size_t print_len = 0;
|
|
||||||
va_list copy_ap;
|
|
||||||
|
|
||||||
va_copy(copy_ap, ap);
|
|
||||||
|
|
||||||
int format_len = ::vsnprintf_P(nullptr, 0, reinterpret_cast<PGM_P>(format), ap);
|
|
||||||
if (format_len > 0) {
|
|
||||||
std::string text(static_cast<std::string::size_type>(format_len), '\0');
|
|
||||||
|
|
||||||
::vsnprintf_P(&text[0], text.capacity() + 1, reinterpret_cast<PGM_P>(format), copy_ap);
|
|
||||||
print_len = print(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(copy_ap);
|
|
||||||
return print_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
// modified by proddy
|
|
||||||
void Shell::print_all_available_commands() {
|
void Shell::print_all_available_commands() {
|
||||||
/*
|
// TODO add back sorting of commands?
|
||||||
commands_->for_each_available_command(*this, [this](std::vector<std::string> & name, std::vector<std::string> & arguments) {
|
|
||||||
CommandLine command_line{name, arguments};
|
|
||||||
|
|
||||||
command_line.escape_initial_parameters(name.size());
|
for (auto & available_command : available_commands()) {
|
||||||
name.clear();
|
CommandLine command_line{available_command.name(), available_command.arguments()};
|
||||||
arguments.clear();
|
|
||||||
|
command_line.escape_initial_parameters(available_command.name().size());
|
||||||
println(command_line.to_string(maximum_command_line_length()));
|
println(command_line.to_string(maximum_command_line_length()));
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
// changed by proddy - sort the help commands
|
|
||||||
std::list<std::string> sorted_cmds;
|
|
||||||
|
|
||||||
commands_->for_each_available_command(*this, [&](std::vector<std::string> & name, std::vector<std::string> & arguments) {
|
|
||||||
CommandLine command_line{name, arguments};
|
|
||||||
command_line.escape_initial_parameters(name.size());
|
|
||||||
name.clear();
|
|
||||||
arguments.clear();
|
|
||||||
sorted_cmds.push_back(command_line.to_string(maximum_command_line_length()));
|
|
||||||
});
|
|
||||||
|
|
||||||
sorted_cmds.sort();
|
|
||||||
for (auto & cl : sorted_cmds) {
|
|
||||||
// print(" ");
|
|
||||||
println(cl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::erase_current_line() {
|
void Shell::erase_current_line() {
|
||||||
print(F("\033[0G\033[K"));
|
print("\033[0G\033[K");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shell::erase_characters(size_t count) {
|
void Shell::erase_characters(size_t count) {
|
||||||
print(std::string(count, '\x08'));
|
print(std::string(count, '\x08'));
|
||||||
print(F("\033[K"));
|
print("\033[K");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace console
|
} // namespace console
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-console - Microcontroller console shell
|
* uuid-console - Microcontroller console shell
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2022 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,12 +28,12 @@ int Shell::available() {
|
|||||||
if (mode_ == Mode::BLOCKING) {
|
if (mode_ == Mode::BLOCKING) {
|
||||||
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
||||||
|
|
||||||
if (!available_char()) {
|
if (!stream_.available()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocking_data->consume_line_feed_) {
|
if (blocking_data->consume_line_feed_) {
|
||||||
const int input = peek_one_char();
|
const int input = stream_.peek();
|
||||||
|
|
||||||
if (input >= 0) {
|
if (input >= 0) {
|
||||||
const unsigned char c = input;
|
const unsigned char c = input;
|
||||||
@@ -42,7 +42,7 @@ int Shell::available() {
|
|||||||
|
|
||||||
if (previous_ == '\x0D' && c == '\x0A') {
|
if (previous_ == '\x0D' && c == '\x0A') {
|
||||||
// Consume the first LF following a CR
|
// Consume the first LF following a CR
|
||||||
read_one_char();
|
stream_.read();
|
||||||
previous_ = c;
|
previous_ = c;
|
||||||
return available();
|
return available();
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ int Shell::available() {
|
|||||||
int Shell::read() {
|
int Shell::read() {
|
||||||
if (mode_ == Mode::BLOCKING) {
|
if (mode_ == Mode::BLOCKING) {
|
||||||
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
||||||
const int input = read_one_char();
|
const int input = stream_.read();
|
||||||
|
|
||||||
if (input >= 0) {
|
if (input >= 0) {
|
||||||
const unsigned char c = input;
|
const unsigned char c = input;
|
||||||
@@ -90,7 +90,7 @@ int Shell::read() {
|
|||||||
int Shell::peek() {
|
int Shell::peek() {
|
||||||
if (mode_ == Mode::BLOCKING) {
|
if (mode_ == Mode::BLOCKING) {
|
||||||
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
|
||||||
const int input = peek_one_char();
|
const int input = stream_.peek();
|
||||||
|
|
||||||
if (blocking_data->consume_line_feed_) {
|
if (blocking_data->consume_line_feed_) {
|
||||||
if (input >= 0) {
|
if (input >= 0) {
|
||||||
@@ -100,7 +100,7 @@ int Shell::peek() {
|
|||||||
|
|
||||||
if (previous_ == '\x0D' && c == '\x0A') {
|
if (previous_ == '\x0D' && c == '\x0A') {
|
||||||
// Consume the first LF following a CR
|
// Consume the first LF following a CR
|
||||||
read_one_char();
|
stream_.read();
|
||||||
previous_ = c;
|
previous_ = c;
|
||||||
return peek();
|
return peek();
|
||||||
}
|
}
|
||||||
@@ -113,6 +113,15 @@ int Shell::peek() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Shell::write(uint8_t data) {
|
||||||
|
return stream_.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Shell::write(const uint8_t * buffer, size_t size) {
|
||||||
|
return stream_.write(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Shell::flush() {
|
void Shell::flush() {
|
||||||
// This is a pure virtual function in Arduino's Stream class, which
|
// This is a pure virtual function in Arduino's Stream class, which
|
||||||
// makes no sense because that class is for input and this is an
|
// makes no sense because that class is for input and this is an
|
||||||
@@ -122,4 +131,4 @@ void Shell::flush() {
|
|||||||
|
|
||||||
} // namespace console
|
} // namespace console
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* uuid-console - Microcontroller console shell
|
|
||||||
* Copyright 2019 Simon Arlott
|
|
||||||
*
|
|
||||||
* 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 3 of the License, or
|
|
||||||
* (at your option) 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <uuid/console.h>
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace uuid {
|
|
||||||
|
|
||||||
namespace console {
|
|
||||||
|
|
||||||
StreamConsole::StreamConsole(Stream & stream)
|
|
||||||
: Shell()
|
|
||||||
, stream_(stream) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// cppcheck-suppress passedByValue
|
|
||||||
StreamConsole::StreamConsole(std::shared_ptr<Commands> commands, Stream & stream, unsigned int context, unsigned int flags)
|
|
||||||
: Shell(std::move(commands), context, flags)
|
|
||||||
, stream_(stream) {
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t StreamConsole::write(uint8_t data) {
|
|
||||||
return stream_.write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t StreamConsole::write(const uint8_t * buffer, size_t size) {
|
|
||||||
return stream_.write(buffer, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StreamConsole::available_char() {
|
|
||||||
return stream_.available() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamConsole::read_one_char() {
|
|
||||||
return stream_.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
int StreamConsole::peek_one_char() {
|
|
||||||
return stream_.peek();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace console
|
|
||||||
|
|
||||||
} // namespace uuid
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -38,20 +38,19 @@ static constexpr const char * pstr_level_lowercase_debug = "debug";
|
|||||||
static constexpr const char * pstr_level_lowercase_trace = "trace";
|
static constexpr const char * pstr_level_lowercase_trace = "trace";
|
||||||
static constexpr const char * pstr_level_lowercase_all = "all";
|
static constexpr const char * pstr_level_lowercase_all = "all";
|
||||||
|
|
||||||
static const __FlashStringHelper * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
|
static const char * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] = {pstr_level_lowercase_off,
|
||||||
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_off),
|
pstr_level_lowercase_emerg,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_emerg),
|
pstr_level_lowercase_crit,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_crit),
|
pstr_level_lowercase_alert,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_alert),
|
pstr_level_lowercase_err,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_err),
|
pstr_level_lowercase_warning,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_warning),
|
pstr_level_lowercase_notice,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_notice),
|
pstr_level_lowercase_info,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_info),
|
pstr_level_lowercase_debug,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_debug),
|
pstr_level_lowercase_trace,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_trace),
|
pstr_level_lowercase_all};
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_all)};
|
|
||||||
|
|
||||||
const __FlashStringHelper * format_level_lowercase(Level level) {
|
const char * format_level_lowercase(Level level) {
|
||||||
return log_level_lowercase[(int)level + 1];
|
return log_level_lowercase[(int)level + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,20 +38,19 @@ static constexpr const char * pstr_level_uppercase_debug = "DEBUG";
|
|||||||
static constexpr const char * pstr_level_uppercase_trace = "TRACE";
|
static constexpr const char * pstr_level_uppercase_trace = "TRACE";
|
||||||
static constexpr const char * pstr_level_uppercase_all = "ALL";
|
static constexpr const char * pstr_level_uppercase_all = "ALL";
|
||||||
|
|
||||||
static const __FlashStringHelper * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
|
static const char * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] = {pstr_level_uppercase_off,
|
||||||
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_off),
|
pstr_level_uppercase_emerg,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_emerg),
|
pstr_level_uppercase_crit,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_crit),
|
pstr_level_uppercase_alert,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_alert),
|
pstr_level_uppercase_err,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_err),
|
pstr_level_uppercase_warning,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_warning),
|
pstr_level_uppercase_notice,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_notice),
|
pstr_level_uppercase_info,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_info),
|
pstr_level_uppercase_debug,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_debug),
|
pstr_level_uppercase_trace,
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_trace),
|
pstr_level_uppercase_all};
|
||||||
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_all)};
|
|
||||||
|
|
||||||
const __FlashStringHelper * format_level_uppercase(Level level) {
|
const char * format_level_uppercase(Level level) {
|
||||||
return log_level_uppercase[(int)level + 1];
|
return log_level_uppercase[(int)level + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ std::string format_timestamp_ms(uint64_t timestamp_ms, unsigned int days_width)
|
|||||||
|
|
||||||
milliseconds = timestamp_ms;
|
milliseconds = timestamp_ms;
|
||||||
|
|
||||||
static std::vector<char> text(10 + 1 /* days */ + 2 + 1 /* hours */ + 2 + 1 /* minutes */ + 2 + 1 /* seconds */ + 3 /* milliseconds */ + 1);
|
std::array<char, 12 + 1 /* days */ + 2 + 1 /* hours */ + 2 + 1 /* minutes */ + 2 + 1 /* seconds */ + 3 /* milliseconds */ + 1> text;
|
||||||
|
|
||||||
snprintf(text.data(), text.size(), ("%0*lu+%02u:%02u:%02u.%03u"), std::min(days_width, 10U), days, hours, minutes, seconds, milliseconds);
|
snprintf(text.data(), text.size(), ("%0*lu+%02u:%02u:%02u.%03u"), std::min(days_width, 10U), days, hours, minutes, seconds, milliseconds);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-common - Microcontroller common utilities
|
* uuid-log - Microcontroller logging framework
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2021 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,24 +16,16 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <uuid/common.h>
|
#include <uuid/log.h>
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
std::string read_flash_string(const __FlashStringHelper * flash_str) {
|
namespace log {
|
||||||
if (flash_str == nullptr) {
|
|
||||||
return std::string(""); // prevent crash
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str(::strlen_P(reinterpret_cast<PGM_P>(flash_str)), '\0');
|
Handler::~Handler() {
|
||||||
|
Logger::unregister_handler(this);
|
||||||
::strncpy_P(&str[0], reinterpret_cast<PGM_P>(flash_str), str.capacity() + 1);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace log
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
@@ -27,17 +27,17 @@ namespace uuid {
|
|||||||
namespace log {
|
namespace log {
|
||||||
|
|
||||||
std::vector<std::string> levels_lowercase() {
|
std::vector<std::string> levels_lowercase() {
|
||||||
return {uuid::read_flash_string(format_level_lowercase(Level::OFF)),
|
return {format_level_lowercase(Level::OFF),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::EMERG)),
|
format_level_lowercase(Level::EMERG),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::ALERT)),
|
format_level_lowercase(Level::ALERT),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::CRIT)),
|
format_level_lowercase(Level::CRIT),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::ERR)),
|
format_level_lowercase(Level::ERR),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::WARNING)),
|
format_level_lowercase(Level::WARNING),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::NOTICE)),
|
format_level_lowercase(Level::NOTICE),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::INFO)),
|
format_level_lowercase(Level::INFO),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::DEBUG)),
|
format_level_lowercase(Level::DEBUG),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::TRACE)),
|
format_level_lowercase(Level::TRACE),
|
||||||
uuid::read_flash_string(format_level_lowercase(Level::ALL))};
|
format_level_lowercase(Level::ALL)};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace log
|
} // namespace log
|
||||||
|
|||||||
@@ -27,17 +27,17 @@ namespace uuid {
|
|||||||
namespace log {
|
namespace log {
|
||||||
|
|
||||||
std::vector<std::string> levels_uppercase() {
|
std::vector<std::string> levels_uppercase() {
|
||||||
return {uuid::read_flash_string(format_level_uppercase(Level::OFF)),
|
return {format_level_uppercase(Level::OFF),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::EMERG)),
|
format_level_uppercase(Level::EMERG),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::ALERT)),
|
format_level_uppercase(Level::ALERT),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::CRIT)),
|
format_level_uppercase(Level::CRIT),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::ERR)),
|
format_level_uppercase(Level::ERR),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::WARNING)),
|
format_level_uppercase(Level::WARNING),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::NOTICE)),
|
format_level_uppercase(Level::NOTICE),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::INFO)),
|
format_level_uppercase(Level::INFO),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::DEBUG)),
|
format_level_uppercase(Level::DEBUG),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::TRACE)),
|
format_level_uppercase(Level::TRACE),
|
||||||
uuid::read_flash_string(format_level_uppercase(Level::ALL))};
|
format_level_uppercase(Level::ALL)};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace log
|
} // namespace log
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-log - Microcontroller logging framework
|
* uuid-log - Microcontroller logging framework
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2021-2022 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,11 +20,15 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -33,8 +37,21 @@ namespace uuid {
|
|||||||
|
|
||||||
namespace log {
|
namespace log {
|
||||||
|
|
||||||
std::map<Handler *, Level> Logger::handlers_;
|
std::atomic<Level> Logger::global_level_{Level::OFF};
|
||||||
Level Logger::level_ = Level::OFF;
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::mutex Logger::mutex_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! @cond false
|
||||||
|
static Level constrain_level(Level level) {
|
||||||
|
if (level < Level::EMERG) {
|
||||||
|
level = Level::EMERG;
|
||||||
|
} else if (level > Level::TRACE) {
|
||||||
|
level = Level::TRACE;
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
//! @endcond
|
||||||
|
|
||||||
Message::Message(uint64_t uptime_ms, Level level, Facility facility, const char * name, const std::string && text)
|
Message::Message(uint64_t uptime_ms, Level level, Facility facility, const char * name, const std::string && text)
|
||||||
: uptime_ms(uptime_ms)
|
: uptime_ms(uptime_ms)
|
||||||
@@ -50,20 +67,46 @@ Logger::Logger(const char * name, Facility facility)
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<std::map<Handler *, Level>> & Logger::registered_handlers() {
|
||||||
|
static std::shared_ptr<std::map<Handler *, Level>> handlers = std::make_shared<std::map<Handler *, Level>>();
|
||||||
|
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
void Logger::register_handler(Handler * handler, Level level) {
|
void Logger::register_handler(Handler * handler, Level level) {
|
||||||
handlers_[handler] = level;
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
auto & handlers = registered_handlers();
|
||||||
|
|
||||||
|
handler->handlers_ = handlers;
|
||||||
|
(*handlers)[handler] = level;
|
||||||
refresh_log_level();
|
refresh_log_level();
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::unregister_handler(Handler * handler) {
|
void Logger::unregister_handler(Handler * handler) {
|
||||||
handlers_.erase(handler);
|
auto handlers = handler->handlers_.lock();
|
||||||
refresh_log_level();
|
|
||||||
|
if (handlers) {
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (handlers->erase(handler)) {
|
||||||
|
refresh_log_level();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Level Logger::get_log_level(const Handler * handler) {
|
Level Logger::get_log_level(const Handler * handler) {
|
||||||
const auto level = handlers_.find(const_cast<Handler *>(handler));
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
auto & handlers = registered_handlers();
|
||||||
|
|
||||||
if (level != handlers_.end()) {
|
const auto level = handlers->find(const_cast<Handler *>(handler));
|
||||||
|
|
||||||
|
if (level != handlers->end()) {
|
||||||
return level->second;
|
return level->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +123,6 @@ void Logger::emerg(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::emerg(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::EMERG)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::EMERG, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::crit(const char * format, ...) const {
|
void Logger::crit(const char * format, ...) const {
|
||||||
if (enabled(Level::CRIT)) {
|
if (enabled(Level::CRIT)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -100,16 +133,6 @@ void Logger::crit(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::crit(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::CRIT)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::CRIT, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::alert(const char * format, ...) const {
|
void Logger::alert(const char * format, ...) const {
|
||||||
if (enabled(Level::ALERT)) {
|
if (enabled(Level::ALERT)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -120,15 +143,6 @@ void Logger::alert(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::alert(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::ALERT)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::ALERT, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
void Logger::err(const char * format, ...) const {
|
void Logger::err(const char * format, ...) const {
|
||||||
if (enabled(Level::ERR)) {
|
if (enabled(Level::ERR)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -139,16 +153,6 @@ void Logger::err(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::err(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::ERR)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::ERR, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::warning(const char * format, ...) const {
|
void Logger::warning(const char * format, ...) const {
|
||||||
if (enabled(Level::WARNING)) {
|
if (enabled(Level::WARNING)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -159,16 +163,6 @@ void Logger::warning(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::warning(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::WARNING)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::WARNING, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::notice(const char * format, ...) const {
|
void Logger::notice(const char * format, ...) const {
|
||||||
if (enabled(Level::NOTICE)) {
|
if (enabled(Level::NOTICE)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -179,16 +173,6 @@ void Logger::notice(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::notice(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::NOTICE)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::NOTICE, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::info(const char * format, ...) const {
|
void Logger::info(const char * format, ...) const {
|
||||||
if (enabled(Level::INFO)) {
|
if (enabled(Level::INFO)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -199,16 +183,6 @@ void Logger::info(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::info(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::INFO)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::INFO, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::debug(const char * format, ...) const {
|
void Logger::debug(const char * format, ...) const {
|
||||||
if (enabled(Level::DEBUG)) {
|
if (enabled(Level::DEBUG)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -219,16 +193,6 @@ void Logger::debug(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::debug(const __FlashStringHelper * format, ...) const {
|
|
||||||
if (enabled(Level::DEBUG)) {
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::DEBUG, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::trace(const char * format, ...) const {
|
void Logger::trace(const char * format, ...) const {
|
||||||
if (enabled(Level::TRACE)) {
|
if (enabled(Level::TRACE)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -239,38 +203,20 @@ void Logger::trace(const char * format, ...) const {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::trace(const __FlashStringHelper * format, ...) const {
|
void Logger::log(Level level, const char * format, ...) const {
|
||||||
if (enabled(Level::TRACE)) {
|
level = constrain_level(level);
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, format);
|
|
||||||
vlog(Level::TRACE, format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Logger::log(Level level, Facility facility, const char * format, ...) const {
|
|
||||||
if (level < Level::EMERG) {
|
|
||||||
level = Level::EMERG;
|
|
||||||
} else if (level > Level::TRACE) {
|
|
||||||
level = Level::TRACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enabled(level)) {
|
if (enabled(level)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
vlog(level, facility, format, ap);
|
vlog(level, format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Logger::log(Level level, Facility facility, const __FlashStringHelper * format, ...) const {
|
void Logger::log(Level level, Facility facility, const char * format, ...) const {
|
||||||
if (level < Level::EMERG) {
|
level = constrain_level(level);
|
||||||
level = Level::EMERG;
|
|
||||||
} else if (level > Level::TRACE) {
|
|
||||||
level = Level::TRACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enabled(level)) {
|
if (enabled(level)) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
@@ -295,41 +241,34 @@ void Logger::vlog(Level level, Facility facility, const char * format, va_list a
|
|||||||
dispatch(level, facility, text);
|
dispatch(level, facility, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::vlog(Level level, const __FlashStringHelper * format, va_list ap) const {
|
|
||||||
vlog(level, facility_, format, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::vlog(Level level, Facility facility, const __FlashStringHelper * format, va_list ap) const {
|
|
||||||
std::vector<char> text(MAX_LOG_LENGTH + 1);
|
|
||||||
|
|
||||||
if (vsnprintf_P(text.data(), text.size(), reinterpret_cast<PGM_P>(format), ap) <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(level, facility, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::dispatch(Level level, Facility facility, std::vector<char> & text) const {
|
void Logger::dispatch(Level level, Facility facility, std::vector<char> & text) const {
|
||||||
std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text.data());
|
std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text.data());
|
||||||
text.resize(0);
|
text.resize(0);
|
||||||
|
|
||||||
for (auto & handler : handlers_) {
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (auto & handler : *registered_handlers()) {
|
||||||
if (level <= handler.second) {
|
if (level <= handler.second) {
|
||||||
*handler.first << message;
|
*handler.first << message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mutex already locked by caller. */
|
||||||
void Logger::refresh_log_level() {
|
void Logger::refresh_log_level() {
|
||||||
level_ = Level::OFF;
|
Level level = Level::OFF;
|
||||||
|
|
||||||
for (auto & handler : handlers_) {
|
for (auto & handler : *registered_handlers()) {
|
||||||
if (level_ < handler.second) {
|
if (level < handler.second) {
|
||||||
level_ = handler.second;
|
level = handler.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global_level_ = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace log
|
} // namespace log
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
@@ -28,7 +28,7 @@ namespace log {
|
|||||||
|
|
||||||
bool parse_level_lowercase(const std::string & name, Level & level) {
|
bool parse_level_lowercase(const std::string & name, Level & level) {
|
||||||
for (auto value : levels()) {
|
for (auto value : levels()) {
|
||||||
if (!strcmp_P(name.c_str(), reinterpret_cast<PGM_P>(format_level_lowercase(value)))) {
|
if (!strcmp(name.c_str(), format_level_lowercase(value))) {
|
||||||
level = value;
|
level = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace log {
|
|||||||
|
|
||||||
bool parse_level_uppercase(const std::string & name, Level & level) {
|
bool parse_level_uppercase(const std::string & name, Level & level) {
|
||||||
for (auto value : levels()) {
|
for (auto value : levels()) {
|
||||||
if (!strcmp_P(name.c_str(), reinterpret_cast<PGM_P>(format_level_uppercase(value)))) {
|
if (!strcmp(name.c_str(), format_level_uppercase(value))) {
|
||||||
level = value;
|
level = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
106
lib/uuid-log/src/print_handler.cpp
Normal file
106
lib/uuid-log/src/print_handler.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* uuid-log - Microcontroller logging framework
|
||||||
|
* Copyright 2022 Simon Arlott
|
||||||
|
*
|
||||||
|
* 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 3 of the License, or
|
||||||
|
* (at your option) 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <uuid/log.h>
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace uuid {
|
||||||
|
|
||||||
|
namespace log {
|
||||||
|
|
||||||
|
PrintHandler::PrintHandler(::Print & print)
|
||||||
|
: print_(print) {
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PrintHandler::maximum_log_messages() const {
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return maximum_log_messages_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHandler::maximum_log_messages(size_t count) {
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
maximum_log_messages_ = std::max((size_t)1, count);
|
||||||
|
|
||||||
|
while (log_messages_.size() > maximum_log_messages_) {
|
||||||
|
log_messages_.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHandler::loop(size_t count) {
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::unique_lock<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
count = std::max((size_t)1, count);
|
||||||
|
|
||||||
|
while (!log_messages_.empty()) {
|
||||||
|
auto message = log_messages_.front();
|
||||||
|
|
||||||
|
log_messages_.pop_front();
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
print_.print(uuid::log::format_timestamp_ms(message->uptime_ms, 3).c_str());
|
||||||
|
print_.print(' ');
|
||||||
|
print_.print(uuid::log::format_level_char(message->level));
|
||||||
|
print_.print(" [");
|
||||||
|
print_.print(message->name);
|
||||||
|
print_.print("] ");
|
||||||
|
print_.println(message->text.c_str());
|
||||||
|
|
||||||
|
count--;
|
||||||
|
if (count == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
::yield();
|
||||||
|
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
lock.lock();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintHandler::operator<<(std::shared_ptr<Message> message) {
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (log_messages_.size() >= maximum_log_messages_) {
|
||||||
|
log_messages_.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
log_messages_.emplace_back(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace log
|
||||||
|
|
||||||
|
} // namespace uuid
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-log - Microcontroller logging framework
|
* uuid-log - Microcontroller logging framework
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2021-2022 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -31,6 +33,24 @@
|
|||||||
|
|
||||||
#include <uuid/common.h>
|
#include <uuid/common.h>
|
||||||
|
|
||||||
|
#ifndef UUID_COMMON_THREAD_SAFE
|
||||||
|
#define UUID_COMMON_THREAD_SAFE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#define UUID_COMMON_STD_MUTEX_AVAILABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#define UUID_LOG_THREAD_SAFE 1
|
||||||
|
#else
|
||||||
|
#define UUID_LOG_THREAD_SAFE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +90,17 @@ namespace uuid {
|
|||||||
|
|
||||||
namespace log {
|
namespace log {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe status of the library.
|
||||||
|
*
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
#if UUID_COMMON_THREAD_SAFE && UUID_LOG_THREAD_SAFE
|
||||||
|
static constexpr bool thread_safe = true;
|
||||||
|
#else
|
||||||
|
static constexpr bool thread_safe = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Severity level of log messages.
|
* Severity level of log messages.
|
||||||
*
|
*
|
||||||
@@ -158,10 +189,10 @@ char format_level_char(Level level);
|
|||||||
* Format a log level as an uppercase string.
|
* Format a log level as an uppercase string.
|
||||||
*
|
*
|
||||||
* @param[in] level Log level.
|
* @param[in] level Log level.
|
||||||
* @return Uppercase name of the log level (flash string).
|
* @return Uppercase name of the log level
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
const __FlashStringHelper * format_level_uppercase(Level level);
|
const char * format_level_uppercase(Level level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all log levels as uppercase strings.
|
* Get all log levels as uppercase strings.
|
||||||
@@ -187,10 +218,10 @@ bool parse_level_uppercase(const std::string & name, Level & level);
|
|||||||
* Format a log level as a lowercase string.
|
* Format a log level as a lowercase string.
|
||||||
*
|
*
|
||||||
* @param[in] level Log level.
|
* @param[in] level Log level.
|
||||||
* @return Lowercase name of the log level (flash string).
|
* @return Lowercase name of the log level
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
const __FlashStringHelper * format_level_lowercase(Level level);
|
const char * format_level_lowercase(Level level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all log levels as lowercase strings.
|
* Get all log levels as lowercase strings.
|
||||||
@@ -227,7 +258,7 @@ struct Message {
|
|||||||
* @param[in] uptime_ms System uptime, see uuid::get_uptime_ms().
|
* @param[in] uptime_ms System uptime, see uuid::get_uptime_ms().
|
||||||
* @param[in] level Severity level of the message.
|
* @param[in] level Severity level of the message.
|
||||||
* @param[in] facility Facility type of the process logging the message.
|
* @param[in] facility Facility type of the process logging the message.
|
||||||
* @param[in] name Logger name (flash string).
|
* @param[in] name Logger name
|
||||||
* @param[in] text Log message text.
|
* @param[in] text Log message text.
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -257,7 +288,7 @@ struct Message {
|
|||||||
const Facility facility;
|
const Facility facility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the logger used (flash string).
|
* Name of the logger used
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -274,14 +305,24 @@ struct Message {
|
|||||||
const std::string text;
|
const std::string text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger handler used to process log messages.
|
* Logger handler used to process log messages.
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
class Handler {
|
class Handler {
|
||||||
|
/**
|
||||||
|
* Logger needs to be able to access the private reference to
|
||||||
|
* the registered log handlers.
|
||||||
|
*
|
||||||
|
* @since 2.1.2
|
||||||
|
*/
|
||||||
|
friend Logger;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Handler() = default;
|
virtual ~Handler();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new log message.
|
* Add a new log message.
|
||||||
@@ -290,9 +331,20 @@ class Handler {
|
|||||||
* processed immediately so that log messages have minimal impact
|
* processed immediately so that log messages have minimal impact
|
||||||
* at the time of use.
|
* at the time of use.
|
||||||
*
|
*
|
||||||
|
* Handlers must avoid holding a lock on a mutex used for adding
|
||||||
|
* messages while processing those messages. Release the lock while
|
||||||
|
* performing the processing.
|
||||||
|
*
|
||||||
* Queues should have a maximum size and discard the oldest message
|
* Queues should have a maximum size and discard the oldest message
|
||||||
* when full.
|
* when full.
|
||||||
*
|
*
|
||||||
|
* It is not safe for the handler to directly or indirectly do any
|
||||||
|
* of the following while this function is being called:
|
||||||
|
* - Log a message.
|
||||||
|
* - Read the log level of any handler.
|
||||||
|
* - Modify the log level of any handler.
|
||||||
|
* - Unregister any handler.
|
||||||
|
*
|
||||||
* @param[in] message New log message, shared by all handlers.
|
* @param[in] message New log message, shared by all handlers.
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
@@ -300,6 +352,17 @@ class Handler {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
Handler() = default;
|
Handler() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Reference to registered log handlers.
|
||||||
|
*
|
||||||
|
* Used in the destructor to safely unregister the handler even if
|
||||||
|
* the underlying map has already been destroyed.
|
||||||
|
*
|
||||||
|
* @since 2.1.2
|
||||||
|
*/
|
||||||
|
std::weak_ptr<std::map<Handler *, Level>> handlers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -309,7 +372,7 @@ class Handler {
|
|||||||
*/
|
*/
|
||||||
class Logger {
|
class Logger {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* This is the maximum length of any log message.
|
* This is the maximum length of any log message.
|
||||||
*
|
*
|
||||||
* Determines the size of the buffer used for format string
|
* Determines the size of the buffer used for format string
|
||||||
@@ -317,16 +380,12 @@ class Logger {
|
|||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
#if defined(EMSESP_STANDALONE)
|
|
||||||
static constexpr size_t MAX_LOG_LENGTH = 500;
|
|
||||||
#else
|
|
||||||
static constexpr size_t MAX_LOG_LENGTH = 255;
|
static constexpr size_t MAX_LOG_LENGTH = 255;
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new logger with the given name and logging facility.
|
* Create a new logger with the given name and logging facility.
|
||||||
*
|
*
|
||||||
* @param[in] name Logger name (flash string).
|
* @param[in] name Logger name
|
||||||
* @param[in] facility Default logging facility for messages.
|
* @param[in] facility Default logging facility for messages.
|
||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
@@ -339,8 +398,6 @@ class Logger {
|
|||||||
*
|
*
|
||||||
* Call again to change the log level.
|
* Call again to change the log level.
|
||||||
*
|
*
|
||||||
* Do not call this function from a static initializer.
|
|
||||||
*
|
|
||||||
* @param[in] handler Handler object that will handle log
|
* @param[in] handler Handler object that will handle log
|
||||||
* messages.
|
* messages.
|
||||||
* @param[in] level Minimum log level that the handler is
|
* @param[in] level Minimum log level that the handler is
|
||||||
@@ -354,8 +411,6 @@ class Logger {
|
|||||||
*
|
*
|
||||||
* It is safe to call this with a handler that is not registered.
|
* It is safe to call this with a handler that is not registered.
|
||||||
*
|
*
|
||||||
* Do not call this function from a static initializer.
|
|
||||||
*
|
|
||||||
* @param[in] handler Handler object that will no longer handle
|
* @param[in] handler Handler object that will no longer handle
|
||||||
* log messages.
|
* log messages.
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
@@ -367,8 +422,6 @@ class Logger {
|
|||||||
*
|
*
|
||||||
* It is safe to call this with a handler that is not registered.
|
* It is safe to call this with a handler that is not registered.
|
||||||
*
|
*
|
||||||
* Do not call this function from a static initializer.
|
|
||||||
*
|
|
||||||
* @param[in] handler Handler object that may handle log
|
* @param[in] handler Handler object that may handle log
|
||||||
* messages.
|
* messages.
|
||||||
* @return The current log level of the specified handler.
|
* @return The current log level of the specified handler.
|
||||||
@@ -377,17 +430,71 @@ class Logger {
|
|||||||
static Level get_log_level(const Handler * handler);
|
static Level get_log_level(const Handler * handler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the current log level is enabled by any registered
|
* Get the current global log level.
|
||||||
* handlers.
|
|
||||||
*
|
*
|
||||||
* @return The current minimum global log level across all
|
* @return The minimum log level across all handlers.
|
||||||
* handlers.
|
* @since 3.0.0
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
*/
|
||||||
static inline bool enabled(Level level) {
|
static Level global_level() {
|
||||||
return level <= level_;
|
return global_level_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the specified log level is enabled by the effective
|
||||||
|
* log level.
|
||||||
|
*
|
||||||
|
* @param[in] level Log level to check.
|
||||||
|
* @return If the specified log level is enabled on this logger.
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
inline bool enabled(Level level) const {
|
||||||
|
return level <= global_level_ && level <= local_level_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default logging facility for new messages of this logger.
|
||||||
|
*
|
||||||
|
* @return The default logging facility for messages.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
inline Facility facility() const {
|
||||||
|
return facility_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log level.
|
||||||
|
*
|
||||||
|
* The effective log level will be depend on handlers.
|
||||||
|
*
|
||||||
|
* @return The log level of this logger.
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
inline Level level() const {
|
||||||
|
return local_level_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log level.
|
||||||
|
*
|
||||||
|
* The effective log level will be depend on handlers.
|
||||||
|
*
|
||||||
|
* @param[in] level Log level for this logger.
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
inline void level(Level level) {
|
||||||
|
local_level_ = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the effective log level.
|
||||||
|
*
|
||||||
|
* @return The effective log level for this logger.
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
Level effective_level() const {
|
||||||
|
return std::min(global_level(), local_level_);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::EMERG.
|
* Log a message at level Level::EMERG.
|
||||||
*
|
*
|
||||||
@@ -396,14 +503,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void emerg(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void emerg(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::EMERG.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void emerg(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::ALERT.
|
* Log a message at level Level::ALERT.
|
||||||
@@ -413,14 +512,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void alert(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void alert(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::ALERT.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void alert(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::CRIT.
|
* Log a message at level Level::CRIT.
|
||||||
@@ -430,14 +521,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void crit(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void crit(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::CRIT.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void crit(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::ERR.
|
* Log a message at level Level::ERR.
|
||||||
@@ -447,14 +530,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void err(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void err(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::ERR.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void err(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::WARNING.
|
* Log a message at level Level::WARNING.
|
||||||
@@ -464,14 +539,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void warning(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void warning(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::WARNING.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void warning(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::NOTICE.
|
* Log a message at level Level::NOTICE.
|
||||||
@@ -481,14 +548,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void notice(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void notice(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::NOTICE.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void notice(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::INFO.
|
* Log a message at level Level::INFO.
|
||||||
@@ -498,13 +557,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void info(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void info(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::INFO.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
*/
|
|
||||||
void info(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::DEBUG.
|
* Log a message at level Level::DEBUG.
|
||||||
@@ -514,14 +566,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void debug(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void debug(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
/**
|
|
||||||
* Log a message at level Level::DEBUG.
|
|
||||||
*
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void debug(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::TRACE.
|
* Log a message at level Level::TRACE.
|
||||||
@@ -531,14 +575,16 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void trace(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void trace(const char * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at level Level::TRACE.
|
* Log a message with default facility.
|
||||||
*
|
*
|
||||||
* @param[in] format Format string (flash string).
|
* @param[in] level Severity level of the message.
|
||||||
|
* @param[in] format Format string.
|
||||||
* @param[in] ... Format string arguments.
|
* @param[in] ... Format string arguments.
|
||||||
* @since 1.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
void trace(const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 2, 3))) */;
|
void log(Level level, const char * format, ...) const /* __attribute__((format (printf, 3, 4))) */;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message with a custom facility.
|
* Log a message with a custom facility.
|
||||||
@@ -549,17 +595,7 @@ class Logger {
|
|||||||
* @param[in] ... Format string arguments.
|
* @param[in] ... Format string arguments.
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void log(Level level, Facility facility, const char * format, ...) const /* __attribute__((format (printf, 3, 4))) */;
|
void log(Level level, Facility facility, const char * format, ...) const /* __attribute__((format (printf, 4, 5))) */;
|
||||||
/**
|
|
||||||
* Log a message with a custom facility.
|
|
||||||
*
|
|
||||||
* @param[in] level Severity level of the message.
|
|
||||||
* @param[in] facility Facility type of the process logging the message.
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ... Format string arguments.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void log(Level level, Facility facility, const __FlashStringHelper * format, ...) const /* __attribute__((format (printf, 4, 5))) */;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
@@ -568,6 +604,13 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
static void refresh_log_level();
|
static void refresh_log_level();
|
||||||
|
/**
|
||||||
|
* Get registered log handlers.
|
||||||
|
*
|
||||||
|
* @return The registered log handlers.
|
||||||
|
* @since 2.1.2
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<std::map<Handler *, Level>> & registered_handlers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at the specified level.
|
* Log a message at the specified level.
|
||||||
@@ -578,15 +621,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void vlog(Level level, const char * format, va_list ap) const;
|
void vlog(Level level, const char * format, va_list ap) const;
|
||||||
/**
|
|
||||||
* Log a message at the specified level.
|
|
||||||
*
|
|
||||||
* @param[in] level Severity level of the message.
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ap Variable arguments pointer for format string.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void vlog(Level level, const __FlashStringHelper * format, va_list ap) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log a message at the specified level and facility.
|
* Log a message at the specified level and facility.
|
||||||
@@ -598,16 +632,6 @@ class Logger {
|
|||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
void vlog(Level level, Facility facility, const char * format, va_list ap) const;
|
void vlog(Level level, Facility facility, const char * format, va_list ap) const;
|
||||||
/**
|
|
||||||
* Log a message at the specified level and facility.
|
|
||||||
*
|
|
||||||
* @param[in] level Severity level of the message.
|
|
||||||
* @param[in] facility Facility type of the process logging the message.
|
|
||||||
* @param[in] format Format string (flash string).
|
|
||||||
* @param[in] ap Variable arguments pointer for format string.
|
|
||||||
* @since 1.0.0
|
|
||||||
*/
|
|
||||||
void vlog(Level level, Facility facility, const __FlashStringHelper * format, va_list ap) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch a log message to all handlers that are registered to
|
* Dispatch a log message to all handlers that are registered to
|
||||||
@@ -623,15 +647,87 @@ class Logger {
|
|||||||
*/
|
*/
|
||||||
void dispatch(Level level, Facility facility, std::vector<char> & text) const;
|
void dispatch(Level level, Facility facility, std::vector<char> & text) const;
|
||||||
|
|
||||||
static std::map<Handler *, Level> handlers_; /*!< Registered log handlers. @since 1.0.0 */
|
static std::atomic<Level> global_level_; /*!< Minimum global log level across all handlers. @since 3.0.0 */
|
||||||
static Level level_; /*!< Minimum global log level across all handlers. @since 1.0.0 */
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
static std::mutex mutex_; /*!< Mutex for handlers. @since 2.3.0 */
|
||||||
|
#endif
|
||||||
|
|
||||||
const char * name_; /*!< Logger name (flash string). @since 1.0.0 */
|
const char * name_; /*!< Logger name */
|
||||||
const Facility facility_; /*!< Default logging facility for messages. @since 1.0.0 */
|
const Facility facility_; /*!< Default logging facility for messages. @since 1.0.0 */
|
||||||
|
Level local_level_{Level::ALL}; /*!< Logger level. @since 3.0.0 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic log handler for writing messages to any object supporting the
|
||||||
|
* Print interface.
|
||||||
|
*
|
||||||
|
* Outputs all queued messages by default, which may result in the
|
||||||
|
* application blocking until writes complete if the Print destination
|
||||||
|
* buffer is full.
|
||||||
|
*
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
class PrintHandler : public uuid::log::Handler {
|
||||||
|
public:
|
||||||
|
static constexpr size_t MAX_LOG_MESSAGES = 50; /*!< Maximum number of log messages to buffer before they are output. @since 2.2.0 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Print log handler.
|
||||||
|
*
|
||||||
|
* @param[in] print Destination for output of log messages.
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
PrintHandler(Print & print);
|
||||||
|
~PrintHandler() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum number of queued log messages.
|
||||||
|
*
|
||||||
|
* @return The maximum number of queued log messages.
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
size_t maximum_log_messages() const;
|
||||||
|
/**
|
||||||
|
* Set the maximum number of queued log messages.
|
||||||
|
*
|
||||||
|
* Defaults to PrintHandler::MAX_LOG_MESSAGES.
|
||||||
|
*
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
void maximum_log_messages(size_t count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch queued log messages.
|
||||||
|
*
|
||||||
|
* @param[in] count Maximum number of messages to output.
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
void loop(size_t count = SIZE_MAX);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new log message.
|
||||||
|
*
|
||||||
|
* This will be put in a queue for output at the next loop()
|
||||||
|
* process. The queue has a maximum size of
|
||||||
|
* get_maximum_log_messages() and will discard the oldest message
|
||||||
|
* first.
|
||||||
|
*
|
||||||
|
* @param[in] message New log message, shared by all handlers.
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
virtual void operator<<(std::shared_ptr<Message> message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Print & print_; /*!< Destination for output of log messages. @since 2.2.0 */
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
mutable std::mutex mutex_; /*!< Mutex for configuration, state and queued log messages. @since 2.3.0 */
|
||||||
|
#endif
|
||||||
|
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum number of log messages to buffer before they are output. @since 2.2.0 */
|
||||||
|
std::list<std::shared_ptr<Message>> log_messages_; /*!< Queued log messages, in the order they were received. @since 2.2.0 */
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace log
|
} // namespace log
|
||||||
|
|
||||||
} // namespace uuid
|
} // namespace uuid
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include "uuid/syslog.h"
|
#include "uuid/syslog.h"
|
||||||
|
|
||||||
|
|
||||||
#include "../../../src/emsesp.h"
|
#include "../../../src/emsesp.h"
|
||||||
|
|
||||||
#ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
#ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
||||||
@@ -32,12 +31,6 @@
|
|||||||
#define UUID_SYSLOG_HAVE_GETTIMEOFDAY 0
|
#define UUID_SYSLOG_HAVE_GETTIMEOFDAY 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
|
||||||
#if defined(ARDUINO_ARCH_ESP8266)
|
|
||||||
#define UUID_SYSLOG_HAVE_IPADDRESS_TYPE 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#ifndef UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
#define UUID_SYSLOG_HAVE_IPADDRESS_TYPE 0
|
#define UUID_SYSLOG_HAVE_IPADDRESS_TYPE 0
|
||||||
#endif
|
#endif
|
||||||
@@ -77,11 +70,26 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <uuid/common.h>
|
#include <uuid/common.h>
|
||||||
#include <uuid/log.h>
|
#include <uuid/log.h>
|
||||||
|
|
||||||
|
#ifndef UUID_SYSLOG_UDP_BASE_MESSAGE_DELAY
|
||||||
|
#define UUID_SYSLOG_UDP_BASE_MESSAGE_DELAY 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UUID_SYSLOG_UDP_IPV4_ARP_MESSAGE_DELAY
|
||||||
|
#define UUID_SYSLOG_UDP_IPV4_ARP_MESSAGE_DELAY 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UUID_SYSLOG_UDP_IPV6_NDP_MESSAGE_DELAY
|
||||||
|
#define UUID_SYSLOG_UDP_IPV6_NDP_MESSAGE_DELAY 10
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char __pstr__logger_name[] = "syslog";
|
static const char __pstr__logger_name[] = "syslog";
|
||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
@@ -91,10 +99,6 @@ namespace syslog {
|
|||||||
uuid::log::Logger SyslogService::logger_{__pstr__logger_name, uuid::log::Facility::SYSLOG};
|
uuid::log::Logger SyslogService::logger_{__pstr__logger_name, uuid::log::Facility::SYSLOG};
|
||||||
bool SyslogService::QueuedLogMessage::time_good_ = false;
|
bool SyslogService::QueuedLogMessage::time_good_ = false;
|
||||||
|
|
||||||
SyslogService::~SyslogService() {
|
|
||||||
uuid::log::Logger::unregister_handler(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyslogService::start() {
|
void SyslogService::start() {
|
||||||
uuid::log::Logger::register_handler(this, uuid::log::Level::ALL);
|
uuid::log::Logger::register_handler(this, uuid::log::Level::ALL);
|
||||||
}
|
}
|
||||||
@@ -104,6 +108,9 @@ uuid::log::Level SyslogService::log_level() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SyslogService::remove_queued_messages(uuid::log::Level level) {
|
void SyslogService::remove_queued_messages(uuid::log::Level level) {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
unsigned long offset = 0;
|
unsigned long offset = 0;
|
||||||
|
|
||||||
for (auto it = log_messages_.begin(); it != log_messages_.end();) {
|
for (auto it = log_messages_.begin(); it != log_messages_.end();) {
|
||||||
@@ -130,17 +137,23 @@ void SyslogService::log_level(uuid::log::Level level) {
|
|||||||
level_set = true;
|
level_set = true;
|
||||||
|
|
||||||
if (level_changed) {
|
if (level_changed) {
|
||||||
logger_.info(F("Log level set to %S"), uuid::log::format_level_uppercase(level));
|
logger_.info("Log level set to %S", uuid::log::format_level_uppercase(level));
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid::log::Logger::register_handler(this, level);
|
uuid::log::Logger::register_handler(this, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SyslogService::maximum_log_messages() const {
|
size_t SyslogService::maximum_log_messages() const {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
return maximum_log_messages_;
|
return maximum_log_messages_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyslogService::maximum_log_messages(size_t count) {
|
void SyslogService::maximum_log_messages(size_t count) {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
maximum_log_messages_ = std::max((size_t)1, count);
|
maximum_log_messages_ = std::max((size_t)1, count);
|
||||||
|
|
||||||
while (log_messages_.size() > maximum_log_messages_) {
|
while (log_messages_.size() > maximum_log_messages_) {
|
||||||
@@ -148,8 +161,16 @@ void SyslogService::maximum_log_messages(size_t count) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SyslogService::current_log_messages() const {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return log_messages_.size();
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<IPAddress, uint16_t> SyslogService::destination() const {
|
std::pair<IPAddress, uint16_t> SyslogService::destination() const {
|
||||||
return std::make_pair(ip_, port_);
|
return std::make_pair(host_, port_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyslogService::destination(IPAddress host, uint16_t port) {
|
void SyslogService::destination(IPAddress host, uint16_t port) {
|
||||||
@@ -159,28 +180,7 @@ void SyslogService::destination(IPAddress host, uint16_t port) {
|
|||||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
if ((uint32_t)ip_ == (uint32_t)0) {
|
||||||
started_ = false;
|
started_ = false;
|
||||||
remove_queued_messages(log_level());
|
remove_queued_messages(log_level());
|
||||||
host_.clear();
|
// host_.clear();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SyslogService::destination(const char * host, uint16_t port) {
|
|
||||||
if (host == nullptr || host[0] == '\0') {
|
|
||||||
started_ = false;
|
|
||||||
remove_queued_messages(log_level());
|
|
||||||
ip_ = (IPAddress)(uint32_t)0;
|
|
||||||
host_.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
host_ = host;
|
|
||||||
port_ = port;
|
|
||||||
if (ip_.fromString(host)) {
|
|
||||||
host_.clear();
|
|
||||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
|
||||||
started_ = false;
|
|
||||||
remove_queued_messages(log_level());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ip_ = (IPAddress)(uint32_t)0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +207,8 @@ void SyslogService::mark_interval(unsigned long interval) {
|
|||||||
SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
|
SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
|
||||||
: id_(id)
|
: id_(id)
|
||||||
, content_(std::move(content)) {
|
, content_(std::move(content)) {
|
||||||
// Added by proddy - check for Ethernet too. This assumes the network has already started.
|
// Added by proddy for EMS-ESP
|
||||||
|
// check for Ethernet too. This assumes the network has already started.
|
||||||
if (time_good_ || emsesp::EMSESP::system_.network_connected()) {
|
if (time_good_ || emsesp::EMSESP::system_.network_connected()) {
|
||||||
#if UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
#if UUID_SYSLOG_HAVE_GETTIMEOFDAY
|
||||||
if (gettimeofday(&time_, nullptr) != 0) {
|
if (gettimeofday(&time_, nullptr) != 0) {
|
||||||
@@ -229,9 +230,9 @@ SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyslogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
/* Mutex already locked by caller. */
|
||||||
|
void SyslogService::add_message(std::shared_ptr<uuid::log::Message> & message) {
|
||||||
if (log_messages_.size() >= maximum_log_messages_) {
|
if (log_messages_.size() >= maximum_log_messages_) {
|
||||||
log_messages_overflow_ = true;
|
|
||||||
log_messages_.pop_front();
|
log_messages_.pop_front();
|
||||||
log_message_fails_++;
|
log_message_fails_++;
|
||||||
}
|
}
|
||||||
@@ -239,89 +240,140 @@ void SyslogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
|||||||
log_messages_.emplace_back(log_message_id_++, std::move(message));
|
log_messages_.emplace_back(log_message_id_++, std::move(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyslogService::operator<<(std::shared_ptr<uuid::log::Message> message) {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
add_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
void SyslogService::loop() {
|
void SyslogService::loop() {
|
||||||
while (!log_messages_.empty() && can_transmit()) {
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
std::unique_lock<std::mutex> lock{mutex_};
|
||||||
|
#endif
|
||||||
|
size_t count = std::max((size_t)1, MAX_LOG_MESSAGES);
|
||||||
|
|
||||||
|
while (!log_messages_.empty()) {
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!can_transmit())
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (log_messages_.empty())
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
auto message = log_messages_.front();
|
auto message = log_messages_.front();
|
||||||
|
|
||||||
started_ = true;
|
started_ = true;
|
||||||
log_messages_overflow_ = false;
|
|
||||||
auto ok = transmit(message);
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto ok = transmit(message);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
// The transmit() may have called yield() allowing
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
// other messages to have been added to the queue.
|
lock.lock();
|
||||||
if (!log_messages_overflow_) {
|
#endif
|
||||||
|
|
||||||
|
last_message_ = last_transmit_;
|
||||||
|
if (!log_messages_.empty() && log_messages_.front().content_ == message.content_) {
|
||||||
log_messages_.pop_front();
|
log_messages_.pop_front();
|
||||||
}
|
}
|
||||||
last_message_ = uuid::get_uptime_ms();
|
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
lock.unlock();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
::yield();
|
::yield();
|
||||||
|
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
lock.lock();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count--;
|
||||||
|
if (count == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (started_ && mark_interval_ != 0 && log_messages_.empty()) {
|
if (started_ && mark_interval_ != 0 && log_messages_.empty()) {
|
||||||
if (uuid::get_uptime_ms() - last_message_ >= mark_interval_) {
|
if (uuid::get_uptime_ms() - last_message_ >= mark_interval_) {
|
||||||
// This is generated manually because the log level may not
|
// This is generated manually because the log level may not
|
||||||
// be high enough to receive INFO messages.
|
// be high enough to receive INFO messages.
|
||||||
operator<<(std::make_shared<uuid::log::Message>(uuid::get_uptime_ms(),
|
auto message = std::make_shared<uuid::log::Message>(uuid::get_uptime_ms(),
|
||||||
uuid::log::Level::INFO,
|
uuid::log::Level::INFO,
|
||||||
uuid::log::Facility::SYSLOG,
|
uuid::log::Facility::SYSLOG,
|
||||||
(__pstr__logger_name),
|
(__pstr__logger_name),
|
||||||
(F("-- MARK --"))));
|
"-- MARK --");
|
||||||
|
add_message(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyslogService::can_transmit() {
|
bool SyslogService::can_transmit() {
|
||||||
|
// TODO add this like in v3.5?
|
||||||
|
/*
|
||||||
if (!host_.empty() && (uint32_t)ip_ == 0) {
|
if (!host_.empty() && (uint32_t)ip_ == 0) {
|
||||||
WiFi.hostByName(host_.c_str(), ip_);
|
WiFi.hostByName(host_.c_str(), ip_);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
if (ip_.isV4() && (uint32_t)ip_ == (uint32_t)0) {
|
if (host_.isV4() && (uint32_t)host_ == (uint32_t)0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if ((uint32_t)ip_ == (uint32_t)0) {
|
if ((uint32_t)host_ == (uint32_t)0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!emsesp::EMSESP::system_.network_connected()) {
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
return false; // added by proddy. Check Ethernet
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t now = uuid::get_uptime_ms();
|
const uint64_t now = uuid::get_uptime_ms();
|
||||||
uint64_t message_delay = 100;
|
uint64_t message_delay = UUID_SYSLOG_UDP_BASE_MESSAGE_DELAY;
|
||||||
|
|
||||||
#if UUID_SYSLOG_ARP_CHECK
|
#if UUID_SYSLOG_ARP_CHECK
|
||||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
if (ip_.isV4())
|
if (host_.isV4())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
message_delay = 10;
|
message_delay = UUID_SYSLOG_UDP_IPV4_ARP_MESSAGE_DELAY;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
if (ip_.isV6()) {
|
if (host_.isV6()) {
|
||||||
message_delay = 10;
|
message_delay = UUID_SYSLOG_UDP_IPV6_NDP_MESSAGE_DELAY;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (now < last_transmit_ || now - last_transmit_ < message_delay) {
|
if (started_ && (now < last_transmit_ || now - last_transmit_ < message_delay)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UUID_SYSLOG_ARP_CHECK
|
#if UUID_SYSLOG_ARP_CHECK
|
||||||
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
if (ip_.isV4())
|
if (host_.isV4())
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
ip4_addr_t ipaddr;
|
ip4_addr_t ipaddr;
|
||||||
|
|
||||||
ip4_addr_set_u32(&ipaddr, (uint32_t)ip_);
|
ip4_addr_set_u32(&ipaddr, (uint32_t)host_);
|
||||||
|
|
||||||
if (!ip4_addr_isloopback(&ipaddr) && !ip4_addr_ismulticast(&ipaddr) && !ip4_addr_isbroadcast(&ipaddr, netif_default)) {
|
if (!ip4_addr_isloopback(&ipaddr) && !ip4_addr_ismulticast(&ipaddr) && !ip4_addr_isbroadcast(&ipaddr, netif_default)) {
|
||||||
struct eth_addr * eth_ret = nullptr;
|
struct eth_addr * eth_ret = nullptr;
|
||||||
@@ -348,10 +400,10 @@ bool SyslogService::can_transmit() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
|
||||||
if (ip_.isV6()) {
|
if (host_.isV6()) {
|
||||||
ip6_addr_t ip6addr;
|
ip6_addr_t ip6addr;
|
||||||
|
|
||||||
IP6_ADDR(&ip6addr, ip_.raw6()[0], ip_.raw6()[1], ip_.raw6()[2], ip_.raw6()[3]);
|
IP6_ADDR(&ip6addr, host_.raw6()[0], host_.raw6()[1], host_.raw6()[2], host_.raw6()[3]);
|
||||||
ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif_default);
|
ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif_default);
|
||||||
|
|
||||||
if (!ip6_addr_isloopback(&ip6addr) && !ip6_addr_ismulticast(&ip6addr)) {
|
if (!ip6_addr_isloopback(&ip6addr) && !ip6_addr_ismulticast(&ip6addr)) {
|
||||||
@@ -361,26 +413,17 @@ bool SyslogService::can_transmit() {
|
|||||||
|
|
||||||
for (size_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
|
for (size_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
|
||||||
if (ip6_addr_isvalid(netif_ip6_addr_state(netif_default, i))) {
|
if (ip6_addr_isvalid(netif_ip6_addr_state(netif_default, i))) {
|
||||||
if (ip6_addr_isglobal(&ip6addr)) {
|
if (ip6_addr_islinklocal(&ip6addr)) {
|
||||||
if (ip6_addr_isglobal(netif_ip6_addr(netif_default, i))) {
|
|
||||||
have_address = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ip6_addr_issitelocal(&ip6addr)) {
|
|
||||||
if (ip6_addr_issitelocal(netif_ip6_addr(netif_default, i))) {
|
|
||||||
have_address = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ip6_addr_isuniquelocal(&ip6addr)) {
|
|
||||||
if (ip6_addr_isuniquelocal(netif_ip6_addr(netif_default, i))) {
|
|
||||||
have_address = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ip6_addr_islinklocal(&ip6addr)) {
|
|
||||||
if (ip6_addr_islinklocal(netif_ip6_addr(netif_default, i))) {
|
if (ip6_addr_islinklocal(netif_ip6_addr(netif_default, i))) {
|
||||||
have_address = true;
|
have_address = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (ip6_addr_isglobal(&ip6addr) || ip6_addr_isuniquelocal(&ip6addr) || ip6_addr_issitelocal(&ip6addr)) {
|
||||||
|
if (ip6_addr_isglobal(netif_ip6_addr(netif_default, i)) || ip6_addr_isuniquelocal(netif_ip6_addr(netif_default, i))
|
||||||
|
|| ip6_addr_issitelocal(netif_ip6_addr(netif_default, i))) {
|
||||||
|
have_address = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
have_address = true;
|
have_address = true;
|
||||||
break;
|
break;
|
||||||
@@ -408,8 +451,11 @@ bool SyslogService::can_transmit() {
|
|||||||
|
|
||||||
bool SyslogService::transmit(const QueuedLogMessage & message) {
|
bool SyslogService::transmit(const QueuedLogMessage & message) {
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
int8_t tzh = 0;
|
|
||||||
int8_t tzm = 0;
|
// Changes for EMS-ESP by MichaelDvP
|
||||||
|
// TODO add this like in v3.5?
|
||||||
|
// int8_t tzh = 0;
|
||||||
|
// int8_t tzm = 0;
|
||||||
|
|
||||||
tm.tm_year = 0;
|
tm.tm_year = 0;
|
||||||
if (message.time_.tv_sec != (time_t)-1) {
|
if (message.time_.tv_sec != (time_t)-1) {
|
||||||
@@ -418,8 +464,10 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
|
|||||||
localtime_r(&message.time_.tv_sec, &tm);
|
localtime_r(&message.time_.tv_sec, &tm);
|
||||||
int16_t diff = 60 * (tm.tm_hour - utc.tm_hour) + tm.tm_min - utc.tm_min;
|
int16_t diff = 60 * (tm.tm_hour - utc.tm_hour) + tm.tm_min - utc.tm_min;
|
||||||
diff = diff > 720 ? diff - 1440 : diff < -720 ? diff + 1440 : diff;
|
diff = diff > 720 ? diff - 1440 : diff < -720 ? diff + 1440 : diff;
|
||||||
tzh = diff / 60;
|
|
||||||
tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
|
// From previous EMS-ESP. Need to check if still needed.
|
||||||
|
// tzh = diff / 60;
|
||||||
|
// tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udp_.beginPacket(ip_, port_) != 1) {
|
if (udp_.beginPacket(ip_, port_) != 1) {
|
||||||
@@ -427,26 +475,54 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
udp_.printf_P(PSTR("<%u>1 "), ((unsigned int)message.content_->facility * 8) + std::min(7U, (unsigned int)message.content_->level));
|
/*
|
||||||
|
* The level is constrained to 0-7 by design in RFC 5424 because higher
|
||||||
|
* severity values would be a different severity in another facility. The
|
||||||
|
* TRACE level and all other invalid values (because of the conversion to
|
||||||
|
* unsigned) are converted to DEBUG.
|
||||||
|
*
|
||||||
|
* RFC 5424 requires that the facility MUST be 0-23. Values here are allowed
|
||||||
|
* to go up to 31 because there is no obvious candidate to convert them to
|
||||||
|
* and because the multiplication by a factor of 256 means that (with the
|
||||||
|
* cast back to uint8_t) higher values just overflow into other facilities
|
||||||
|
* (which is easier than doing it by modulo 24). The enum doesn't allow
|
||||||
|
* values that are out of range of the RFC.
|
||||||
|
*
|
||||||
|
* The maximum possible priority value does not exceed the requirement that
|
||||||
|
* the PRI part MUST be 3-5 characters.
|
||||||
|
*/
|
||||||
|
udp_.printf("<%u>1 ", (uint8_t)(message.content_->facility * 8U) + std::min(7U, (unsigned int)message.content_->level));
|
||||||
|
|
||||||
if (tm.tm_year != 0) {
|
if (tm.tm_year != 0) {
|
||||||
udp_.printf_P(PSTR("%04u-%02u-%02uT%02u:%02u:%02u.%06u%+02d:%02d"),
|
udp_.printf_P("%04u-%02u-%02uT%02u:%02u:%02u.%06luZ",
|
||||||
tm.tm_year + 1900,
|
tm.tm_year + 1900,
|
||||||
tm.tm_mon + 1,
|
tm.tm_mon + 1,
|
||||||
tm.tm_mday,
|
tm.tm_mday,
|
||||||
tm.tm_hour,
|
tm.tm_hour,
|
||||||
tm.tm_min,
|
tm.tm_min,
|
||||||
tm.tm_sec,
|
tm.tm_sec,
|
||||||
(uint32_t)message.time_.tv_usec,
|
(unsigned long)message.time_.tv_usec);
|
||||||
tzh,
|
|
||||||
tzm);
|
// udp_.printf("%04u-%02u-%02uT%02u:%02u:%02u.%06u%+02d:%02d",
|
||||||
|
// tm.tm_year + 1900,
|
||||||
|
// tm.tm_mon + 1,
|
||||||
|
// tm.tm_mday,
|
||||||
|
// tm.tm_hour,
|
||||||
|
// tm.tm_min,
|
||||||
|
// tm.tm_sec,
|
||||||
|
// (unsigned long)message.time_.tv_usec,
|
||||||
|
// tzh,
|
||||||
|
// tzm);
|
||||||
} else {
|
} else {
|
||||||
udp_.print('-');
|
udp_.print('-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changes for EMS-ESP by MichaelDvP
|
||||||
|
|
||||||
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), (message.content_->name));
|
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), (message.content_->name));
|
||||||
|
|
||||||
char id_c_str[15];
|
char id_c_str[15];
|
||||||
snprintf_P(id_c_str, sizeof(id_c_str), PSTR(" %lu: "), message.id_);
|
snprintf(id_c_str, sizeof(id_c_str), " %lu: ", message.id_);
|
||||||
std::string msgstr = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3) + ' ' + uuid::log::format_level_char(message.content_->level) + id_c_str
|
std::string msgstr = uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3) + ' ' + uuid::log::format_level_char(message.content_->level) + id_c_str
|
||||||
+ message.content_->text;
|
+ message.content_->text;
|
||||||
for (uint16_t i = 0; i < msgstr.length(); i++) {
|
for (uint16_t i = 0; i < msgstr.length(); i++) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* uuid-syslog - Syslog service
|
* uuid-syslog - Microcontroller syslog service
|
||||||
* Copyright 2019 Simon Arlott
|
* Copyright 2019,2021-2022 Simon Arlott
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,21 +20,34 @@
|
|||||||
#define UUID_SYSLOG_H_
|
#define UUID_SYSLOG_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#else
|
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#endif
|
|
||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <uuid/log.h>
|
#include <uuid/log.h>
|
||||||
|
|
||||||
|
#ifndef UUID_LOG_THREAD_SAFE
|
||||||
|
#define UUID_LOG_THREAD_SAFE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#define UUID_COMMON_STD_MUTEX_AVAILABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UUID_COMMON_STD_MUTEX_AVAILABLE
|
||||||
|
#define UUID_SYSLOG_THREAD_SAFE 1
|
||||||
|
#else
|
||||||
|
#define UUID_SYSLOG_THREAD_SAFE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if UUID_LOG_THREAD_SAFE
|
||||||
|
#include <mutex>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace uuid {
|
namespace uuid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,6 +58,17 @@ namespace uuid {
|
|||||||
*/
|
*/
|
||||||
namespace syslog {
|
namespace syslog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread-safe status of the library.
|
||||||
|
*
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
#if UUID_COMMON_THREAD_SAFE && UUID_LOG_THREAD_SAFE && UUID_SYSLOG_THREAD_SAFE
|
||||||
|
static constexpr bool thread_safe = true;
|
||||||
|
#else
|
||||||
|
static constexpr bool thread_safe = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log handler for sending messages to a syslog server.
|
* Log handler for sending messages to a syslog server.
|
||||||
*
|
*
|
||||||
@@ -60,9 +84,8 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
*
|
*
|
||||||
* @since 1.0.0
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
SyslogService() = default;
|
SyslogService() = default;
|
||||||
|
~SyslogService() = default;
|
||||||
~SyslogService();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the log handler with the logging framework.
|
* Register the log handler with the logging framework.
|
||||||
@@ -110,6 +133,14 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
*/
|
*/
|
||||||
void maximum_log_messages(size_t count);
|
void maximum_log_messages(size_t count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current number of queued log messages.
|
||||||
|
*
|
||||||
|
* @return The current number of queued log messages.
|
||||||
|
* @since 2.1.0
|
||||||
|
*/
|
||||||
|
size_t current_log_messages() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the server to send messages to.
|
* Get the server to send messages to.
|
||||||
*
|
*
|
||||||
@@ -129,7 +160,6 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
void destination(IPAddress host, uint16_t port = DEFAULT_PORT);
|
void destination(IPAddress host, uint16_t port = DEFAULT_PORT);
|
||||||
void destination(const char * host, uint16_t port = DEFAULT_PORT);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get local hostname.
|
* Get local hostname.
|
||||||
@@ -185,7 +215,7 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* added MichaelDvP
|
* added for EMS-ESP
|
||||||
* query status variables
|
* query status variables
|
||||||
*/
|
*/
|
||||||
size_t queued() {
|
size_t queued() {
|
||||||
@@ -227,14 +257,27 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content);
|
QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content);
|
||||||
~QueuedLogMessage() = default;
|
~QueuedLogMessage() = default;
|
||||||
|
|
||||||
unsigned long id_; /*!< Sequential identifier for this log message. @since 1.0.0 */
|
unsigned long id_; /*!< Sequential identifier for this log message. @since 1.0.0 */
|
||||||
struct timeval time_; /*!< Time message was received. @since 1.0.0 */
|
struct timeval time_; /*!< Time message was received. @since 1.0.0 */
|
||||||
const std::shared_ptr<const uuid::log::Message> content_; /*!< Log message content. @since 1.0.0 */
|
std::shared_ptr<const uuid::log::Message> content_; /*!< Log message content. @since 1.0.0 */
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool time_good_; /*!< System time appears to be valid. @since 1.0.0 */
|
static bool time_good_; /*!< System time appears to be valid. @since 1.0.0 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new log message.
|
||||||
|
*
|
||||||
|
* This will be put in a queue for output at the next loop()
|
||||||
|
* process. The queue has a maximum size of
|
||||||
|
* get_maximum_log_messages() and will discard the oldest message
|
||||||
|
* first.
|
||||||
|
*
|
||||||
|
* @param[in] message New log message, shared by all handlers.
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
void add_message(std::shared_ptr<uuid::log::Message> & message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove messages that were queued before the log level was set.
|
* Remove messages that were queued before the log level was set.
|
||||||
*
|
*
|
||||||
@@ -264,20 +307,25 @@ class SyslogService : public uuid::log::Handler {
|
|||||||
|
|
||||||
static uuid::log::Logger logger_; /*!< uuid::log::Logger instance for syslog services. @since 1.0.0 */
|
static uuid::log::Logger logger_; /*!< uuid::log::Logger instance for syslog services. @since 1.0.0 */
|
||||||
|
|
||||||
bool started_ = false; /*!< Flag to indicate that messages have started being transmitted. @since 1.0.0 */
|
bool started_ = false; /*!< Flag to indicate that messages have started being transmitted. @since 1.0.0 */
|
||||||
WiFiUDP udp_; /*!< UDP client. @since 1.0.0 */
|
bool level_set_ = false; /*!< Flag to indicate that the log level has been set at least once. @since 2.2.0 */
|
||||||
IPAddress ip_; /*!< Host-IP to send messages to. @since 1.0.0 */
|
WiFiUDP udp_; /*!< UDP client. @since 1.0.0 */
|
||||||
std::string host_; /*!< Host to send messages to. */
|
IPAddress host_; /*!< Host to send messages to. @since 1.0.0 */
|
||||||
uint16_t port_ = DEFAULT_PORT; /*!< Port to send messages to. @since 1.0.0 */
|
uint16_t port_ = DEFAULT_PORT; /*!< Port to send messages to. @since 1.0.0 */
|
||||||
uint64_t last_transmit_ = 0; /*!< Last transmit time. @since 1.0.0 */
|
uint64_t last_transmit_ = 0; /*!< Last transmit time. @since 1.0.0 */
|
||||||
std::string hostname_{'-'}; /*!< Local hostname. @since 1.0.0 */
|
std::string hostname_{'-'}; /*!< Local hostname. @since 1.0.0 */
|
||||||
|
#if UUID_SYSLOG_THREAD_SAFE
|
||||||
|
mutable std::mutex mutex_; /*!< Mutex for queued log messages. @since 2.2.0 */
|
||||||
|
#endif
|
||||||
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum number of log messages to buffer before they are output. @since 1.0.0 */
|
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum number of log messages to buffer before they are output. @since 1.0.0 */
|
||||||
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 1.0.0 */
|
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 1.0.0 */
|
||||||
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 1.0.0 */
|
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 1.0.0 */
|
||||||
std::atomic<bool> log_messages_overflow_{false}; /*!< Check if log messages have overflowed the buffer. @since 1.0.0 */
|
uint64_t mark_interval_ = 0; /*!< Mark interval in milliseconds. @since 2.0.0 */
|
||||||
uint64_t mark_interval_ = 0; /*!< Mark interval in milliseconds. @since 2.0.0 */
|
uint64_t last_message_ = 0; /*!< Last message/mark time. @since 2.0.0 */
|
||||||
uint64_t last_message_ = 0; /*!< Last message/mark time. @since 2.0.0 */
|
|
||||||
unsigned long log_message_fails_ = 0;
|
// added by MichaelDvP for EMS-ESP
|
||||||
|
IPAddress ip_; /*!< Host-IP to send messages to. @since 1.0.0 */
|
||||||
|
unsigned long log_message_fails_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace syslog
|
} // namespace syslog
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ TelnetService::TelnetService(std::shared_ptr<uuid::console::Commands> commands,
|
|||||||
TelnetService::TelnetService(uint16_t port, std::shared_ptr<uuid::console::Commands> commands, unsigned int context, unsigned int flags)
|
TelnetService::TelnetService(uint16_t port, std::shared_ptr<uuid::console::Commands> commands, unsigned int context, unsigned int flags)
|
||||||
: TelnetService(port,
|
: TelnetService(port,
|
||||||
[commands, context, flags](Stream & stream, const IPAddress & addr __attribute__((unused)), uint16_t port __attribute__((unused)))
|
[commands, context, flags](Stream & stream, const IPAddress & addr __attribute__((unused)), uint16_t port __attribute__((unused)))
|
||||||
-> std::shared_ptr<uuid::console::Shell> { return std::make_shared<uuid::console::StreamConsole>(commands, stream, context, flags); }) {
|
-> std::shared_ptr<uuid::console::Shell> { return std::make_shared<uuid::console::Shell>(stream, commands, context, flags); }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TelnetService::TelnetService(shell_factory_function shell_factory)
|
TelnetService::TelnetService(shell_factory_function shell_factory)
|
||||||
@@ -101,14 +101,13 @@ size_t TelnetService::maximum_connections() const {
|
|||||||
void TelnetService::maximum_connections(size_t count) {
|
void TelnetService::maximum_connections(size_t count) {
|
||||||
maximum_connections_ = std::max((size_t)1, count);
|
maximum_connections_ = std::max((size_t)1, count);
|
||||||
|
|
||||||
while (connections_.size() > maximum_connections_) {
|
if (connections_.size() > maximum_connections_) {
|
||||||
|
size_t stop = connections_.size() - maximum_connections_;
|
||||||
|
|
||||||
for (auto it = connections_.begin(); it != connections_.end();) {
|
for (auto it = connections_.begin(); it != connections_.end();) {
|
||||||
if (it->active()) {
|
if (it->stop()) {
|
||||||
it->stop();
|
if (--stop == 0)
|
||||||
it = connections_.erase(it);
|
break;
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
it = connections_.erase(it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,21 +142,21 @@ void TelnetService::loop() {
|
|||||||
if (client) {
|
if (client) {
|
||||||
if (connections_.size() >= maximum_connections_) {
|
if (connections_.size() >= maximum_connections_) {
|
||||||
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
||||||
logger_.info(F("New connection from [%s]:%u rejected (connection limit reached)"),
|
logger_.info("New connection from [%s]:%u rejected (connection limit reached)",
|
||||||
uuid::printable_to_string(client.remoteIP()).c_str(),
|
uuid::printable_to_string(client.remoteIP()).c_str(),
|
||||||
client.remotePort());
|
client.remotePort());
|
||||||
#else
|
#else
|
||||||
logger_.info(F("New connection rejected (connection limit reached)"));
|
logger_.info("New connection rejected (connection limit reached)");
|
||||||
#endif
|
#endif
|
||||||
client.println(F("Maximum connection limit reached"));
|
client.println("Maximum connection limit reached");
|
||||||
client.stop();
|
client.stop();
|
||||||
} else {
|
} else {
|
||||||
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
||||||
logger_.info(F("New connection from [%s]:%u accepted"), uuid::printable_to_string(client.remoteIP()).c_str(), client.remotePort());
|
logger_.info("New connection from [%s]:%u accepted", uuid::printable_to_string(client.remoteIP()).c_str(), client.remotePort());
|
||||||
#endif
|
#endif
|
||||||
connections_.emplace_back(shell_factory_, std::move(client), initial_idle_timeout_, write_timeout_);
|
connections_.emplace_back(shell_factory_, std::move(client), initial_idle_timeout_, write_timeout_);
|
||||||
#if !(UUID_TELNET_HAVE_WIFICLIENT_REMOTE)
|
#if !(UUID_TELNET_HAVE_WIFICLIENT_REMOTE)
|
||||||
logger_.info(F("New connection %p accepted"), &connections_.back());
|
logger_.info("New connection %p accepted", &connections_.back());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,34 +193,37 @@ TelnetService::Connection::Connection(shell_factory_function & shell_factory, Wi
|
|||||||
shell->idle_timeout(idle_timeout);
|
shell->idle_timeout(idle_timeout);
|
||||||
shell->start();
|
shell->start();
|
||||||
shell_ = shell;
|
shell_ = shell;
|
||||||
} else {
|
|
||||||
shell_ = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TelnetService::Connection::active() {
|
|
||||||
return shell_.use_count() > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TelnetService::Connection::loop() {
|
bool TelnetService::Connection::loop() {
|
||||||
if (active()) {
|
if (!shell_.expired()) {
|
||||||
if (!client_.connected()) {
|
if (!client_.connected()) {
|
||||||
shell_->stop();
|
auto shell = shell_.lock();
|
||||||
|
|
||||||
|
if (shell) {
|
||||||
|
shell->stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
#if UUID_TELNET_HAVE_WIFICLIENT_REMOTE
|
||||||
logger_.info(F("Connection from [%s]:%u closed"), uuid::printable_to_string(addr_).c_str(), port_);
|
logger_.info("Connection from [%s]:%u closed", uuid::printable_to_string(addr_).c_str(), port_);
|
||||||
#else
|
#else
|
||||||
logger_.info(F("Connection %p closed"), this);
|
logger_.info("Connection %p closed", this);
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TelnetService::Connection::stop() {
|
bool TelnetService::Connection::stop() {
|
||||||
if (shell_) {
|
auto shell = shell_.lock();
|
||||||
shell_->stop();
|
|
||||||
|
if (shell) {
|
||||||
|
shell->stop();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -392,36 +392,30 @@ class TelnetService {
|
|||||||
Connection(shell_factory_function & shell_factory, WiFiClient && client, unsigned long idle_timeout, unsigned long write_timeout);
|
Connection(shell_factory_function & shell_factory, WiFiClient && client, unsigned long idle_timeout, unsigned long write_timeout);
|
||||||
~Connection() = default;
|
~Connection() = default;
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the shell is still active.
|
|
||||||
*
|
|
||||||
* @return Active status of the shell.
|
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
|
||||||
bool active();
|
|
||||||
/**
|
/**
|
||||||
* Stop the shell if the client is not connected.
|
* Stop the shell if the client is not connected.
|
||||||
*
|
*
|
||||||
* @return Active status of the shell.
|
* @return True if the shell had not already stopped.
|
||||||
* @since 0.1.0
|
* @since 0.1.0
|
||||||
*/
|
*/
|
||||||
bool loop();
|
bool loop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the shell.
|
* Stop the shell.
|
||||||
*
|
*
|
||||||
* @since 0.1.0
|
* @since 0.2.0
|
||||||
*/
|
*/
|
||||||
void stop();
|
bool stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Connection(const Connection &) = delete;
|
Connection(const Connection &) = delete;
|
||||||
Connection & operator=(const Connection &) = delete;
|
Connection & operator=(const Connection &) = delete;
|
||||||
|
|
||||||
WiFiClient client_; /*!< Client connection. @since 0.1.0 */
|
WiFiClient client_; /*!< Client connection. @since 0.1.0 */
|
||||||
TelnetStream stream_; /*!< Telnet stream for the connection. @since 0.1.0 */
|
TelnetStream stream_; /*!< Telnet stream for the connection. @since 0.1.0 */
|
||||||
std::shared_ptr<uuid::console::Shell> shell_; /*!< Shell for connection. @since 0.1.0 */
|
std::weak_ptr<uuid::console::Shell> shell_; /*!< Shell for connection. @since 0.2.0 */
|
||||||
IPAddress addr_; /*!< Remote address of connection. @since 0.1.0 */
|
IPAddress addr_; /*!< Remote address of connection. @since 0.1.0 */
|
||||||
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
|
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
|
||||||
};
|
};
|
||||||
|
|
||||||
TelnetService(const TelnetService &) = delete;
|
TelnetService(const TelnetService &) = delete;
|
||||||
|
|||||||
Reference in New Issue
Block a user