AdaptiveBrightness/AdaptiveBrightnessFirmware/Inc/terminal.hpp

172 lines
4.7 KiB
C++
Raw Normal View History

2020-06-29 00:33:14 +02:00
#pragma once
#include <cctype>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include "light_sensors.hpp"
2020-06-29 00:33:14 +02:00
namespace detail {
constexpr auto ENDL = "\r\n";
constexpr auto HELP_CMD = "help";
constexpr auto READ_CMD = "read";
constexpr auto VERSION_CMD = "version";
constexpr auto VERSION = "1.0";
static inline bool substringEquals(const char* str1, const char* str2, const size_t& size)
{
return (std::strncmp(str1, str2, size) == 0);
}
static inline bool stringEquals(const char* str1, const char* str2, const size_t& size)
{
if(size == std::strlen(str2)) {
return substringEquals(str1, str2, size);
}
return false;
}
} // namespace detail
template<class Uart>
class Terminal {
public:
static void init()
{
m_serial.init();
m_serial << detail::ENDL;
printVersion();
m_serial << detail::ENDL << "$ ";
}
static void callback()
{
if(receiveInput()) {
parseInput();
}
m_serial.flushTx();
2020-06-29 00:33:14 +02:00
}
private:
static constexpr auto INPUT_BUFFER_SIZE = 128;
static constexpr auto BACKSPACE = uint8_t{0x7f};
static constexpr auto CTRL_C = uint8_t{0x03};
static Uart m_serial;
static char m_inputBuffer[INPUT_BUFFER_SIZE];
static uint16_t m_inputSize;
static LightSensors m_lightSensors;
2020-06-29 00:33:14 +02:00
static bool receiveInput()
{
uint8_t inputByte;
while(m_serial.rxByte(inputByte)) {
if(std::isprint(inputByte) || inputByte == CTRL_C) {
m_inputBuffer[m_inputSize++] = inputByte;
// Handle Ctrl + C
if(inputByte == CTRL_C) {
m_serial << "^C" << detail::ENDL;
return true;
}
// Echo
else {
m_serial << static_cast<char>(inputByte);
}
}
// Handle backspace
if(inputByte == BACKSPACE && m_inputSize > 0) {
m_serial << "\b \b";
--m_inputSize;
}
// Handle line terminator
else if(inputByte == '\r' || inputByte == '\n') {
// Consume possible second line terminator
if(m_serial.peek(inputByte) && (inputByte == '\r' || inputByte == '\n')) {
m_serial.rxByte(inputByte);
}
m_serial << detail::ENDL;
return true;
}
if(m_inputSize >= INPUT_BUFFER_SIZE) {
m_serial << detail::ENDL << "WARNING: Terminal input buffer overflow!" << detail::ENDL;
return true;
}
}
return false;
}
static void parseInput()
{
if(m_inputSize) {
if(m_inputBuffer[m_inputSize - 1] == CTRL_C) {
handleCtrlC();
}
else {
if(detail::substringEquals(m_inputBuffer, detail::HELP_CMD, m_inputSize)) {
printHelp();
}
else if(detail::substringEquals(m_inputBuffer, detail::READ_CMD, m_inputSize)) {
readSensors();
2020-06-29 00:33:14 +02:00
}
else if(detail::substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
printVersion();
}
else {
printUnknown();
}
}
}
m_inputSize = 0;
m_serial << "$ ";
}
static void handleCtrlC() { m_serial << "Abort!" << detail::ENDL; }
static void printHelp()
{
m_serial << "AdaptiveBrightness command overview: " << detail::ENDL;
m_serial << detail::HELP_CMD << " .......: prints this help message" << detail::ENDL;
m_serial << detail::READ_CMD << " .......: reads and displays all LDR values" << detail::ENDL;
m_serial << detail::VERSION_CMD << " ....: displays firmware version" << detail::ENDL;
}
static void readSensors()
{
const auto sensorValues = m_lightSensors.getValues();
m_serial << "Sensor values: ";
for(const auto& ldrValue: sensorValues) {
m_serial.txNumber(ldrValue);
m_serial << ", ";
}
m_serial << detail::ENDL;
}
2020-06-29 00:33:14 +02:00
static void printVersion() { m_serial << "AdaptiveBrightness v" << detail::VERSION << detail::ENDL; }
static void printUnknown()
{
m_serial << "Unknown command \"";
for(uint16_t i = 0; i < m_inputSize; ++i) {
m_serial << static_cast<char>(m_inputBuffer[i]);
}
m_serial << "\"" << detail::ENDL;
}
};
template<class Uart>
char Terminal<Uart>::m_inputBuffer[INPUT_BUFFER_SIZE];
template<class Uart>
uint16_t Terminal<Uart>::m_inputSize = 0;