Merge remote-tracking branch 'origin/tech-upgrade' into dev

This commit is contained in:
Proddy
2023-02-27 16:38:38 +01:00
298 changed files with 18497 additions and 24785 deletions

View File

@@ -1,6 +1,13 @@
ArduinoJson: change log
=======================
v6.20.1 (2023-02-08)
-------
* Remove explicit exclusion of `as<char*>()` and `as<char>()` (issue #1860)
If you try to call them, you'll now get the same error message as any unsupported type.
You could also add a custom converter for `char*` and `char`.
v6.20.0 (2022-12-26)
-------

View File

@@ -1,10 +1,10 @@
The MIT License (MIT)
---------------------
Copyright © 2014-2022, Benoit BLANCHON
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The MIT License (MIT)
---------------------
Copyright © 2014-2022, Benoit BLANCHON
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,17 +1,18 @@
<p align="center">
<a href="https://arduinojson.org/"><img alt="ArduinoJson" src="logo.svg" /></a>
<a href="https://arduinojson.org/"><img alt="ArduinoJson" src="https://arduinojson.org/images/logo.svg" width="200" /></a>
</p>
---
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=6.x)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=6.x&logo=github)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
[![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
[![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.20.0&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.20.0)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.20.0)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.20.0)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github)](https://github.com/sponsors/bblanchon)
[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.20.1&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.20.1)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.20.1)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.20.1)
[![ESP IDF](https://img.shields.io/static/v1?label=ESP+IDF&message=v6.20.1&logo=cpu&logoColor=white&color=blue)](https://components.espressif.com/components/bblanchon/arduinojson)
[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)
ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
@@ -99,7 +100,6 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
* [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
* [Discord server](https://discord.gg/DzN6hHHD4h)
## Quickstart

View File

@@ -6,8 +6,8 @@
#include <ArduinoJson/Misc/SafeBoolIdiom.hpp>
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
#include <ArduinoJson/Polyfills/preprocessor.hpp>
#include <ArduinoJson/Polyfills/static_array.hpp>
#if ARDUINOJSON_ENABLE_STD_STREAM
# include <ostream>
@@ -74,16 +74,16 @@ class DeserializationError : public SafeBoolIdom<DeserializationError> {
#if ARDUINOJSON_ENABLE_PROGMEM
const __FlashStringHelper* f_str() const {
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s0, "Ok");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s1, "EmptyInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s2, "IncompleteInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s3, "InvalidInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s4, "NoMemory");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s5, "TooDeep");
ARDUINOJSON_DEFINE_STATIC_ARRAY(
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s0, "Ok");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s1, "EmptyInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s2, "IncompleteInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s3, "InvalidInput");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s4, "NoMemory");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s5, "TooDeep");
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(
const char*, messages, ARDUINOJSON_EXPAND6({s0, s1, s2, s3, s4, s5}));
return ARDUINOJSON_READ_STATIC_ARRAY(const __FlashStringHelper*, messages,
_code);
return reinterpret_cast<const __FlashStringHelper*>(
pgm_read(messages + _code));
}
#endif

View File

@@ -37,7 +37,7 @@ class MsgPackDeserializer {
NestingLimit nestingLimit) {
DeserializationError::Code err;
uint8_t code = 0;
uint8_t code = 0; // TODO: why do we need to initialize this variable?
err = readByte(code);
if (err)
return err;

View File

@@ -10,8 +10,8 @@
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Polyfills/alias_cast.hpp>
#include <ArduinoJson/Polyfills/math.hpp>
#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
#include <ArduinoJson/Polyfills/preprocessor.hpp>
#include <ArduinoJson/Polyfills/static_array.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
namespace ARDUINOJSON_NAMESPACE {
@@ -49,7 +49,7 @@ struct FloatTraits<T, 8 /*64bits*/> {
}
static T positiveBinaryPowerOfTen(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
uint32_t, factors,
ARDUINOJSON_EXPAND18({
0x40240000, 0x00000000, // 1e1
@@ -62,13 +62,12 @@ struct FloatTraits<T, 8 /*64bits*/> {
0x5A827748, 0xF9301D32, // 1e128
0x75154FDD, 0x7F73BF3C // 1e256
}));
return forge(
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
return forge(pgm_read(factors + 2 * index),
pgm_read(factors + 2 * index + 1));
}
static T negativeBinaryPowerOfTen(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
uint32_t, factors,
ARDUINOJSON_EXPAND18({
0x3FB99999, 0x9999999A, // 1e-1
@@ -81,13 +80,12 @@ struct FloatTraits<T, 8 /*64bits*/> {
0x255BBA08, 0xCF8C979D, // 1e-128
0x0AC80628, 0x64AC6F43 // 1e-256
}));
return forge(
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
return forge(pgm_read(factors + 2 * index),
pgm_read(factors + 2 * index + 1));
}
static T negativeBinaryPowerOfTenPlusOne(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY( //
ARDUINOJSON_DEFINE_PROGMEM_ARRAY( //
uint32_t, factors,
ARDUINOJSON_EXPAND18({
0x3FF00000, 0x00000000, // 1e0
@@ -100,9 +98,8 @@ struct FloatTraits<T, 8 /*64bits*/> {
0x25915445, 0x81B7DEC2, // 1e-127
0x0AFE07B2, 0x7DD78B14 // 1e-255
}));
return forge(
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
return forge(pgm_read(factors + 2 * index),
pgm_read(factors + 2 * index + 1));
}
static T nan() {
@@ -175,42 +172,42 @@ struct FloatTraits<T, 4 /*32bits*/> {
}
static T positiveBinaryPowerOfTen(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x41200000, // 1e1f
0x42c80000, // 1e2f
0x461c4000, // 1e4f
0x4cbebc20, // 1e8f
0x5a0e1bca, // 1e16f
0x749dc5ae // 1e32f
}));
return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x41200000, // 1e1f
0x42c80000, // 1e2f
0x461c4000, // 1e4f
0x4cbebc20, // 1e8f
0x5a0e1bca, // 1e16f
0x749dc5ae // 1e32f
}));
return forge(pgm_read(factors + index));
}
static T negativeBinaryPowerOfTen(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x3dcccccd, // 1e-1f
0x3c23d70a, // 1e-2f
0x38d1b717, // 1e-4f
0x322bcc77, // 1e-8f
0x24e69595, // 1e-16f
0x0a4fb11f // 1e-32f
}));
return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x3dcccccd, // 1e-1f
0x3c23d70a, // 1e-2f
0x38d1b717, // 1e-4f
0x322bcc77, // 1e-8f
0x24e69595, // 1e-16f
0x0a4fb11f // 1e-32f
}));
return forge(pgm_read(factors + index));
}
static T negativeBinaryPowerOfTenPlusOne(int index) {
ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x3f800000, // 1e0f
0x3dcccccd, // 1e-1f
0x3a83126f, // 1e-3f
0x33d6bf95, // 1e-7f
0x26901d7d, // 1e-15f
0x0c01ceb3 // 1e-31f
}));
return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
ARDUINOJSON_EXPAND6({
0x3f800000, // 1e0f
0x3dcccccd, // 1e-1f
0x3a83126f, // 1e-3f
0x33d6bf95, // 1e-7f
0x26901d7d, // 1e-15f
0x0c01ceb3 // 1e-31f
}));
return forge(pgm_read(factors + index));
}
static T forge(uint32_t bits) {

View File

@@ -5,20 +5,41 @@
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/pgmspace.hpp>
#include <ArduinoJson/Polyfills/type_traits.hpp>
#if ARDUINOJSON_ENABLE_PROGMEM
# include <ArduinoJson/Polyfills/pgmspace.hpp>
# include <ArduinoJson/Polyfills/type_traits.hpp>
#endif
namespace ARDUINOJSON_NAMESPACE {
template <typename T>
typename enable_if<is_pointer<T>::value, T>::type pgm_read(const void* p) {
return reinterpret_cast<T>(pgm_read_ptr(p));
}
#if ARDUINOJSON_ENABLE_PROGMEM
# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value) \
static type const name[] PROGMEM = value;
# endif
template <typename T>
typename enable_if<is_same<T, uint32_t>::value, T>::type pgm_read(
const void* p) {
inline const T* pgm_read(const T* const* p) {
return reinterpret_cast<const T*>(pgm_read_ptr(p));
}
inline uint32_t pgm_read(const uint32_t* p) {
return pgm_read_dword(p);
}
#else
# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value) \
static type const name[] = value;
# endif
template <typename T>
inline T pgm_read(const T* p) {
return *p;
}
#endif
} // namespace ARDUINOJSON_NAMESPACE

View File

@@ -1,30 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2022, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Configuration.hpp>
#if ARDUINOJSON_ENABLE_PROGMEM
# include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
# ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
# define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value) \
static type const name[] PROGMEM = value;
# endif
# define ARDUINOJSON_DEFINE_STATIC_ARRAY ARDUINOJSON_DEFINE_PROGMEM_ARRAY
# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \
pgm_read<type>(name + index)
#else // i.e. ARDUINOJSON_ENABLE_PROGMEM == 0
# define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
static type const name[] = value;
# define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index]
#endif

View File

@@ -22,7 +22,7 @@ struct Comparer;
template <typename T>
struct Comparer<T, typename enable_if<IsString<T>::value>::type>
: ComparerBase {
T rhs;
T rhs; // TODO: store adapted string?
explicit Comparer(T value) : rhs(value) {}

View File

@@ -46,11 +46,9 @@ class VariantRefBase : public VariantTag {
// Casts the value to the specified type.
// https://arduinojson.org/v6/api/jsonvariant/as/
template <typename T>
FORCE_INLINE typename enable_if<!is_same<T, char*>::value &&
!is_same<T, char>::value &&
!ConverterNeedsWriteableRef<T>::value,
T>::type
as() const {
FORCE_INLINE
typename enable_if<!ConverterNeedsWriteableRef<T>::value, T>::type
as() const {
return Converter<T>::fromJson(getVariantConst());
}

View File

@@ -4,7 +4,7 @@
#pragma once
#define ARDUINOJSON_VERSION "6.20.0"
#define ARDUINOJSON_VERSION "6.20.1"
#define ARDUINOJSON_VERSION_MAJOR 6
#define ARDUINOJSON_VERSION_MINOR 20
#define ARDUINOJSON_VERSION_REVISION 0
#define ARDUINOJSON_VERSION_REVISION 1

View File

@@ -79,7 +79,8 @@ class MsgpackAsyncJsonResponse : public AsyncAbstractResponse {
}
size_t setLength() {
_contentLength = measureMsgPack(_root);
//_headers.add(new AsyncWebHeader("Json-Length", String(_jsonBuffer.memoryUsage()))); // For determening size of EMSESP_JSON_SIZE_XXLARGE_DYN (Sunbuzz)
// EMS-ESP
//_headers.add(new AsyncWebHeader("Json-Length", String(_jsonBuffer.memoryUsage()))); // For determining size of EMSESP_JSON_SIZE_XXLARGE (Sunbuzz)
// Json-Length: 10635
if (_contentLength) {
_isValid = true;

View File

@@ -25,6 +25,7 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest * request) {
AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len);
response->addHeader("Content-Encoding", "gzip");
// response->addHeader("Content-Encoding", "br"); // only works over HTTPS
request->send(response);
};
server->on(uri.c_str(), HTTP_GET, requestHandler);

View File

@@ -31,16 +31,6 @@ class FSPersistence {
DeserializationError error = deserializeJson(jsonDocument, settingsFile);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject jsonObject = jsonDocument.as<JsonObject>();
// debug added by Proddy
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_USE_SERIAL)
Serial.println();
Serial.printf("Reading file: %s: ", _filePath);
serializeJson(jsonDocument, Serial);
Serial.println();
#endif
#endif
_statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
settingsFile.close();
return;
@@ -48,15 +38,8 @@ class FSPersistence {
settingsFile.close();
}
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_USE_SERIAL)
Serial.println();
Serial.printf("Applying defaults for %s: ", _filePath);
Serial.println();
#endif
#endif
// If we reach here we have not been successful in loading the config,
// hard-coded emergency defaults are now applied.
applyDefaults();
writeToFS(); // added to make sure the initial file is created
}
@@ -82,26 +65,9 @@ class FSPersistence {
// failed to open file, return false
if (!settingsFile || !jsonObject.size()) {
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_USE_SERIAL)
Serial.println();
Serial.printf("Cannot write to file system.");
Serial.println();
#endif
#endif
return false;
}
// debug added by Proddy
#if defined(EMSESP_DEBUG)
#if defined(EMSESP_USE_SERIAL)
Serial.println();
Serial.printf("Writing to file: %s: ", _filePath);
serializeJson(jsonDocument, Serial);
Serial.println();
#endif
#endif
// serialize the data to the file
serializeJson(jsonDocument, settingsFile);
settingsFile.close();

View File

@@ -169,6 +169,7 @@ void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
root["ha_enabled"] = settings.ha_enabled;
root["nested_format"] = settings.nested_format;
root["discovery_prefix"] = settings.discovery_prefix;
root["discovery_type"] = settings.discovery_type;
root["publish_single"] = settings.publish_single;
root["publish_single2cmd"] = settings.publish_single2cmd;
root["send_response"] = settings.send_response;
@@ -201,6 +202,7 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
newSettings.ha_enabled = root["ha_enabled"] | EMSESP_DEFAULT_HA_ENABLED;
newSettings.nested_format = root["nested_format"] | EMSESP_DEFAULT_NESTED_FORMAT;
newSettings.discovery_prefix = root["discovery_prefix"] | EMSESP_DEFAULT_DISCOVERY_PREFIX;
newSettings.discovery_type = root["discovery_type"] | EMSESP_DEFAULT_DISCOVERY_TYPE;
newSettings.publish_single = root["publish_single"] | EMSESP_DEFAULT_PUBLISH_SINGLE;
newSettings.publish_single2cmd = root["publish_single2cmd"] | EMSESP_DEFAULT_PUBLISH_SINGLE2CMD;
newSettings.send_response = root["send_response"] | EMSESP_DEFAULT_SEND_RESPONSE;
@@ -223,6 +225,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
changed = true;
}
if (newSettings.discovery_type != settings.discovery_type) {
changed = true;
}
if (newSettings.entity_format != settings.entity_format) {
changed = true;
}

View File

@@ -89,6 +89,7 @@ class MqttSettings {
bool ha_enabled;
uint8_t nested_format;
String discovery_prefix;
uint8_t discovery_type;
bool publish_single;
bool publish_single2cmd;
bool send_response;

View File

@@ -49,7 +49,7 @@ void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
void NTPSettingsService::configureNTP() {
emsesp::EMSESP::system_.ntp_connected(false);
if (connected_ && _state.enabled) {
emsesp::EMSESP::logger().info("Starting NTP");
emsesp::EMSESP::logger().info("Starting NTP service");
sntp_set_sync_interval(3600000); // one hour
sntp_set_time_sync_notification_cb(ntp_received);
configTzTime(_state.tzFormat.c_str(), _state.server.c_str());

View File

@@ -39,12 +39,11 @@ void OTASettingsService::configureArduinoOTA() {
_arduinoOTA->onEnd([]() { emsesp::EMSESP::system_.upload_status(false); });
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
#if defined(EMSESP_USE_SERIAL)
Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
#endif
// Serial.printf("Progress: %u%%\r\n", (progress / (total / 100)));
});
_arduinoOTA->onError([](ota_error_t error) {
#if defined(EMSESP_USE_SERIAL)
/*
#if defined(EMSESP_DEBUG)
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR)
Serial.println("Auth Failed");
@@ -57,6 +56,7 @@ void OTASettingsService::configureArduinoOTA() {
else if (error == OTA_END_ERROR)
Serial.println("End Failed");
#endif
*/
});
_arduinoOTA->setMdnsEnabled(false); // disable as handled in NetworkSettingsService.cpp. https://github.com/emsesp/EMS-ESP32/issues/161

View File

@@ -31,7 +31,7 @@ void RestartService::partition(AsyncWebServerRequest * request) {
uint64_t buffer;
esp_partition_read(ota_partition, 0, &buffer, 8);
if (buffer == 0xFFFFFFFFFFFFFFFF) { // partition empty
request->send(400); // bad request
request->send(400); // bad request
return;
}
esp_ota_set_boot_partition(ota_partition);

View File

@@ -12,13 +12,16 @@ SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityMa
}
void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE);
JsonObject root = response->getRoot();
root["emsesp_version"] = EMSESP_APP_VERSION;
root["esp_platform"] = EMSESP_PLATFORM;
root["cpu_freq_mhz"] = ESP.getCpuFreqMHz();
root["max_alloc_heap"] = ESP.getMaxAllocHeap() / 1024;
root["free_heap"] = ESP.getFreeHeap() / 1024;
root["max_alloc_heap"] = emsesp::EMSESP::system_.getMaxAllocMem();
root["free_heap"] = emsesp::EMSESP::system_.getHeapMem();
root["sdk_version"] = ESP.getSdkVersion();
root["flash_chip_size"] = ESP.getFlashChipSize() / 1024;
root["flash_chip_speed"] = ESP.getFlashChipSpeed();
@@ -28,6 +31,7 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
root["fs_used"] = FSused;
root["fs_free"] = emsesp::EMSESP::system_.FStotal() - FSused;
root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
if (emsesp::EMSESP::system_.PSram()) {
root["psram_size"] = emsesp::EMSESP::system_.PSram();
root["free_psram"] = ESP.getFreePsram() / 1024;
@@ -41,7 +45,7 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
uint64_t buffer;
esp_partition_read(partition, 0, &buffer, 8);
const esp_partition_t * running = esp_ota_get_running_partition();
root["has_loader"] = (buffer != 0xFFFFFFFFFFFFFFFF && running->size != partition->size);
root["has_loader"] = (buffer != 0xFFFFFFFFFFFFFFFF && running->size != partition->size);
}
}

View File

@@ -29,14 +29,6 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
std::string extension = fname.substr(position + 1);
size_t fsize = request->contentLength();
#ifdef EMSESP_DEBUG
#if defined(EMSESP_USE_SERIAL)
Serial.println();
Serial.printf("Received filename: %s, len: %d, index: %d, ext: %s, fsize: %d", filename.c_str(), len, index, extension.c_str(), fsize);
Serial.println();
#endif
#endif
is_firmware = false;
if ((extension == "bin") && (fsize > 1000000)) {
is_firmware = true;
@@ -77,16 +69,13 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
}
#endif
// it's firmware - initialize the ArduinoOTA updater
if (Update.begin(fsize)) {
if (Update.begin()) {
if (strlen(md5) == 32) {
Update.setMD5(md5);
md5[0] = '\0';
}
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
} else {
#if defined(EMSESP_USE_SERIAL)
Update.printError(Serial);
#endif
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
return;
}
@@ -104,16 +93,10 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
// if we haven't delt with an error, continue with the firmware update
if (!request->_tempObject) {
if (Update.write(data, len) != len) {
#if defined(EMSESP_USE_SERIAL)
Update.printError(Serial);
#endif
handleError(request, 500);
}
if (final) {
if (!Update.end(true)) {
#if defined(EMSESP_USE_SERIAL)
Update.printError(Serial);
#endif
handleError(request, 500);
}
}

View File

@@ -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

View File

@@ -22,7 +22,7 @@
namespace uuid {
// added by proddy, modified
// added by proddy for EMS-ESP
static uint64_t now_millis = 0;
// returns system uptime in seconds

View File

@@ -26,6 +26,20 @@
#include <string>
#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.
*
@@ -35,31 +49,15 @@
namespace uuid {
/**
* String compare two flash strings
* Thread-safe status of the library.
*
* The flash string must be stored with appropriate alignment for
* 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
* @since 1.1.2
*/
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b);
int compare_flash_string(const __FlashStringHelper * a, const __FlashStringHelper * b, size_t n);
/**
* Read a string from flash and convert it to a std::string.
*
* 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);
#if UUID_COMMON_THREAD_SAFE
static constexpr bool thread_safe = true;
#else
static constexpr bool thread_safe = false;
#endif
/**
* Append to a std::string by printing a Printable object.
@@ -104,8 +102,8 @@ void loop();
*/
uint64_t get_uptime_ms();
uint32_t get_uptime(); // added by proddy
uint32_t get_uptime_sec(); // added by proddy
uint32_t get_uptime(); // added by proddy for EMS-ESP
uint32_t get_uptime_sec(); // added by proddy for EMS-ESP
void set_uptime();

View File

@@ -1,6 +1,6 @@
/*
* 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
* 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
* 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/>.
*/
@@ -25,6 +25,7 @@
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#ifndef __cpp_lib_make_unique
@@ -43,23 +44,39 @@ namespace uuid {
namespace console {
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) {
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) {
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) {
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) {
add_command(context, flags, name, arguments, function, nullptr);
add_command(context, flags, 0, name, arguments, function, nullptr);
}
void Commands::add_command(unsigned int context,
@@ -68,34 +85,30 @@ void Commands::add_command(unsigned int context,
const string_vector & arguments,
command_function 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);
}
// added by proddy
// note we should really iterate and free up the lambda code and any flashstrings
void Commands::remove_all_commands() {
commands_.clear();
void Commands::add_command(unsigned int context, unsigned int flags, unsigned int not_flags, const string_vector & name, command_function function) {
add_command(context, flags, not_flags, name, string_vector{}, function, nullptr);
}
// added by proddy
// note we should really iterate and free up the lambda code and any flashstrings
void Commands::remove_context_commands(unsigned int context) {
commands_.erase(context);
void Commands::add_command(unsigned int context,
unsigned int flags,
unsigned int not_flags,
const string_vector & name,
const string_vector & arguments,
command_function function) {
add_command(context, flags, not_flags, name, arguments, function, nullptr);
}
/*
auto commands = commands_.equal_range(context);
for (auto command_it = commands.first; command_it != commands.second; command_it++) {
shell.printf("Got: ");
for (auto flash_name : command_it->second.name_) {
shell.printf("%s ", read_flash_string(flash_name).c_str());
}
shell.println();
}
size_t nun = commands_.erase(context);
shell.printfln("Erased %d commands", nun);
*/
void Commands::add_command(unsigned int context,
unsigned int flags,
unsigned int not_flags,
const string_vector & name,
const string_vector & arguments,
command_function function,
argument_completion_function arg_function) {
commands_.emplace(std::piecewise_construct, std::forward_as_tuple(context), std::forward_as_tuple(flags, not_flags, name, arguments, function, arg_function));
}
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;
if (commands.exact.empty()) {
result.error = F("Command not found");
result.error = "Command not found";
} else if (commands.exact.count(longest->first) == 1) {
auto & command = longest->second;
std::vector<std::string> arguments;
@@ -117,16 +130,16 @@ Commands::Execution Commands::execute_command(Shell & shell, CommandLine && comm
command_line.reset();
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()) {
result.error = F("Not enough arguments for command");
result.error = "Not enough arguments for command";
} else if (arguments.size() > command->maximum_arguments()) {
result.error = F("Too many arguments for command");
result.error = "Too many arguments for command";
} else {
command->function_(shell, arguments);
}
} else {
result.error = F("Fatal error (multiple commands found)");
result.error = "Fatal error (multiple commands found)";
}
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 (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;
break;
}
@@ -158,7 +171,7 @@ bool Commands::find_longest_common_prefix(const std::multimap<size_t, const Comm
auto name_it = first.begin();
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++;
}
}
@@ -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++) {
// This relies on the null terminator character limiting the
// 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;
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) {
@@ -223,12 +242,16 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
auto match = commands.partial.begin();
size_t count;
bool multiple_matches;
if (match != commands.partial.end()) {
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()) {
// Use prev() because both iterators must be forwards
match = std::prev(commands.exact.end());
count = commands.exact.count(match->first);
match = std::prev(commands.exact.end());
count = commands.exact.count(match->first);
multiple_matches = false;
} else {
return result;
}
@@ -237,20 +260,13 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
std::vector<std::string> temp_command_name;
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())) {
// There are multiple partial matching commands, find the longest common prefix
if (multiple_matches && (commands.exact.empty() || command_line.total_size() > commands.exact.begin()->second->name_.size())) {
// There are multiple matching commands, find the longest common prefix
bool whole_components = find_longest_common_prefix(commands.partial, temp_command_name);
if (count == 1 && whole_components && temp_command_name.size() == match->first) {
// 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;
}
// Construct a temporary command with the longest common prefix to use as the replacement
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;
match = commands.partial.end();
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
auto & matching_command = match->second;
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()
@@ -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
if (!command_line.trailing_space) {
@@ -340,7 +357,7 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
remaining_help.escape_initial_parameters();
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
result.replacement.trailing_space = true;
}
} else if (count > 1 || temp_command) {
} else if (count != 0) {
// 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;
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) {
// 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--;
}
// Exact match that is shorter than the replacement
if (flash_name_it + skip > (*command_it)->name_.cend()) {
continue;
}
flash_name_it += skip;
while (line_it != command_line->cend()) {
line_it++;
}
}
for (; flash_name_it != command_it->second->name_.cend(); flash_name_it++) {
std::string name = (*flash_name_it);
for (; flash_name_it != (*command_it)->name_.cend(); flash_name_it++) {
std::string name = *flash_name_it;
// Skip parts of the command name that match the command line
if (line_it != command_line->cend()) {
@@ -404,22 +426,22 @@ Commands::Completion Commands::complete_command(Shell & shell, const CommandLine
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
if (line_it != command_line->cend()) {
line_it++;
continue;
}
help->push_back(std::move((argument)));
help->push_back(argument);
}
result.help.push_back(std::move(help));
}
}
if (count > 1 && !commands.exact.empty()) {
// Try to add a space to exact matches
if (multiple_matches && !commands.exact.empty() && result.replacement.total_size() == 0) {
// Try to add a space to exact matches if there's no other partial match replacement
auto longest = commands.exact.crbegin();
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 exact = true;
if (!shell.has_flags(command.flags_)) {
if (!shell.has_flags(command.flags_, command.not_flags_)) {
continue;
}
@@ -457,7 +479,7 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
auto line_it = command_line->cbegin();
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);
if (found == std::string::npos) {
@@ -491,41 +513,21 @@ Commands::Match Commands::find_command(Shell & shell, const CommandLine & comman
} else {
commands.partial.emplace(command.name_.size(), &command);
}
commands.all.push_back(&command);
}
}
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,
unsigned int not_flags,
const string_vector name,
const string_vector arguments,
command_function function,
argument_completion_function arg_function)
: flags_(flags)
, not_flags_(not_flags)
, name_(name)
, arguments_(arguments)
, function_(function)

View 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

View File

@@ -45,21 +45,23 @@ namespace uuid {
namespace console {
// cppcheck-suppress passedByValue
Shell::Shell(std::shared_ptr<Commands> commands, unsigned int context, unsigned int flags)
: commands_(std::move(commands))
Shell::Shell(Stream & stream, std::shared_ptr<Commands> commands, unsigned int context, unsigned int flags)
: stream_(stream)
, commands_(std::move(commands))
, flags_(flags) {
enter_context(context);
}
Shell::~Shell() {
uuid::log::Logger::unregister_handler(this);
void Shell::started() {
}
bool Shell::running() const {
return !stopped_;
}
void Shell::start() {
#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::INFO); // added by proddy
#if defined(EMSESP_DEBUG)
uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added for EMS-ESP
#else
uuid::log::Logger::register_handler(this, uuid::log::Level::INFO);
#endif
@@ -71,18 +73,11 @@ void Shell::start() {
}
display_banner();
display_prompt();
shells_.insert(shared_from_this());
registered_shells().insert(shared_from_this());
idle_time_ = uuid::get_uptime_ms();
started();
};
void Shell::started() {
}
bool Shell::running() const {
return !stopped_;
}
void Shell::stop() {
if (mode_ == Mode::BLOCKING) {
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() {
if (!running()) {
return;
@@ -144,7 +143,7 @@ void Shell::set_command_str(const char * str) {
}
void Shell::loop_normal() {
const int input = read_one_char();
const int input = stream_.read();
if (input < 0) {
check_idle_timeout();
@@ -160,6 +159,8 @@ void Shell::loop_normal() {
println();
cursor_ = 0;
line_no_ = 0;
// prompt_displayed_ = false;
// display_prompt();
break;
case '\x04':
@@ -264,7 +265,7 @@ void Shell::loop_normal() {
} else if (esc_ == 11) { // F1
set_command_str("help");
} else if (esc_ == 12) { // F2
set_command_str("show");
set_command_str("show values");
} else if (esc_ == 13) { // F3
set_command_str("log notice");
} else if (esc_ == 14) { // F4
@@ -304,18 +305,20 @@ void Shell::loop_normal() {
}
// common for all, display the complete line
// added for EMS-ESP
erase_current_line();
prompt_displayed_ = false;
display_prompt();
if (cursor_) {
printf(F("\033[%dD"), cursor_);
printf("\033[%dD", cursor_);
}
previous_ = c;
// This is a hack to let TelnetStream know that command
// execution is complete and that output can be flushed.
available_char();
stream_.available();
idle_time_ = uuid::get_uptime_ms();
}
@@ -326,7 +329,7 @@ Shell::PasswordData::PasswordData(const char * password_prompt, password_functio
}
void Shell::loop_password() {
const int input = read_one_char();
const int input = stream_.read();
if (input < 0) {
check_idle_timeout();
@@ -393,7 +396,7 @@ void Shell::loop_password() {
// This is a hack to let TelnetStream know that command
// execution is complete and that output can be flushed.
available_char();
stream_.available();
idle_time_ = uuid::get_uptime_ms();
}
@@ -414,9 +417,10 @@ void Shell::loop_delay() {
function_copy(*this);
// if (running()) {
// display_prompt();
// }
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}
idle_time_ = uuid::get_uptime_ms();
}
@@ -444,9 +448,10 @@ void Shell::loop_blocking() {
stop();
}
// if (running()) {
// display_prompt();
// }
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}
idle_time_ = uuid::get_uptime_ms();
}
@@ -513,14 +518,17 @@ void Shell::maximum_command_line_length(size_t length) {
}
void Shell::process_command() {
// added for EMS-ESP
if (line_buffer_.empty()) {
println();
return;
}
uint8_t no = line_no_ ? line_no_ : MAX_LINES;
while (--no) {
line_old_[no] = line_old_[no - 1];
}
line_no_ = 0;
line_old_[0] = line_buffer_;
while (!line_buffer_.empty()) {
@@ -534,6 +542,7 @@ void Shell::process_command() {
line_buffer_.clear();
cursor_ = 0;
}
CommandLine command_line{line1};
println();
@@ -542,18 +551,23 @@ void Shell::process_command() {
if (!command_line->empty()) {
if (commands_) {
auto execution = commands_->execute_command(*this, std::move(command_line));
if (execution.error != nullptr) {
println(execution.error);
}
} else {
println(F("No commands configured"));
println("No commands configured");
}
}
::yield();
}
// if (running()) {
// display_prompt();
// }
// TODO comment this block out like we had < v3.5?
if (running()) {
display_prompt();
}
// don't think we need this for EMS-ESP on ESP32
// ::yield();
}
void Shell::process_completion() {
@@ -561,9 +575,11 @@ void Shell::process_completion() {
if (!command_line->empty() && commands_) {
auto completion = commands_->complete_command(*this, command_line);
bool redisplay = false;
if (!completion.help.empty()) {
println();
redisplay = true;
for (auto & help : completion.help) {
std::string help_line = help.to_string(maximum_command_line_length_);
@@ -573,8 +589,18 @@ void Shell::process_completion() {
}
if (!completion.replacement->empty()) {
if (!redisplay) {
erase_current_line();
prompt_displayed_ = false;
redisplay = true;
}
line_buffer_ = completion.replacement.to_string(maximum_command_line_length_);
}
if (redisplay) {
display_prompt();
}
}
::yield();
@@ -599,9 +625,15 @@ void Shell::process_password(bool completed) {
void Shell::invoke_command(const std::string & line) {
erase_current_line();
prompt_displayed_ = false;
line_buffer_ = line;
display_prompt();
if (!line_buffer_.empty()) {
println();
prompt_displayed_ = false;
}
if (!prompt_displayed_) {
display_prompt();
}
line_buffer_ = line;
print(line_buffer_);
process_command();
}

View File

@@ -22,6 +22,9 @@
#include <algorithm>
#include <memory>
#if UUID_CONSOLE_THREAD_SAFE
#include <mutex>
#endif
#include <string>
#include <uuid/log.h>
@@ -30,8 +33,13 @@ namespace uuid {
namespace console {
static const char __pstr__logger_name[] = "shell";
const uuid::log::Logger Shell::logger_{(__pstr__logger_name), uuid::log::Facility::LPR};
static const char __pstr__logger_name[] = "shell";
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)
: 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) {
#if UUID_CONSOLE_THREAD_SAFE
std::lock_guard<std::mutex> lock{mutex_};
#endif
if (log_messages_.size() >= maximum_log_messages_) {
log_messages_.pop_front();
}
@@ -55,10 +66,18 @@ void Shell::log_level(uuid::log::Level level) {
}
size_t Shell::maximum_log_messages() const {
#if UUID_CONSOLE_THREAD_SAFE
std::lock_guard<std::mutex> lock{mutex_};
#endif
return maximum_log_messages_;
}
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);
while (log_messages_.size() > maximum_log_messages_) {
log_messages_.pop_front();
@@ -66,39 +85,71 @@ void Shell::maximum_log_messages(size_t count) {
}
void Shell::output_logs() {
if (!log_messages_.empty()) {
if (mode_ != Mode::DELAY) {
erase_current_line();
prompt_displayed_ = false;
}
#if UUID_CONSOLE_THREAD_SAFE
std::unique_lock<std::mutex> lock{mutex_};
#endif
while (!log_messages_.empty()) {
auto message = std::move(log_messages_.front());
log_messages_.pop_front();
if (log_messages_.empty())
return;
print(uuid::log::format_timestamp_ms(message.content_->uptime_ms, 3));
printf(F(" %c %lu: [%S] "), uuid::log::format_level_char(message.content_->level), message.id_, message.content_->name);
size_t count = std::max((size_t)1, MAX_LOG_MESSAGES);
auto message = log_messages_.front();
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);
}
log_messages_.pop_front();
#if UUID_CONSOLE_THREAD_SAFE
lock.unlock();
#endif
::yield();
}
display_prompt();
if (mode_ != Mode::DELAY) {
erase_current_line();
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

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -25,15 +25,21 @@ namespace uuid {
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() {
for (auto shell = shells_.begin(); shell != shells_.end();) {
auto & shells = registered_shells();
for (auto shell = shells.begin(); shell != shells.end();) {
shell->get()->loop_one();
// This avoids copying the shared_ptr every time loop_one() is called
if (!shell->get()->running()) {
shell = shells_.erase(shell);
shell = shells.erase(shell);
} else {
shell++;
}

View File

@@ -51,16 +51,6 @@ size_t Shell::printf(const char * format, ...) {
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, ...) {
va_list ap;
@@ -72,17 +62,6 @@ size_t Shell::printfln(const char * format, ...) {
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 print_len = 0;
va_list copy_ap;
@@ -101,64 +80,24 @@ size_t Shell::vprintf(const char * format, va_list ap) {
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() {
/*
commands_->for_each_available_command(*this, [this](std::vector<std::string> & name, std::vector<std::string> & arguments) {
CommandLine command_line{name, arguments};
for (auto & available_command : available_commands()) {
CommandLine command_line{available_command.name(), available_command.arguments()};
command_line.escape_initial_parameters(name.size());
name.clear();
arguments.clear();
command_line.escape_initial_parameters(available_command.name().size());
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() {
print(F("\033[0G\033[K"));
print("\033[0G\033[K");
}
void Shell::erase_characters(size_t count) {
print(std::string(count, '\x08'));
print(F("\033[K"));
print("\033[K");
}
} // namespace console
} // namespace uuid
} // namespace uuid

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -28,12 +28,12 @@ int Shell::available() {
if (mode_ == Mode::BLOCKING) {
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
if (!available_char()) {
if (!stream_.available()) {
return 0;
}
if (blocking_data->consume_line_feed_) {
const int input = peek_one_char();
const int input = stream_.peek();
if (input >= 0) {
const unsigned char c = input;
@@ -42,7 +42,7 @@ int Shell::available() {
if (previous_ == '\x0D' && c == '\x0A') {
// Consume the first LF following a CR
read_one_char();
stream_.read();
previous_ = c;
return available();
}
@@ -62,7 +62,7 @@ int Shell::available() {
int Shell::read() {
if (mode_ == Mode::BLOCKING) {
auto * blocking_data = reinterpret_cast<Shell::BlockingData *>(mode_data_.get());
const int input = read_one_char();
const int input = stream_.read();
if (input >= 0) {
const unsigned char c = input;
@@ -90,7 +90,7 @@ int Shell::read() {
int Shell::peek() {
if (mode_ == Mode::BLOCKING) {
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 (input >= 0) {
@@ -100,7 +100,7 @@ int Shell::peek() {
if (previous_ == '\x0D' && c == '\x0A') {
// Consume the first LF following a CR
read_one_char();
stream_.read();
previous_ = c;
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() {
// 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
@@ -122,4 +131,4 @@ void Shell::flush() {
} // namespace console
} // namespace uuid
} // namespace uuid

View File

@@ -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

View File

@@ -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_all = "all";
static const __FlashStringHelper * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_off),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_emerg),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_crit),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_alert),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_err),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_warning),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_notice),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_info),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_debug),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_trace),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_lowercase_all)};
static const char * log_level_lowercase[(int)Level::ALL - (int)Level::OFF + 1] = {pstr_level_lowercase_off,
pstr_level_lowercase_emerg,
pstr_level_lowercase_crit,
pstr_level_lowercase_alert,
pstr_level_lowercase_err,
pstr_level_lowercase_warning,
pstr_level_lowercase_notice,
pstr_level_lowercase_info,
pstr_level_lowercase_debug,
pstr_level_lowercase_trace,
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];
}

View File

@@ -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_all = "ALL";
static const __FlashStringHelper * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] __attribute__((__aligned__(sizeof(uint32_t))))
PROGMEM = {reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_off),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_emerg),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_crit),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_alert),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_err),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_warning),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_notice),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_info),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_debug),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_trace),
reinterpret_cast<const __FlashStringHelper *>(pstr_level_uppercase_all)};
static const char * log_level_uppercase[(int)Level::ALL - (int)Level::OFF + 1] = {pstr_level_uppercase_off,
pstr_level_uppercase_emerg,
pstr_level_uppercase_crit,
pstr_level_uppercase_alert,
pstr_level_uppercase_err,
pstr_level_uppercase_warning,
pstr_level_uppercase_notice,
pstr_level_uppercase_info,
pstr_level_uppercase_debug,
pstr_level_uppercase_trace,
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];
}

View File

@@ -20,6 +20,7 @@
#include <Arduino.h>
#include <array>
#include <cstdint>
#include <string>
@@ -45,7 +46,7 @@ std::string format_timestamp_ms(uint64_t timestamp_ms, unsigned int days_width)
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);

View File

@@ -1,6 +1,6 @@
/*
* uuid-common - Microcontroller common utilities
* Copyright 2019 Simon Arlott
* uuid-log - Microcontroller logging framework
* Copyright 2021 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
@@ -16,24 +16,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <uuid/common.h>
#include <Arduino.h>
#include <string>
#include <uuid/log.h>
namespace uuid {
std::string read_flash_string(const __FlashStringHelper * flash_str) {
if (flash_str == nullptr) {
return std::string(""); // prevent crash
}
namespace log {
std::string str(::strlen_P(reinterpret_cast<PGM_P>(flash_str)), '\0');
::strncpy_P(&str[0], reinterpret_cast<PGM_P>(flash_str), str.capacity() + 1);
return str;
Handler::~Handler() {
Logger::unregister_handler(this);
}
} // namespace log
} // namespace uuid

View File

@@ -25,17 +25,7 @@ namespace uuid {
namespace log {
std::vector<Level> levels() {
return {Level::OFF,
Level::EMERG,
Level::ALERT,
Level::CRIT,
Level::ERR,
Level::WARNING,
Level::NOTICE,
Level::INFO,
Level::DEBUG,
Level::TRACE,
Level::ALL};
return {Level::OFF, Level::EMERG, Level::ALERT, Level::CRIT, Level::ERR, Level::WARNING, Level::NOTICE, Level::INFO, Level::DEBUG, Level::TRACE, Level::ALL};
}
} // namespace log

View File

@@ -27,17 +27,17 @@ namespace uuid {
namespace log {
std::vector<std::string> levels_lowercase() {
return {uuid::read_flash_string(format_level_lowercase(Level::OFF)),
uuid::read_flash_string(format_level_lowercase(Level::EMERG)),
uuid::read_flash_string(format_level_lowercase(Level::ALERT)),
uuid::read_flash_string(format_level_lowercase(Level::CRIT)),
uuid::read_flash_string(format_level_lowercase(Level::ERR)),
uuid::read_flash_string(format_level_lowercase(Level::WARNING)),
uuid::read_flash_string(format_level_lowercase(Level::NOTICE)),
uuid::read_flash_string(format_level_lowercase(Level::INFO)),
uuid::read_flash_string(format_level_lowercase(Level::DEBUG)),
uuid::read_flash_string(format_level_lowercase(Level::TRACE)),
uuid::read_flash_string(format_level_lowercase(Level::ALL))};
return {format_level_lowercase(Level::OFF),
format_level_lowercase(Level::EMERG),
format_level_lowercase(Level::ALERT),
format_level_lowercase(Level::CRIT),
format_level_lowercase(Level::ERR),
format_level_lowercase(Level::WARNING),
format_level_lowercase(Level::NOTICE),
format_level_lowercase(Level::INFO),
format_level_lowercase(Level::DEBUG),
format_level_lowercase(Level::TRACE),
format_level_lowercase(Level::ALL)};
}
} // namespace log

View File

@@ -27,17 +27,17 @@ namespace uuid {
namespace log {
std::vector<std::string> levels_uppercase() {
return {uuid::read_flash_string(format_level_uppercase(Level::OFF)),
uuid::read_flash_string(format_level_uppercase(Level::EMERG)),
uuid::read_flash_string(format_level_uppercase(Level::ALERT)),
uuid::read_flash_string(format_level_uppercase(Level::CRIT)),
uuid::read_flash_string(format_level_uppercase(Level::ERR)),
uuid::read_flash_string(format_level_uppercase(Level::WARNING)),
uuid::read_flash_string(format_level_uppercase(Level::NOTICE)),
uuid::read_flash_string(format_level_uppercase(Level::INFO)),
uuid::read_flash_string(format_level_uppercase(Level::DEBUG)),
uuid::read_flash_string(format_level_uppercase(Level::TRACE)),
uuid::read_flash_string(format_level_uppercase(Level::ALL))};
return {format_level_uppercase(Level::OFF),
format_level_uppercase(Level::EMERG),
format_level_uppercase(Level::ALERT),
format_level_uppercase(Level::CRIT),
format_level_uppercase(Level::ERR),
format_level_uppercase(Level::WARNING),
format_level_uppercase(Level::NOTICE),
format_level_uppercase(Level::INFO),
format_level_uppercase(Level::DEBUG),
format_level_uppercase(Level::TRACE),
format_level_uppercase(Level::ALL)};
}
} // namespace log

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -20,11 +20,15 @@
#include <Arduino.h>
#include <atomic>
#include <cstdarg>
#include <cstdint>
#include <list>
#include <map>
#include <memory>
#if UUID_LOG_THREAD_SAFE
#include <mutex>
#endif
#include <string>
#include <utility>
#include <vector>
@@ -33,8 +37,21 @@ namespace uuid {
namespace log {
std::map<Handler *, Level> Logger::handlers_;
Level Logger::level_ = Level::OFF;
std::atomic<Level> Logger::global_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)
: 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) {
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();
};
void Logger::unregister_handler(Handler * handler) {
handlers_.erase(handler);
refresh_log_level();
auto handlers = handler->handlers_.lock();
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) {
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;
}
@@ -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 {
if (enabled(Level::CRIT)) {
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 {
if (enabled(Level::ALERT)) {
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 {
if (enabled(Level::ERR)) {
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 {
if (enabled(Level::WARNING)) {
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 {
if (enabled(Level::NOTICE)) {
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 {
if (enabled(Level::INFO)) {
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 {
if (enabled(Level::DEBUG)) {
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 {
if (enabled(Level::TRACE)) {
va_list ap;
@@ -239,38 +203,20 @@ void Logger::trace(const char * format, ...) const {
}
};
void Logger::trace(const __FlashStringHelper * format, ...) const {
if (enabled(Level::TRACE)) {
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;
}
void Logger::log(Level level, const char * format, ...) const {
level = constrain_level(level);
if (enabled(level)) {
va_list ap;
va_start(ap, format);
vlog(level, facility, format, ap);
vlog(level, format, ap);
va_end(ap);
}
};
void Logger::log(Level level, Facility facility, const __FlashStringHelper * format, ...) const {
if (level < Level::EMERG) {
level = Level::EMERG;
} else if (level > Level::TRACE) {
level = Level::TRACE;
}
void Logger::log(Level level, Facility facility, const char * format, ...) const {
level = constrain_level(level);
if (enabled(level)) {
va_list ap;
@@ -295,41 +241,34 @@ void Logger::vlog(Level level, Facility facility, const char * format, va_list a
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 {
std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text.data());
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) {
*handler.first << message;
}
}
}
/* Mutex already locked by caller. */
void Logger::refresh_log_level() {
level_ = Level::OFF;
Level level = Level::OFF;
for (auto & handler : handlers_) {
if (level_ < handler.second) {
level_ = handler.second;
for (auto & handler : *registered_handlers()) {
if (level < handler.second) {
level = handler.second;
}
}
global_level_ = level;
}
} // namespace log
} // namespace uuid
} // namespace uuid

View File

@@ -28,7 +28,7 @@ namespace log {
bool parse_level_lowercase(const std::string & name, Level & level) {
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;
return true;
}

View File

@@ -28,7 +28,7 @@ namespace log {
bool parse_level_uppercase(const std::string & name, Level & level) {
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;
return true;
}

View 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

View File

@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
#include <Arduino.h>
#include <algorithm>
#include <atomic>
#include <cstdarg>
#include <cstdint>
#include <list>
@@ -31,6 +33,24 @@
#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 {
/**
@@ -70,6 +90,17 @@ namespace uuid {
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.
*
@@ -158,10 +189,10 @@ char format_level_char(Level level);
* Format a log level as an uppercase string.
*
* @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
*/
const __FlashStringHelper * format_level_uppercase(Level level);
const char * format_level_uppercase(Level level);
/**
* 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.
*
* @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
*/
const __FlashStringHelper * format_level_lowercase(Level level);
const char * format_level_lowercase(Level level);
/**
* 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] level Severity level of 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.
* @since 1.0.0
*/
@@ -257,7 +288,7 @@ struct Message {
const Facility facility;
/**
* Name of the logger used (flash string).
* Name of the logger used
*
* @since 1.0.0
*/
@@ -274,14 +305,24 @@ struct Message {
const std::string text;
};
class Logger;
/**
* Logger handler used to process log messages.
*
* @since 1.0.0
*/
class Handler {
/**
* Logger needs to be able to access the private reference to
* the registered log handlers.
*
* @since 2.1.2
*/
friend Logger;
public:
virtual ~Handler() = default;
virtual ~Handler();
/**
* Add a new log message.
@@ -290,9 +331,20 @@ class Handler {
* processed immediately so that log messages have minimal impact
* 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
* 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.
* @since 1.0.0
*/
@@ -300,6 +352,17 @@ class Handler {
protected:
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 {
public:
/**
/**
* This is the maximum length of any log message.
*
* Determines the size of the buffer used for format string
@@ -317,16 +380,12 @@ class Logger {
*
* @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;
#endif
/**
* 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.
*
* @since 1.0.0
@@ -339,8 +398,6 @@ class Logger {
*
* 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
* messages.
* @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.
*
* Do not call this function from a static initializer.
*
* @param[in] handler Handler object that will no longer handle
* log messages.
* @since 1.0.0
@@ -367,8 +422,6 @@ class Logger {
*
* 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
* messages.
* @return The current log level of the specified handler.
@@ -377,17 +430,71 @@ class Logger {
static Level get_log_level(const Handler * handler);
/**
* Determine if the current log level is enabled by any registered
* handlers.
* Get the current global log level.
*
* @return The current minimum global log level across all
* handlers.
* @since 1.0.0
* @return The minimum log level across all handlers.
* @since 3.0.0
*/
static inline bool enabled(Level level) {
return level <= level_;
static Level global_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.
*
@@ -396,14 +503,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -413,14 +512,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -430,14 +521,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -447,14 +530,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -464,14 +539,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -481,14 +548,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -498,13 +557,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -514,14 +566,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -531,14 +575,16 @@ class Logger {
* @since 1.0.0
*/
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.
* @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.
@@ -549,17 +595,7 @@ class Logger {
* @param[in] ... Format string arguments.
* @since 1.0.0
*/
void log(Level level, Facility facility, const char * format, ...) const /* __attribute__((format (printf, 3, 4))) */;
/**
* 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))) */;
void log(Level level, Facility facility, const char * format, ...) const /* __attribute__((format (printf, 4, 5))) */;
private:
/**
@@ -568,6 +604,13 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -578,15 +621,6 @@ class Logger {
* @since 1.0.0
*/
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.
@@ -598,16 +632,6 @@ class Logger {
* @since 1.0.0
*/
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
@@ -623,15 +647,87 @@ class Logger {
*/
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 Level level_; /*!< Minimum global log level across all handlers. @since 1.0.0 */
static std::atomic<Level> global_level_; /*!< Minimum global log level across all handlers. @since 3.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 Facility facility_; /*!< Default logging facility for messages. @since 1.0.0 */
const char * name_; /*!< Logger name */
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 uuid
#endif
#endif

View File

@@ -18,7 +18,6 @@
#include "uuid/syslog.h"
#include "../../../src/emsesp.h"
#ifndef UUID_SYSLOG_HAVE_GETTIMEOFDAY
@@ -32,12 +31,6 @@
#define UUID_SYSLOG_HAVE_GETTIMEOFDAY 0
#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
#define UUID_SYSLOG_HAVE_IPADDRESS_TYPE 0
#endif
@@ -77,11 +70,26 @@
#include <algorithm>
#include <list>
#include <memory>
#if UUID_SYSLOG_THREAD_SAFE
#include <mutex>
#endif
#include <string>
#include <uuid/common.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";
namespace uuid {
@@ -91,10 +99,6 @@ namespace syslog {
uuid::log::Logger SyslogService::logger_{__pstr__logger_name, uuid::log::Facility::SYSLOG};
bool SyslogService::QueuedLogMessage::time_good_ = false;
SyslogService::~SyslogService() {
uuid::log::Logger::unregister_handler(this);
}
void SyslogService::start() {
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) {
#if UUID_SYSLOG_THREAD_SAFE
std::lock_guard<std::mutex> lock{mutex_};
#endif
unsigned long offset = 0;
for (auto it = log_messages_.begin(); it != log_messages_.end();) {
@@ -125,22 +132,32 @@ void SyslogService::log_level(uuid::log::Level level) {
remove_queued_messages(level);
}
static bool level_set = false;
bool level_changed = !level_set || (level != log_level());
level_set = true;
// commented out for EMS-ESP
/*
static bool level_set = false;
level_set = true;
bool level_changed = !level_set || (level != log_level());
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);
}
size_t SyslogService::maximum_log_messages() const {
#if UUID_SYSLOG_THREAD_SAFE
std::lock_guard<std::mutex> lock{mutex_};
#endif
return maximum_log_messages_;
}
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);
while (log_messages_.size() > maximum_log_messages_) {
@@ -148,12 +165,20 @@ 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 {
return std::make_pair(ip_, port_);
}
void SyslogService::destination(IPAddress host, uint16_t port) {
ip_ = host;
void SyslogService::destination(IPAddress ip, uint16_t port) {
ip_ = ip;
port_ = port;
if ((uint32_t)ip_ == (uint32_t)0) {
@@ -207,7 +232,8 @@ void SyslogService::mark_interval(unsigned long interval) {
SyslogService::QueuedLogMessage::QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content)
: id_(id)
, content_(std::move(content)) {
// Added by proddy - check for Ethernet too. This assumes the network has already started.
// Added for EMS-ESP
// check for Ethernet too. This assumes the network has already started.
if (time_good_ || emsesp::EMSESP::system_.network_connected()) {
#if UUID_SYSLOG_HAVE_GETTIMEOFDAY
if (gettimeofday(&time_, nullptr) != 0) {
@@ -229,9 +255,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_) {
log_messages_overflow_ = true;
log_messages_.pop_front();
log_message_fails_++;
}
@@ -239,46 +265,95 @@ void SyslogService::operator<<(std::shared_ptr<uuid::log::Message> 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() {
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();
started_ = true;
log_messages_overflow_ = false;
auto ok = transmit(message);
started_ = true;
#if UUID_SYSLOG_THREAD_SAFE
lock.unlock();
#endif
auto ok = transmit(message);
if (ok) {
// The transmit() may have called yield() allowing
// other messages to have been added to the queue.
if (!log_messages_overflow_) {
#if UUID_SYSLOG_THREAD_SAFE
lock.lock();
#endif
last_message_ = last_transmit_;
if (!log_messages_.empty() && log_messages_.front().content_ == message.content_) {
log_messages_.pop_front();
}
last_message_ = uuid::get_uptime_ms();
#if UUID_SYSLOG_THREAD_SAFE
lock.unlock();
#endif
}
::yield();
#if UUID_SYSLOG_THREAD_SAFE
lock.lock();
#endif
if (!ok) {
break;
}
count--;
if (count == 0) {
break;
}
}
if (started_ && mark_interval_ != 0 && log_messages_.empty()) {
if (uuid::get_uptime_ms() - last_message_ >= mark_interval_) {
// This is generated manually because the log level may not
// be high enough to receive INFO messages.
operator<<(std::make_shared<uuid::log::Message>(uuid::get_uptime_ms(),
uuid::log::Level::INFO,
uuid::log::Facility::SYSLOG,
(__pstr__logger_name),
(F("-- MARK --"))));
auto message = std::make_shared<uuid::log::Message>(uuid::get_uptime_ms(),
uuid::log::Level::INFO,
uuid::log::Facility::SYSLOG,
(__pstr__logger_name),
"-- MARK --");
add_message(message);
}
}
}
bool SyslogService::can_transmit() {
// TODO this should be checked for Eth
if (!host_.empty() && (uint32_t)ip_ == 0) {
WiFi.hostByName(host_.c_str(), ip_);
}
#if UUID_SYSLOG_HAVE_IPADDRESS_TYPE
if (ip_.isV4() && (uint32_t)ip_ == (uint32_t)0) {
return false;
@@ -294,23 +369,23 @@ bool SyslogService::can_transmit() {
}
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_HAVE_IPADDRESS_TYPE
if (ip_.isV4())
#endif
{
message_delay = 10;
message_delay = UUID_SYSLOG_UDP_IPV4_ARP_MESSAGE_DELAY;
}
#endif
#if UUID_SYSLOG_NDP_CHECK && UUID_SYSLOG_HAVE_IPADDRESS_TYPE
if (ip_.isV6()) {
message_delay = 10;
message_delay = UUID_SYSLOG_UDP_IPV6_NDP_MESSAGE_DELAY;
}
#endif
if (now < last_transmit_ || now - last_transmit_ < message_delay) {
if (started_ && (now < last_transmit_ || now - last_transmit_ < message_delay)) {
return false;
}
@@ -361,26 +436,17 @@ bool SyslogService::can_transmit() {
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_isglobal(&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(&ip6addr)) {
if (ip6_addr_islinklocal(netif_ip6_addr(netif_default, i))) {
have_address = true;
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 {
have_address = true;
break;
@@ -408,8 +474,10 @@ bool SyslogService::can_transmit() {
bool SyslogService::transmit(const QueuedLogMessage & message) {
struct tm tm;
int8_t tzh = 0;
int8_t tzm = 0;
// Changes for EMS-ESP
int8_t tzh = 0;
int8_t tzm = 0;
tm.tm_year = 0;
if (message.time_.tv_sec != (time_t)-1) {
@@ -418,8 +486,10 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
localtime_r(&message.time_.tv_sec, &tm);
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;
tzh = diff / 60;
tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
// added for EMS-ESP
tzh = diff / 60;
tzm = diff < 0 ? (0 - diff) % 60 : diff % 60;
}
if (udp_.beginPacket(ip_, port_) != 1) {
@@ -427,26 +497,53 @@ bool SyslogService::transmit(const QueuedLogMessage & message) {
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) {
udp_.printf_P(PSTR("%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,
(uint32_t)message.time_.tv_usec,
tzh,
tzm);
// udp_.printf_P("%04u-%02u-%02uT%02u:%02u:%02u.%06luZ",
// 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);
// added for EMS-ESP
udp_.printf("%04u-%02u-%02uT%02u:%02u:%02u.%06lu%+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 {
udp_.print('-');
}
udp_.printf_P(PSTR(" %s %s - - - "), hostname_.c_str(), (message.content_->name));
udp_.printf(" %s %s - - - ", hostname_.c_str(), message.content_->name);
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
+ message.content_->text;
for (uint16_t i = 0; i < msgstr.length(); i++) {

View File

@@ -1,6 +1,6 @@
/*
* uuid-syslog - Syslog service
* Copyright 2019 Simon Arlott
* uuid-syslog - Microcontroller syslog service
* Copyright 2019,2021-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
@@ -20,21 +20,34 @@
#define UUID_SYSLOG_H_
#include <Arduino.h>
#ifdef ARDUINO_ARCH_ESP8266
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif
#include <WiFiUdp.h>
#include <time.h>
#include <atomic>
#include <list>
#include <memory>
#include <string>
#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 {
/**
@@ -45,6 +58,17 @@ namespace uuid {
*/
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.
*
@@ -60,9 +84,8 @@ class SyslogService : public uuid::log::Handler {
*
* @since 1.0.0
*/
SyslogService() = default;
~SyslogService();
SyslogService() = default;
~SyslogService() = default;
/**
* 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);
/**
* 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.
*
@@ -129,6 +160,8 @@ class SyslogService : public uuid::log::Handler {
* @since 2.0.0
*/
void destination(IPAddress host, uint16_t port = DEFAULT_PORT);
// added for EMS-ESP
void destination(const char * host, uint16_t port = DEFAULT_PORT);
/**
@@ -185,7 +218,7 @@ class SyslogService : public uuid::log::Handler {
virtual void operator<<(std::shared_ptr<uuid::log::Message> message);
/**
* added MichaelDvP
* added for EMS-ESP
* query status variables
*/
size_t queued() {
@@ -227,14 +260,27 @@ class SyslogService : public uuid::log::Handler {
QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content);
~QueuedLogMessage() = default;
unsigned long id_; /*!< Sequential identifier for this log message. @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 */
unsigned long id_; /*!< Sequential identifier for this log message. @since 1.0.0 */
struct timeval time_; /*!< Time message was received. @since 1.0.0 */
std::shared_ptr<const uuid::log::Message> content_; /*!< Log message content. @since 1.0.0 */
private:
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.
*
@@ -264,20 +310,25 @@ class SyslogService : public uuid::log::Handler {
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 */
WiFiUDP udp_; /*!< UDP client. @since 1.0.0 */
IPAddress ip_; /*!< Host-IP to send messages to. @since 1.0.0 */
std::string host_; /*!< Host to send messages to. */
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 */
std::string hostname_{'-'}; /*!< Local hostname. @since 1.0.0 */
bool started_ = false; /*!< Flag to indicate that messages have started being transmitted. @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 */
WiFiUDP udp_; /*!< UDP client. @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 */
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 */
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::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 last_message_ = 0; /*!< Last message/mark time. @since 2.0.0 */
unsigned long log_message_fails_ = 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 */
// added by MichaelDvP for EMS-ESP
IPAddress ip_; /*!< Host to send messages to. @since 1.0.0 */
std::string host_; /*!< Host-IP to send messages to */
unsigned long log_message_fails_ = 0;
};
} // namespace syslog

View File

@@ -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(port,
[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)
@@ -101,14 +101,13 @@ size_t TelnetService::maximum_connections() const {
void TelnetService::maximum_connections(size_t 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();) {
if (it->active()) {
it->stop();
it = connections_.erase(it);
break;
} else {
it = connections_.erase(it);
if (it->stop()) {
if (--stop == 0)
break;
}
}
}
@@ -143,21 +142,21 @@ void TelnetService::loop() {
if (client) {
if (connections_.size() >= maximum_connections_) {
#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(),
client.remotePort());
#else
logger_.info(F("New connection rejected (connection limit reached)"));
logger_.info("New connection rejected (connection limit reached)");
#endif
client.println(F("Maximum connection limit reached"));
client.println("Maximum connection limit reached");
client.stop();
} else {
#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
connections_.emplace_back(shell_factory_, std::move(client), initial_idle_timeout_, write_timeout_);
#if !(UUID_TELNET_HAVE_WIFICLIENT_REMOTE)
logger_.info(F("New connection %p accepted"), &connections_.back());
logger_.info("New connection %p accepted", &connections_.back());
#endif
}
}
@@ -194,34 +193,37 @@ TelnetService::Connection::Connection(shell_factory_function & shell_factory, Wi
shell->idle_timeout(idle_timeout);
shell->start();
shell_ = shell;
} else {
shell_ = nullptr;
}
}
bool TelnetService::Connection::active() {
return shell_.use_count() > 1;
}
bool TelnetService::Connection::loop() {
if (active()) {
if (!shell_.expired()) {
if (!client_.connected()) {
shell_->stop();
auto shell = shell_.lock();
if (shell) {
shell->stop();
}
}
return true;
} else {
#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
logger_.info(F("Connection %p closed"), this);
logger_.info("Connection %p closed", this);
#endif
return false;
}
}
void TelnetService::Connection::stop() {
if (shell_) {
shell_->stop();
bool TelnetService::Connection::stop() {
auto shell = shell_.lock();
if (shell) {
shell->stop();
return true;
} else {
return false;
}
}

View File

@@ -392,36 +392,30 @@ class TelnetService {
Connection(shell_factory_function & shell_factory, WiFiClient && client, unsigned long idle_timeout, unsigned long write_timeout);
~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.
*
* @return Active status of the shell.
* @return True if the shell had not already stopped.
* @since 0.1.0
*/
bool loop();
/**
* Stop the shell.
*
* @since 0.1.0
* @since 0.2.0
*/
void stop();
bool stop();
private:
Connection(const Connection &) = delete;
Connection & operator=(const Connection &) = delete;
WiFiClient client_; /*!< Client 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 */
IPAddress addr_; /*!< Remote address of connection. @since 0.1.0 */
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
WiFiClient client_; /*!< Client connection. @since 0.1.0 */
TelnetStream stream_; /*!< Telnet stream for the 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 */
uint16_t port_; /*!< Remote port of connection. @since 0.1.0 */
};
TelnetService(const TelnetService &) = delete;