1
0
mirror of https://github.com/bdring/Grbl_Esp32.git synced 2025-08-30 17:49:56 +02:00

Implemented runtime settings support.

This commit is contained in:
Stefan de Bruijn
2021-06-13 21:39:28 +02:00
parent 2bba2b01c7
commit 2f0e7e716e
4 changed files with 144 additions and 30 deletions

View File

@@ -18,22 +18,25 @@
#include "RuntimeSetting.h"
#include "../Report.h"
#include <cstdlib>
#include <atomic>
namespace Configuration {
RuntimeSetting::RuntimeSetting(const char* runtimeSetting) : setting_(runtimeSetting + 1), start_(runtimeSetting + 1) {
RuntimeSetting::RuntimeSetting(const char* key, const char* value, WebUI::ESPResponseStream* out) :
setting_(key), start_(key), newValue_(value), out_(out) {
// Read fence for config. Shouldn't be necessary, but better safe than sorry.
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst);
}
void RuntimeSetting::enterSection(const char* name, Configuration::Configurable* value) {
if (is(name) && this->value() == nullptr) {
if (is(name) && !isHandled_) {
auto previous = start_;
// Figure out next node
auto next = start_;
for (; *next && *next != '=' && *next != '/'; ++next) {}
for (; *next && *next != '/'; ++next) {}
// Do we have a child?
if (*next == '/') {
@@ -49,30 +52,108 @@ namespace Configuration {
}
}
void RuntimeSetting::item(const char* name, bool& value) {
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%s\r\n", setting_, value ? "true" : "false");
} else {
value = (!strcmp(newValue_, "true"));
}
}
}
void RuntimeSetting::item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) {
if (is(name) && this->value() != nullptr) {
value = atoi(this->value());
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%d\r\n", setting_, value);
} else {
value = atoi(newValue_);
}
}
}
void RuntimeSetting::item(const char* name, float& value, float minValue, float maxValue) {
if (is(name) && this->value() != nullptr) {
char* floatEnd;
value = strtof(this->value(), &floatEnd);
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%.3f\r\n", setting_, value);
} else {
char* floatEnd;
value = strtof(newValue_, &floatEnd);
}
}
}
void RuntimeSetting::item(const char* name, StringRange& value, int minLength, int maxLength) {
if (is(name) && this->value() != nullptr) {
value = this->value();
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%s\r\n", setting_, value.str().c_str());
} else {
value = StringRange(newValue_);
}
}
}
void RuntimeSetting::item(const char* name, int& value, EnumItem* e) {
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
for (; e->name; ++e) {
if (e->value == value) {
grbl_sendf(out_->client(), "$%s=%s\r\n", setting_, e->name);
return;
}
}
} else {
for (; e->name; ++e) {
if (!strcmp(newValue_, e->name)) {
value = e->value;
return;
}
}
if (strlen(newValue_) == 0) {
value = e->value;
return;
} else {
Assert(false, "Provided enum value %s is not valid", newValue_);
}
}
}
}
void RuntimeSetting::item(const char* name, IPAddress& value) {
if (is(name)) {
isHandled_ = true;
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%s\r\n", setting_, value.toString().c_str());
} else {
IPAddress ip;
auto str = String(newValue_);
if (!ip.fromString(str)) {
Assert(false, "Expected an IP address like 192.168.0.100");
}
value = ip;
}
}
}
void RuntimeSetting::item(const char* name, Pin& value) {
if (is(name) && this->value() != nullptr) {
auto parsed = Pin::create(StringRange(this->value()));
value.swap(parsed);
/*
Runtime settings of PIN objects is NOT supported!
if (is(name)) {
if (newValue_ == nullptr) {
grbl_sendf(out_->client(), "$%s=%s\r\n", setting_, value.name().c_str());
} else {
auto parsed = Pin::create(StringRange(this->value()));
value.swap(parsed);
}
}
*/
}
RuntimeSetting::~RuntimeSetting() {

View File

@@ -20,48 +20,46 @@
#include "HandlerBase.h"
#include "Configurable.h"
#include "../WebUI/ESPResponse.h"
namespace Configuration {
class RuntimeSetting : public Configuration::HandlerBase {
private:
const char* setting_; // $foo/bar=12
const char* setting_; // foo/bar
const char* start_;
const char* newValue_; // null (read) or 123 (value)
WebUI::ESPResponseStream* out_;
bool is(const char* name) const {
if (start_ != nullptr) {
auto len = strlen(name);
return !strncmp(name, start_, len) && (start_[len] == '=' || start_[len] == '/');
auto result = !strncasecmp(name, start_, len) && (start_[len] == '\0' || start_[len] == '/');
return result;
} else {
return false;
}
}
const char* value() const {
for (const char* it = start_; *it; ++it) {
if (*it == '/') {
return nullptr;
} else if (*it == '=') {
return it + 1;
}
}
return nullptr;
}
protected:
void enterSection(const char* name, Configuration::Configurable* value) override;
bool matchesUninitialized(const char* name) override { return false; }
public:
RuntimeSetting(const char* runtimeSetting);
RuntimeSetting(const char* key, const char* value, WebUI::ESPResponseStream* out);
void item(const char* name, bool& value) override;
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override;
void item(const char* name, float& value, float minValue, float maxValue) override;
void item(const char* name, StringRange& value, int minLength, int maxLength) override;
void item(const char* name, Pin& value) override;
void item(const char* name, int& value, EnumItem* e) override {}
void item(const char* name, IPAddress& value) override;
void item(const char* name, int& value, EnumItem* e) override;
HandlerType handlerType() override { return HandlerType::Runtime; }
bool isHandled_ = false;
virtual ~RuntimeSetting();
};

View File

@@ -86,6 +86,7 @@ enum class Error : uint8_t {
AnotherInterfaceBusy = 120,
JogCancelled = 130,
BadPinSpecification = 150,
BadRuntimeConfigSetting = 151,
};
extern std::map<Error, const char*> ErrorNames;

View File

@@ -3,6 +3,10 @@
#include "Regex.h"
#include "MachineConfig.h"
#include "Configuration/RuntimeSetting.h"
#include "Configuration/AfterParse.h"
#include "Configuration/Validator.h"
#include "Configuration/ParseException.h"
// WG Readable and writable as guest
// WU Readable and writable as user and admin
@@ -546,7 +550,37 @@ Error do_command_or_setting(const char* key, char* value, WebUI::AuthenticationL
// $key= with nothing following the = . It is important to distinguish
// those cases so that you can say "$N0=" to clear a startup line.
// First search the settings list by text name. If found, set a new
// First search the yaml settings by name. If found, set a new
// value if one is given, otherwise display the current value
try {
Configuration::RuntimeSetting rts(key, value, out);
config->group(rts);
if (rts.isHandled_) {
try {
Configuration::Validator validator;
config->validate();
config->group(validator);
} catch (std::exception& ex) {
log_error("Validation error: " << ex.what());
return Error::BadRuntimeConfigSetting;
}
Configuration::AfterParse afterParseHandler;
config->afterParse();
config->group(afterParseHandler);
return Error::Ok;
}
} catch (const Configuration::ParseException& ex) {
log_error("Configuration parse error: " << ex.What() << " @ " << ex.LineNumber() << ":" << ex.ColumnNumber());
return Error::BadRuntimeConfigSetting;
} catch (const AssertionFailed& ex) {
log_error("Configuration change failed: " << ex.what());
return Error::BadRuntimeConfigSetting;
}
// Next search the settings list by text name. If found, set a new
// value if one is given, otherwise display the current value
for (Setting* s = Setting::List; s; s = s->next()) {
if (strcasecmp(s->getName(), key) == 0) {