Commit bf859344 authored by Giuseppe Carboni's avatar Giuseppe Carboni
Browse files

Fix #771, first implementation of parseAnswer and related tests (#772)

* Issue #771, first implementation of parseAnswer and related tests

Right now the function is not robust, it should check for errors in the answer format

* Fix #771, complete implementation of `parseAnswer` function

Tests were updated as well
parent 08ac4a9b
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -52,6 +52,15 @@ public:
     */
    static std::string offset(std::string servo_id, boost::python::list& coordinates);

    /*
     * Parses the received answer by splitting it and synamically populating a std::map
     * This is an overload of the original SRTMinorServoCommandLibrary::parseAnswer function
     * An overload was needed in order to replace the original std::map object with a boost::python::dict
     * @param answer the string containing the answer received from the VBrain proxy
     * @return a Python dictionary containing the answer splitted into keys and values. The keys are always strings, the values can either be int, double or strings.
     */
    static boost::python::dict parseAnswer(std::string answer);

private:
    /*
     * Converts the given Python list into a C++ std::vector object
@@ -81,5 +90,6 @@ BOOST_PYTHON_MODULE(libPySRTMinorServoCommandLibrary)
    def("preset", &PySRTMinorServoCommandLibrary::preset);
    def("programTrack", &PySRTMinorServoCommandLibrary::programTrack, programTrack(arg("start_time") = -1));
    def("offset", &PySRTMinorServoCommandLibrary::offset);
    def("parseAnswer", &PySRTMinorServoCommandLibrary::parseAnswer);
}
#endif
+9 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@

#include <sstream>
#include <vector>
#include <map>
#include <variant>

/**
 * SRT Minor Servo Command Library
@@ -73,6 +75,13 @@ public:
     * @return the composed message
     */
    static std::string offset(std::string servo_id, std::vector<double> coordinates);

    /*
     * Parses the received answer by splitting it and synamically populating a std::map
     * @param answer the string containing the answer received from the VBrain proxy
     * @return a std::map (dictionary) containing the answer splitted into keys and values. The keys are always std::string, the values can either be int, double or std::string.
     */
    static std::map<std::string, std::variant<int, double, std::string> > parseAnswer(std::string answer);
};

#endif
+3 −1
Original line number Diff line number Diff line
@@ -64,9 +64,11 @@ LIBRARIES_L =
SRTMinorServoLibrary_OBJECTS = hexlib
SRTMinorServoLibrary_LIBS = gsl gslcblas m
SRTMinorServoCommandLibrary_OBJECTS = SRTMinorServoCommandLibrary
SRTMinorServoCommandLibrary_CFLAGS = -std=c++17
SRTMinorServoCommandLibrary_LIBS =
PySRTMinorServoCommandLibrary_OBJECTS = PySRTMinorServoCommandLibrary
PySRTMinorServoCommandLibrary_LIBS = SRTMinorServoCommandLibrary boost_python
PySRTMinorServoCommandLibrary_CFLAGS = -std=c++17
PySRTMinorServoCommandLibrary_LIBS = SRTMinorServoCommandLibrary boost_python3

#
# <brief description of lllll library>
+16 −0
Original line number Diff line number Diff line
@@ -24,3 +24,19 @@ std::vector<double> PySRTMinorServoCommandLibrary::pylist2cppvector(boost::pytho
    }
    return cpp_vector;
}

boost::python::dict PySRTMinorServoCommandLibrary::parseAnswer(std::string answer)
{
    auto args = SRTMinorServoCommandLibrary::parseAnswer(answer);

    boost::python::dict dictionary;

    typename std::map<std::string, std::variant<int, double, std::string> >::iterator iter;

    for(iter = args.begin(); iter != args.end(); ++iter)
    {
        std::visit([dictionary, iter](const auto& var) mutable { dictionary[iter->first] = var; }, iter->second);
    }

    return dictionary;
}
+74 −0
Original line number Diff line number Diff line
@@ -6,6 +6,9 @@

#include "SRTMinorServoCommandLibrary.h"

#include <iostream>
#include <algorithm>

std::string SRTMinorServoCommandLibrary::status(std::string servo_id)
{
    std::stringstream command;
@@ -84,3 +87,74 @@ std::string SRTMinorServoCommandLibrary::offset(std::string servo_id, std::vecto
    command << "\r\n";
    return command.str();
}

std::map<std::string, std::variant<int, double, std::string> > SRTMinorServoCommandLibrary::parseAnswer(std::string answer)
{
    // First thing first, standardize the separators
    std::replace(answer.begin(), answer.end(), ':', '=');
    std::replace(answer.begin(), answer.end(), '|', ',');

    // Create the dictionary
    std::map<std::string, std::variant<int, double, std::string> > args;

    std::stringstream ss(answer);
    std::string token;

    try
    {
        // Loop through the tokens
        while(std::getline(ss, token, ','))
        {
            std::stringstream sss(token);
            std::string key, value;
            std::getline(sss, key, '=');
            std::getline(sss, value);

            // No value, should be the timestamp
            if(value.empty())
            {
                if(args.find("TIMESTAMP") != args.end())    // Timestamp already found, some other value is missing its key
                    throw std::invalid_argument(std::string("Missing key for value " + value));

                value = key;
                key = "TIMESTAMP";
            }

            if(key == "OUTPUT")
            {
                if(value != "GOOD" && value != "BAD")
                    throw std::invalid_argument(std::string("Unrecognized OUTPUT value: " + value));

                args[key] = value;
            }
            else if(key == "TIMESTAMP")
            {
                size_t last_char;
                args[key] = std::stod(value, &last_char);
                if(last_char != value.size())
                    throw std::invalid_argument(std::string("Wrong TIMESTAMP value: " + value));
            }
            else
            {
                size_t last_char;
                args[key] = std::stoi(value, &last_char);
                if(last_char != value.size())
                    args[key] = std::stod(value);
            }
        }

        if(args.find("OUTPUT") == args.end())
            throw std::invalid_argument(std::string("Missing OUTPUT value!"));
        else if(args.find("TIMESTAMP") == args.end())
            throw std::invalid_argument(std::string("Missing TIMESTAMP value!"));
    }
    catch(const std::invalid_argument& e)
    {
        // If we are not able to convert any of the values to the correct type,
        // or if OUTPUT and/or TIMESTAMP is missing, we send back an empty dictionary.
        // It will be the caller's duty to understand that something was wrong with the answer.
        args.clear();
    }

    return args;
}
Loading