diff --git a/code/espurna/rpnrules.cpp b/code/espurna/rpnrules.cpp index 68ad98c0..94434bb0 100644 --- a/code/espurna/rpnrules.cpp +++ b/code/espurna/rpnrules.cpp @@ -86,8 +86,8 @@ void _rpnMQTTCallback(unsigned int type, const char * topic, const char * payloa if (rpn_topic.equals(topic)) { String rpn_name = getSetting({"rpnName", i}); if (rpn_name.length()) { - rpn_variable_set(_rpn_ctxt, rpn_name.c_str(), atof(payload)); - _rpn_last = millis(); + rpn_value value { atof(payload) }; + rpn_variable_set(_rpn_ctxt, rpn_name, value); _rpn_run = true; break; } @@ -109,11 +109,14 @@ void _rpnConfigure() { void _rpnBrokerCallback(const String& topic, unsigned char id, double value, const char*) { char name[32] = {0}; - snprintf(name, sizeof(name), "%s%u", topic.c_str(), id); - rpn_variable_set(_rpn_ctxt, name, value); - _rpn_last = millis(); + if (topic == MQTT_TOPIC_RELAY) { + rpn_variable_set(_rpn_ctxt, name, rpn_value(static_cast(value))); + } else { + rpn_variable_set(_rpn_ctxt, name, rpn_value(value)); + } + _rpn_run = true; } @@ -124,50 +127,93 @@ void _rpnBrokerStatus(const String& topic, unsigned char id, unsigned int value) #if NTP_SUPPORT -bool _rpnNtpNow(rpn_context & ctxt) { - if (!ntpSynced()) return false; - rpn_stack_push(ctxt, now()); - return true; +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; } -bool _rpnNtpFunc(rpn_context & ctxt, int (*func)(time_t)) { - float timestamp; - rpn_stack_pop(ctxt, timestamp); - rpn_stack_push(ctxt, func(time_t(timestamp))); - return true; +rpn_error _rpnNtpFunc(rpn_context & ctxt, rpn_int (*func)(time_t)) { + rpn_value value; + rpn_stack_pop(ctxt, value); + + value = rpn_value(func(value.toInt())); + rpn_stack_push(ctxt, value); + + return 0; } -#endif +#endif // NTP_SUPPORT + +String _rpnValueToString(const rpn_value& value) { + String out; + if (value.isString()) { + out = value.toString(); + } else if (value.isFloat()) { + out = String(value.toFloat(), 10); + } else if (value.isInt()) { + out = String(value.toInt(), 10); + } else if (value.isUint()) { + out = String(value.toUint(), 10); + } else if (value.isBoolean()) { + out = String(value.toBoolean() ? "true" : "false"); + } else if (value.isNull()) { + out = F("(null)"); + } + return out; +} + +char _rpnStackTypeTag(rpn_stack_value::Type type) { + switch (type) { + case rpn_stack_value::Type::None: + return 'N'; + case rpn_stack_value::Type::Variable: + return '$'; + case rpn_stack_value::Type::Array: + return 'A'; + case rpn_stack_value::Type::Value: + default: + return ' '; + } +} #if RELAY_SUPPORT -bool _rpnRelayStatus(rpn_context & ctxt, bool force) { - float status, id; +rpn_error _rpnRelayStatus(rpn_context & ctxt, bool force) { + rpn_value id; + rpn_value status; + rpn_stack_pop(ctxt, id); rpn_stack_pop(ctxt, status); - if (int(status) == 2) { - relayToggle(int(id)); - } else if (force || (relayStatusTarget(int(id)) != (int(status) == 1))) { - relayStatus(int(id), int(status) == 1); + rpn_uint value = status.toUint(); + if (value == 2) { + relayToggle(id.toUint()); + } else if (relayStatusTarget(id.toUint()) != (value == 1)) { + relayStatus(id.toUint(), value == 1); } - return true; + + return 0; } -#endif +#endif // RELAY_SUPPORT void _rpnDump() { - float value; DEBUG_MSG_P(PSTR("[RPN] Stack:\n")); - unsigned char num = rpn_stack_size(_rpn_ctxt); - if (0 == num) { + + auto index = rpn_stack_size(_rpn_ctxt); + if (!index) { DEBUG_MSG_P(PSTR(" (empty)\n")); - } else { - unsigned char index = num - 1; - while (rpn_stack_get(_rpn_ctxt, index, value)) { - DEBUG_MSG_P(PSTR(" %02d: %s\n"), index--, String(value).c_str()); - } + return; } + + rpn_stack_foreach(_rpn_ctxt, [&index](rpn_stack_value::Type type, const rpn_value& value) { + DEBUG_MSG_P(PSTR("%c %02u: %s\n"), + _rpnStackTypeTag(type), index--, + _rpnValueToString(value).c_str() + ); + }); } @@ -223,10 +269,11 @@ void _rpnInit() { // using classic Sunday as first, but instead of 0 it is 1 // Implementation above also uses 1 for Sunday, staying compatible with TimeLib #if NTP_SUPPORT && NTP_LEGACY_SUPPORT - rpn_operator_set(_rpn_ctxt, "utc", 0, [](rpn_context & ctxt) { - if (!ntpSynced()) return false; - rpn_stack_push(ctxt, ntpLocal2UTC(now())); - return true; + rpn_operator_set(_rpn_ctxt, "utc", 0, [](rpn_context & ctxt) -> rpn_error { + if (!ntpSynced()) return rpn_operator_error::CannotContinue; + rpn_value ts { static_cast(ntpLocal2UTC(now())) }; + rpn_stack_push(ctxt, ts); + return 0; }); rpn_operator_set(_rpn_ctxt, "now", 0, _rpnNtpNow); @@ -247,12 +294,6 @@ void _rpnInit() { }); #endif - // Dumps RPN stack contents - rpn_operator_set(_rpn_ctxt, "debug", 0, [](rpn_context & ctxt) { - _rpnDump(); - return true; - }); - // Accept relay number and numeric API status value (0, 1 and 2) #if RELAY_SUPPORT @@ -271,26 +312,49 @@ void _rpnInit() { // Channel operators #if RELAY_PROVIDER == RELAY_PROVIDER_LIGHT - rpn_operator_set(_rpn_ctxt, "update", 0, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "update", 0, [](rpn_context & ctxt) -> rpn_error { lightUpdate(true, true); - return true; + return 0; }); - rpn_operator_set(_rpn_ctxt, "black", 0, [](rpn_context & ctxt) { + rpn_operator_set(_rpn_ctxt, "black", 0, [](rpn_context & ctxt) -> rpn_error { lightColor((unsigned long) 0); - return true; + return 0; }); - rpn_operator_set(_rpn_ctxt, "channel", 2, [](rpn_context & ctxt) { - float value, id; + rpn_operator_set(_rpn_ctxt, "channel", 2, [](rpn_context & ctxt) -> rpn_error { + rpn_value value; + rpn_value id; rpn_stack_pop(ctxt, id); rpn_stack_pop(ctxt, value); - lightChannel(int(id), int(value)); - return true; + lightChannel(id.toUint(), id.toInt()); + return 0; }); #endif + // Some debugging. Dump stack contents + rpn_operator_set(_rpn_ctxt, "debug", 0, [](rpn_context & ctxt) -> rpn_error { + _rpnDump(); + return 0; + }); + + // And, simple string logging + #if DEBUG_SUPPORT + rpn_operator_set(_rpn_ctxt, "log", 1, [](rpn_context & ctxt) -> rpn_error { + rpn_value message; + rpn_stack_pop(ctxt, message); + + DEBUG_MSG_P(PSTR("[RPN] %s\n"), message.toString().c_str()); + + return 0; + }); + #endif + + rpn_operator_set(_rpn_ctxt, "millis", 0, [](rpn_context & ctxt) -> rpn_error { + rpn_stack_push(ctxt, rpn_value(static_cast(millis()))); + return 0; + }); } #if TERMINAL_SUPPORT @@ -298,34 +362,25 @@ void _rpnInit() { void _rpnInitCommands() { terminalRegisterCommand(F("RPN.VARS"), [](const terminal::CommandContext&) { - unsigned char num = rpn_variables_size(_rpn_ctxt); - if (0 == num) { - DEBUG_MSG_P(PSTR("[RPN] No variables\n")); - } else { - DEBUG_MSG_P(PSTR("[RPN] Variables:\n")); - for (unsigned char i=0; i %p\n"), name.c_str(), argc, ptr); + }); terminalOK(); }); terminalRegisterCommand(F("RPN.TEST"), [](const terminal::CommandContext& ctx) { if (ctx.argc == 2) { DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), ctx.argv[1].c_str()); - rpn_process(_rpn_ctxt, ctx.argv[1].c_str(), true); + rpn_process(_rpn_ctxt, ctx.argv[1].c_str()); _rpnDump(); rpn_stack_clear(_rpn_ctxt); terminalOK(); @@ -339,11 +394,22 @@ void _rpnInitCommands() { void _rpnRun() { + if (!_rpn_run) { + return; + } + + if (millis() - _rpn_last <= _rpn_delay) { + return; + } + + _rpn_last = millis(); + _rpn_run = false; + unsigned char i = 0; String rule = getSetting({"rpnRule", i}); while (rule.length()) { //DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), rule.c_str()); - rpn_process(_rpn_ctxt, rule.c_str(), true); + rpn_process(_rpn_ctxt, rule.c_str()); //_rpnDump(); rule = getSetting({"rpnRule", ++i}); rpn_stack_clear(_rpn_ctxt); @@ -357,10 +423,7 @@ void _rpnRun() { void _rpnLoop() { - if (_rpn_run && (millis() - _rpn_last > _rpn_delay)) { - _rpnRun(); - _rpn_run = false; - } + _rpnRun(); } @@ -395,13 +458,13 @@ void rpnSetup() { static const String tick_every_hour(F("tick1h")); static const String tick_every_minute(F("tick1m")); - const char* ptr = + const char* ptr = (tick == NtpTick::EveryMinute) ? tick_every_minute.c_str() : (tick == NtpTick::EveryHour) ? tick_every_hour.c_str() : nullptr; if (ptr != nullptr) { - rpn_variable_set(_rpn_ctxt, ptr, timestamp); - _rpn_last = millis(); + rpn_value value { static_cast(timestamp) }; + rpn_variable_set(_rpn_ctxt, ptr, value); _rpn_run = true; } }); @@ -416,6 +479,9 @@ void rpnSetup() { espurnaRegisterReload(_rpnConfigure); espurnaRegisterLoop(_rpnLoop); + _rpn_last = millis(); + _rpn_run = true; + } #endif // RPN_RULES_SUPPORT diff --git a/code/platformio.ini b/code/platformio.ini index 02d67718..d18328d7 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -148,7 +148,7 @@ lib_deps = PubSubClient rc-switch https://github.com/LowPowerLab/RFM69#7008d57a - https://github.com/xoseperez/rpnlib.git#0.3.0 + https://github.com/mcspr/rpnlib.git#0.23.0 NewPing https://github.com/sparkfun/SparkFun_VEML6075_Arduino_Library#V_1.0.3 https://github.com/pololu/vl53l1x-arduino#1.0.1