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:
@@ -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() {
|
||||
|
@@ -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();
|
||||
};
|
||||
|
@@ -86,6 +86,7 @@ enum class Error : uint8_t {
|
||||
AnotherInterfaceBusy = 120,
|
||||
JogCancelled = 130,
|
||||
BadPinSpecification = 150,
|
||||
BadRuntimeConfigSetting = 151,
|
||||
};
|
||||
|
||||
extern std::map<Error, const char*> ErrorNames;
|
||||
|
@@ -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) {
|
||||
|
Reference in New Issue
Block a user