Mirror of espurna firmware for wireless switches and more
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

277 lines
5.5 KiB

/*
Part of the RELAY MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2019-2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
#include <cstdint>
namespace espurna {
namespace relay {
namespace pulse {
namespace {
struct Result {
Result() = default;
explicit Result(Duration duration) :
_duration(duration)
{}
template <typename T>
Result& operator+=(T duration) {
_result = true;
_duration += std::chrono::duration_cast<Duration>(duration);
return *this;
}
explicit operator bool() const {
return _result;
}
void reset() {
_result = false;
_duration = Duration::min();
}
Duration duration() const {
return _duration;
}
Duration::rep count() const {
return _duration.count();
}
private:
bool _result { false };
Duration _duration { Duration::min() };
};
namespace internal {
enum class Type {
Unknown,
Seconds,
Minutes,
Hours
};
bool validNextType(Type lhs, Type rhs) {
switch (lhs) {
case Type::Unknown:
return true;
case Type::Hours:
return (rhs == Type::Minutes) || (rhs == Type::Seconds);
case Type::Minutes:
return (rhs == Type::Seconds);
case Type::Seconds:
break;
}
return false;
}
Result parse(const char* begin, const char* end) {
Result out;
String token;
Type last { Type::Unknown };
Type type { Type::Unknown };
const char* ptr { begin };
if (!begin || !end || (begin == end)) {
goto output;
}
loop:
while (ptr != end) {
switch (*ptr) {
case '\0':
if (last == Type::Unknown) {
goto update_floating;
}
goto output;
case '0'...'9':
token += (*ptr);
++ptr;
break;
case 'h':
if (validNextType(last, Type::Hours)) {
type = Type::Hours;
goto update_decimal;
}
goto reset;
case 'm':
if (validNextType(last, Type::Minutes)) {
type = Type::Minutes;
goto update_decimal;
}
goto reset;
case 's':
if (validNextType(last, Type::Seconds)) {
type = Type::Seconds;
goto update_decimal;
}
goto reset;
case ',':
case '.':
if (out) {
goto reset;
}
goto read_floating;
}
}
if (token.length()) {
goto update_floating;
}
goto output;
update_floating:
{
char* endp { nullptr };
auto value = strtod(token.c_str(), &endp);
if (endp && (endp != token.c_str()) && endp[0] == '\0') {
out += Seconds(value);
goto output;
}
goto reset;
}
update_decimal:
last = type;
++ptr;
if (type != Type::Unknown) {
const auto result = parseUnsigned(token, 10);
if (result.ok) {
switch (type) {
case Type::Hours: {
out += ::espurna::duration::Hours { result.value };
break;
}
case Type::Minutes: {
out += ::espurna::duration::Minutes { result.value };
break;
}
case Type::Seconds: {
out += ::espurna::duration::Seconds { result.value };
break;
}
case Type::Unknown:
goto reset;
}
type = Type::Unknown;
token = "";
goto loop;
}
}
goto reset;
read_floating:
switch (*ptr) {
case ',':
case '.':
token += '.';
++ptr;
break;
default:
goto reset;
}
while (ptr != end) {
switch (*ptr) {
case '\0':
goto update_floating;
case '0'...'9':
token += (*ptr);
break;
case 'e':
case 'E':
token += (*ptr);
++ptr;
while (ptr != end) {
switch (*ptr) {
case '\0':
goto reset;
case '-':
case '+':
token += (*ptr);
++ptr;
goto read_floating_exponent;
case '0'...'9':
goto read_floating_exponent;
}
}
goto reset;
case ',':
case '.':
goto reset;
}
++ptr;
}
goto update_floating;
read_floating_exponent:
while (ptr != end) {
switch (*ptr) {
case '0'...'9':
token += *(ptr);
++ptr;
break;
}
goto reset;
}
goto update_floating;
reset:
out.reset();
output:
return out;
}
} // namespace internal
Result parse(StringView value) {
return internal::parse(value.begin(), value.end());
}
#if 0
void test() {
auto report = [](const String& value) {
const auto result = parse(value);
DEBUG_MSG_P(PSTR(":\"%s\" is #%c -> %u (ms)\n"),
value.c_str(),
static_cast<bool>(result) ? 't' : 'f',
result.count());
};
report("5h");
report("7h6h");
report("15m");
report("19m1h");
report("12345");
report("1.5");
}
#endif
} // namespace
} // namespace pulse
} // namespace relay
} // namespace espurna