519 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			519 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, NULL}
 | |
| , _viewFuncUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL, NULL}
 | |
| , _currentViewUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL, NULL}
 | |
| , _viewFunctionFailedToExecute{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL, 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()
 | |
| {
 | |
|   return 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 true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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;
 | |
|       default:
 | |
|         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(isListEmpty(_viewLinkedList))return;
 | |
| 
 | |
|   if(_currentView == NO_CURRENT_VIEW)
 | |
|   {
 | |
|     _currentView = _viewLinkedList;
 | |
|   }
 | |
|   else if(!isListEmpty(_currentView->next))
 | |
|   {
 | |
|     _currentView = _currentView->next;
 | |
|   }
 | |
|   //End of the views, we cycle again :)
 | |
|   else if(isListEmpty(_currentView->next))
 | |
|   {
 | |
|     _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(isListEmpty(_tail))return;
 | |
| 
 | |
|   if(_currentView == NO_CURRENT_VIEW)
 | |
|   {
 | |
|     _currentView = _tail;
 | |
|   }
 | |
|   else if(!isListEmpty(_currentView->previous))
 | |
|   {
 | |
|     _currentView = _currentView->previous;
 | |
|   }
 | |
|   //End of the views, we cycle again :)
 | |
|   else if(isListEmpty(_currentView->previous))
 | |
|   {
 | |
|     _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(toDelete);
 | |
|   }
 | |
|   //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;
 | |
|   }
 | |
| }
 |