Browse Source

rpn: update to rpnlib 0.24.1 (#2314)

* rpn: bump to rpnlib 0.24.1

- `i` and `u` suffixes for numbers in expressions, parse as Integer or Unsigned respectively
- `checkedTo...()` method variants for numeric conversions
- strings can contain escape sequences (\x61\x62\x63, \n, \t, \r)
- improve float number parsing
- more consistent whitespace checks, tokens also can be separated by \n or \t
- various parser fixes

* fix captures
mcspr-patch-1
Max Prokhorov 4 years ago
committed by GitHub
parent
commit
2b69c7eb6d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 151 additions and 32 deletions
  1. +150
    -31
      code/espurna/rpnrules.cpp
  2. +1
    -1
      code/platformio.ini

+ 150
- 31
code/espurna/rpnrules.cpp View File

@ -19,6 +19,7 @@ Copyright (C) 2019 by Xose Pérez <xose dot perez at gmail dot com>
#include "terminal.h" #include "terminal.h"
#include "ws.h" #include "ws.h"
#include <vector>
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Custom commands // Custom commands
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -28,6 +29,42 @@ bool _rpn_run = false;
unsigned long _rpn_delay = RPN_DELAY; unsigned long _rpn_delay = RPN_DELAY;
unsigned long _rpn_last = 0; unsigned long _rpn_last = 0;
struct RpnRunner {
enum class Policy {
OneShot,
Periodic
};
RpnRunner(Policy policy_, uint32_t period_) :
policy(policy_),
period(period_),
last(millis())
{}
Policy policy { Policy::Periodic };
uint32_t period { 0ul };
uint32_t last { 0ul };
bool expired { false };
};
std::vector<RpnRunner> _rpn_runners;
rpn_operator_error _rpnRunnerHandler(rpn_context & ctxt, RpnRunner::Policy policy, uint32_t time) {
for (auto& runner : _rpn_runners) {
if ((policy == runner.policy) && (time == runner.period)) {
return runner.expired
? rpn_operator_error::Ok
: rpn_operator_error::CannotContinue;
}
}
_rpn_runners.emplace_back(policy, time);
return rpn_operator_error::CannotContinue;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
bool _rpnWebSocketOnKeyCheck(const char * key, JsonVariant& value) { bool _rpnWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
@ -199,24 +236,23 @@ rpn_error _rpnRelayStatus(rpn_context & ctxt, bool force) {
#endif // RELAY_SUPPORT #endif // RELAY_SUPPORT
void _rpnDump() {
DEBUG_MSG_P(PSTR("[RPN] Stack:\n"));
void _rpnShowStack(Print& print) {
print.println(F("Stack:"));
auto index = rpn_stack_size(_rpn_ctxt); auto index = rpn_stack_size(_rpn_ctxt);
if (!index) { if (!index) {
DEBUG_MSG_P(PSTR(" (empty)\n"));
print.println(F(" (empty)"));
return; return;
} }
rpn_stack_foreach(_rpn_ctxt, [&index](rpn_stack_value::Type type, const rpn_value& value) {
DEBUG_MSG_P(PSTR("%c %02u: %s\n"),
rpn_stack_foreach(_rpn_ctxt, [&index, &print](rpn_stack_value::Type type, const rpn_value& value) {
print.printf("%c %02u: %s\n",
_rpnStackTypeTag(type), index--, _rpnStackTypeTag(type), index--,
_rpnValueToString(value).c_str() _rpnValueToString(value).c_str()
); );
}); });
} }
void _rpnInit() { void _rpnInit() {
// Init context // Init context
@ -333,15 +369,29 @@ void _rpnInit() {
#endif #endif
#if MQTT_SUPPORT
rpn_operator_set(_rpn_ctxt, "mqtt_send", 2, [](rpn_context & ctxt) -> rpn_error {
rpn_value message;
rpn_stack_pop(ctxt, message);
rpn_value topic;
rpn_stack_pop(ctxt, topic);
return mqttSendRaw(topic.toString().c_str(), message.toString().c_str())
? rpn_operator_error::Ok
: rpn_operator_error::CannotContinue;
});
#endif
// Some debugging. Dump stack contents // Some debugging. Dump stack contents
rpn_operator_set(_rpn_ctxt, "debug", 0, [](rpn_context & ctxt) -> rpn_error {
_rpnDump();
rpn_operator_set(_rpn_ctxt, "showstack", 0, [](rpn_context & ctxt) -> rpn_error {
_rpnShowStack(terminalDefaultStream());
return 0; return 0;
}); });
// And, simple string logging // And, simple string logging
#if DEBUG_SUPPORT #if DEBUG_SUPPORT
rpn_operator_set(_rpn_ctxt, "log", 1, [](rpn_context & ctxt) -> rpn_error {
rpn_operator_set(_rpn_ctxt, "dbgmsg", 1, [](rpn_context & ctxt) -> rpn_error {
rpn_value message; rpn_value message;
rpn_stack_pop(ctxt, message); rpn_stack_pop(ctxt, message);
@ -355,43 +405,113 @@ void _rpnInit() {
rpn_stack_push(ctxt, rpn_value(static_cast<uint32_t>(millis()))); rpn_stack_push(ctxt, rpn_value(static_cast<uint32_t>(millis())));
return 0; return 0;
}); });
rpn_operator_set(_rpn_ctxt, "oneshot_ms", 1, [](rpn_context & ctxt) -> rpn_error {
auto every = rpn_stack_pop(ctxt);
return _rpnRunnerHandler(ctxt, RpnRunner::Policy::OneShot, every.toUint());
});
rpn_operator_set(_rpn_ctxt, "every_ms", 1, [](rpn_context & ctxt) -> rpn_error {
auto every = rpn_stack_pop(ctxt);
return _rpnRunnerHandler(ctxt, RpnRunner::Policy::Periodic, every.toUint());
});
} }
#if TERMINAL_SUPPORT #if TERMINAL_SUPPORT
void _rpnInitCommands() { void _rpnInitCommands() {
terminalRegisterCommand(F("RPN.VARS"), [](const terminal::CommandContext&) {
DEBUG_MSG_P(PSTR("[RPN] Variables:\n"));
rpn_variables_foreach(_rpn_ctxt, [](const String& name, const rpn_value& value) {
DEBUG_MSG_P(PSTR(" %s: %s\n"), name.c_str(), _rpnValueToString(value).c_str());
terminalRegisterCommand(F("RPN.RUNNERS"), [](const terminal::CommandContext& ctx) {
if (!_rpn_runners.size()) {
terminalError(ctx, F("No active runners"));
return;
}
for (auto& runner : _rpn_runners) {
char buffer[128] = {0};
snprintf_P(buffer, sizeof(buffer), PSTR("%p %s %u ms, last %u ms"),
&runner, (RpnRunner::Policy::Periodic == runner.policy) ? "every" : "one-shot",
runner.period, runner.last
);
ctx.output.println(buffer);
}
terminalOK(ctx);
});
terminalRegisterCommand(F("RPN.VARS"), [](const terminal::CommandContext& ctx) {
rpn_variables_foreach(_rpn_ctxt, [&ctx](const String& name, const rpn_value& value) {
char buffer[256] = {0};
snprintf_P(buffer, sizeof(buffer), PSTR(" %s: %s"), name.c_str(), _rpnValueToString(value).c_str());
ctx.output.println(buffer);
}); });
terminalOK();
terminalOK(ctx);
}); });
terminalRegisterCommand(F("RPN.OPS"), [](const terminal::CommandContext&) {
DEBUG_MSG_P(PSTR("[RPN] Operators:\n"));
rpn_operators_foreach(_rpn_ctxt, [](const String& name, size_t argc, rpn_operator_callback_f ptr) {
DEBUG_MSG_P(PSTR(" %s (%d) -> %p\n"), name.c_str(), argc, ptr);
terminalRegisterCommand(F("RPN.OPS"), [](const terminal::CommandContext& ctx) {
rpn_operators_foreach(_rpn_ctxt, [&ctx](const String& name, size_t argc, rpn_operator::callback_type) {
char buffer[128] = {0};
snprintf_P(buffer, sizeof(buffer), PSTR(" %s (%d)"), name.c_str(), argc);
ctx.output.println(buffer);
}); });
terminalOK();
terminalOK(ctx);
}); });
terminalRegisterCommand(F("RPN.TEST"), [](const terminal::CommandContext& ctx) { 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());
_rpnDump();
rpn_stack_clear(_rpn_ctxt);
terminalOK();
} else {
if (ctx.argc != 2) {
terminalError(F("Wrong arguments")); terminalError(F("Wrong arguments"));
return;
}
ctx.output.print(F("Running RPN expression: "));
ctx.output.println(ctx.argv[1].c_str());
if (!rpn_process(_rpn_ctxt, ctx.argv[1].c_str())) {
rpn_stack_clear(_rpn_ctxt);
char buffer[64] = {0};
snprintf_P(buffer, sizeof(buffer), PSTR("position=%u category=%d code=%d"),
_rpn_ctxt.error.position, static_cast<int>(_rpn_ctxt.error.category), _rpn_ctxt.error.code);
terminalError(ctx, buffer);
return;
} }
_rpnShowStack(ctx.output);
rpn_stack_clear(_rpn_ctxt);
terminalOK(ctx);
}); });
} }
#endif #endif
// enables us to use rules without any events firing
// notice: requires rpnRun to trigger at least once so that we can install runners
void _rpnRunnersCheck() {
auto ts = millis();
for (auto& runner : _rpn_runners) {
if (ts - runner.last >= runner.period) {
runner.expired = true;
runner.last = ts;
_rpn_run = true;
}
}
}
void _rpnRunnersReset() {
auto old = std::remove_if(_rpn_runners.begin(), _rpn_runners.end(), [](RpnRunner& runner) {
return (RpnRunner::Policy::OneShot == runner.policy) && runner.expired;
});
if (old != _rpn_runners.end()) {
_rpn_runners.erase(old, _rpn_runners.end());
}
for (auto& runner : _rpn_runners) {
runner.expired = false;
}
}
void _rpnRun() { void _rpnRun() {
if (!_rpn_run) { if (!_rpn_run) {
@ -405,13 +525,10 @@ void _rpnRun() {
_rpn_last = millis(); _rpn_last = millis();
_rpn_run = false; _rpn_run = false;
String rule;
unsigned char i = 0; unsigned char i = 0;
String rule = getSetting({"rpnRule", i});
while (rule.length()) {
//DEBUG_MSG_P(PSTR("[RPN] Running \"%s\"\n"), rule.c_str());
while ((rule = getSetting({"rpnRule", i++})).length()) {
rpn_process(_rpn_ctxt, rule.c_str()); rpn_process(_rpn_ctxt, rule.c_str());
//_rpnDump();
rule = getSetting({"rpnRule", ++i});
rpn_stack_clear(_rpn_ctxt); rpn_stack_clear(_rpn_ctxt);
} }
@ -423,7 +540,9 @@ void _rpnRun() {
void _rpnLoop() { void _rpnLoop() {
_rpnRunnersCheck();
_rpnRun(); _rpnRun();
_rpnRunnersReset();
} }


+ 1
- 1
code/platformio.ini View File

@ -148,7 +148,7 @@ lib_deps =
PubSubClient PubSubClient
rc-switch rc-switch
https://github.com/LowPowerLab/RFM69#7008d57a https://github.com/LowPowerLab/RFM69#7008d57a
https://github.com/mcspr/rpnlib.git#0.23.0
https://github.com/mcspr/rpnlib.git#0.24.1
NewPing NewPing
https://github.com/sparkfun/SparkFun_VEML6075_Arduino_Library#V_1.0.3 https://github.com/sparkfun/SparkFun_VEML6075_Arduino_Library#V_1.0.3
https://github.com/pololu/vl53l1x-arduino#1.0.1 https://github.com/pololu/vl53l1x-arduino#1.0.1


Loading…
Cancel
Save