mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-02 10:53:01 +02:00
Prototype / WIP for settings.
This commit is contained in:
@@ -81,7 +81,7 @@ namespace PinUsers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
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);
|
auto native = pin.getNative(Pin::Capabilities::PWM | Pin::Capabilities::Native);
|
||||||
|
|
||||||
pwmChannel_ = TryGrabChannel(frequency);
|
pwmChannel_ = TryGrabChannel(frequency);
|
||||||
|
@@ -23,11 +23,253 @@
|
|||||||
*/
|
*/
|
||||||
#include "Spindle.h"
|
#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 {
|
namespace Spindles {
|
||||||
// This adds support for PWM
|
// This adds support for PWM
|
||||||
class PWM : public Spindle {
|
class PWM : public Spindle {
|
||||||
public:
|
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(const PWM&) = delete;
|
||||||
PWM(PWM&&) = delete;
|
PWM(PWM&&) = delete;
|
||||||
@@ -70,3 +312,7 @@ namespace Spindles {
|
|||||||
uint8_t calc_pwm_precision(uint32_t freq);
|
uint8_t calc_pwm_precision(uint32_t freq);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Spindles::PWM::Settings pwmSettings;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user