Program Listing for File terminal_serial.cpp

Return to documentation for file (src/tap/communication/serial/terminal_serial.cpp)

/*
 * Copyright (c) 2020-2021 Advanced Robotics at the University of Washington <robomstr@uw.edu>
 *
 * This file is part of Taproot.
 *
 * Taproot is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Taproot is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Taproot.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "terminal_serial.hpp"

#include "tap/algorithms/strtok.hpp"
#include "tap/drivers.hpp"

namespace tap::communication::serial
{
constexpr char TerminalSerial::DELIMITERS[];

TerminalSerial::TerminalSerial(Drivers *drivers)
    : device(drivers),
      stream(device),
      streamingTimer(STREAMING_PERIOD),
      drivers(drivers)
{
}

void TerminalSerial::initialize() { device.initialize(); }

void TerminalSerial::update()
{
    char nextC;
    stream.get(nextC);
    if (nextC == modm::IOStream::eof)
    {
        if (currStreamer != nullptr && streamingTimer.execute())
        {
            currStreamer->terminalSerialStreamCallback(stream);
        }
        return;
    }
    if (currStreamer != nullptr)
    {
        currStreamer = nullptr;
    }
    // Some systems use '\r', some '\n' when the user hits enter
    if (nextC == '\r' || nextC == '\n')
    {
        // If the rx buff doesn't contain a string just return
        if (rxBuff[0] == '\0')
        {
            return;
        }
        rxBuff[currLineSize] = '\0';

        char *strtokSavePtr = rxBuff;
        char *headerStr = strtokR(strtokSavePtr, DELIMITERS, &strtokSavePtr);

        if (headerCallbackMap.count(headerStr) == 0)
        {
            stream << "Header \'" << headerStr << "\' not found" << modm::endl;
            printUsage();
        }
        else
        {
            constexpr char STREAMING_ID[] = "-S";
            // strlen("-S"), but strlen isn't guaranteed to be evaluated at runtime
            constexpr int STREAMING_ID_LEN = 2;
            if (strncmp(STREAMING_ID, strtokSavePtr, STREAMING_ID_LEN) == 0)
            {
                currStreamer = headerCallbackMap[headerStr];
                strtokSavePtr += STREAMING_ID_LEN;
            }

            if (!headerCallbackMap[headerStr]
                     ->terminalSerialCallback(strtokSavePtr, stream, currStreamer != nullptr))
            {
                stream << "invalid arguments" << modm::endl;
                currStreamer = nullptr;
            }
        }
        currLineSize = 0;
        rxBuff[0] = '\0';
    }
    else
    {
        if (currLineSize >= MAX_LINE_LENGTH - 1)
        {
            stream << "input line too long, throwing away data" << modm::endl;
            currLineSize = 0;
        }
        else
        {
            // Only put one space between received words
            bool space = isspace(nextC);
            if (!space || !prevCharSpace)
            {
                rxBuff[currLineSize++] = nextC;
            }
            prevCharSpace = space;
        }
    }
}

void TerminalSerial::addHeader(const char *header, TerminalSerialCallbackInterface *callback)
{
    if (callback == nullptr)
    {
        return;
    }
    headerCallbackMap[header] = callback;
}

void TerminalSerial::printUsage()
{
    stream << "Usage: <header> [-S] <args>\n"
              "  Where\n"
              "    -S Enable streaming mode\n"
              "  and <header> is one of\n";
    for (const auto &header : headerCallbackMap)
    {
        stream << "    " << header.first << modm::endl;
    }
    stream << "  and <args> is specific to <header> (query <header> -H for more help)\n";
}
}  // namespace tap::communication::serial