Выбор иерархии классов для разнотипных данных (дата/время/дни/таймер)

Потихоньку переписываю древний проект по поливу огурцов в определенные дни и время. С отображение информации на графическом экране вопросов нет, с логикой тоже все норм. Задался вопросом отображением информации необходимого параметра (например текущего времени или выбора дня полива) в режиме настройки.
Т е будет две кнопки, одна выбирает что меняется, этот элемент начинает мигать, второй кнопкой меняется значение, получается подобие многоуровнего меню.
Думал просто функциями, получется нагромождение case/if . Думал запихнуть все это в классы, но совсем не соображу иерархию всего этого.

Это делается интерфейсами.

Т.е. делается базовый класс “редактируемое значение”. В базовом классе есть базовые функции типа: “показать текущее значение”, “обработать нажатие кнопки “вверх””, “обработать нажатие кнопки “вниз””, "обработать нажатие “средней кнопки” и т.п.

Затем все редактируемые параметры оформляются классами, производными от базового.

Основная программа знает какой из параметров сейчас “активен” и когда начинается редактирование, просто вызывает методы базового класса в контексте данного параметра, т.е. она по большому счёту и не знает, что на самом деле редактирует.

Если надо, я могу придумать небольшой пример подобной организации. Только, чтобы от этого была польза, скажите, что у Вас есть, чтобы я сделал пример на таком же железе, чтобы Вы могли легко запустить у себя и посмотреть. Ну или можно сделать на мониторе порта, в принципе - не так красиво будет, но разобраться можно. А так, у меня есть 16х2, 20х4, дисплейчик от Nokia, может ещё чего, смотреть надо).

1 лайк

Точно будет интересно

когда допишу (ближе к осени) выложу.

to ЕвгенийП, спасибо! Пошел изучать…

нет пока ничего, программа на STM32CubeIDE, которая тупо что то отображает, но буду конечно все переписывать, вопросы будут - задам, спасибо!

Прооосим, прооосим, прооосим, прооосим… :slight_smile:

1 лайк

Делаешь полностью независимый обработчик клавиатуры, который пережевывает все отскоки, двойные нажатия, и подобные вещи и выдает четкие коды значимых для тебя событий и ничего лишнего.
Делаешь виртуальный класс с методом, принимающим код события.
От него наследуешь все свои элементы и в каждом пишешь свою логику обработки клавиш через switch.
Делать кучу разных обработчиков на каждый чих это лишние буквы и в таких простых меню не нужно.

Что то получается в первом приближении:

Спойлер
class ifaceItem { // интерфейс элемента внутри группы, например текущий день недели, или часы полива
private:
protected:
	ifaceItem * parentItem;
	unsigned short countChildren = 0;
	ifaceItem * childItems[32];
	unsigned char fontSize;
	unsigned long blinkTimer;
	unsigned char modeShow = 0; // режим отображения 0-показывается 1-нет 2-моргает
public:
	unsigned short posX, posY, sizeX, sizeY, backgroundColor = 0x0000;
	unsigned short iX, iY, itemColor = 0x0000;
    void addChild(ifaceItem * newChild) {this->childItems[this->countChildren] = newChild; ++this->countChildren;}
    ifaceItem * getChild(unsigned char posChild) {return this->childItems[posChild];}
    unsigned char getCountChildren(void) {return this->countChildren;}
    virtual void showItem(void){}; // отобразить элемент
    virtual void changeValue() {}; // изменить значение элемента
    virtual void blinkItem() {}; // мигает позиция
    virtual ~ifaceItem(void) {} // деструктор
};

class baseUnit:public ifaceItem { // группа
private:
protected:
public:
    baseUnit(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned short sX = 32, unsigned short sY = 64, unsigned short bColor =  0x00);
    virtual void showItem(void) override;
    virtual ~baseUnit(void) {}; // деструктор
};

class baseDayOfWeekCurrentDateTime:public ifaceItem { // элемент день денели текстом выводится
private:
	unsigned char * textWDay[8] = {(unsigned char *)"Sunday", (unsigned char *)"Monday", (unsigned char *)"Tuesday", (unsigned char *)"Wednesday", (unsigned char *)"Thursday", (unsigned char *)"Friday", (unsigned char *)"Saturday", (unsigned char *)"Sunday"};
	unsigned char currentDayOfWeek = 0;
protected:
public:
	baseDayOfWeekCurrentDateTime(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned iSize = 0, unsigned short iColor =  0x00);
    virtual void showItem(void) override;
    virtual ~baseDayOfWeekCurrentDateTime(void) {}; // деструктор
};

baseUnit::baseUnit(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned short sX, unsigned short sY, unsigned short bColor) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	if (itemParent != nullptr) {
		this->posX = pX;
		this->posY = pY;
		this->sizeX = sX;
		this->sizeY = sY;
		this->backgroundColor = bColor;
		this->modeShow = 0;
	}
}

void baseUnit::showItem(void) {
	f103st7735fillRect(this->backgroundColor, this->posX, this->posY, this->sizeX-1, this->sizeY-1);
}

baseDayOfWeekCurrentDateTime::baseDayOfWeekCurrentDateTime(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned iSize, unsigned short iColor) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	this->iX = pX;
	this->iY = pY;
	this->itemColor = iColor;
	this->modeShow = 0;
}

void baseDayOfWeekCurrentDateTime::showItem(void) {
	f103st7735printString((unsigned char *)this->textWDay[this->currentDayOfWeek], this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
}

baseUnit iMainDisplay;
baseUnit iCurrentDateTime((ifaceItem *)&iMainDisplay, 0, 0,
		st7735sizeX, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize], currentWDayBack);
baseUnit iPumperDaysTime((ifaceItem *)&iMainDisplay, 0, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize],
		st7735sizeX, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2,
		pumpBackground);
baseUnit iPeriodTime((ifaceItem *)&iMainDisplay, 0, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2,
		st7735sizeX, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2+fontSizeY[pumpPeriodSize]+fontSizeY[0]*2,
		pumpPeriodBack);
baseDayOfWeekCurrentDateTime iDayOfWeekCurrentDateTime((ifaceItem *)&iCurrentDateTime, 0, 0, currentWDaySize, currentWDayColor);

void initPumper(void) {
	iMainDisplay.addChild((ifaceItem *)&iCurrentDateTime);
	iCurrentDateTime.showItem();
	iMainDisplay.addChild((ifaceItem *)&iPumperDaysTime);
	iPumperDaysTime.showItem();
	iMainDisplay.addChild((ifaceItem *)&iPeriodTime);
	iPeriodTime.showItem();
	//--current date time
	iCurrentDateTime.addChild((ifaceItem *)&iDayOfWeekCurrentDateTime);
	iDayOfWeekCurrentDateTime.showItem();
}

1 лайк

Спасибо! за пинок в правильном направлении, все прекрасно выбирается/мигает/настройки меняются, все красиво!

Спойлер
/*
 * pumper.h
 *
 *  Created on: 13 июн. 2023 г.
 *      Author: seleznev_a
 */

#ifndef PUMPER_H_
#define PUMPER_H_

class ifaceItem { // интерфейс элемента внутри группы, например текущий день недели, или часы полива
private:
protected:
	ifaceItem * parentItem;
	ifaceItem * childItems[32];
	unsigned char noEditItem = 0;
	unsigned short currentValue = 0;
	unsigned short minValue = 0;
	unsigned short maxValue = 1;
	unsigned short countChildren = 0;
public:
	unsigned short posX, posY, sizeX, sizeY, backgroundColor = 0x0000;
    void addChild(ifaceItem * newChild) {this->childItems[this->countChildren] = newChild; ++this->countChildren;}
    ifaceItem * getChild(unsigned char posChild) {return this->childItems[posChild];}
    unsigned char getCountChildren(void) {return this->countChildren;}
    void setNoEditedItem(void) {this->noEditItem = 1;}
    unsigned char getNoEditedItem(void) {return this->noEditItem;}
    unsigned char getValue(void) {return this->currentValue;}
    virtual void showItem(void){}; // отобразить элемент
    virtual void changeValue() {++this->currentValue; if (this->currentValue > this->maxValue) this->currentValue = this->minValue;} // изменить значение элемента
    virtual void blinkItem() {}; // мигает позиция
    virtual void setValue(unsigned short inValue) {this->currentValue = inValue;}
    //virtual void minValueSet(unsigned short inValue) {this->minValue = inValue;}
    //virtual void maxValueSet(unsigned short inValue) {this->maxValue = inValue;}
};

class baseUnit:public ifaceItem { // группа
private:
protected:
	unsigned short iX, iY, itemColor = 0x0000;
	unsigned char fontSize;
	unsigned char modeShow = 0; // режим отображения 0-показывается 1+2-моргает
	unsigned long blinkTimer;
public:
    baseUnit(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned short sX = 32, unsigned short sY = 64, unsigned short bColor =  0x00);
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

class baseOneCharValue:public baseUnit { // элемент - один символ
private:
protected:
public:
	baseOneCharValue(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned short mV = 0, unsigned short xV = 1);
    virtual void showItem(void) override;
    //virtual void blinkItem(void) override;
};

class baseNum2digValue:public baseOneCharValue { // элемент - число из двух цифр
private:
protected:
public:
	baseNum2digValue(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned short mV = 0, unsigned short xV = 1):
		baseOneCharValue(itemParent, pX, pY, iSize, iColor, mV, xV) {};
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

class baseDictWDayValue:public baseOneCharValue { // элемент - техт из справочника день недели
private:
protected:
	unsigned char * textWDay[9] = {(unsigned char *)"Sunday", (unsigned char *)"Monday", (unsigned char *)"Tuesday", (unsigned char *)"Wednesday", (unsigned char *)"Thursday", (unsigned char *)"Friday", (unsigned char *)"Saturday", (unsigned char *)"         ", (unsigned char *)"-no set- "};
public:
	baseDictWDayValue(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned short mV = 0, unsigned short xV = 1):
		baseOneCharValue(itemParent, pX, pY, iSize, iColor, mV, xV) {};
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

class baseDictMonthValue:public baseOneCharValue { // элемент - техт из справочника месяцы
private:
	unsigned char * textMonths[14] = {(unsigned char *)"January  ", (unsigned char *)"February ", (unsigned char *)"March    ", (unsigned char *)"April    ", (unsigned char *)"May      ", (unsigned char *)"June     ", (unsigned char *)"July     ", (unsigned char *)"August   ", (unsigned char *)"September", (unsigned char *)"October  ", (unsigned char *)"November ", (unsigned char *)"December ", (unsigned char *)"         ", (unsigned char *)"-no set- "};
protected:
public:
	baseDictMonthValue(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned short mV = 0, unsigned short xV = 1):
		baseOneCharValue(itemParent, pX, pY, iSize, iColor, mV, xV) {};
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

class baseDictPumpDaysValue:public baseDictWDayValue { // элемент - по два символа из дней недели - дни полива, value подсвеченный выбранный день
private:
	unsigned char weekDay = 0; // день недели 0...6
	unsigned short colorDay = 0x5555; // цвет выбранного дня
	unsigned short colorLabel = 0xAAAA; // цвет фона выбранного дня
protected:
public:
	baseDictPumpDaysValue(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned char inDay = 0, unsigned short daColor = 0xAAAA, unsigned short lbColor = 0x5555);
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

class baseNum2digText:public baseNum2digValue { // элемент - число из двух цифр + текст
private:
	unsigned char * endText;
protected:
public:
	baseNum2digText(ifaceItem * itemParent = nullptr, unsigned short pX = 0, unsigned short pY = 0, unsigned char iSize = 0, unsigned short iColor =  0x00, unsigned char * inText = nullptr, unsigned short mV = 0, unsigned short xV = 1);
    virtual void showItem(void) override;
    virtual void blinkItem(void) override;
};

void loopPumper(void);
void f1InitPumpGpio(void);
void initPumper(void);

#define blinkPeriod 500UL

#define keys_short_press 50UL
#define keys_long_press 2000UL

#define currentWDayColor ST7735_MAGENTA
#define currentWDayBack ST7735_BLACK
#define currentWDaySize 0

#define currentDateColor ST7735_GREEN
#define currentDateSize 0

#define currentTimeColor ST7735_YELLOW
#define currentTimeSize 2
#define currentTimeStartX 18

#define pumpBackground ST7735_WHITE
#define pumpDaysColor ST7735_BLACK
#define pumpSelectedDay ST7735_YELLOW
#define pumpSelectedBack ST7735_BLUE
#define pumpDaysSize 0

#define pumpTimeColor ST7735_GREEN
#define pumpTimeSize 2

#define pumpPeriodColor ST7735_ORANGE
#define pumpPeriodBack ST7735_BLUE
#define pumpPeriodSize 2

#endif /* PUMPER_H_ */



/*
 * pumper.c
 *
 *  Created on: 13 июн. 2023 г.
 *      Author: seleznev_a
 */

#include "pumper.h"
#include "f103ds3231.h"
#include "stm32f10x.h"
#include <time.h>
#include "f103c8t6.h"
#include "f103st7735.h"
#include "stdlib.h"

unsigned char timeIsGood = 0;

unsigned char pumpDays[7] = {0, 0, 1, 0, 1, 0, 0};
unsigned char pumpTime[2] = {20, 18};
unsigned char pumpPeriod = 5;

unsigned char devModes = 0; // 1-settings mode
unsigned long unusedDevice; // таймер бездействия пользователя

baseUnit iMainDisplay;

//--current date time
baseUnit iCurrentDateTime((ifaceItem *)&iMainDisplay, 0, 0,
		st7735sizeX, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize], currentWDayBack);
baseDictWDayValue iDayOfWeekCurrentDateTime((ifaceItem *)&iCurrentDateTime, 0, 0,
		currentWDaySize, currentWDayColor, 0, 6);
baseNum2digValue iHourTimeCurrentDateTime((ifaceItem *)&iCurrentDateTime, currentTimeStartX,
		fontSizeY[currentWDaySize], currentTimeSize, currentTimeColor, 0, 23);
baseOneCharValue iPointTimeCurrentDateTime((ifaceItem *)&iCurrentDateTime, currentTimeStartX + fontSizeX[currentTimeSize] * 2, fontSizeY[currentWDaySize],
		currentTimeSize, currentTimeColor);
baseNum2digValue iMinTimeCurrentDateTime((ifaceItem *)&iCurrentDateTime, currentTimeStartX + fontSizeX[currentTimeSize] * 3, fontSizeY[currentWDaySize],
		currentTimeSize, currentTimeColor, 0, 59);
baseNum2digValue iMDayCurrentDateTime((ifaceItem *)&iCurrentDateTime, 0, fontSizeY[currentWDaySize]+fontSizeY[currentTimeSize],
		currentDateSize, currentDateColor, 1, 31);
baseDictMonthValue iMonthCurrentDateTime((ifaceItem *)&iCurrentDateTime, fontSizeX[currentDateSize] * 3, fontSizeY[currentWDaySize]+fontSizeY[currentTimeSize],
		currentDateSize, currentDateColor, 0, 11);
baseNum2digValue iYearCurrentDateTime((ifaceItem *)&iCurrentDateTime, fontSizeX[currentDateSize] * 13, fontSizeY[currentWDaySize]+fontSizeY[currentTimeSize],
		currentDateSize, currentDateColor, 23, 30);

//-- pump data
baseUnit iPumperDaysTime((ifaceItem *)&iMainDisplay, 0, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize],
		st7735sizeX, fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2,
		pumpBackground);
baseDictPumpDaysValue i1DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0], fontSizeY[0], pumpDaysSize, pumpDaysColor, 1, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i2DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*4, fontSizeY[0], pumpDaysSize, pumpDaysColor, 2, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i3DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*8, fontSizeY[0], pumpDaysSize, pumpDaysColor, 3, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i4DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*12, fontSizeY[0], pumpDaysSize, pumpDaysColor, 4, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i5DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*2, fontSizeY[0]+fontSizeY[pumpDaysSize], pumpDaysSize, pumpDaysColor, 5, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i6DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*6, fontSizeY[0]+fontSizeY[pumpDaysSize], pumpDaysSize, pumpDaysColor, 6, pumpSelectedDay, pumpSelectedBack);
baseDictPumpDaysValue i0DayPumper((ifaceItem *)&iPumperDaysTime, fontSizeX[0]+fontSizeX[pumpDaysSize]*10, fontSizeY[0]+fontSizeY[pumpDaysSize], pumpDaysSize, pumpDaysColor, 0, pumpSelectedDay, pumpSelectedBack);
baseNum2digValue iHourPumpTime((ifaceItem *)&iPumperDaysTime, currentTimeStartX, fontSizeY[0]+fontSizeY[pumpDaysSize]*2,
		pumpTimeSize, pumpTimeColor, 8, 22);
baseOneCharValue iPointPumpTime((ifaceItem *)&iPumperDaysTime, currentTimeStartX + fontSizeX[pumpTimeSize] * 2, fontSizeY[0]+fontSizeY[pumpDaysSize]*2,
		pumpTimeSize, pumpTimeColor);
baseNum2digValue iMinPumpTime((ifaceItem *)&iPumperDaysTime, currentTimeStartX + fontSizeX[pumpTimeSize] * 3, fontSizeY[0]+fontSizeY[pumpDaysSize]*2,
		pumpTimeSize, pumpTimeColor, 0, 59);

//--period pump
baseUnit iPeriodTime((ifaceItem *)&iMainDisplay, 0,
		fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+
			fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2,
		st7735sizeX,
		fontSizeY[currentWDaySize]+fontSizeY[currentDateSize]+fontSizeY[currentTimeSize]+
			fontSizeY[pumpDaysSize]*2+fontSizeY[pumpTimeSize]+fontSizeY[0]*2+
				fontSizeY[pumpPeriodSize]+fontSizeY[0]*2,
		pumpPeriodBack);
baseNum2digText iPeriodPump((ifaceItem *)&iPeriodTime, 0, fontSizeY[0],
		pumpPeriodSize, pumpPeriodColor, (unsigned char *)" min", 5, 40);

baseUnit::baseUnit(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned short sX, unsigned short sY, unsigned short bColor) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	if (itemParent != nullptr) {
		this->posX = pX;
		this->posY = pY;
		this->sizeX = sX;
		this->sizeY = sY;
		this->backgroundColor = bColor;
		this->modeShow = 0;
	}
}

void baseUnit::showItem(void) {
	this->modeShow = 0;
	f103st7735fillRect(this->backgroundColor, this->posX, this->posY, this->sizeX-1, this->sizeY-1);
	if (this->countChildren) for(unsigned char i=0; i<this->countChildren; ++i) this->childItems[i]->showItem();
}

baseOneCharValue::baseOneCharValue(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned char iSize, unsigned short iColor, unsigned short mV, unsigned short xV) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	this->iX = pX;
	this->iY = pY;
	this->itemColor = iColor;
	this->modeShow = 0;
	this->fontSize = iSize;
	this->minValue = mV;
	this->maxValue = xV;
}

void baseOneCharValue::showItem(void) {
	this->modeShow = 0;
	f103st7735printOneChar(this->currentValue, this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
}

void baseNum2digValue::showItem(void) {
	this->modeShow = 0;
	unsigned short posX = 0;
	f103st7735printOneChar((this->currentValue/10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
	posX += fontSizeX[this->fontSize];
	f103st7735printOneChar((this->currentValue%10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
}

void baseDictWDayValue::showItem(void) {
	this->modeShow = 0;
	f103st7735printString((unsigned char *)this->textWDay[this->currentValue], this->itemColor, this->parentItem->backgroundColor,
			this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
}

void baseDictMonthValue::showItem(void) {
	this->modeShow = 0;
	f103st7735printString((unsigned char *)this->textMonths[this->currentValue], this->itemColor, this->parentItem->backgroundColor,
			this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
}

baseDictPumpDaysValue::baseDictPumpDaysValue(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned char iSize, unsigned short iColor, unsigned char inDay, unsigned short daColor, unsigned short lbColor) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	this->iX = pX;
	this->iY = pY;
	this->itemColor = iColor;
	this->modeShow = 0;
	this->fontSize = iSize;
	this->weekDay = inDay;
	this->colorDay = daColor;
	this->colorLabel = lbColor;
}

void baseDictPumpDaysValue::showItem(void) {
	this->modeShow = 0;
	if (this->currentValue) {
		f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][0], this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
		f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][1], this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
	} else {
		f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][0], this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
		f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][1], this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
	}
}

baseNum2digText::baseNum2digText(ifaceItem * itemParent, unsigned short pX, unsigned short pY, unsigned char iSize, unsigned short iColor, unsigned char * inText, unsigned short mV, unsigned short xV) {
	this->countChildren = 0;
	this->parentItem = itemParent;
	this->iX = pX;
	this->iY = pY;
	this->itemColor = iColor;
	this->modeShow = 0;
	this->fontSize = iSize;
	this->endText = inText;
	this->minValue = mV;
	this->maxValue = xV;
}

void baseNum2digText::showItem(void) {
	this->modeShow = 0;
	unsigned short posX = 0;
	if (this->currentValue < 10) {
		f103st7735printOneChar('0', this->itemColor, this->parentItem->backgroundColor, this->fontSize,
				this->parentItem->posX+this->iX,
					this->parentItem->posY+this->iY);
		posX += fontSizeX[this->fontSize];
	}
	f103st7735printUInt(this->currentValue, this->itemColor, this->parentItem->backgroundColor, this->fontSize,
			this->parentItem->posX+this->iX+posX,
				this->parentItem->posY+this->iY);
	posX += fontSizeX[this->fontSize];
	f103st7735printString((unsigned char *)this->endText, this->itemColor, this->parentItem->backgroundColor, this->fontSize, posX, this->parentItem->posY+this->iY);
}

void fillPumpDaysFromData() {
	if (iPumperDaysTime.getCountChildren()) {
		for(unsigned char i=0; ((i<iPumperDaysTime.getCountChildren()) && (i<7)); ++i) {
			if (pumpDays[i]) iPumperDaysTime.getChild(i)->setValue(1); else iPumperDaysTime.getChild(i)->setValue(0);
		}
		if ((iPumperDaysTime.getCountChildren()>7) && (iPumperDaysTime.getCountChildren()<11)) {
			iHourPumpTime.setValue(pumpTime[0]);
			iMinPumpTime.setValue(pumpTime[1]);
		}
	}
}

void initPumper(void) {
	//--current date time
	iMainDisplay.addChild((ifaceItem *)&iCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iDayOfWeekCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iHourTimeCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iPointTimeCurrentDateTime);
	iPointTimeCurrentDateTime.setValue(' ');
	iPointTimeCurrentDateTime.setNoEditedItem();
	iCurrentDateTime.addChild((ifaceItem *)&iMinTimeCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iMDayCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iMonthCurrentDateTime);
	iCurrentDateTime.addChild((ifaceItem *)&iYearCurrentDateTime);
	//iCurrentDateTime.showItem();
	//-- pump data
	iMainDisplay.addChild((ifaceItem *)&iPumperDaysTime);
	iPumperDaysTime.addChild((ifaceItem *)&i0DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i1DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i2DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i3DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i4DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i5DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&i6DayPumper);
	iPumperDaysTime.addChild((ifaceItem *)&iHourPumpTime);
	iPumperDaysTime.addChild((ifaceItem *)&iPointPumpTime);
	iPointPumpTime.setValue('.');
	iPointPumpTime.setNoEditedItem();
	iPumperDaysTime.addChild((ifaceItem *)&iMinPumpTime);
	//fillPumpDaysFromData();
	//iPumperDaysTime.showItem();
	//--period pump
	iMainDisplay.addChild((ifaceItem *)&iPeriodTime);
	iPeriodTime.addChild((ifaceItem *)&iPeriodPump);
	//iPeriodPump.setValue(pumpPeriod);
	//iPeriodTime.showItem();
}

void f1InitPumpGpio(void) {
	RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // GPIO port B
	// PB14 - key 1
	GPIOB->CRH &= ~GPIO_CRH_MODE14; // 00: Input mode (reset state)
	GPIOB->CRH &= ~GPIO_CRH_CNF14; // 10: Input with pull-up / pull-down
	GPIOB->CRH |= GPIO_CRH_CNF14_1; // 10: Input with pull-up / pull-down
	GPIOB->ODR |= GPIO_ODR_ODR14; // Input with pull-up
	// PB15 - key 2
	GPIOB->CRH &= ~GPIO_CRH_MODE15; // 00: Input mode (reset state)
	GPIOB->CRH &= ~GPIO_CRH_CNF15; // 10: Input with pull-up / pull-down
	GPIOB->CRH |= GPIO_CRH_CNF15_1; // 10: Input with pull-up / pull-down
	GPIOB->ODR |= GPIO_ODR_ODR15; // Input with pull-up
}

void showDateTime(void) {
	static unsigned long timerShowDateTime = 59000UL;
	static unsigned long timerBlinkSec = 0;
	static unsigned char secBlink =0;
	if ((millis() - timerShowDateTime) >= 60000UL) {
		timerShowDateTime = millis();
		if (getDS3231currentDateTime((struct tm *)&currentDateTime) == dsI2CerrorReturn) {
			timeIsGood = 0;
			f103st7735printString((unsigned char *)"Error", ST7735_RED, currentWDayBack, currentWDaySize, 0, 0);
		} else {
			timeIsGood = 1;
			iDayOfWeekCurrentDateTime.setValue(currentDateTime.tm_wday);
			iHourTimeCurrentDateTime.setValue(currentDateTime.tm_hour);
			iMinTimeCurrentDateTime.setValue(currentDateTime.tm_min);
			iMDayCurrentDateTime.setValue(currentDateTime.tm_mday);
			iMonthCurrentDateTime.setValue(currentDateTime.tm_mon);
			iYearCurrentDateTime.setValue((currentDateTime.tm_year+1900)%100);
			iCurrentDateTime.showItem();
		}
	} else	if (timeIsGood && ((millis() - timerBlinkSec) >= 1000UL)) {
			timerBlinkSec = millis();
			if (secBlink) {
				iPointTimeCurrentDateTime.setValue(':');
				secBlink = 0;
			} else {
				iPointTimeCurrentDateTime.setValue(' ');
				secBlink = 1;
			}
			iPointTimeCurrentDateTime.showItem();
	}
}

void baseUnit::blinkItem(void) {
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		f103st7735fillRect(this->backgroundColor, this->posX, this->posY, this->sizeX-1, this->sizeY-1);
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
		} else {
			this->modeShow = 1;
			if (this->countChildren) for(unsigned char i=0; i<this->countChildren; ++i) this->childItems[i]->showItem();
		}
	}
}

void baseNum2digValue::blinkItem(void) {
	unsigned short posX = 0;
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
		} else {
			this->modeShow = 1;
			f103st7735printOneChar((this->currentValue/10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printOneChar((this->currentValue%10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
		}
	}
}

void baseDictWDayValue::blinkItem(void) {
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			f103st7735printString((unsigned char *)this->textWDay[7], this->itemColor, this->parentItem->backgroundColor,
					this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
		} else {
			this->modeShow = 1;
			f103st7735printString((unsigned char *)this->textWDay[this->currentValue], this->itemColor, this->parentItem->backgroundColor,
					this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
		}
	}
}

void baseDictMonthValue::blinkItem(void) {
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			f103st7735printString((unsigned char *)this->textMonths[12], this->itemColor, this->parentItem->backgroundColor,
					this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
		} else {
			this->modeShow = 1;
			f103st7735printString((unsigned char *)this->textMonths[this->currentValue], this->itemColor, this->parentItem->backgroundColor,
					this->fontSize, this->iX+this->parentItem->posX, this->iY+this->parentItem->posY);
		}
	}
}

/*void baseOneCharValue::blinkItem(void) {
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
		} else {
			this->modeShow = 1;
			f103st7735printOneChar(this->currentValue, this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
		}
	}
}*/

void baseDictPumpDaysValue::blinkItem(void) {
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			if (this->currentValue) {
				f103st7735printOneChar(' ', this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
				f103st7735printOneChar(' ', this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
			} else {
				f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
				f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
			}
		} else {
			this->modeShow = 1;
			if (this->currentValue) {
				f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][0], this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
				f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][1], this->colorDay, this->colorLabel, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
			} else {
				f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][0], this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX, this->parentItem->posY+this->iY);
				f103st7735printOneChar((unsigned char)this->textWDay[this->weekDay][1], this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+fontSizeY[this->fontSize], this->parentItem->posY+this->iY);
			}
		}
	}
}

void baseNum2digText::blinkItem(void) {
	unsigned short posX = 0;
	if ((millis() - this->blinkTimer) >= blinkPeriod) {
		this->blinkTimer = millis();
		if ((this->modeShow == 0) || (this->modeShow == 1)) {
			this->modeShow = 2;
			f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printOneChar(' ', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printString((unsigned char *)"     ", this->itemColor, this->parentItem->backgroundColor, this->fontSize, posX, this->parentItem->posY+this->iY);
		} else {
			this->modeShow = 1;
			f103st7735printOneChar((this->currentValue/10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printOneChar((this->currentValue%10)+'0', this->itemColor, this->parentItem->backgroundColor, this->fontSize, this->parentItem->posX+this->iX+posX, this->parentItem->posY+this->iY);
			posX += fontSizeX[this->fontSize];
			f103st7735printString((unsigned char *)this->endText, this->itemColor, this->parentItem->backgroundColor, this->fontSize, posX, this->parentItem->posY+this->iY);
		}
	}
}

unsigned char getKey1(void) { // выдает 1 если нажата 1 кнопка
	static unsigned char last1Level = 1;
	static unsigned char flag1KeysDown = 0;
	static unsigned long start1TimeDown;
	unsigned char current1Level;
	if (GPIOB->IDR & GPIO_IDR_IDR14) current1Level = 1; else current1Level = 0;
	if (current1Level != last1Level) { // change level
		unusedDevice = millis();
		if (current1Level == 0) { // key down
			if (flag1KeysDown == 0) { // no timer
				flag1KeysDown = 1; // begin timer
				start1TimeDown = millis();
			}
		} else { // key up
			if (flag1KeysDown != 0) { // timer started
				if ((millis() - start1TimeDown) >= 500UL) { // error press
					flag1KeysDown = 0; // stop timer
				} else if ((millis() - start1TimeDown) >= keys_short_press) { // short press
					flag1KeysDown = 0; // stop timer
					last1Level = current1Level; // save current level
					return 1;
				}
			}
		}
	} else { // level not change
		if ((flag1KeysDown != 0) && (current1Level == 0)) { // timer started and key down
			if ((millis() - start1TimeDown) >= keys_long_press) { // long press
				flag1KeysDown = 0; // stop timer
				last1Level = current1Level; // save current level
				return 2;
			}
		} else if (current1Level != 0) { // key up
			flag1KeysDown = 0; // reset timer
		}
	}
	last1Level = current1Level; // save current level
	return 0;
}

unsigned char getKey2(void) { // выдает 1 если нажата 2 кнопка
	static unsigned char last2Level = 1;
	static unsigned char flag2KeysDown = 0;
	static unsigned long start2TimeDown;
	unsigned char current2Level;
	if (GPIOB->IDR & GPIO_IDR_IDR15) current2Level = 1; else current2Level = 0;
	if (current2Level != last2Level) { // change level
		unusedDevice = millis();
		if (current2Level == 0) { // key down
			if (flag2KeysDown == 0) { // no timer
				flag2KeysDown = 1; // begin timer
				start2TimeDown = millis();
			}
		} else { // key up
			if (flag2KeysDown != 0) { // timer started
				if ((millis() - start2TimeDown) >= 500UL) { // error press
					flag2KeysDown = 0; // stop timer
				} else if ((millis() - start2TimeDown) >= keys_short_press) { // short press
					flag2KeysDown = 0; // stop timer
					last2Level = current2Level; // save current level
					return 1;
				}
			}
		}
	} else { // level not change
		if ((flag2KeysDown != 0) && (current2Level == 0)) { // timer started and key down
			if ((millis() - start2TimeDown) >= keys_long_press) { // long press
				flag2KeysDown = 0; // stop timer
				last2Level = current2Level; // save current level
				return 2;
			}
		} else if (current2Level != 0) { // key up
			flag2KeysDown = 0; // reset timer
		}
	}
	last2Level = current2Level; // save current level
	return 0;
}

unsigned char controlDevice(void) { // выдать 1 если изменились параметры и их все надо обновить на экране
	unsigned char data1key = getKey1();
	unsigned char data2key = getKey2();
	static unsigned char numEditMainUnit = 0; // номер главного модуля который редактируется
	static unsigned char enteredSubUnit = 0; // перешли в редактирование значения в модуле
	static unsigned char numSubItem = 0; // номер элемента который редактируется
	if (!devModes) { // standby mode
		if (data1key) { // enter set mode
			devModes = 1;  // enter set mode
			numEditMainUnit = 0; // номер главного модуля который редактируется
			enteredSubUnit = 0; // моргаем пока модулем
		}
	} else { // settings mode
		if ((millis()-unusedDevice) >= 10000UL) { // если кнопки долго не нажимали
			devModes = 0; // возвращаемся в ждущий режим
			return 1; // с обновлением данных
		}
		if (enteredSubUnit) { // в режиме редактирования элемента
			iMainDisplay.getChild(numEditMainUnit)->getChild(numSubItem)->blinkItem(); // моргаем элементом
			if (data2key) { // если продолжается нажиматься вторая кнопка
				iMainDisplay.getChild(numEditMainUnit)->getChild(numSubItem)->showItem(); // предыдущий элемент просто отобразим
				++numSubItem; // будем моргать следующим элементом
				if (numSubItem >= iMainDisplay.getChild(numEditMainUnit)->getCountChildren()) numSubItem = 0;
				if (iMainDisplay.getChild(numEditMainUnit)->getChild(numSubItem)->getNoEditedItem()) { // если элемент не редактируемый
					++numSubItem; // будем моргать следующим элементом
					if (numSubItem >= iMainDisplay.getChild(numEditMainUnit)->getCountChildren()) numSubItem = 0;
				}
			} else {
				if (data1key) { // нажалась кнопка 1 - изменить значение
					iMainDisplay.getChild(numEditMainUnit)->getChild(numSubItem)->changeValue();
				}
			}
		} else { // в режиме выбора модуля
			iMainDisplay.getChild(numEditMainUnit)->blinkItem(); // моргаем модулем
			if (data1key) { // если продолжается нажиматься первая кнопка
				iMainDisplay.getChild(numEditMainUnit)->showItem(); // предыдущий модуль просто отобразим
				++numEditMainUnit; // будем моргать следующим модулем
				if (numEditMainUnit >= iMainDisplay.getCountChildren()) numEditMainUnit = 0;
			} else {
				if (data2key) { // если нажалась вторая кнопка
					iMainDisplay.getChild(numEditMainUnit)->showItem(); // текущий модуль просто отобразим
					enteredSubUnit = 1; // перешли в редактирование значения в модуле
					numSubItem = 0; // номер элемента который редактируется
				}
			}
		}
	}
	return 0;
}

void loopPumper(void) {
	static unsigned char needShowPumpDate = 1; // в обычном ждущем режиме один раз показать данные полива
	if (!devModes) { // standby mode
		showDateTime();
		if (needShowPumpDate) {
			needShowPumpDate = 0;
			fillPumpDaysFromData();
			iPumperDaysTime.showItem();
			iPeriodPump.setValue(pumpPeriod);
			iPeriodTime.showItem();
		}
	}
	needShowPumpDate = controlDevice(); // основная работа с кнопками и режимами настройки
}

Неужели нельзя сделать универсальной ???

наверное можно, разбить разнотипные данные на еще более мелкие обьекты. Т е у меня есть, текст(символ)/число/три типа элементов справочника. Но это потом…сначала функционал добить.

шаблонами проще было бы, наерна. Во главе абстрактный класс без хранилища данных и pure virtual функции для обработки. И наследуй потом от него разных потомков.

это пока выше моего понимания :thinking:

У Вас нешаблонное мышление?

3 лайка

Не знаю какое у меня мышление)
Но пока не очень получается…научусь.

https://youtu.be/rF5-7VZekz8
Это как выглядит в реальности.

1 лайк

и в каком месте тут шаблоны? :slight_smile:

Ну так типы-то разные, а работать с ними надо единообразно. Я подумаю тщательно на досуге. :slight_smile:

Хотя, нет, шаблоны не проще, ты прав. Простое наследование падёт.

у меня получается шесть переопределенных функций blinkItem

если я сделаю щесть функций clearItem, т е очистка поля вывода информации, то тогда я смогу прописать в головном классе одну функцию blinkItem и будет наверное немного красивее и логичнее.