mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-10-27 06:21:26 +00:00
[ValueStruct] Fix issues formatting longer numbers + add parse Str
This commit is contained in:
1 parent
24884cdc7f
commit
c6df9e0fbd
7 files changed
+230
-38
No files matched your search
@@ -1,7 +1,9 @@
|
||||
#include "../DataStructs/ValueStruct.h"
|
||||
|
||||
#include "../Helpers/Numerical.h"
|
||||
#include "../Helpers/Memory.h"
|
||||
#include "../Helpers/PrintToString.h"
|
||||
#include "../Helpers/StringConverter.h"
|
||||
#include "../Helpers/StringConverter_Numerical.h"
|
||||
|
||||
#define VALUE_STRUCT_SSO_FIRST_CHAR_INDEX 1
|
||||
@@ -13,13 +15,10 @@
|
||||
// ********************************************************************************
|
||||
ValueStruct::~ValueStruct()
|
||||
{
|
||||
const auto vtype = static_cast<ValueStruct::ValueType>(_valueType);
|
||||
|
||||
if (!_isSSO && (vtype == ValueStruct::ValueType::String))
|
||||
{
|
||||
if (str_val != nullptr) {
|
||||
free(str_val);
|
||||
}
|
||||
if (!_isSSO &&
|
||||
(static_cast<ValueStruct::ValueType>(_valueType) == ValueStruct::ValueType::String)
|
||||
&& (str_val != nullptr)) {
|
||||
free(str_val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +28,50 @@ ValueStruct::ValueStruct(ValueStruct&& rhs)
|
||||
memset(rhs.bytes_all, 0, sizeof(bytes_all));
|
||||
}
|
||||
|
||||
void ValueStruct::setPreferredFormat(ValueStruct::PreferredFormat format)
|
||||
{
|
||||
const auto vtype = getValueType();
|
||||
|
||||
if ((vtype == ValueStruct::ValueType::Int) ||
|
||||
(vtype == ValueStruct::ValueType::UInt))
|
||||
{
|
||||
_preferredFormat = (uint64_t)format;
|
||||
}
|
||||
}
|
||||
|
||||
void ValueStruct::clear() {
|
||||
if (!_isSSO
|
||||
&& (getValueType() == ValueStruct::ValueType::String)
|
||||
&& (str_val != nullptr)) {
|
||||
free(str_val);
|
||||
}
|
||||
memset(bytes_all, 0, sizeof(bytes_all));
|
||||
}
|
||||
|
||||
ValueStruct& ValueStruct::operator=(ValueStruct&& rhs)
|
||||
{
|
||||
clear();
|
||||
memcpy(bytes_all, rhs.bytes_all, sizeof(bytes_all));
|
||||
|
||||
// Make sure rhs will not free allocated string
|
||||
memset(rhs.bytes_all, 0, sizeof(bytes_all));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueStruct& ValueStruct::deepCopy(const ValueStruct& rhs)
|
||||
{
|
||||
if (!rhs._isSSO && (rhs.getValueType() == ValueStruct::ValueType::String))
|
||||
{
|
||||
this->operator=(ValueStruct(rhs.toString()));
|
||||
} else {
|
||||
clear();
|
||||
|
||||
// RHS does not have anything heap-allocated, so can make literal copy
|
||||
memcpy(bytes_all, rhs.bytes_all, sizeof(bytes_all));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ValueStruct::ValueStruct(const bool& val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::Bool),
|
||||
@@ -46,6 +82,8 @@ ValueStruct::ValueStruct(const bool& val) :
|
||||
ValueStruct::ValueStruct(int val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::Int),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
i64_val(val)
|
||||
{}
|
||||
@@ -54,6 +92,8 @@ ValueStruct::ValueStruct(int val) :
|
||||
ValueStruct::ValueStruct(int32_t val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::Int),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
i64_val(val)
|
||||
{}
|
||||
@@ -62,6 +102,8 @@ ValueStruct::ValueStruct(int32_t val) :
|
||||
ValueStruct::ValueStruct(uint32_t val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::UInt),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
u64_val(val)
|
||||
{}
|
||||
@@ -70,6 +112,8 @@ ValueStruct::ValueStruct(uint32_t val) :
|
||||
ValueStruct::ValueStruct(size_t val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::UInt),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
u64_val(val)
|
||||
{}
|
||||
@@ -78,6 +122,8 @@ ValueStruct::ValueStruct(size_t val) :
|
||||
ValueStruct::ValueStruct(const uint64_t& val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::UInt),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
u64_val(val)
|
||||
{}
|
||||
@@ -85,6 +131,8 @@ ValueStruct::ValueStruct(const uint64_t& val) :
|
||||
ValueStruct::ValueStruct(const int64_t& val) :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::Int),
|
||||
_preferredFormat((uint64_t)PreferredFormat::Default),
|
||||
_minNrDigits(1),
|
||||
_size(sizeof(val) * 8),
|
||||
i64_val(val)
|
||||
{}
|
||||
@@ -196,6 +244,109 @@ ValueStruct::ValueStruct(const __FlashStringHelper *val) :
|
||||
str_val((void *)(val))
|
||||
{}
|
||||
|
||||
ValueStruct::ValueType ValueStruct::fromString(const __FlashStringHelper *val)
|
||||
{
|
||||
if (fromString(String(val)) == ValueStruct::ValueType::String)
|
||||
{
|
||||
// We can store it as a flash string, which doesn't need memory allocation
|
||||
this->operator=(ValueStruct(val));
|
||||
}
|
||||
return getValueType();
|
||||
}
|
||||
|
||||
ValueStruct::ValueType ValueStruct::fromString(const String& val)
|
||||
{
|
||||
clear();
|
||||
|
||||
String numStr;
|
||||
NumericalType detectedType = NumericalType::Not_a_number;
|
||||
bool negativeValue{};
|
||||
|
||||
if (!val.isEmpty())
|
||||
{
|
||||
String trimmedVal(val);
|
||||
trimmedVal.trim();
|
||||
|
||||
const bool b_true = trimmedVal.equalsIgnoreCase(F("true"));
|
||||
const bool b_false = trimmedVal.equalsIgnoreCase(F("false"));
|
||||
|
||||
if (b_true || b_false) {
|
||||
this->operator=(ValueStruct(b_true));
|
||||
return ValueStruct::ValueType::Bool;
|
||||
}
|
||||
|
||||
numStr = getNumerical(trimmedVal, NumericalType::FloatingPoint, detectedType);
|
||||
numStr.trim();
|
||||
|
||||
if (!numStr.isEmpty() && (numStr[0] == '-')) {
|
||||
negativeValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch (detectedType)
|
||||
{
|
||||
case NumericalType::Not_a_number:
|
||||
break;
|
||||
case NumericalType::FloatingPoint:
|
||||
{
|
||||
ESPEASY_RULES_FLOAT_TYPE val_f{};
|
||||
int nrDec{};
|
||||
|
||||
if (validDoubleFromString(numStr, val_f, nrDec) || (nrDec < 0))
|
||||
{
|
||||
this->operator=(ValueStruct(val_f, nrDec));
|
||||
return ValueStruct::ValueType::Float;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (negativeValue) {
|
||||
int64_t result{};
|
||||
|
||||
if (validInt64FromString(numStr, result))
|
||||
{
|
||||
if (std::numeric_limits<int32_t>::min() < result) {
|
||||
this->operator=(ValueStruct(static_cast<int32_t>(result)));
|
||||
}
|
||||
else {
|
||||
this->operator=(ValueStruct(result));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint64_t result{};
|
||||
|
||||
if (validUInt64FromString(numStr, result))
|
||||
{
|
||||
if (std::numeric_limits<uint32_t>::max() > result) {
|
||||
this->operator=(ValueStruct(static_cast<uint32_t>(result)));
|
||||
}
|
||||
else {
|
||||
this->operator=(ValueStruct(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSet()) {
|
||||
PreferredFormat format(PreferredFormat::Default);
|
||||
|
||||
if (detectedType == NumericalType::BinaryUint) { format = PreferredFormat::Bin; }
|
||||
else if (detectedType == NumericalType::HexadecimalUInt) { format = PreferredFormat::Hex; }
|
||||
|
||||
setPreferredFormat(format);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSet()) {
|
||||
// Just store it as a string type
|
||||
this->operator=(ValueStruct(val));
|
||||
}
|
||||
|
||||
return getValueType();
|
||||
}
|
||||
|
||||
String ValueStruct::toString() const
|
||||
{
|
||||
ValueType valueType;
|
||||
@@ -214,7 +365,7 @@ String ValueStruct::toString(ValueType& valueType) const
|
||||
|
||||
int64_t ValueStruct::toInt() const
|
||||
{
|
||||
switch(getValueType())
|
||||
switch (getValueType())
|
||||
{
|
||||
case ValueStruct::ValueType::Bool:
|
||||
{
|
||||
@@ -243,7 +394,7 @@ int64_t ValueStruct::toInt() const
|
||||
|
||||
double ValueStruct::toFloat() const
|
||||
{
|
||||
switch(getValueType())
|
||||
switch (getValueType())
|
||||
{
|
||||
case ValueStruct::ValueType::Bool:
|
||||
{
|
||||
@@ -326,16 +477,26 @@ size_t ValueStruct::print(Print& out, ValueType& valueType) const
|
||||
}
|
||||
case ValueStruct::ValueType::Int:
|
||||
{
|
||||
if (_size == 8) {
|
||||
return out.print(ll2String(i64_val));
|
||||
if ((_size > 32) || _minNrDigits) {
|
||||
return out.print(ll2String(i64_val, DEC, _minNrDigits));
|
||||
}
|
||||
auto v = static_cast<int32_t>(i64_val);
|
||||
return out.print(v);
|
||||
}
|
||||
case ValueStruct::ValueType::UInt:
|
||||
{
|
||||
if (_size == 8) {
|
||||
return out.print(ull2String(u64_val));
|
||||
const auto format = getPreferredFormat();
|
||||
|
||||
if (format == PreferredFormat::Bin) {
|
||||
return out.print(concat(F("0b"), ull2String(u64_val, BIN, _minNrDigits)));
|
||||
}
|
||||
|
||||
if (format == PreferredFormat::Hex) {
|
||||
return out.print(concat(F("0x"), ull2String(u64_val, HEX, _minNrDigits)));
|
||||
}
|
||||
|
||||
if ((_size > 32) || _minNrDigits) {
|
||||
return out.print(ull2String(u64_val, DEC, _minNrDigits));
|
||||
}
|
||||
auto v = static_cast<uint32_t>(u64_val);
|
||||
return out.print(v);
|
||||
@@ -361,8 +522,3 @@ bool ValueStruct::isEmpty() const
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ValueStruct::clear()
|
||||
{
|
||||
this->operator=(ValueStruct());
|
||||
}
|
||||
@@ -24,6 +24,14 @@ public:
|
||||
|
||||
};
|
||||
|
||||
enum class PreferredFormat : uint8_t {
|
||||
Default = 0,
|
||||
Dec,
|
||||
Bin,
|
||||
Hex
|
||||
|
||||
};
|
||||
|
||||
ValueStruct() :
|
||||
_isSSO(0),
|
||||
_valueType((uint64_t)ValueStruct::ValueType::Unset),
|
||||
@@ -73,24 +81,47 @@ public:
|
||||
return static_cast<ValueStruct::ValueType>(_valueType);
|
||||
}
|
||||
|
||||
ValueStruct::PreferredFormat getPreferredFormat() const
|
||||
{
|
||||
return static_cast<ValueStruct::PreferredFormat>(_preferredFormat);
|
||||
}
|
||||
|
||||
void setPreferredFormat(ValueStruct::PreferredFormat format);
|
||||
|
||||
void setMinNrDigits(uint8_t minNrDigits) { _minNrDigits = (uint64_t)minNrDigits; }
|
||||
|
||||
ValueStruct& operator=(ValueStruct&& rhs);
|
||||
ValueStruct& operator=(const ValueStruct& rhs) = delete;
|
||||
|
||||
String toString() const;
|
||||
// We really try to enforce moving the ValueStruct, but when needed a deepcopy is possible
|
||||
ValueStruct& deepCopy(const ValueStruct& rhs);
|
||||
|
||||
String toString(ValueType& valueType) const;
|
||||
operator bool() const {
|
||||
return getValueType() != ValueStruct::ValueType::Unset;
|
||||
}
|
||||
|
||||
int64_t toInt() const;
|
||||
// Try to interpret the given string and store the value as compact as possible
|
||||
// If the given string is a numerical, try to detect:
|
||||
// - preferred notation (Hex/Dec/Bin)
|
||||
// - number of decimals
|
||||
ValueStruct::ValueType fromString(const __FlashStringHelper *val);
|
||||
ValueStruct::ValueType fromString(const String& val);
|
||||
|
||||
double toFloat() const;
|
||||
String toString() const;
|
||||
|
||||
size_t print(Print& out) const;
|
||||
String toString(ValueType& valueType) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
int64_t toInt() const;
|
||||
|
||||
void clear();
|
||||
double toFloat() const;
|
||||
|
||||
bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; }
|
||||
size_t print(Print& out) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void clear();
|
||||
|
||||
bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -101,11 +132,12 @@ private:
|
||||
struct {
|
||||
uint64_t _isSSO : 1;
|
||||
uint64_t _trimTrailingZeros : 1;
|
||||
uint64_t _valueType : 6;
|
||||
uint64_t _valueType : 4;
|
||||
uint64_t _preferredFormat : 2;
|
||||
uint64_t _nrDecimals : 8;
|
||||
uint64_t _size : 16;
|
||||
|
||||
uint64_t unused : 32;
|
||||
uint64_t _minNrDigits : 8; // For printing ints with leading zeroes
|
||||
uint64_t _size : 16; // Length of string or nr of bits
|
||||
uint64_t unused : 24;
|
||||
|
||||
union {
|
||||
void *str_val;
|
||||
|
||||
@@ -383,5 +383,5 @@ bool isNumerical(const String& tBuf, NumericalType& detectedType) {
|
||||
return result.length() >= tmp.length();
|
||||
}
|
||||
|
||||
return result.length() > 0;
|
||||
return false;
|
||||
}
|
||||
@@ -349,14 +349,14 @@ void replaceUnicodeByChar(String& line, char replChar) {
|
||||
|
||||
void padToMinimumLength(String& line, uint32_t minimumLength, char padChar)
|
||||
{
|
||||
if (line.length() >= minimumLength) return;
|
||||
if (minimumLength == 0 || line.length() >= minimumLength) return;
|
||||
line.reserve(minimumLength);
|
||||
while (line.length() < minimumLength) line += padChar;
|
||||
}
|
||||
|
||||
void prefixToMinimumLength(String& line, uint32_t minimumLength, char prefixChar)
|
||||
{
|
||||
if (line.length() >= minimumLength) return;
|
||||
if (minimumLength == 0 || line.length() >= minimumLength) return;
|
||||
|
||||
String prefix;
|
||||
padToMinimumLength(prefix, minimumLength - line.length(), prefixChar);
|
||||
|
||||
@@ -181,6 +181,8 @@ bool doubleToValidString(String& str,
|
||||
str = F("Inf");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decimalPlaces == 255) decimalPlaces = 0;
|
||||
#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE
|
||||
|
||||
// We use some trick here to prevent rounding errors
|
||||
|
||||
@@ -117,7 +117,7 @@ void TaskValuesWriterHelper::writeCustom(
|
||||
uint8_t varNr,
|
||||
const String& label,
|
||||
const String& val,
|
||||
const String& attr,
|
||||
ValueStruct&& attr,
|
||||
bool isLast)
|
||||
{
|
||||
if (!validDeviceIndex(deviceIndex)) { return; }
|
||||
@@ -132,7 +132,7 @@ void TaskValuesWriterHelper::writeCustom(
|
||||
_isLast = isLast;
|
||||
valName = label;
|
||||
value = val;
|
||||
attribute = attr;
|
||||
attribute = std::move(attr);
|
||||
write();
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ void TaskValuesWriterHelper::pluginWebformShowValue()
|
||||
}
|
||||
#endif // if FEATURE_STRING_VARIABLES
|
||||
|
||||
addHtmlDiv(F("div_l"), valName, format_ID(ID_type::ValueName), attribute);
|
||||
addHtmlDiv(F("div_l"), valName, format_ID(ID_type::ValueName), attribute.toString());
|
||||
|
||||
if (!value_tmp.isEmpty() || !_preformatted) {
|
||||
addHtmlDiv(F("div_r"), value_tmp, format_ID(ID_type::Value));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../../ESPEasy_common.h"
|
||||
|
||||
#include "../DataStructs/ValueStruct.h"
|
||||
#include "../DataTypes/DeviceIndex.h"
|
||||
|
||||
// Helper function to create formatted custom values for display in the devices overview page.
|
||||
@@ -53,7 +54,7 @@ struct TaskValuesWriterHelper {
|
||||
void writeCustom(uint8_t varNr,
|
||||
const String& label,
|
||||
const String& val,
|
||||
const String& attr,
|
||||
ValueStruct&& attr,
|
||||
bool isLast = false);
|
||||
|
||||
private:
|
||||
@@ -70,7 +71,8 @@ private:
|
||||
public:
|
||||
|
||||
EventStruct * const event = nullptr;
|
||||
String valName, value, attribute;
|
||||
String valName, value;
|
||||
ValueStruct attribute;
|
||||
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|
||||
String uom;
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user