#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 *cfgDictionary = (CFGDictionary *) 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; } }