mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-02 19:02:35 +02:00
Prototype / WIP for settings.
This commit is contained in:
@@ -81,7 +81,7 @@ namespace PinUsers {
|
||||
}
|
||||
|
||||
public:
|
||||
NativePwm(Pin pin, uint32_t frequency, uint32_t maxDuty) : frequency_(frequency), maxDuty_(maxDuty), pin_(pin) {
|
||||
NativePwm(Pin pin, uint32_t frequency, uint32_t maxDuty) : pin_(pin), frequency_(frequency), maxDuty_(maxDuty){
|
||||
auto native = pin.getNative(Pin::Capabilities::PWM | Pin::Capabilities::Native);
|
||||
|
||||
pwmChannel_ = TryGrabChannel(frequency);
|
||||
|
@@ -23,11 +23,253 @@
|
||||
*/
|
||||
#include "Spindle.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
namespace Settings {
|
||||
class YamlParser {
|
||||
int n = 0;
|
||||
|
||||
public:
|
||||
// TODO FIXME: Create a parser. This is just a test.
|
||||
//
|
||||
// We have to think this through a bit. We want to stream the key/values
|
||||
// in some way, but at the same time make it easy to use. Perhaps a
|
||||
// simple 'movenext', 'token type', 'key' and 'value' will do.
|
||||
|
||||
bool moveNext() { return n++ < 1; }
|
||||
const char* key() { return "spindles"; }
|
||||
const char* value() { return "gpio.12"; }
|
||||
/*
|
||||
const char* getValue(const char* key) {
|
||||
if (!strcmp(key, "output")) {
|
||||
return "gpio.12";
|
||||
} else if (!strcmp(key, "direction")) {
|
||||
return "gpio.13";
|
||||
} else if (!strcmp(key, "enable")) {
|
||||
return "gpio.14";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* getNextSection()
|
||||
{
|
||||
if (n == 0) {
|
||||
return "spindles";
|
||||
}
|
||||
else {
|
||||
return "pwm";
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
// Everything that uses the parser derives from this.
|
||||
class SettingsParser {
|
||||
public:
|
||||
virtual void* parse(YamlParser& parser) = 0;
|
||||
};
|
||||
|
||||
class SettingLeaf;
|
||||
|
||||
class SettingsNode : public SettingsParser {
|
||||
protected:
|
||||
std::vector<SettingLeaf*> myLeafs;
|
||||
|
||||
public:
|
||||
static SettingsNode*& CurrentContainer() {
|
||||
static SettingsNode* current = nullptr;
|
||||
return current;
|
||||
}
|
||||
|
||||
SettingsNode() { CurrentContainer() = this; }
|
||||
|
||||
void Add(SettingLeaf* leaf) { myLeafs.push_back(leaf); }
|
||||
};
|
||||
|
||||
class SettingsCollection {
|
||||
struct Item {
|
||||
const char* name;
|
||||
const char* parent;
|
||||
SettingsNode* builder;
|
||||
};
|
||||
|
||||
SettingsCollection() = default;
|
||||
|
||||
static SettingsCollection& instance() {
|
||||
static SettingsCollection instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
std::vector<Item> builders_;
|
||||
|
||||
public:
|
||||
static void registerSetting(const char* name, const char* parent, SettingsNode* builder) {
|
||||
instance().builders_.push_back({ name, parent, builder });
|
||||
}
|
||||
|
||||
static SettingsNode* find(const char* name, const char* parent) {
|
||||
if (parent != nullptr) {
|
||||
for (auto& it : instance().builders_) {
|
||||
if (!strcmp(it.name, name) && !strcmp(it.parent, parent)) {
|
||||
return it.builder;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& it : instance().builders_) {
|
||||
if (!strcmp(it.name, name) && it.parent == nullptr) {
|
||||
return it.builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// Leafs: basically key's with some value.
|
||||
class SettingLeaf {
|
||||
protected:
|
||||
const char* key_;
|
||||
|
||||
public:
|
||||
SettingLeaf(const char* key) : key_(key) {
|
||||
auto currentContainer = SettingsNode::CurrentContainer();
|
||||
if (currentContainer != nullptr) {
|
||||
currentContainer->Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
const char* key() const { return key_; }
|
||||
virtual void parse(YamlParser& parser) = 0;
|
||||
};
|
||||
|
||||
class PinSetting : SettingLeaf {
|
||||
Pin value_;
|
||||
|
||||
public:
|
||||
PinSetting(const char* key) : SettingLeaf(key) {}
|
||||
|
||||
void parse(YamlParser& parser) override { value_ = Pin::create(parser.value()); }
|
||||
Pin value() const { return value_; }
|
||||
};
|
||||
|
||||
// When an error occurs, we want details. Let's just throw an error with these details, and have the
|
||||
// framework clean up the mess.
|
||||
class SettingsError {
|
||||
SettingsError() = default;
|
||||
|
||||
public:
|
||||
SettingsError(const char* message, ...) {
|
||||
// vsnprintf etc.
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: Settings just _define_ settings, and are very temporary objects. The lifetime of the members in a
|
||||
// Settings object is basically the lifetime at which that section is parsed. This is very important, because
|
||||
// if you use member variables for -say- motors, they can bleed through in other axis. In other words: it's
|
||||
// best not to use member variables in settings expect for leaf settings.
|
||||
template <typename Category>
|
||||
class Setting : SettingsNode {
|
||||
public:
|
||||
Setting(const char* settingName, const char* parentName) { SettingsCollection::registerSetting(settingName, parentName, this); }
|
||||
|
||||
virtual Category* create() = 0;
|
||||
|
||||
virtual void* parse(YamlParser& parser) {
|
||||
for (auto leaf : myLeafs) {
|
||||
leaf->parse(parser);
|
||||
}
|
||||
return create();
|
||||
}
|
||||
|
||||
virtual ~Setting() {}
|
||||
};
|
||||
}
|
||||
|
||||
class SpindleCollection {
|
||||
public:
|
||||
std::vector<Spindles::Spindle*> spindles;
|
||||
|
||||
struct MySettings : Settings::Setting<SpindleCollection> {
|
||||
const char* collectionName = "spindles";
|
||||
|
||||
void* parse(Settings::YamlParser& parser) override {
|
||||
SpindleCollection collection;
|
||||
|
||||
while (parser.moveNext()) {
|
||||
auto builder = Settings::SettingsCollection::find(parser.key(), collectionName);
|
||||
Assert(builder != nullptr, "Settings invalid; incorrect key found: %s", parser.key());
|
||||
|
||||
// Unfortunately we cannot use dynamic_cast here, because we lack RTTI.
|
||||
auto spindle = static_cast<Spindles::Spindle*>(builder->parse(parser));
|
||||
collection.spindles.push_back(spindle);
|
||||
}
|
||||
|
||||
// Use copy constructor here. It costs some time, but makes RAII much easier.
|
||||
return new SpindleCollection(collection);
|
||||
}
|
||||
};
|
||||
|
||||
~SpindleCollection() {
|
||||
for (auto spindle : spindles) {
|
||||
delete spindle;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
SpindleCollection* spindles = nullptr;
|
||||
// Axis, misc devices, etc.
|
||||
|
||||
struct MySettings : Settings::Setting<Machine> {
|
||||
MySettings() : Setting("machine", nullptr) {}
|
||||
|
||||
void* parse(Settings::YamlParser& parser) override {
|
||||
Machine machine;
|
||||
|
||||
while (parser.moveNext()) {
|
||||
auto builder = Settings::SettingsCollection::find(parser.key(), nullptr);
|
||||
Assert(builder != nullptr, "Settings invalid; incorrect key found: %s", parser.key());
|
||||
|
||||
// Unfortunately we cannot use dynamic_cast here, because we lack RTTI.
|
||||
if (!strcmp(parser.key(), "spindles")) {
|
||||
Assert(machine.spindles == nullptr, "No spindles should be defined at this point. Only one spindle section is allowed.");
|
||||
machine.spindles = static_cast<SpindleCollection*>(builder->parse(parser));
|
||||
}
|
||||
// else more sections...
|
||||
}
|
||||
|
||||
// Use copy constructor here. It costs some time, but makes RAII much easier.
|
||||
return new Machine(machine);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
namespace Spindles {
|
||||
// This adds support for PWM
|
||||
class PWM : public Spindle {
|
||||
public:
|
||||
PWM() = default;
|
||||
struct MySettings : Settings::Setting<Spindle> {
|
||||
MySettings() : Setting("pwm", "spindles") {} // note we can make 'spindles' a const char* in the Spindle class
|
||||
|
||||
Settings::PinSetting outputPin = "output";
|
||||
Settings::PinSetting directionPin = "direction";
|
||||
Settings::PinSetting enablePin = "enable";
|
||||
|
||||
Spindle* create() override {
|
||||
if (outputPin.value() == Pin::UNDEFINED) {
|
||||
throw Settings::SettingsError("Output pin is undefined.");
|
||||
}
|
||||
// etc.
|
||||
|
||||
return new PWM(outputPin.value(), enablePin.value(), directionPin.value(), 0, 10000);
|
||||
}
|
||||
};
|
||||
|
||||
PWM(Pin output, Pin enable, Pin direction, uint32_t minRpm, uint32_t maxRpm) :
|
||||
_output_pin(output), _enable_pin(enable), _direction_pin(direction), _min_rpm(minRpm), _max_rpm(maxRpm) {}
|
||||
|
||||
PWM(const PWM&) = delete;
|
||||
PWM(PWM&&) = delete;
|
||||
@@ -70,3 +312,7 @@ namespace Spindles {
|
||||
uint8_t calc_pwm_precision(uint32_t freq);
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
Spindles::PWM::Settings pwmSettings;
|
||||
}
|
||||
|
Reference in New Issue
Block a user