Skip to content

Commit

Permalink
Input interface tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
peteGSX committed Dec 24, 2024
1 parent 34bc8f4 commit 42d659d
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 42 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ myConfig.h
build
_build
venv
docs/ex-display/
7 changes: 5 additions & 2 deletions Configurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
* along with this code. If not, see <https://www.gnu.org/licenses/>.
*/

#include "Configurator.h"
#include "AtFinder.h"
#include "Configurator.h"
#include "Version.h"


Configurator::Configurator(Stream *consoleStream, Stream *commandStationStream, LogLevel logLevel)
: _consoleStream(consoleStream), _commandStationStream(commandStationStream) {
_logger = new Logger(_consoleStream);
Expand All @@ -27,7 +28,9 @@ Configurator::Configurator(Stream *consoleStream, Stream *commandStationStream,
_displayManager->setLogger(_logger);
_screenManager = new ScreenManager();
_screenManager->setLogger(_logger);
_controller = new Controller(_consoleStream, _commandStationStream, _displayManager, _screenManager, _logger);
unsigned long pauseDisplayUpdates = STARTUP_INFO_DELAY + millis();
_controller = new Controller(_consoleStream, _commandStationStream, _displayManager, _screenManager, _logger,
pauseDisplayUpdates);
}

void Configurator::initialise() {
Expand Down
109 changes: 105 additions & 4 deletions Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,56 @@
#include "Controller.h"

Controller::Controller(Stream *consoleStream, Stream *commandStationStream, DisplayManager *displayManager,
ScreenManager *screenManager, Logger *logger)
ScreenManager *screenManager, Logger *logger, unsigned long pauseDisplayUpdatesUntil)
: _consoleStream(consoleStream), _commandStationStream(commandStationStream), _displayManager(displayManager),
_screenManager(screenManager) {
_screenManager(screenManager), _pauseDisplayUpdatesUntil(pauseDisplayUpdatesUntil) {
_logger = logger;
if (_pauseDisplayUpdatesUntil > 0) {
_pauseDisplayUpdates = true;
} else {
_pauseDisplayUpdates = false;
}
}

void Controller::update() {
// If the stream from the CommandStation is valid and has data, send it through AtFinder
if (_commandStationStream != nullptr && _commandStationStream->available()) {
char csChar = _commandStationStream->read();
AtFinder::processInputChar(csChar);
}
// If the stream from the console is valid and has data, send it through AtFinder
if (_consoleStream != nullptr && _consoleStream->available()) {
char consoleChar = _consoleStream->read();
AtFinder::processInputChar(consoleChar);
}
if (_displayManager != nullptr && _screenManager != nullptr) {
// On startup, EX-Display version is displayed, this ensures it remains on screen until the elapsed time set by the
// Configurator
// When the time has passed, turn the pause of and clear each display to enable normal display operation
if (_pauseDisplayUpdates && millis() > _pauseDisplayUpdatesUntil) {
_pauseDisplayUpdatesUntil = 0;
_pauseDisplayUpdates = false;
for (auto *display = _displayManager->getFirstDisplay(); display; display = display->getNext()) {
display->clearScreen();
}
}

// Only process displays if there is a valid DisplayManager and ScreenManager
if (_displayManager != nullptr && _screenManager != nullptr && !_pauseDisplayUpdates) {
// Iterate through each physical display, auto means we don't care about the type as we're using the interface
for (auto *display = _displayManager->getFirstDisplay(); display; display = display->getNext()) {
// If the screen ID is invalid, set it to the first screen ID if there is one, otherwise continue to next display
if (display->getScreenId() == -1) {
if (_screenManager->getFirstScreen() == nullptr) {
continue;
}
display->setScreenId(_screenManager->getFirstScreen()->getId());
}
// Get the screen for this display if it exists, otherwise continue to next display
Screen *screen = _screenManager->getScreenById(display->getScreenId());
if (screen == nullptr) {
continue;
}
// Display the rows needing to be updated
for (ScreenRow *row = screen->getFirstScreenRow(); row; row = row->getNext()) {
if (row->needsRedraw()) {
display->displayRow(row->getId(), row->getText(), false, 0);
Expand All @@ -61,7 +90,47 @@ void Controller::updateScreen(uint8_t screenId, uint8_t row, const char *text) {
}
}

void Controller::onInputAction(InputAction action) { LOG(LogLevel::DEBUG, "Controller::onInputAction(%d)", action); }
void Controller::onInputAction(InputAction action) {
// No inputs are valid if there are no displays or screens, so just bail out
if (_displayManager == nullptr || _screenManager == nullptr || _displayManager->getFirstDisplay() == nullptr ||
_screenManager->getFirstScreen() == nullptr) {
return;
}
auto *display = _displayManager->getFirstDisplay();
switch (action) {
// Left press selects the previous screen for display
case InputAction::PRESS_LEFT: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(InputAction::PRESS_LEFT)");
_selectPreviousScreen(display);
break;
}
// Right press selects the next screen for display
case InputAction::PRESS_RIGHT: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(InputAction::PRESS_RIGHT)");
_selectNextScreen(display);
break;
}
// Up scrolls the screen up one row
case InputAction::PRESS_UP: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(InputAction::PRESS_UP)");
break;
}
// Down scrolls the screen down one row
case InputAction::PRESS_DOWN: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(InputAction::PRESS_DOWN)");
break;
}
// Centre moves input control to the next display - does nothing until multiple displays are supported
case InputAction::PRESS_CENTRE: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(InputAction::PRESS_CENTRE)");
break;
}
default: {
LOG(LogLevel::DEBUG, "Controller::onInputAction(Unknown)");
break;
}
}
}

Controller::~Controller() {
_displayManager = nullptr;
Expand All @@ -70,3 +139,35 @@ Controller::~Controller() {
_commandStationStream = nullptr;
_logger = nullptr;
}

void Controller::_selectPreviousScreen(DisplayInterface *display) {
// If screen ID is invalid, select the first screen
int screenId = display->getScreenId();
int previousId = -1;
if (screenId == -1) {
previousId = _screenManager->getFirstScreen()->getId();
} else {
// Otherwise get the ID of the previous screen and update it
Screen *screen = _screenManager->getScreenById(screenId);
previousId = _screenManager->getPreviousScreen(screen)->getId();
}
display->setScreenId(previousId);
LOG(LogLevel::DEBUG, "Controller::_selectPreviousScreen: _displayId=%d|screenId=%d|previousId=%d", display->getId(),
screenId, previousId);
}

void Controller::_selectNextScreen(DisplayInterface *display) {
// If screen ID is invalid, select the first screen
int screenId = display->getScreenId();
int nextId = -1;
if (screenId == -1) {
nextId = _screenManager->getFirstScreen()->getId();
} else {
// Otherwise get the ID of the next screen and update it
Screen *screen = _screenManager->getScreenById(screenId);
nextId = _screenManager->getNextScreen(screen)->getId();
}
display->setScreenId(nextId);
LOG(LogLevel::DEBUG, "Controller::_selectPreviousScreen: _displayId=%d|screenId=%d|nextId=%d", display->getId(),
screenId, nextId);
}
20 changes: 18 additions & 2 deletions Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@
/// All application activities are controlled through this class to manage screens, displays, and user input
class Controller : public CallbackInterface {
public:
/// @brief Constructor for the Controller
/// @brief
/// @param consoleStream
/// @param commandStationStream
/// @param displayManager
/// @param screenManager
/// @param logger
/// @param pauseDisplayUpdatesUntil
Controller(Stream *consoleStream, Stream *commandStationStream, DisplayManager *displayManager,
ScreenManager *screenManager, Logger *logger);
ScreenManager *screenManager, Logger *logger, unsigned long pauseDisplayUpdatesUntil = 0);

/// @brief Processes all ongoing activities, monitoring streams, receiving user input, updates displays, etc.
/// Call at least once per main loop iteration
Expand All @@ -54,6 +60,16 @@ class Controller : public CallbackInterface {
Stream *_commandStationStream;
DisplayManager *_displayManager;
ScreenManager *_screenManager;
unsigned long _pauseDisplayUpdatesUntil;
bool _pauseDisplayUpdates;

/// @brief Selects the previous screen for the selected display
/// @param display Pointer to the DisplayInterface to set
void _selectPreviousScreen(DisplayInterface *display);

/// @brief Selects the next screen for the selected display
/// @param display Pointer to the DisplayInterface to set
void _selectNextScreen(DisplayInterface *display);
};

#endif // CONTROLLER_H
17 changes: 10 additions & 7 deletions Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,7 @@
#else
#warning myConfig.h not found, using defaults
#endif // myConfig
#endif // PIO_UNIT_TESTING

// Default log level WARN
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_WARN
#endif // LOG_LEVEL

#ifndef PIO_UNIT_TESTING // Cannot use these for testing
// Default settings for streams
#ifndef CONSOLE_STREAM
#define CONSOLE_STREAM Serial
Expand All @@ -54,4 +47,14 @@
#endif // COMMANDSTATION_STREAM
#endif // PIO_UNIT_TESTING

// Default log level WARN unless overridden
#ifndef LOG_LEVEL
#define LOG_LEVEL LOG_WARN
#endif // LOG_LEVEL

// Default to showing the EX-Display version for 2 seconds unless overridden
#ifndef STARTUP_INFO_DELAY
#define STARTUP_INFO_DELAY 3000
#endif // STARTUP_INFO_DELAY

#endif // DEFINES_H
9 changes: 5 additions & 4 deletions DisplayInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class DisplayInterface {
/// @param row Row number as specified in the SCREEN() command (not pixels)
/// @param text Text to be displayed on this row
/// @param underlined (Optional) Flag to underline this row - default false
/// @param column Column number to start displaying at (based on text width, not pixels)
virtual void displayRow(uint8_t row, const char *text, bool underlined = false, uint8_t column = 0) = 0;

/// @brief Clear the specified row
Expand Down Expand Up @@ -65,11 +66,11 @@ class DisplayInterface {

/// @brief Set the Screen ID this display is currently displaying
/// @param screenId Screen ID
void setScreenId(uint8_t screenId) { _screenId = screenId; }
void setScreenId(int screenId) { _screenId = screenId; }

/// @brief Get the Screen ID this display is currently displaing
/// @return Screen ID
uint8_t getScreenId() { return _screenId; }
int getScreenId() { return _screenId; }

/// @brief Destructor for a DisplayInterface
virtual ~DisplayInterface() = default;
Expand All @@ -85,8 +86,8 @@ class DisplayInterface {
Logger *_logger = nullptr;
/// @brief ID for this display instance
uint8_t _displayId = 0;
/// @brief ID of the screen this display is currently displaying
uint8_t _screenId = 0;
/// @brief ID of the screen this display is currently displaying, defaults to -1 to flag it is not a valid ID
int _screenId = -1;
/// @brief Orientation of this display, most displays require this setting otherwise ignore it
uint8_t _rotation = 0;
/// @brief Multiplier for text size, most displays require this setting otherwise ignore it
Expand Down
26 changes: 24 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@

extensions = [
'breathe',
'exhale',
'sphinx.ext.autodoc',
'sphinx.ext.graphviz'
'sphinx.ext.graphviz',
]

graphviz_output_format = 'svg'
graphviz_output_format = 'png'

templates_path = ['_templates']
exclude_patterns = []
Expand Down Expand Up @@ -75,3 +76,24 @@
breathe_show_include_files = True
breathe_show_grouped_typedefs = True
breathe_use_inheritance_diagram = True
breathe_graph_maxoutdegree = 10
breathe_graph_maxdepth = 5
breathe_use_inheritance_diagram = True
breathe_show_include_graph = True
breathe_show_collaboration_graph = True
breathe_show_programlisting = True

# -- Breathe configuration -------------------------------------------------

exhale_args = {
# Required arguments
"containmentFolder": "./ex-display",
"rootFileName": "ex-display_root.rst",
"doxygenStripFromPath": "..",
# Suggested optional arguments
"createTreeView": True,
"treeViewIsBootstrap": True,
# Include all the doxygen generated graphs
"exhaleExecutesDoxygen": False,
"generateBreatheFileDirectives": True,
}
13 changes: 6 additions & 7 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
EX-Display documentation
========================

Add your content using ``reStructuredText`` syntax. See the
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
documentation for details.


.. toctree::
:maxdepth: 2
:caption: Contents:

.. doxygenindex::
:project: EX-Display
ex-display/ex-display_root

Indices and tables
==================

* :ref:`genindex`
32 changes: 32 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
alabaster==1.0.0
babel==2.16.0
beautifulsoup4==4.12.3
breathe==4.35.0
certifi==2024.12.14
charset-normalizer==3.4.0
colorama==0.4.6
docutils==0.21.2
exhale==0.3.7
graphviz==0.20.3
idna==3.10
imagesize==1.4.1
Jinja2==3.1.5
lxml==5.3.0
MarkupSafe==3.0.2
packaging==24.2
Pygments==2.18.0
requests==2.32.3
six==1.17.0
snowballstemmer==2.2.0
soupsieve==2.6
Sphinx==8.1.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
tomli==2.2.1
urllib3==2.3.0
Loading

0 comments on commit 42d659d

Please sign in to comment.