Авто-генерация кода работы с EERPOM
- Войдите на сайт для отправки комментариев
Возможно, изобретаю велосипед. Но при разработке платы управления кофеваркой (https://prokofe.ru/plugins/forum/forum_viewtopic.php?246050) возникла необходимость сохранять в EEPROM сложную, древовидную структуру настроек. Например, нужно сохранять несколько рецептов, в каждом рецепте – несколько шагов, каждый шаг – несколько параметров разных типов. Чтобы не писать много похожего кода, решил сгенерировать его автоматически из описания структуры данных. В результате вот такой код на Java
new StoreCGenerator( Store.newInstance().addClassField("CommonSettings")
.addBoolean("AutoDetectCup")
.addWord("MinCupWeight")
.addByte("PumpCleaningWaterSeconds") // Time to suck water after brewing for cleaning
.addByte("CleaningSeconds") // Time to clean brew group after brewing.
.addFloat("Temperature")
.endClassField().addArrayField("ScaleSettings", 8)
.addDWord("RawReading")
.endClassField().addArrayField("PIDParamsSettings", 5)
.addFloat("DivCoef")
.addFloat("DifCoef")
.addFloat("IntegralCoef")
.addFloat("ConstCoef")
.addFloat("OnTargetDif")
.addBoolean("AutoSetup")
.addFloat("MaxRandomShiftPercent")
.endClassField().addArrayField("Recipe", 8)
.addArrayField("Step", "StepSettings", 7)
.addByte("Type") // 0 - Nothing, the end, 1 - Power, 2 - Flow, 3 - Pressure
.addByte("Value")
.addByte("OppositeParameterMaxValue")
.addByte("OppositeParameterMinValue")
.addByte("DurationInSeconds")
.addByte("StopWhenOppositeParameterGrowsTo")
.addByte("StopWhenOppositeParameterFallsTo")
.addByte("StopOnWeight")
.addByte("CurveType") // 0 - Immediately, 1 - Linear, 2 - Fast, 3 - Slow
.endClassField()
.endClassField()).generateCode();
Генерирует в 10 раз больше строк кода на C:
#include <avr/eeprom.h>
class CommonSettings {
public:
CommonSettings();
boolean getAutoDetectCup();
void setAutoDetectCup(boolean value);
uint16_t getMinCupWeight();
void setMinCupWeight(uint16_t value);
uint8_t getPumpCleaningWaterSeconds();
void setPumpCleaningWaterSeconds(uint8_t value);
uint8_t getCleaningSeconds();
void setCleaningSeconds(uint8_t value);
float getTemperature();
void setTemperature(float value);
};
CommonSettings::CommonSettings() {
};
boolean CommonSettings::getAutoDetectCup() {
return eeprom_read_byte((const uint8_t*)0);
};
uint16_t CommonSettings::getMinCupWeight() {
return eeprom_read_word((const uint16_t*)1);
};
uint8_t CommonSettings::getPumpCleaningWaterSeconds() {
return eeprom_read_byte((const uint8_t*)3);
};
uint8_t CommonSettings::getCleaningSeconds() {
return eeprom_read_byte((const uint8_t*)4);
};
float CommonSettings::getTemperature() {
return eeprom_read_float((const float*)5);
};
void CommonSettings::setAutoDetectCup(boolean value) {
eeprom_update_byte((uint8_t*)0, value);
};
void CommonSettings::setMinCupWeight(uint16_t value) {
eeprom_update_word((uint16_t*)1, value);
};
void CommonSettings::setPumpCleaningWaterSeconds(uint8_t value) {
eeprom_update_byte((uint8_t*)3, value);
};
void CommonSettings::setCleaningSeconds(uint8_t value) {
eeprom_update_byte((uint8_t*)4, value);
};
void CommonSettings::setTemperature(float value) {
eeprom_update_float((float*)5, value);
};
class ScaleSettings {
public:
ScaleSettings();
// Array of capacity 8
int index = 0;
void setIndex(int _index);
int startAddress = 0;
void setStartAddress(int _startAddress);
uint32_t getRawReading();
void setRawReading(uint32_t value);
};
ScaleSettings::ScaleSettings() {
};
uint32_t ScaleSettings::getRawReading() {
return eeprom_read_dword((const uint32_t*)startAddress);
};
void ScaleSettings::setIndex(int _index) {
if (index != _index) {
int oldIndex = index;
index = _index;
setStartAddress(startAddress - oldIndex * 4);
}
};
void ScaleSettings::setStartAddress(int _startAddress) {
startAddress = _startAddress + index * 4;
};
void ScaleSettings::setRawReading(uint32_t value) {
eeprom_update_dword((uint32_t*)startAddress, value);
};
class PIDParamsSettings {
public:
PIDParamsSettings();
// Array of capacity 5
int index = 0;
void setIndex(int _index);
int startAddress = 0;
void setStartAddress(int _startAddress);
float getDivCoef();
void setDivCoef(float value);
float getDifCoef();
void setDifCoef(float value);
float getIntegralCoef();
void setIntegralCoef(float value);
float getConstCoef();
void setConstCoef(float value);
float getOnTargetDif();
void setOnTargetDif(float value);
boolean getAutoSetup();
void setAutoSetup(boolean value);
float getMaxRandomShiftPercent();
void setMaxRandomShiftPercent(float value);
};
PIDParamsSettings::PIDParamsSettings() {
};
float PIDParamsSettings::getDivCoef() {
return eeprom_read_float((const float*)startAddress);
};
float PIDParamsSettings::getDifCoef() {
return eeprom_read_float((const float*)(startAddress + 4));
};
float PIDParamsSettings::getIntegralCoef() {
return eeprom_read_float((const float*)(startAddress + 8));
};
float PIDParamsSettings::getConstCoef() {
return eeprom_read_float((const float*)(startAddress + 12));
};
float PIDParamsSettings::getOnTargetDif() {
return eeprom_read_float((const float*)(startAddress + 16));
};
boolean PIDParamsSettings::getAutoSetup() {
return eeprom_read_byte((const uint8_t*)(startAddress + 20));
};
float PIDParamsSettings::getMaxRandomShiftPercent() {
return eeprom_read_float((const float*)(startAddress + 21));
};
void PIDParamsSettings::setIndex(int _index) {
if (index != _index) {
int oldIndex = index;
index = _index;
setStartAddress(startAddress - oldIndex * 25);
}
};
void PIDParamsSettings::setStartAddress(int _startAddress) {
startAddress = _startAddress + index * 25;
};
void PIDParamsSettings::setDivCoef(float value) {
eeprom_update_float((float*)startAddress, value);
};
void PIDParamsSettings::setDifCoef(float value) {
eeprom_update_float((float*)(startAddress + 4), value);
};
void PIDParamsSettings::setIntegralCoef(float value) {
eeprom_update_float((float*)(startAddress + 8), value);
};
void PIDParamsSettings::setConstCoef(float value) {
eeprom_update_float((float*)(startAddress + 12), value);
};
void PIDParamsSettings::setOnTargetDif(float value) {
eeprom_update_float((float*)(startAddress + 16), value);
};
void PIDParamsSettings::setAutoSetup(boolean value) {
eeprom_update_byte((uint8_t*)(startAddress + 20), value);
};
void PIDParamsSettings::setMaxRandomShiftPercent(float value) {
eeprom_update_float((float*)(startAddress + 21), value);
};
class StepSettings {
public:
StepSettings();
// Array of capacity 7
int index = 0;
void setIndex(int _index);
int startAddress = 0;
void setStartAddress(int _startAddress);
uint8_t getType();
void setType(uint8_t value);
uint8_t getValue();
void setValue(uint8_t value);
uint8_t getOppositeParameterMaxValue();
void setOppositeParameterMaxValue(uint8_t value);
uint8_t getOppositeParameterMinValue();
void setOppositeParameterMinValue(uint8_t value);
uint8_t getDurationInSeconds();
void setDurationInSeconds(uint8_t value);
uint8_t getStopWhenOppositeParameterGrowsTo();
void setStopWhenOppositeParameterGrowsTo(uint8_t value);
uint8_t getStopWhenOppositeParameterFallsTo();
void setStopWhenOppositeParameterFallsTo(uint8_t value);
uint8_t getStopOnWeight();
void setStopOnWeight(uint8_t value);
uint8_t getCurveType();
void setCurveType(uint8_t value);
};
StepSettings::StepSettings() {
};
uint8_t StepSettings::getType() {
return eeprom_read_byte((const uint8_t*)startAddress);
};
uint8_t StepSettings::getValue() {
return eeprom_read_byte((const uint8_t*)(startAddress + 1));
};
uint8_t StepSettings::getOppositeParameterMaxValue() {
return eeprom_read_byte((const uint8_t*)(startAddress + 2));
};
uint8_t StepSettings::getOppositeParameterMinValue() {
return eeprom_read_byte((const uint8_t*)(startAddress + 3));
};
uint8_t StepSettings::getDurationInSeconds() {
return eeprom_read_byte((const uint8_t*)(startAddress + 4));
};
uint8_t StepSettings::getStopWhenOppositeParameterGrowsTo() {
return eeprom_read_byte((const uint8_t*)(startAddress + 5));
};
uint8_t StepSettings::getStopWhenOppositeParameterFallsTo() {
return eeprom_read_byte((const uint8_t*)(startAddress + 6));
};
uint8_t StepSettings::getStopOnWeight() {
return eeprom_read_byte((const uint8_t*)(startAddress + 7));
};
uint8_t StepSettings::getCurveType() {
return eeprom_read_byte((const uint8_t*)(startAddress + 8));
};
void StepSettings::setIndex(int _index) {
if (index != _index) {
int oldIndex = index;
index = _index;
setStartAddress(startAddress - oldIndex * 9);
}
};
void StepSettings::setStartAddress(int _startAddress) {
startAddress = _startAddress + index * 9;
};
void StepSettings::setType(uint8_t value) {
eeprom_update_byte((uint8_t*)startAddress, value);
};
void StepSettings::setValue(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 1), value);
};
void StepSettings::setOppositeParameterMaxValue(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 2), value);
};
void StepSettings::setOppositeParameterMinValue(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 3), value);
};
void StepSettings::setDurationInSeconds(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 4), value);
};
void StepSettings::setStopWhenOppositeParameterGrowsTo(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 5), value);
};
void StepSettings::setStopWhenOppositeParameterFallsTo(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 6), value);
};
void StepSettings::setStopOnWeight(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 7), value);
};
void StepSettings::setCurveType(uint8_t value) {
eeprom_update_byte((uint8_t*)(startAddress + 8), value);
};
class Recipe {
public:
Recipe();
// Array of capacity 8
int index = 0;
void setIndex(int _index);
int startAddress = 0;
void setStartAddress(int _startAddress);
StepSettings step;
StepSettings getStep();
};
Recipe::Recipe() {
};
StepSettings Recipe::getStep() {
return step;
};
void Recipe::setIndex(int _index) {
if (index != _index) {
int oldIndex = index;
index = _index;
setStartAddress(startAddress - oldIndex * 63);
}
};
void Recipe::setStartAddress(int _startAddress) {
startAddress = _startAddress + index * 63;
step.setStartAddress(startAddress);
};
class Store {
public:
Store();
CommonSettings commonSettings;
CommonSettings getCommonSettings();
ScaleSettings scaleSettings;
ScaleSettings getScaleSettings();
PIDParamsSettings pIDParamsSettings;
PIDParamsSettings getPIDParamsSettings();
Recipe recipe;
Recipe getRecipe();
};
Store::Store() {
};
CommonSettings Store::getCommonSettings() {
return commonSettings;
};
ScaleSettings Store::getScaleSettings() {
return scaleSettings;
};
PIDParamsSettings Store::getPIDParamsSettings() {
return pIDParamsSettings;
};
Recipe Store::getRecipe() {
return recipe;
};
Кроме того, генерируется ещё похожий код на Java так как программа платы пишется на Java, а потом конвертируется в C под Arduino. Если кому интересно – пишите – могу поделиться кодом генераторов.
Ужас! :-)
Напиши везде inline static, хотя бы, а то никакой памяти не оберешься. И не C, а C++.
Объясните, пожалуйста, на что тут будет расходоваться память, если каждого класса – по одному экземпляру? В смысле, дополнительно по ссылке на каждый экземпляр? А если сделать все методы и переменные статическими, то экземпляров и ссылок вообще не нужно будет? Т.е. минус 6 ссылок?
maxalex397,
Вы с ума-то не сходите - столько нагенерить! Нахрена? Тем более, что в структурах даже память динамически нигде не запрашивается.
Вы слышали про такое объектно-ориентированное программирование? Нет? Оно и в Яве есть, и в С++.
Вся Ваша байда делается в три строки + сами структуры для хранения.
На код будет расходоваться память. Не оперативная.
ЕвгенийП,
А где в моем коде память динамически запрашивается и сколько её запрашивается? Структура такая вся в памяти должна быть, а это уже сейчас более 600 байт. А у меня под листья дерева переменные не выделяются. Кроме того, насколько я понимаю, в 3 строчки структуру целиком предлагаете записывать, даже если в ней один байтик поменяется? А Вам EEPROM не жалко?
rkit,
Понял, спасибо! Если не будет хватать - будет, на чем сэкономить.
А где в моем коде память динамически запрашивается и сколько её запрашивается?
Нигде. Вы читать умеете? Попробуйте прочитать пост #4 ещё раз, может со второго раза получится? Если нет, - то я дико извиняюсь.
насколько я понимаю, в 3 строчки структуру целиком предлагаете записывать, даже если в ней один байтик поменяется?
Вы неправильно понимаете.
В три строчки (можно даже в две) - это с побайтовой проверкой и записью только тех байтов (именно байтов, а не полей структуры), которые поменялись.
Вот у Вас есть некоторая структура:
struct ConfigParameter { char name [8]; int intParam; double doubleParam; // и ещё сколько угодно параметров // ... };Добавляем к ней две (прописью: две) строки
struct ConfigParameter { inline void store(const uint16_t address) { EEPROM.put(address, * this); } inline void load(const uint16_t address) { EEPROM.get(address, * this); } char name [8]; int intParam; double doubleParam; // и ещё сколько угодно параметров // ... };и после этого спокойно используем конструкции типа
Можно поступить "красивше" - всунуть эти строки через наследование, шаблон или ещё как. Можно адрес сохранения внутрь структуры упрятать, но суть от этого не меняется. И не надо сходить с ума с сотнями строк на ровном месте.