diff --git a/code/espurna/rpnrules.cpp b/code/espurna/rpnrules.cpp index 9bd0c0a2..f7bba481 100644 --- a/code/espurna/rpnrules.cpp +++ b/code/espurna/rpnrules.cpp @@ -24,6 +24,7 @@ Copyright (C) 2019 by Xose PĂ©rez #include "ws.h" #include +#include #include // ----------------------------------------------------------------------------- @@ -170,23 +171,110 @@ void _rpnBrokerStatus(const String& topic, unsigned char id, unsigned int value) #if NTP_SUPPORT -rpn_error _rpnNtpNow(rpn_context & ctxt) { - if (!ntpSynced()) return rpn_operator_error::CannotContinue; - rpn_value ts { static_cast(now()) }; - rpn_stack_push(ctxt, ts); - return 0; -} +namespace { + +constexpr bool time_t_is_32bit { sizeof(time_t) == 4 }; +constexpr bool time_t_is_64bit { sizeof(time_t) == 8 }; +static_assert(time_t_is_32bit || time_t_is_64bit, ""); + +template +using split_t = std::integral_constant; -rpn_error _rpnNtpFunc(rpn_context & ctxt, rpn_int (*func)(time_t)) { - rpn_value value; - rpn_stack_pop(ctxt, value); +using RpnNtpFunc = rpn_int(*)(time_t); - value = rpn_value(func(value.toInt())); +rpn_error _rpnNtpPopTimestampPair(rpn_context& ctxt, RpnNtpFunc func) { + rpn_value rhs = rpn_stack_pop(ctxt); + rpn_value lhs = rpn_stack_pop(ctxt); + + auto timestamp = (static_cast(lhs.toInt()) << 32ll) + | (static_cast(rhs.toInt())); + + rpn_value value(func(timestamp)); rpn_stack_push(ctxt, value); return 0; } +rpn_error _rpnNtpPopTimestampSingle(rpn_context& ctxt, RpnNtpFunc func) { + rpn_value input = rpn_stack_pop(ctxt); + rpn_value result(func(input.toInt())); + rpn_stack_push(ctxt, result); + return 0; +} + +void _rpnNtpPushTimestampPair(rpn_context& ctxt, time_t timestamp) { + rpn_value lhs(static_cast((static_cast(timestamp) >> 32ll) & 0xffffffffll)); + rpn_value rhs(static_cast(static_cast(timestamp) & 0xffffffffll)); + + rpn_stack_push(ctxt, lhs); + rpn_stack_push(ctxt, rhs); +} + +void _rpnNtpPushTimestampSingle(rpn_context& ctxt, time_t timestamp) { + rpn_value result(static_cast(timestamp)); + rpn_stack_push(ctxt, result); +} + +inline rpn_error _rpnNtpPopTimestamp(const std::true_type&, rpn_context& ctxt, RpnNtpFunc func) { + return _rpnNtpPopTimestampPair(ctxt, func); +} + +inline rpn_error _rpnNtpPopTimestamp(const std::false_type&, rpn_context& ctxt, RpnNtpFunc func) { + return _rpnNtpPopTimestampSingle(ctxt, func); +} + +rpn_error _rpnNtpPopTimestamp(rpn_context& ctxt, RpnNtpFunc func) { + return _rpnNtpPopTimestamp(split_t{}, ctxt, func); +} + +inline void _rpnNtpPushTimestamp(const std::true_type&, rpn_context& ctxt, time_t timestamp) { + _rpnNtpPushTimestampPair(ctxt, timestamp); +} + +inline void _rpnNtpPushTimestamp(const std::false_type&, rpn_context& ctxt, time_t timestamp) { + _rpnNtpPushTimestampSingle(ctxt, timestamp); +} + +void _rpnNtpPushTimestamp(rpn_context& ctxt, time_t timestamp) { + _rpnNtpPushTimestamp(split_t{}, ctxt, timestamp); +} + +rpn_error _rpnNtpNow(rpn_context & ctxt) { + if (ntpSynced()) { + _rpnNtpPushTimestamp(ctxt, now()); + return 0; + } + + return rpn_operator_error::CannotContinue; +} + +rpn_error _rpnNtpFunc(rpn_context & ctxt, RpnNtpFunc func) { + return _rpnNtpPopTimestamp(ctxt, func); +} + +bool _rpn_ntp_tick_minute { false }; +bool _rpn_ntp_tick_hour { false }; + +rpn_error _rpnNtpTickMinute(rpn_context& ctxt) { + if (_rpn_ntp_tick_minute) { + _rpn_ntp_tick_minute = false; + return 0; + } + + return rpn_operator_error::CannotContinue; +} + +rpn_error _rpnNtpTickHour(rpn_context& ctxt) { + if (_rpn_ntp_tick_hour) { + _rpn_ntp_tick_hour = false; + return 0; + } + + return rpn_operator_error::CannotContinue; +} + +} // namespace + #endif // NTP_SUPPORT String _rpnValueToString(const rpn_value& value) { @@ -476,43 +564,50 @@ void _rpnInit() { // TODO: since 1.15.0, timelib+ntpclientlib are no longer used with latest Cores // `now` is always in UTC, `utc_...` functions to be used instead to convert time #if NTP_SUPPORT && !NTP_LEGACY_SUPPORT + { + constexpr size_t time_t_argc { split_t{} ? 2 : 1 }; + + rpn_operator_set(_rpn_ctxt, "tick_1h", 0, _rpnNtpTickHour); + rpn_operator_set(_rpn_ctxt, "tick_1m", 0, _rpnNtpTickMinute); + rpn_operator_set(_rpn_ctxt, "utc", 0, _rpnNtpNow); rpn_operator_set(_rpn_ctxt, "now", 0, _rpnNtpNow); - rpn_operator_set(_rpn_ctxt, "utc_month", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "utc_month", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, utc_month); }); - rpn_operator_set(_rpn_ctxt, "month", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "month", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, month); }); - rpn_operator_set(_rpn_ctxt, "utc_day", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "utc_day", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, utc_day); }); - rpn_operator_set(_rpn_ctxt, "day", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "day", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, day); }); - rpn_operator_set(_rpn_ctxt, "utc_dow", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "utc_dow", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, utc_weekday); }); - rpn_operator_set(_rpn_ctxt, "dow", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "dow", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, weekday); }); - rpn_operator_set(_rpn_ctxt, "utc_hour", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "utc_hour", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, utc_hour); }); - rpn_operator_set(_rpn_ctxt, "hour", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "hour", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, hour); }); - rpn_operator_set(_rpn_ctxt, "utc_minute", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "utc_minute", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, utc_minute); }); - rpn_operator_set(_rpn_ctxt, "minute", 1, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "minute", time_t_argc, [](rpn_context & ctxt) { return _rpnNtpFunc(ctxt, minute); }); + } #endif // TODO: 1.14.0 weekday(...) conversion seemed to have 0..6 range with Monday as 0 @@ -896,22 +991,19 @@ void rpnSetup() { mqttRegister(_rpnMQTTCallback); #endif - #if NTP_SUPPORT - NtpBroker::Register([](NtpTick tick, time_t timestamp, const String& datetime) { - static const String tick_every_hour(F("tick1h")); - static const String tick_every_minute(F("tick1m")); - - const char* ptr = - (tick == NtpTick::EveryMinute) ? tick_every_minute.c_str() : - (tick == NtpTick::EveryHour) ? tick_every_hour.c_str() : nullptr; - - if (ptr != nullptr) { - rpn_value value { static_cast(timestamp) }; - rpn_variable_set(_rpn_ctxt, ptr, value); - _rpn_run = true; - } - }); - #endif +#if NTP_SUPPORT + NtpBroker::Register([](NtpTick tick, time_t, const String&) { + switch (tick) { + case NtpTick::EveryMinute: + _rpn_ntp_tick_minute = true; + break; + case NtpTick::EveryHour: + _rpn_ntp_tick_hour = true; + break; + } + _rpn_run = true; + }); +#endif StatusBroker::Register(_rpnBrokerStatus);