1
0
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:
Stefan de Bruijn
2020-11-03 22:49:27 +01:00
parent 8bfce04eda
commit bc630b64f9
2 changed files with 248 additions and 2 deletions

View File

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

View File

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