513 lines
13 KiB
C++
513 lines
13 KiB
C++
#include "ScreenManager.h"
|
|
|
|
ScreenManager::ScreenManager(Adafruit_SSD1306 &display, SDCardManager *sdCardManager) : _displayRef(display), _error(OK)
|
|
, _displayColorInverted(false), _displayDimmed(false), _enabled(true), _refreshRateHz(1), _refreshInterval(1000), _timeRef(0),_forceRefresh(false)
|
|
, _currentView(NO_CURRENT_VIEW), _tail(NULL)
|
|
, _viewNotFound{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL}
|
|
, _viewFuncUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL}
|
|
, _currentViewUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL}
|
|
, _viewFunctionFailedToExecute{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL}
|
|
,_sdCardManager(sdCardManager)
|
|
{
|
|
_viewLinkedList = (ViewLinkedList) createEmptyList();
|
|
//We set the error messages :
|
|
((ErrorInfo *)_viewNotFound.pData)->errorMessage = FPSTR("View does not exist");
|
|
((ErrorInfo *)_viewFunctionFailedToExecute.pData)->errorMessage = FPSTR("View function failed\nto execute");
|
|
((ErrorInfo *)_viewFuncUndefined.pData)->errorMessage = FPSTR("View function is NULL");
|
|
}
|
|
|
|
boolean ScreenManager::init()
|
|
{
|
|
applyCfgFromSD();
|
|
}
|
|
|
|
boolean ScreenManager::applyCfgFromSD()
|
|
{
|
|
if(_sdCardManager != NULL)
|
|
{
|
|
CFGFileParser cfgFileParser(*_sdCardManager, SCREEN_CFG_FILE);
|
|
CFGDictionary<CFGParameterValue> *cfgDictionary = (CFGDictionary<CFGParameterValue> *) cfgFileParser.parseFile();
|
|
|
|
if(cfgDictionary == NULL)
|
|
{
|
|
setDefault();
|
|
return false;
|
|
}
|
|
|
|
|
|
if( cfgDictionary->get("ENABLED") != NULL &&
|
|
cfgDictionary->get("DIMMED") != NULL &&
|
|
cfgDictionary->get("INVERTED") != NULL &&
|
|
cfgDictionary->get("ORIENTATION") != NULL &&
|
|
cfgDictionary->get("AUTO_OFF") != NULL)
|
|
{
|
|
setEnabled(cfgDictionary->get("ENABLED")->booleanValue());
|
|
_displayRef.setRotation(orientationTranslator(cfgDictionary->get("ORIENTATION")->intValue()));
|
|
_displayRef.invertDisplay(cfgDictionary->get("INVERTED")->booleanValue());
|
|
_displayRef.dim(cfgDictionary->get("DIMMED")->booleanValue());
|
|
_displayRef.setTextColor(WHITE);
|
|
|
|
delete cfgDictionary;
|
|
return true;
|
|
}
|
|
else //Default value applied
|
|
{
|
|
setDefault();
|
|
delete cfgDictionary;
|
|
return false;
|
|
}
|
|
}
|
|
else //Default value applied
|
|
{
|
|
setDefault();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ScreenManager::setDefault()
|
|
{
|
|
_displayRef.setRotation(OR_0);
|
|
_displayRef.setTextColor(WHITE);
|
|
}
|
|
|
|
boolean ScreenManager::addView(boolean (*viewLogicFunction)(Adafruit_SSD1306&, void*), void *pData, const unsigned char UID)
|
|
{
|
|
ViewLink viewLink ={viewLogicFunction, pData, UID, NULL, NULL};
|
|
return addNewLinkAtTheEnd(&_viewLinkedList, viewLink);
|
|
}
|
|
|
|
void *ScreenManager::createEmptyList()
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
boolean ScreenManager::addNewLinkAtTheEnd(ViewLinkedList *viewLinkedList, ViewLink viewLink)
|
|
{
|
|
ViewLink *newViewLink = (ViewLink*) malloc(sizeof(ViewLink));
|
|
if(newViewLink == NULL)
|
|
{
|
|
_error = MALLOC_FAILED;
|
|
return false;
|
|
}
|
|
|
|
//Because of the const member
|
|
memcpy(newViewLink, &viewLink, sizeof(*newViewLink));
|
|
|
|
//If this is the first link
|
|
if(isListEmpty(*viewLinkedList))
|
|
{
|
|
//The link list points to the first link (aka the head)
|
|
*viewLinkedList = newViewLink;
|
|
//The previous pointer is still NULL;
|
|
//The _tail pointer is the same as the head one.
|
|
_tail = *viewLinkedList;
|
|
}
|
|
else //If there are some links already
|
|
{
|
|
ViewLinkedList *cursor = viewLinkedList;
|
|
ViewLink *previousLink = NULL;
|
|
|
|
while(*cursor != NULL)
|
|
{
|
|
previousLink = *cursor;
|
|
//We check if the UID already exists, if yes we replace the link
|
|
if((*cursor)->UID == newViewLink->UID)
|
|
{
|
|
ViewLink *toDelete = *cursor;
|
|
*cursor = newViewLink;
|
|
newViewLink->next = toDelete->next;
|
|
newViewLink->previous = toDelete->previous;
|
|
//We check if the link as a next link registered
|
|
if(toDelete->next != NULL)
|
|
{
|
|
//We update it's previous link since it's going to be deleted
|
|
toDelete->next->previous = newViewLink;
|
|
}
|
|
//We do not forget to check if the link replaced is also the tail, in this case, we must update the tail
|
|
if(_tail == toDelete)_tail = newViewLink;
|
|
free(toDelete);
|
|
|
|
return true;
|
|
}
|
|
cursor = &(*cursor)->next;
|
|
}
|
|
newViewLink->previous = previousLink;
|
|
*cursor = newViewLink;
|
|
//We need to update the tail
|
|
_tail = newViewLink;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
boolean ScreenManager::removeView(const unsigned char UID)
|
|
{
|
|
return removeLinkByUID(&_viewLinkedList ,UID);
|
|
}
|
|
|
|
boolean ScreenManager::removeLinkByUID(ViewLinkedList *viewLinkedList, const unsigned char UID)
|
|
{
|
|
if(isListEmpty(*viewLinkedList))return false;
|
|
|
|
ViewLinkedList *cursor = viewLinkedList;
|
|
|
|
while(*cursor != NULL)
|
|
{
|
|
//We check if this is the link we want to delete
|
|
if((*cursor)->UID == UID)
|
|
{
|
|
ViewLink *toDelete = *cursor;
|
|
*cursor = (*cursor)->next;
|
|
|
|
if(toDelete->next != NULL)
|
|
{
|
|
//We update the previous link of the next member since it's going to be deleted
|
|
toDelete->next->previous = toDelete->previous;
|
|
}
|
|
//We also update the tail if the link we delete was the tail
|
|
if(_tail == toDelete)_tail = toDelete->previous;
|
|
|
|
free(toDelete);
|
|
return true;
|
|
}
|
|
cursor = &(*cursor)->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
boolean ScreenManager::isListEmpty(ViewLinkedList viewLinkedList)
|
|
{
|
|
return viewLinkedList == NULL;
|
|
}
|
|
|
|
ScreenManager::Error ScreenManager::getError() const
|
|
{
|
|
return _error;
|
|
}
|
|
|
|
void ScreenManager::iterateThroughList()
|
|
{
|
|
Serial.println("Lets go through");
|
|
ViewLinkedList temp = _viewLinkedList;
|
|
while(!isListEmpty(temp))
|
|
{
|
|
Serial.print("UID : ");Serial.println(temp->UID);
|
|
temp = temp->next;
|
|
}
|
|
}
|
|
|
|
ScreenManager::ViewLink* ScreenManager::getLinkByUID(ViewLinkedList viewLinkedList, const unsigned char UID)
|
|
{
|
|
if(isListEmpty(viewLinkedList))
|
|
return NULL;
|
|
|
|
while(!isListEmpty(viewLinkedList))
|
|
{
|
|
if(viewLinkedList->UID == UID)return viewLinkedList;
|
|
viewLinkedList = viewLinkedList->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ScreenManager::run()
|
|
{
|
|
if((millis() - _timeRef < _refreshInterval && !_forceRefresh) || !_enabled) return;
|
|
|
|
_timeRef = millis();
|
|
|
|
if(((_error == OK || _error == VIEW_FAILED_TO_EXECUTE) || _forceRefresh) && _currentView != NO_CURRENT_VIEW)
|
|
{
|
|
_displayRef.clearDisplay();
|
|
_displayRef.setCursor(0,0);
|
|
_displayRef.setTextSize(1);
|
|
|
|
switch(_error)
|
|
{
|
|
case OK:
|
|
case VIEW_FAILED_TO_EXECUTE:
|
|
if(!(*_currentView->viewLogicFunction)(_displayRef, _currentView->pData))
|
|
{
|
|
_displayRef.clearDisplay();
|
|
//Error while executing the view function
|
|
((ErrorInfo *)_viewFunctionFailedToExecute.pData)->viewUID = _currentView->UID;
|
|
(*_viewFunctionFailedToExecute.viewLogicFunction)(_displayRef, _viewFunctionFailedToExecute.pData);
|
|
|
|
_error = VIEW_FAILED_TO_EXECUTE;
|
|
}
|
|
else
|
|
_error = OK;
|
|
break;
|
|
case VIEW_NOT_FOUND:
|
|
(*_viewNotFound.viewLogicFunction)(_displayRef, _viewNotFound.pData);
|
|
break;
|
|
case VIEW_FUNC_UNDEFINED:
|
|
(*_viewFuncUndefined.viewLogicFunction)(_displayRef, _viewFuncUndefined.pData);
|
|
break;
|
|
}
|
|
|
|
_displayRef.display();
|
|
_forceRefresh = false;
|
|
}
|
|
}
|
|
|
|
void ScreenManager::forceRefresh()
|
|
{
|
|
if(!_enabled) return;
|
|
_forceRefresh = true;
|
|
run();
|
|
}
|
|
|
|
boolean ScreenManager::displayView(const uint8_t UID)
|
|
{
|
|
if(!_enabled) return true;
|
|
_forceRefresh = true;
|
|
ViewLink *viewLink = getLinkByUID(_viewLinkedList, UID);
|
|
|
|
if(viewLink == NULL)
|
|
{
|
|
//We display an error message on the screen
|
|
((ErrorInfo *)_viewNotFound.pData)->viewUID = UID;
|
|
_error = VIEW_NOT_FOUND;
|
|
return false;
|
|
}
|
|
else if(viewLink->viewLogicFunction == NULL)
|
|
{
|
|
//We display an error message on the screen
|
|
((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = UID;
|
|
_error = VIEW_FUNC_UNDEFINED;
|
|
_currentView = viewLink;
|
|
return false;
|
|
}
|
|
|
|
_currentView = viewLink;
|
|
_error = OK;
|
|
run();
|
|
return _error == OK;
|
|
}
|
|
|
|
void ScreenManager::displayNextView()
|
|
{
|
|
_forceRefresh = true;
|
|
_error = OK;
|
|
if(_currentView == NO_CURRENT_VIEW && !isListEmpty(_viewLinkedList))
|
|
{
|
|
_currentView = _viewLinkedList;
|
|
}
|
|
else if(!isListEmpty(_currentView->next))
|
|
{
|
|
_currentView = _currentView->next;
|
|
}
|
|
//End of the views, we cycle again :)
|
|
else if(isListEmpty(_currentView->next) && !isListEmpty(_viewLinkedList))
|
|
{
|
|
_currentView = _viewLinkedList;
|
|
}
|
|
|
|
//We check if the view function is defined :
|
|
if(_currentView != NO_CURRENT_VIEW)
|
|
{
|
|
if(_currentView->viewLogicFunction == NULL)
|
|
{
|
|
((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = _currentView->UID;
|
|
_error = VIEW_FUNC_UNDEFINED;
|
|
}
|
|
}
|
|
|
|
run();
|
|
}
|
|
|
|
void ScreenManager::displayPreviousView()
|
|
{
|
|
_forceRefresh = true;
|
|
_error = OK;
|
|
if(_currentView == NO_CURRENT_VIEW && !isListEmpty(_tail))
|
|
{
|
|
_currentView = _tail;
|
|
}
|
|
else if(!isListEmpty(_currentView->previous))
|
|
{
|
|
_currentView = _currentView->previous;
|
|
}
|
|
//End of the views, we cycle again :)
|
|
else if(isListEmpty(_currentView->previous) && !isListEmpty(_tail))
|
|
{
|
|
_currentView = _tail;
|
|
}
|
|
|
|
//We check if the view function is defined :
|
|
if(_currentView != NO_CURRENT_VIEW)
|
|
{
|
|
if(_currentView->viewLogicFunction == NULL)
|
|
{
|
|
((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = _currentView->UID;
|
|
_error = VIEW_FUNC_UNDEFINED;
|
|
}
|
|
}
|
|
|
|
run();
|
|
}
|
|
|
|
void ScreenManager::invertDisplayColor(const boolean inverted)
|
|
{
|
|
_displayRef.invertDisplay(inverted);
|
|
_displayColorInverted = inverted;
|
|
}
|
|
|
|
void ScreenManager::dimDisplay(const boolean dimmed)
|
|
{
|
|
_displayRef.dim(dimmed);
|
|
_displayDimmed = dimmed;
|
|
}
|
|
|
|
void ScreenManager::sleep()
|
|
{
|
|
_displayRef.sleep();
|
|
}
|
|
|
|
void ScreenManager::wakeUp()
|
|
{
|
|
_displayRef.wakeUp();
|
|
}
|
|
|
|
void ScreenManager::orientDisplay(const Orientation orientation)
|
|
{
|
|
_displayRef.setRotation(orientation);
|
|
}
|
|
|
|
boolean ScreenManager::isDisplayColorInverted() const
|
|
{
|
|
return _displayColorInverted;
|
|
}
|
|
|
|
Orientation ScreenManager::getDisplayOrientation() const
|
|
{
|
|
return (Orientation) _displayRef.getRotation();
|
|
}
|
|
|
|
boolean ScreenManager::isDisplayDimmed() const
|
|
{
|
|
return _displayDimmed;
|
|
}
|
|
|
|
void ScreenManager::clearDisplay(const boolean bufferOnly)
|
|
{
|
|
if(bufferOnly)
|
|
_displayRef.clearDisplay();
|
|
else
|
|
{
|
|
_displayRef.clearDisplay();
|
|
_displayRef.display();
|
|
}
|
|
}
|
|
|
|
void ScreenManager::clearViews()
|
|
{
|
|
ViewLinkedList cursor = _viewLinkedList;
|
|
while(!isListEmpty(cursor))
|
|
{
|
|
ViewLink *toDelete = cursor;
|
|
cursor = cursor->next;
|
|
free(cursor);
|
|
}
|
|
//Do not forget to mark the list as empty
|
|
_viewLinkedList = NULL;
|
|
_tail = NULL;
|
|
}
|
|
|
|
void ScreenManager::setEnabled(boolean value)
|
|
{
|
|
_enabled = value;
|
|
if(value)
|
|
wakeUp();
|
|
else
|
|
sleep();
|
|
}
|
|
|
|
boolean ScreenManager::getEnabled()
|
|
{
|
|
return _enabled;
|
|
}
|
|
|
|
unsigned char ScreenManager::getViewCount()
|
|
{
|
|
unsigned char counter = 0;
|
|
ViewLinkedList temp = _viewLinkedList;
|
|
while(!isListEmpty(temp))
|
|
{
|
|
counter++;
|
|
temp = temp->next;
|
|
}
|
|
return counter;
|
|
}
|
|
|
|
int ScreenManager::getCurrentViewUID() const
|
|
{
|
|
if(_currentView == NO_CURRENT_VIEW)
|
|
return -1;
|
|
return _currentView->UID < 0 ? -1 : _currentView->UID;
|
|
}
|
|
|
|
boolean ScreenManager::displayError(Adafruit_SSD1306 &display, void *pData)
|
|
{
|
|
const ErrorInfo *errorInfo = (ErrorInfo *) pData;
|
|
|
|
display.setTextSize(3);
|
|
display.setCursor(19,3);
|
|
display.drawRect(0,0,display.width(), 27, WHITE);
|
|
display.print(F("ERROR"));
|
|
display.setTextSize(1);
|
|
display.setCursor(0,29);
|
|
display.print(errorInfo->errorMessage);
|
|
//We then display the view UID that caused the probleme
|
|
|
|
if(errorInfo->viewUID >= 0)
|
|
{
|
|
display.setCursor(0,56);
|
|
char buff[30] = "";
|
|
sprintf(buff,"VIEW UID : %d" ,errorInfo->viewUID);
|
|
display.println(buff);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const char* ScreenManager::getErrorMessage()const
|
|
{
|
|
//NO_VIEW_ERROR, MALLOC_FAILED, VIEW_NOT_FOUND, VIEW_FUNC_UNDEFINED, VIEW_FAILED_TO_EXECUTE, CURRENT_VIEW_UNDEFINED
|
|
switch(_error)
|
|
{
|
|
case OK:
|
|
return "NO ERROR";
|
|
case MALLOC_FAILED:
|
|
return "MALLOC FAILED";
|
|
case VIEW_NOT_FOUND:
|
|
return "VIEW NOT FOUND";
|
|
case VIEW_FUNC_UNDEFINED:
|
|
return "VIEW FUNCTION UNDEFINED (NULL)";
|
|
case VIEW_FAILED_TO_EXECUTE:
|
|
return "VIEW FAILED TO EXECUTE (RETURNED FALSE)";
|
|
case CURRENT_VIEW_UNDEFINED:
|
|
return "CURRENT VIEW UNDEFINED";
|
|
default :
|
|
return "UNKNOWN ERROR";
|
|
}
|
|
}
|
|
|
|
uint8_t ScreenManager::orientationTranslator(uint16_t degres)
|
|
{
|
|
switch(degres)
|
|
{
|
|
case 0:
|
|
return 2;
|
|
case 90:
|
|
return 3;
|
|
case 180:
|
|
return 0;
|
|
case 270:
|
|
return 1;
|
|
default :
|
|
return 2;
|
|
}
|
|
}
|