Browse Source

Update rpnlib to 0.23.x (#2274)

## [0.23.0] 2020-07-26
### Added
- `p` operator to print the top of the stack via debug function
- `&var` syntax to create variable reference in expression
- `=` operator for variable assignment in expression
- `exists` operator to check for variable existance (only for references)
- `deref` operator to convert variable reference into a value (only for references)
- Allow to use either float or double as floating type, parse numbers in expressions as specified type
- Add boolean type, parse `true` and `false` in expressions
- Add null type, parse `null` in expressions
- Add string type, parse double-quoted `"string"` in expressions
- Add integer and unsigned integer type, used in operators
- Allow to configure underlying types from rpnlib\_config.h and -D... flags
- Return `rpn_error` from operators, split error types into categories
- Create a new stack by using `[` keyword. Move stack contents into the previous stack + size by using `]`.

### Changed
- Stack structure no longer holds raw `float`, but internal `rpn_value` type
- rpn\_... setter and getter methods use `rpn_value` type
- Operator functions return `rpn_error` type, allowing to return both value and operator errors
- Variables in expressions are no longer required to exist when using `&var`
  Expression will automatically create the variable, set it to `null` and push it's reference on the stack
- It is possible to create 'reference' stack values
- Improve precision of `e` and `pi`

### Fixed
- Proper value for `e` constant
- Allow to use multiple contexts simultaniously, replace `rpn_error` and `rpn_debug_callback`
  with the current `rpn_context` members `error` and `debug_callback` respectively
mcspr-patch-1
Max Prokhorov 4 years ago
committed by GitHub
parent
commit
35305f1f36
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 141 additions and 75 deletions
  1. +140
    -74
      code/espurna/rpnrules.cpp
  2. +1
    -1
      code/platformio.ini

+ 140
- 74
code/espurna/rpnrules.cpp View File

@ -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<bool>(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<rpn_int>(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<rpn_int>(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<uint32_t>(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<num; i++) {
char * name = rpn_variable_name(_rpn_ctxt, i);
float value;
rpn_variable_get(_rpn_ctxt, name, value);
DEBUG_MSG_P(PSTR(" %s: %s\n"), name, String(value).c_str());
}
}
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());
});
terminalOK();
});
terminalRegisterCommand(F("RPN.OPS"), [](const terminal::CommandContext&) {
unsigned char num = _rpn_ctxt.operators.size();
DEBUG_MSG_P(PSTR("[RPN] Operators:\n"));
for (unsigned char i=0; i<num; i++) {
DEBUG_MSG_P(PSTR(" %s (%d)\n"), _rpn_ctxt.operators[i].name, _rpn_ctxt.operators[i].argc);
}
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);
});
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<rpn_int>(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

+ 1
- 1
code/platformio.ini View File

@ -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


Loading…
Cancel
Save