Program Listing for File dji_motor_terminal_serial_handler.cpp

Return to documentation for file (src/tap/motor/dji_motor_terminal_serial_handler.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 "dji_motor_terminal_serial_handler.hpp"

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

#include "dji_motor_tx_handler.hpp"

namespace tap
{
namespace motor
{
constexpr char DjiMotorTerminalSerialHandler::HEADER[];
constexpr char DjiMotorTerminalSerialHandler::USAGE[];

void DjiMotorTerminalSerialHandler::terminalSerialStreamCallback(modm::IOStream& outputStream)
{
    printInfo(outputStream);
}

void DjiMotorTerminalSerialHandler::init() { drivers->terminalSerial.addHeader(HEADER, this); }

bool DjiMotorTerminalSerialHandler::terminalSerialCallback(
    char* inputLine,
    modm::IOStream& outputStream,
    bool streamingEnabled)
{
    char* arg;
    motorId = 0;
    canBusValid = false;
    motorIdValid = false;
    canBus = 0;
    printAll = false;
    while (
        (arg = strtokR(inputLine, communication::serial::TerminalSerial::DELIMITERS, &inputLine)))
    {
        if (strcmp(arg, "motor") == 0)
        {
            arg = strtokR(inputLine, communication::serial::TerminalSerial::DELIMITERS, &inputLine);
            if (arg == nullptr)
            {
                outputStream << "motorinfo: must specify motor id" << modm::endl;
                return false;
            }
            char* indexEnd;
            motorId = strtol(arg, &indexEnd, 10);
            if (indexEnd != arg + strlen(arg) ||
                motorId < (DJI_MOTOR_TO_NORMALIZED_ID(MotorId::MOTOR1) + 1) ||
                motorId > (DJI_MOTOR_TO_NORMALIZED_ID(MotorId::MOTOR8) + 1))
            {
                outputStream << "motorinfo: Invalid motorID" << modm::endl << USAGE;
                return false;
            }
            motorId--;
            motorIdValid = true;
        }
        else if (strcmp(arg, "can") == 0)
        {
            arg = strtokR(inputLine, communication::serial::TerminalSerial::DELIMITERS, &inputLine);
            if (arg == nullptr)
            {
                outputStream << "motorinfo: must specify can bus" << modm::endl;
                return false;
            }
            char* indexEnd;
            canBus = strtol(arg, &indexEnd, 10);
            if (indexEnd != arg + strlen(arg) || (canBus != 1 && canBus != 2))
            {
                outputStream << "motorinfo: Invalid can bus ID" << modm::endl << USAGE;
                return false;
            }
            canBusValid = true;
        }
        else if (strcmp(arg, "all") == 0)
        {
            printAll = true;
            break;
        }
        else if (strcmp(arg, "-H") == 0)
        {
            outputStream << USAGE;
            // If streamingEnabled == true, we want to return false to indicate we shouldn't start
            // streaming. Also if any of the other inputs have been set, return false since the user
            // shouldn't specify -H and another argument.
            return !streamingEnabled && !canBusValid && !motorIdValid && !printAll &&
                   (*inputLine == '\0');
        }
        else
        {
            outputStream << USAGE;
            return false;
        }
    }

    if (((canBusValid || motorIdValid) && printAll) || (*inputLine != '\0'))
    {
        outputStream << USAGE;
        return false;
    }

    return printInfo(outputStream);
}

bool DjiMotorTerminalSerialHandler::printInfo(modm::IOStream& outputStream)
{
    if (printAll)
    {
        outputStream << "CAN 1:" << modm::endl;
        printAllMotorInfo(&DjiMotorTxHandler::getCan1Motor, outputStream);
        outputStream.flush();
        outputStream << "CAN 2:" << modm::endl;
        printAllMotorInfo(&DjiMotorTxHandler::getCan2Motor, outputStream);
    }
    else if (!canBusValid && !motorIdValid)
    {
        outputStream << USAGE;
        return false;
    }
    else if (canBusValid && !motorIdValid)
    {
        if (canBus == 1)
        {
            printAllMotorInfo(&DjiMotorTxHandler::getCan1Motor, outputStream);
        }
        else
        {
            printAllMotorInfo(&DjiMotorTxHandler::getCan2Motor, outputStream);
        }
    }
    else if (!canBusValid && motorIdValid)
    {
        outputStream << "CAN 1:" << modm::endl;
        getMotorInfoToString(
            drivers->djiMotorTxHandler.getCan1Motor(
                static_cast<MotorId>(motorId + tap::motor::MOTOR1)),
            outputStream);
        outputStream << "CAN 2:" << modm::endl;
        getMotorInfoToString(
            drivers->djiMotorTxHandler.getCan2Motor(
                static_cast<MotorId>(motorId + tap::motor::MOTOR1)),
            outputStream);
    }
    else
    {
        if (canBus == 1)
        {
            getMotorInfoToString(
                drivers->djiMotorTxHandler.getCan1Motor(
                    static_cast<MotorId>(motorId + tap::motor::MOTOR1)),
                outputStream);
        }
        else
        {
            getMotorInfoToString(
                drivers->djiMotorTxHandler.getCan2Motor(
                    static_cast<MotorId>(motorId + tap::motor::MOTOR1)),
                outputStream);
        }
    }
    return true;
}

void DjiMotorTerminalSerialHandler::getMotorInfoToString(
    const DjiMotor* motor,
    modm::IOStream& outputStream)
{
    if (motor != nullptr)
    {
        outputStream << (DJI_MOTOR_TO_NORMALIZED_ID(motor->getMotorIdentifier()) + 1) << ". "
                     << motor->getName() << ": online: " << (motor->isMotorOnline() ? "yes" : "no")
                     << ", enc: " << motor->getEncoderWrapped() << ", rpm: " << motor->getShaftRPM()
                     << ", out des: " << motor->getOutputDesired() << modm::endl;
    }
}

void DjiMotorTerminalSerialHandler::printAllMotorInfo(
    getMotorByIdFunc func,
    modm::IOStream& outputStream)
{
    for (int i = static_cast<int>(MOTOR1); i <= static_cast<int>(MOTOR8); i++)
    {
        const DjiMotor* motor = (drivers->djiMotorTxHandler.*(func))(static_cast<MotorId>(i));
        getMotorInfoToString(motor, outputStream);
    }
}
}  // namespace motor
}  // namespace tap