Extreme makeover to timer manager, now properly shows one shot next day timers in timer chart.

Added cursor showing time of day on Timer chart.
This commit is contained in:
Ray Jones 2019-07-28 17:40:12 +10:00
parent f154580eb2
commit c1207e66ef
15 changed files with 221 additions and 105 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -132,4 +132,37 @@
<varNfHeight>const uint8_t {0}Height</varNfHeight>
<displayName>splash</displayName>
</OutputConfiguration>
<OutputConfiguration>
<commentVariableName>true</commentVariableName>
<commentCharVisualizer>true</commentCharVisualizer>
<commentCharDescriptor>true</commentCharDescriptor>
<commentStyle>Cpp</commentStyle>
<bmpVisualizerChar>#</bmpVisualizerChar>
<rotation>RotateNinety</rotation>
<flipHorizontal>true</flipHorizontal>
<flipVertical>false</flipVertical>
<paddingRemovalHorizontal>Fixed</paddingRemovalHorizontal>
<paddingRemovalVertical>Tighest</paddingRemovalVertical>
<lineWrap>AtColumn</lineWrap>
<bitLayout>RowMajor</bitLayout>
<byteOrder>MsbFirst</byteOrder>
<byteFormat>Hex</byteFormat>
<byteLeadingString>0x</byteLeadingString>
<generateLookupArray>true</generateLookupArray>
<descCharWidth>DisplayInBits</descCharWidth>
<descCharHeight>DisplayInBits</descCharHeight>
<descFontHeight>DisplayInBits</descFontHeight>
<generateLookupBlocks>false</generateLookupBlocks>
<lookupBlocksNewAfterCharCount>80</lookupBlocksNewAfterCharCount>
<descImgWidth>DisplayInBits</descImgWidth>
<descImgHeight>DisplayInBits</descImgHeight>
<generateSpaceCharacterBitmap>true</generateSpaceCharacterBitmap>
<spaceGenerationPixels>2</spaceGenerationPixels>
<varNfBitmaps>const uint8_t PROGMEM {0}Bitmaps </varNfBitmaps>
<varNfCharInfo>const FONT_CHAR_INFO PROGMEM {0}Descriptors</varNfCharInfo>
<varNfFontInfo>const FONT_INFO {0}FontInfo</varNfFontInfo>
<varNfWidth>const uint8_t {0}Width</varNfWidth>
<varNfHeight>const uint8_t {0}Height</varNfHeight>
<displayName>Fonts</displayName>
</OutputConfiguration>
</ArrayOfOutputConfiguration>

View File

@ -269,7 +269,6 @@ CScreenManager::begin(bool bNoClock)
if(!bNoClock)
menuloop.push_back(new CClockScreen(*_pDisplay, *this)); // clock
menuloop.push_back(new CPrimingScreen(*_pDisplay, *this)); // mode / priming
// menuloop.push_back(new CWiFiScreen(*_pDisplay, *this)); // comms info
menuloop.push_back(new CGPIOInfoScreen(*_pDisplay, *this)); // GPIO info
menuloop.push_back(new CMenuTrunkScreen(*_pDisplay, *this));
_Screens.push_back(menuloop);

View File

@ -35,7 +35,7 @@
#include "../Utility/macros.h"
CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreenHeader(display, mgr)
CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : CScreen(display, mgr)
{
_initUI();
}
@ -43,7 +43,7 @@ CSetClockScreen::CSetClockScreen(C128x64_OLED& display, CScreenManager& mgr) : C
void
CSetClockScreen::onSelect()
{
CScreenHeader::onSelect();
CScreen::onSelect();
_initUI();
}
@ -68,18 +68,18 @@ CSetClockScreen::show()
if(deltaT >= 0) {
_nextT += 1000;
CScreenHeader::show(false);
CScreen::show();
_display.clearDisplay();
char str[16];
int xPos, yPos;
_printInverted(0, 15, " Set Clock ", true);
_showTitle("Set Clock");
const BTCDateTime& now = Clock.get();
if(_rowSel == 0) {
// update printable values
working = now;
// DELIBERATE DROP THROUGH HERE
}
if(_SaveTime) {
@ -90,12 +90,13 @@ CSetClockScreen::show()
}
}
else {
yPos = 28;
yPos = 20;
xPos = 6;
// date
if(_rowSel==0) {
xPos = 20;
xPos = 18;
_printMenuText(xPos, yPos, working.dowStr());
xPos = 20;
}
sprintf(str, "%d", working.day());
@ -107,7 +108,7 @@ CSetClockScreen::show()
sprintf(str, "%d", working.year());
_printMenuText(xPos, yPos, str, _rowSel==3);
// time
yPos = 40;
yPos = 32;
xPos = 26;
sprintf(str, "%02d", working.hour());
_printMenuText(xPos, yPos, str, _rowSel==4);
@ -123,22 +124,23 @@ CSetClockScreen::show()
_printMenuText(xPos, yPos, str, _rowSel==6);
if(_rowSel>=1)
_printMenuText(_display.width()-border, yPos, "SET", _rowSel==7, eRightJustify);
}
// navigation line
xPos = _display.xCentre();
if(_rowSel == 0) {
yPos = 53;
_printMenuText(_display.width(), yPos, "\030Edit", false, eRightJustify);
_printMenuText(xPos, yPos, " Exit ", true, eCentreJustify);
}
else {
_display.drawFastHLine(0, 52, 128, WHITE);
_printMenuText(xPos, 56, "\033\032 Sel \030\031 Adj", false, eCentreJustify);
if(_rowSel == 7) {
_printMenuText(xPos, 56, "Save", false, eCentreJustify);
// navigation line
xPos = _display.xCentre();
if(_rowSel == 0) {
yPos = 53;
_printMenuText(_display.width(), yPos, "\030Edit", false, eRightJustify);
_printMenuText(xPos, yPos, " Exit ", true, eCentreJustify);
}
else {
_printMenuText(xPos, 56, "Abort", false, eCentreJustify);
_display.drawFastHLine(0, 52, 128, WHITE);
_printMenuText(xPos, 56, "\033\032 Sel \030\031 Adj", false, eCentreJustify);
if(_rowSel == 7) {
_printMenuText(xPos, 56, "Save", false, eCentreJustify);
}
else {
_printMenuText(xPos, 56, "Abort", false, eCentreJustify);
}
}
}
}

View File

@ -30,7 +30,7 @@ class C128x64_OLED;
class CScreenManager;
class CProtocol;
class CSetClockScreen : public CScreenHeader {
class CSetClockScreen : public CScreen {
int _rowSel;
unsigned long _nextT;
BTCDateTime working;

View File

@ -72,7 +72,7 @@ CSetTimerScreen::show()
int xPos, yPos;
if(_rowSel == 0) {
NVstore.getTimerInfo(_timerID, _timerInfo);
NVstore.getTimerInfo(_timerID, _timerInfo); // ensure actual data when on base menu bar
}
sprintf(str, "Set Timer #%d", _timerID + 1);
_showTitle(str);
@ -156,34 +156,11 @@ CSetTimerScreen::keyHandler(uint8_t event)
static bool bHeld = false;
// handle initial key press
if(event & keyPressed) {
_repeatCount = 0;
bHeld = false;
// press CENTRE
if(event & key_Centre) {
if(_rowSel == 0) {
_ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // exit: return to clock screen
}
else if(_rowSel == 2) { // exit from per day settings
_rowSel = 1;
_colSel = 4;
}
else { // in config fields, save new settings
// test if the setting conflict with an already defined timer
_conflictID = CTimerManager::conflictTest(_timerInfo);
if(_conflictID) {
_timerInfo.enabled = 0; // cancel enabled status
_ConflictTime = millis() + 1500;
_ScreenManager.reqUpdate();
_rowSel = 1;
_colSel = 4; // select enable/disable
}
else {
_SaveTime = millis() + 1500;
_ScreenManager.reqUpdate();
_rowSel = 0;
_colSel = 0;
}
CTimerManager::setTimer(_timerInfo);
}
// ON KEY RELEASE
}
// press LEFT - navigate fields, or screens
if(event & key_Left) {
@ -225,8 +202,14 @@ CSetTimerScreen::keyHandler(uint8_t event)
// handle held down keys
if(event & keyRepeat) {
_repeatCount++;
bHeld = true;
if(_rowSel == 1) {
if(event & key_Centre) {
_ScreenManager.reqUpdate();
_rowSel = 0;
_colSel = 0;
}
if(_colSel < 4) {
// fast repeat of hour/minute adjustments by holding up or down keys
if(event & key_Down) _adjust(-1);
@ -244,9 +227,37 @@ CSetTimerScreen::keyHandler(uint8_t event)
}
if(event & keyReleased) {
if(!bHeld) {
int maskDOW = 0x01 << _colSel;
if(!bHeld) {
if(event & key_Centre) {
if(_rowSel == 0) {
_ScreenManager.selectMenu(CScreenManager::RootMenuLoop); // exit: return to clock screen
}
else if(_rowSel == 2) { // exit from per day settings
_rowSel = 1;
_colSel = 4;
}
else { // in config fields, save new settings
// test if the setting conflict with an already defined timer
_conflictID = CTimerManager::conflictTest(_timerInfo);
if(_conflictID) {
_timerInfo.enabled = 0; // cancel enabled status
_ConflictTime = millis() + 1500;
_ScreenManager.reqUpdate();
_rowSel = 1;
_colSel = 4; // select enable/disable
}
else {
_SaveTime = millis() + 1500;
_ScreenManager.reqUpdate();
_rowSel = 0;
_colSel = 0;
}
CTimerManager::setTimer(_timerInfo);
}
}
int maskDOW = 0x01 << _colSel;
// released DOWN - can only leave adjustment by using OK (centre button)
if(event & key_Down) {
// adjust selected item

View File

@ -33,6 +33,7 @@ class CProtocol;
class CSetTimerScreen : public CScreen {
int _rowSel;
int _colSel;
int _repeatCount;
int _timerID;
unsigned long _SaveTime;
unsigned long _ConflictTime;

View File

@ -35,6 +35,7 @@
#include "../../lib/RTClib/RTClib.h"
#include "fonts/MiniFont.h"
#include "../RTC/TimerManager.h"
#include "../RTC/Clock.h"
static uint8_t condensed[7][120];
@ -50,7 +51,6 @@ CTimerChartScreen::CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr,
void
CTimerChartScreen::onSelect()
{
CTimerManager::createMap();
CTimerManager::condenseMap(condensed);
}
@ -145,10 +145,27 @@ CTimerChartScreen::show()
}
}
}
cursor();
return true;
}
void
CTimerChartScreen::cursor()
{
static bool bShowWhite = true;
// create masking based upon TODAY
const BTCDateTime tNow = Clock.get();
int dow = tNow.dayOfTheWeek();
int todayTime = tNow.hour() * 60 + tNow.minute();
int yPos = 6 + 7*dow;
int xPos = 9 + int(todayTime * 0.0833333); // 1/12
_display.drawFastVLine(xPos, yPos, 8, bShowWhite ? WHITE : BLACK);
bShowWhite = !bShowWhite;
}
bool
CTimerChartScreen::keyHandler(uint8_t event)

View File

@ -34,6 +34,7 @@ class CTimerChartScreen : public CScreen {
int _colSel;
int _instance;
unsigned long _SaveTime;
void cursor();
public:
CTimerChartScreen(C128x64_OLED& display, CScreenManager& mgr, int instance);

View File

@ -68,9 +68,9 @@ DebugPort.println("Using millis() based psuedo \"Real Time Clock\"");
_nextRTCfetch = millis();
CTimerManager::createMap();
update();
CTimerManager::createMap();
}
const BTCDateTime&

View File

@ -34,7 +34,7 @@
#include "../Utility/NVStorage.h"
#include "../Utility/helpers.h"
uint8_t CTimerManager::weekTimerIDs[7][CTimerManager::_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID
uint8_t CTimerManager::weekMap[7][CTimerManager::_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID
int CTimerManager::activeTimer = 0;
int CTimerManager::activeDow = 0;
@ -42,6 +42,17 @@ int CTimerManager::nextTimer = 0;
int CTimerManager::nextStart = 0;
bool CTimerManager::timerChanged = false;
#define SET_MAPS() { \
if(pTimerMap) { \
pTimerMap[dayMinute] |= activeday; \
if(pTimerIDs) \
pTimerIDs[dayMinute] |= timerBit; \
} \
else { \
weekMap[dow][dayMinute] = recordTimer; \
} \
}
// create a bitmap that describes the pattern of on/off times
void
CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs)
@ -52,7 +63,8 @@ CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs
memset(pTimerIDs, 0, _dayMinutes*sizeof(uint16_t));
}
else {
memset(weekTimerIDs, 0, _dayMinutes*7*sizeof(uint8_t));
DebugPort.println("Erasing weekMap");
memset(weekMap, 0, _dayMinutes*7*sizeof(uint8_t));
}
for(int timerID=0; timerID < 14; timerID++) {
@ -74,15 +86,14 @@ CTimerManager::createMap(int timerMask, uint16_t* pTimerMap, uint16_t* pTimerIDs
void
CTimerManager::createMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs)
{
int timerBit = 0x0001 << timer.timerID;
const BTCDateTime tNow = Clock.get();
int todayDoW = 1 << tNow.dayOfTheWeek();
int todayTime = tNow.hour() * 60 + tNow.minute();
if(createOneShotMap(timer, pTimerMap, pTimerIDs)) {
return;
}
if(timer.enabled) {
// create linear minute of day values for start & stop
// note that if stop < start, that is treated as a timer that rolls over midnight
// create linear minutes of day values for start & stop
// note that if stop < start, the timer rolls over midnight
int timerBit = 0x0001 << timer.timerID; // bit required for timer ID map
int timestart = timer.start.hour * 60 + timer.start.min; // linear minute of day
int timestop = timer.stop.hour * 60 + timer.stop.min;
for(int dayMinute = 0; dayMinute < _dayMinutes; dayMinute++) {
@ -90,62 +101,43 @@ CTimerManager::createMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs
int dayBit = 0x01 << dow;
if(timer.enabled & dayBit || timer.enabled & 0x80) { // specific or everyday
uint16_t activeday = dayBit; // may also hold non repeat flag later
uint8_t recordTimer = (timer.timerID + 1) | (timer.repeat ? 0x80 : 0x00);
uint8_t recordTimer = (timer.timerID + 1) | (timer.repeat ? 0x80 : 0x00); // full week timer ID map
if(!timer.repeat) {
// flag timers that should get cancelled
activeday |= (activeday << 8); // combine one shot status in MS byte
}
if(timer.enabled == 0x80 && !timer.repeat) { // on-shot next occurrence timer
if(todayTime > timestart) {
}
}
// SET_MAPS() macro saves values as below:
//
// activeday -> pTimerMap[dayMinute] (if pTimerMap != NULL)
// timerBit -> pTimerID[dayMinute] (if pTimerMap != NULL AND pTimerID != NULL)
// recordTimer -> weekMap[dow][dayMinute] (if pTimerMap == NULL)
//
if(timestop > timestart) {
// treat normal start < stop times (within same day)
if((dayMinute >= timestart) && (dayMinute < timestop)) {
if(pTimerMap) {
pTimerMap[dayMinute] |= activeday;
if(pTimerIDs)
pTimerIDs[dayMinute] |= timerBit;
}
else {
weekTimerIDs[dow][dayMinute] = recordTimer;
}
SET_MAPS();
}
}
else {
// time straddles a day, start > stop, special treatment required
if(dayMinute >= timestart) {
// true from start until midnight
if(pTimerMap) {
pTimerMap[dayMinute] |= activeday;
if(pTimerIDs)
pTimerIDs[dayMinute] |= timerBit;
}
else {
weekTimerIDs[dow][dayMinute] = recordTimer;
}
SET_MAPS();
}
if(dayMinute < timestop) {
// after midnight, before stop time, i.e. next day
// adjust for next day, taking care to wrap week
if(dow == 6) { // last day of week?
activeday >>= 6; // roll back to start of week
if(pTimerMap == NULL) {
weekTimerIDs[0][dayMinute] = recordTimer;
}
dow = 0;
// because activeday holds both cancel and day info, shift it
activeday >>= 6; // roll back to start of week -
}
else {
dow++;
activeday <<= 1; // next day
if(pTimerMap == NULL) {
weekTimerIDs[dow+1][dayMinute] = recordTimer;
}
}
if(pTimerMap) {
pTimerMap[dayMinute] |= activeday;
if(pTimerIDs)
pTimerIDs[dayMinute] |= timerBit;
}
SET_MAPS();
}
}
}
@ -210,7 +202,7 @@ CTimerManager::condenseMap(uint8_t timerMap[7][120])
uint8_t condense = 0;
for(int subInterval = 0; subInterval < 12; subInterval++, dayMinute++) {
if(!condense)
condense = weekTimerIDs[dow][dayMinute];
condense = weekMap[dow][dayMinute];
}
timerMap[dow][opIndex++] = condense;
}
@ -228,7 +220,7 @@ CTimerManager::manageTime(int _hour, int _minute, int _dow)
int retval = 0;
int dayMinute = (hour * 60) + minute;
int newID = weekTimerIDs[dow][dayMinute];
int newID = weekMap[dow][dayMinute];
if(activeTimer != newID) {
DebugPort.printf("Timer ID change detected: %d", activeTimer & 0x0f);
@ -292,8 +284,8 @@ CTimerManager::findNextTimer(int hour, int minute, int dow)
int limit = 24*60*7;
while(limit--) {
if(weekTimerIDs[dow][dayMinute] & 0x0f) {
nextTimer = weekTimerIDs[dow][dayMinute];
if(weekMap[dow][dayMinute] & 0x0f) {
nextTimer = weekMap[dow][dayMinute];
nextStart = dow*_dayMinutes + dayMinute;
return nextTimer;
}
@ -352,3 +344,60 @@ CTimerManager::conflictTest(int ID)
timerChanged = true;
return conflictID;
}
// special handling for next occurence of one-shot, non-repeating timers
bool
CTimerManager::createOneShotMap(sTimer& timer, uint16_t* pTimerMap, uint16_t* pTimerIDs)
{
if((timer.enabled == 0x80) && !timer.repeat) { // on-shot next occurrence timer
DebugPort.printf("One shot, next occurence timer #%d\r\n", timer.timerID+1);
int timerBit = 0x0001 << timer.timerID; // value required for full week map
int timestart = timer.start.hour * 60 + timer.start.min; // linear minute of day
int timestop = timer.stop.hour * 60 + timer.stop.min;
// create masking based upon TODAY
const BTCDateTime tNow = Clock.get();
int dow = tNow.dayOfTheWeek();
int todayTime = tNow.hour() * 60 + tNow.minute();
// wrap to next day if start time falls behind current time
if(todayTime >= timestart) {
dow++;
WRAPUPPERLIMIT(dow, 6, 0);
}
// create masks and record values for the assorted target arrays
uint16_t activeday = 1 << dow; // set day bit
activeday |= activeday << 8; // and set non repeat flag in MSB
uint8_t recordTimer = (timer.timerID + 1);
// SET_MAPS() macro saves values as below:
//
// activeday -> pTimerMap[dayMinute] (if pTimerMap != NULL)
// timerBit -> pTimerID[dayMinute] (if pTimerMap != NULL AND pTimerID != NULL)
// recordTimer -> weekMap[dow][dayMinute] (if pTimerMap == NULL)
//
if(timestart < timestop) {
// timer does not wrap midnight - easy linear workout :-)
for(int dayMinute = timestart; dayMinute < timestop; dayMinute++) {
SET_MAPS();
}
}
else {
// timer rolls over midnight
// fill map up from start time till end of day
for(int dayMinute = timestart; dayMinute < _dayMinutes; dayMinute++) {
SET_MAPS();
}
// advance to next day, wrapping if required
dow++;
WRAPUPPERLIMIT(dow, 6, 0);
activeday = 1 << dow; // set day bit
activeday |= activeday << 8; // and set non repeat flag in MSB
// complete map from midnight till stop time
for(int dayMinute = 0; dayMinute < timestop; dayMinute++) {
SET_MAPS();
}
}
return true;
}
return false;
}

View File

@ -38,6 +38,7 @@ struct sTimer;
class CTimerManager {
public:
static const int _dayMinutes = 24*60;
static bool createOneShotMap(sTimer& timer, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL);
static void createMap(int timermask = 0x3fff, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL);
static void createMap(sTimer& timer, uint16_t* timerMap = NULL, uint16_t* timerIDs = NULL);
static void condenseMap(uint16_t timerMap[_dayMinutes], int factor);
@ -56,7 +57,7 @@ private:
static int prevState;
static int nextTimer;
static int nextStart;
static uint8_t weekTimerIDs[7][_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID
static uint8_t weekMap[7][_dayMinutes]; // b[7] = repeat flag, b[3..0] = timer ID
static bool timerChanged;
};

View File

@ -562,7 +562,7 @@ void updateJSONclients(bool report)
getBluetoothClient().send( expand.c_str() );
unsigned long tBT = millis() - tStart;
bNewTimerInfo = true;
DebugPort.printf("JSON times : %ld,%ld,%ld\r\n", tJSON, tBT, tWF);
// DebugPort.printf("JSON times : %ld,%ld,%ld\r\n", tJSON, tBT, tWF);
}
}
// request timer refesh upon clients
@ -577,7 +577,9 @@ void updateJSONclients(bool report)
root.set("TimerRefresh", 1);
root.printTo(jsonStr, 800);
DebugPort.printf("JSON send: %s\r\n", jsonStr);
if (report) {
DebugPort.printf("JSON send: %s\r\n", jsonStr);
}
sendWebSocketString( jsonStr );
std::string expand = jsonStr;
Expand(expand);

View File

@ -63,7 +63,7 @@ CFuelGauge::Integrate(float Hz)
float fuelDelta = _pumpStrokes - _lastStoredVal;
bool bStoppedSave = (Hz == 0) && (_pumpStrokes != _lastStoredVal);
if(fuelDelta > 10 || bStoppedSave) { // record fuel usage every 10 minutes, or every 10 strokes
DebugPort.printf("Storing fuel gauge: %.2f strokes\r\n", _pumpStrokes);
// DebugPort.printf("Storing fuel gauge: %.2f strokes\r\n", _pumpStrokes);
RTC_Store.setFuelGauge(_pumpStrokes); // uses RTC registers to store this
_lastStoredVal = _pumpStrokes;
}