mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-01 18:32:37 +02:00
Implemented runtime settings support.
This commit is contained in:
@@ -18,22 +18,25 @@
|
|||||||
|
|
||||||
#include "RuntimeSetting.h"
|
#include "RuntimeSetting.h"
|
||||||
|
|
||||||
|
#include "../Report.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
namespace Configuration {
|
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.
|
// Read fence for config. Shouldn't be necessary, but better safe than sorry.
|
||||||
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst);
|
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RuntimeSetting::enterSection(const char* name, Configuration::Configurable* value) {
|
void RuntimeSetting::enterSection(const char* name, Configuration::Configurable* value) {
|
||||||
if (is(name) && this->value() == nullptr) {
|
if (is(name) && !isHandled_) {
|
||||||
auto previous = start_;
|
auto previous = start_;
|
||||||
|
|
||||||
// Figure out next node
|
// Figure out next node
|
||||||
auto next = start_;
|
auto next = start_;
|
||||||
for (; *next && *next != '=' && *next != '/'; ++next) {}
|
for (; *next && *next != '/'; ++next) {}
|
||||||
|
|
||||||
// Do we have a child?
|
// Do we have a child?
|
||||||
if (*next == '/') {
|
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) {
|
void RuntimeSetting::item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) {
|
||||||
if (is(name) && this->value() != nullptr) {
|
if (is(name)) {
|
||||||
value = atoi(this->value());
|
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) {
|
void RuntimeSetting::item(const char* name, float& value, float minValue, float maxValue) {
|
||||||
if (is(name) && this->value() != nullptr) {
|
if (is(name)) {
|
||||||
char* floatEnd;
|
isHandled_ = true;
|
||||||
value = strtof(this->value(), &floatEnd);
|
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) {
|
void RuntimeSetting::item(const char* name, StringRange& value, int minLength, int maxLength) {
|
||||||
if (is(name) && this->value() != nullptr) {
|
if (is(name)) {
|
||||||
value = this->value();
|
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) {
|
void RuntimeSetting::item(const char* name, Pin& value) {
|
||||||
if (is(name) && this->value() != nullptr) {
|
/*
|
||||||
auto parsed = Pin::create(StringRange(this->value()));
|
Runtime settings of PIN objects is NOT supported!
|
||||||
value.swap(parsed);
|
|
||||||
|
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() {
|
RuntimeSetting::~RuntimeSetting() {
|
||||||
|
@@ -20,49 +20,47 @@
|
|||||||
|
|
||||||
#include "HandlerBase.h"
|
#include "HandlerBase.h"
|
||||||
#include "Configurable.h"
|
#include "Configurable.h"
|
||||||
|
#include "../WebUI/ESPResponse.h"
|
||||||
|
|
||||||
namespace Configuration {
|
namespace Configuration {
|
||||||
class RuntimeSetting : public Configuration::HandlerBase {
|
class RuntimeSetting : public Configuration::HandlerBase {
|
||||||
private:
|
private:
|
||||||
const char* setting_; // $foo/bar=12
|
const char* setting_; // foo/bar
|
||||||
const char* start_;
|
const char* start_;
|
||||||
|
|
||||||
|
const char* newValue_; // null (read) or 123 (value)
|
||||||
|
|
||||||
|
WebUI::ESPResponseStream* out_;
|
||||||
|
|
||||||
bool is(const char* name) const {
|
bool is(const char* name) const {
|
||||||
if (start_ != nullptr) {
|
if (start_ != nullptr) {
|
||||||
auto len = strlen(name);
|
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 {
|
} else {
|
||||||
return false;
|
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:
|
protected:
|
||||||
void enterSection(const char* name, Configuration::Configurable* value) override;
|
void enterSection(const char* name, Configuration::Configurable* value) override;
|
||||||
|
|
||||||
bool matchesUninitialized(const char* name) override { return false; }
|
bool matchesUninitialized(const char* name) override { return false; }
|
||||||
|
|
||||||
public:
|
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, 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, float& value, float minValue, float maxValue) override;
|
||||||
void item(const char* name, StringRange& value, int minLength, int maxLength) 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, 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; }
|
HandlerType handlerType() override { return HandlerType::Runtime; }
|
||||||
|
|
||||||
|
bool isHandled_ = false;
|
||||||
|
|
||||||
virtual ~RuntimeSetting();
|
virtual ~RuntimeSetting();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -86,6 +86,7 @@ enum class Error : uint8_t {
|
|||||||
AnotherInterfaceBusy = 120,
|
AnotherInterfaceBusy = 120,
|
||||||
JogCancelled = 130,
|
JogCancelled = 130,
|
||||||
BadPinSpecification = 150,
|
BadPinSpecification = 150,
|
||||||
|
BadRuntimeConfigSetting = 151,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::map<Error, const char*> ErrorNames;
|
extern std::map<Error, const char*> ErrorNames;
|
||||||
|
@@ -3,6 +3,10 @@
|
|||||||
#include "Regex.h"
|
#include "Regex.h"
|
||||||
|
|
||||||
#include "MachineConfig.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
|
// WG Readable and writable as guest
|
||||||
// WU Readable and writable as user and admin
|
// 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
|
// $key= with nothing following the = . It is important to distinguish
|
||||||
// those cases so that you can say "$N0=" to clear a startup line.
|
// 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
|
// value if one is given, otherwise display the current value
|
||||||
for (Setting* s = Setting::List; s; s = s->next()) {
|
for (Setting* s = Setting::List; s; s = s->next()) {
|
||||||
if (strcasecmp(s->getName(), key) == 0) {
|
if (strcasecmp(s->getName(), key) == 0) {
|
||||||
|
Reference in New Issue
Block a user