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

Updated SRTMinorServoLibraries

parent d1263fce
Loading
Loading
Loading
Loading
+66 −63
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@
#include "SRTMinorServoCommandLibrary.h"


namespace MinorServo
{
    /**
     * SRT Minor Servo Command Library Python Wrapper
     *
@@ -40,7 +42,7 @@ public:
         * @param start_time only mandatory for the first point in the trajectory, a double representing the UNIX epoch of the starting instant of the trajectory
         * @return the composed message
         */
    static std::string programTrack(std::string servo_id, unsigned int trajectory_id, unsigned int point_id, boost::python::list& coordinates, double start_time=-1);
        static std::string programTrack(std::string servo_id, unsigned long trajectory_id, unsigned long point_id, boost::python::list& coordinates, double start_time=-1);

        /*
         * Builds the command used to provide a set of offsets to a given servo
@@ -69,13 +71,14 @@ private:
         */
        static std::vector<double> pylist2cppvector(boost::python::list& py_list);
    };
}

/*
 * The following 3 lines of code allow the overloaded functions to ignore the optional parameter and use the default one defined in the original SRTMinorServoCommandLibrary header file
 */
BOOST_PYTHON_FUNCTION_OVERLOADS(status, PySRTMinorServoCommandLibrary::status, 0, 1)
BOOST_PYTHON_FUNCTION_OVERLOADS(stow, PySRTMinorServoCommandLibrary::stow, 1, 2)
BOOST_PYTHON_FUNCTION_OVERLOADS(programTrack, PySRTMinorServoCommandLibrary::programTrack, 4, 5)
BOOST_PYTHON_FUNCTION_OVERLOADS(status, MinorServo::PySRTMinorServoCommandLibrary::status, 0, 1)
BOOST_PYTHON_FUNCTION_OVERLOADS(stow, MinorServo::PySRTMinorServoCommandLibrary::stow, 1, 2)
BOOST_PYTHON_FUNCTION_OVERLOADS(programTrack, MinorServo::PySRTMinorServoCommandLibrary::programTrack, 4, 5)

/*
 * Python module definition. Since the original SRTMinorServoCommandLibrary only contains static functions, we write the Python module with static functions only, omitting the class
@@ -83,13 +86,13 @@ BOOST_PYTHON_FUNCTION_OVERLOADS(programTrack, PySRTMinorServoCommandLibrary::pro
BOOST_PYTHON_MODULE(libPySRTMinorServoCommandLibrary)
{
    using namespace boost::python;
    def("status", &PySRTMinorServoCommandLibrary::status, status(arg("servo_id") = ""));
    def("setup", &PySRTMinorServoCommandLibrary::setup);
    def("stow", &PySRTMinorServoCommandLibrary::stow, stow(arg("stow_position") = 1));
    def("stop", &PySRTMinorServoCommandLibrary::stop);
    def("preset", &PySRTMinorServoCommandLibrary::preset);
    def("programTrack", &PySRTMinorServoCommandLibrary::programTrack, programTrack(arg("start_time") = -1));
    def("offset", &PySRTMinorServoCommandLibrary::offset);
    def("parseAnswer", &PySRTMinorServoCommandLibrary::parseAnswer);
    def("status", &MinorServo::PySRTMinorServoCommandLibrary::status, status(arg("servo_id") = ""));
    def("setup", &MinorServo::PySRTMinorServoCommandLibrary::setup);
    def("stow", &MinorServo::PySRTMinorServoCommandLibrary::stow, stow(arg("stow_position") = 1));
    def("stop", &MinorServo::PySRTMinorServoCommandLibrary::stop);
    def("preset", &MinorServo::PySRTMinorServoCommandLibrary::preset);
    def("programTrack", &MinorServo::PySRTMinorServoCommandLibrary::programTrack, programTrack(arg("start_time") = -1));
    def("offset", &MinorServo::PySRTMinorServoCommandLibrary::offset);
    def("parseAnswer", &MinorServo::PySRTMinorServoCommandLibrary::parseAnswer);
}
#endif
+232 −0
Original line number Diff line number Diff line
#ifndef _SRTMINORSERVOANSWERMAP_H
#define _SRTMINORSERVOANSWERMAP_H

#include <sstream>
#include <map>
#include <variant>
#include <type_traits>
#include <mutex>
#include <shared_mutex>
#include <IRA>

#include <Cplusplus11Helper.h>
C11_IGNORE_WARNING_PUSH
C11_IGNORE_WARNING("-Wunused-function")

static std::ostream& operator<<(std::ostream& out, const std::variant<long, double, std::string>& value)
{
    std::visit([&out](const auto& val) { out << val; }, value);
    return out;
}

C11_IGNORE_WARNING_POP


namespace MinorServo
{
    /**
     * The following templates are useful if you want to check if a given type for the SRTMinorServoAnswerMap is accepted
     */
    template <typename T>
    struct is_string : public std::disjunction<std::is_same<char*, std::decay_t<T>>, std::is_same<const char*, std::decay_t<T>>, std::is_same<std::string, std::decay_t<T>>> {};

    template <typename T>
    inline constexpr bool is_string_v = is_string<T>::value;

    template <typename T>
    struct is_known : public std::disjunction<std::is_arithmetic<std::decay_t<T>>, is_string<std::decay_t<T>>> {};

    template <typename T>
    inline constexpr bool is_known_v = is_known<T>::value;

    class SRTMinorServoAnswerMap : private  std::map<std::string, std::variant<long, double, std::string>>
    {
        /**
         * This class privately extends the type std::map<std::string, std::variant<long, double, std::string>>.
         * It is therefore an std::map which can hold different types of values, such as long, double and str::string, in the same container.
         * It is used to store the answers received from the SRTMinorServo Leonardo system.
         * This design was critical since all the received values have heterogeneous keys and values.
         * With this object, the SRTMinorServoSocket can correctly retrieve and store all the received values without having to know the keys or types a priori.
         */

        /*
         * Declare this class as friend since it will have to iterate through the inner map
         */
        friend class PySRTMinorServoCommandLibrary;
    public:
        /**
         * Use the same clear method of the parent class
         */
        using std::map<std::string, std::variant<long, double, std::string>>::clear;

        /**
         * Default constructor, initialize the std::map object and the synchronization mutex
         */
        SRTMinorServoAnswerMap() : std::map<std::string, std::variant<long, double, std::string>>(), m_mutex() {}

        /**
         * Initialize the std::map with the content of another SRTMinorServoAnswerMap, initialize the mutex, lock both objects
         * @param other the SRTMinorServoAnswerMap with which the content of the current object will be initialized
         */
        SRTMinorServoAnswerMap(const SRTMinorServoAnswerMap& other) : m_mutex()
        {
            std::unique_lock<std::shared_mutex> lockThis(m_mutex, std::defer_lock);
            std::unique_lock<std::shared_mutex> lockOther(other.m_mutex, std::defer_lock);
            std::lock(lockThis, lockOther);
            static_cast<std::map <std::string, std::variant<long, double, std::string>>&>(*this) = static_cast<const std::map<std::string, std::variant<long, double, std::string>>&>(other);
        }

        /**
         * Assignment operator. It lock both the current object and the assigned one's mutexes
         * @param other the SRTMinorServoAnswerMap which values have to be stored in the current object
         */
        SRTMinorServoAnswerMap& operator=(const SRTMinorServoAnswerMap& other)
        {
            if(this != &other)
            {
                std::unique_lock<std::shared_mutex> lockThis(m_mutex, std::defer_lock);
                std::unique_lock<std::shared_mutex> lockOther(other.m_mutex, std::defer_lock);
                std::lock(lockThis, lockOther);
                static_cast<std::map <std::string, std::variant<long, double, std::string>>&>(*this) = static_cast<const std::map<std::string, std::variant<long, double, std::string>>&>(other);
            }

            return *this;
        }

        /**
         * Equality operator, only check the std::map and avoid comparing the mutexes, which will obviously be different
         * @param other the SRTMinorServoAnswerMap object to compare the current object with
         */
        bool operator==(const SRTMinorServoAnswerMap& other) const
        {
            return static_cast<const std::map<std::string, std::variant<long, double, std::string>>&>(*this) == static_cast<const std::map<std::string, std::variant<long, double, std::string>>&>(other);
        }

        /**
         * get method. It must be used with a template parameter, in order for the SRTMinorServoAnswerMap to be able to retrieve the correct type of object for the given key.
         * The method will automatically convert the retrieved long, double or std::string to the given template type.
         * @param T the type (i.e.: int, long, double, char*) of the object to be retrieved. It can be anything derived from integral, floating point or string values.
         * @param key the key assigned to the value you want to retrieve
         * @return the value associated to given key 'key', returned as template type 'T', if possible. Be aware that some casts (i.e.: long to int, double to float) will lose precision and/or overflow
         * @throw std::bad_variant_access when retrieving the stored value by asking the wrong type (i.e.: stored type is a double but T is char*)
         * @throw std::runtime_error when attempting to retrieve a value with a type which cannot be stored in the container (anything not integral, not floating point and not similar to std::string)
         */
        template <typename T>
        T get(const std::string& key) const
        {
            if constexpr(std::negation_v<MinorServo::is_known<T>>)
            {
                throw std::runtime_error("Unsupported type.");
            }

            std::shared_lock<std::shared_mutex> lock(m_mutex);

            if constexpr(std::is_integral_v<T>)
            {
                return (T)std::get<long>(this->at(key));
            }
            else if constexpr(std::is_floating_point_v<T>)
            {
                return (T)std::get<double>(this->at(key));
            }
            else if constexpr(is_string_v<T>)
            {
                return (T)std::get<std::string>(this->at(key)).c_str();
            }
        }

        /**
         * put method. The template parameter is automatically deducted from the 'value' argument. Stores the given 'value' associated with key 'key'
         * @param key the key associated to the stored value 'value'
         * @param value the value we are storing with the given key 'key'
         * @throw std::runtime_error when attempting to store a value with a type which cannot be stored in the container (anything not integral, not floating point and not similar to std::string)
         */
        template <typename T>
        void put(const std::string& key, const T& value)
        {
            if constexpr(std::negation_v<MinorServo::is_known<T>>)
            {
                throw std::runtime_error("Unsupported type.");
            }

            std::unique_lock<std::shared_mutex> lock(m_mutex);

            if constexpr(std::is_integral_v<T>)
            {
                this->operator[](key) = long(value);
            }
            else if constexpr(std::is_floating_point_v<T>)
            {
                this->operator[](key) = double(value);
            }
            else if constexpr(is_string_v<T>)
            {
                this->operator[](key) = std::string(value);
            }
        }

        /**
         * This method checks whether the container holds a value for the given key 'key'
         * @param key the key for the value we want to check if it's present in the container
         * @return true if the value is present in the container, false otherwise
         */
        bool contains(const std::string& key) const
        {
            std::shared_lock<std::shared_mutex> lock(m_mutex);
            return this->find(key) != this->end();
        }

        /**
         * This methods returns the std::variant type index for the value associated to the given key 'key'
         * @param key the key for the value we want to retrieve the type index
         * @throw std::out_of_range if the key is not found in the object
         * @return 0 for long, 1 for double, 2 for std::string
         */
        unsigned int index(const std::string& key) const
        {
            std::shared_lock<std::shared_mutex> lock(m_mutex);
            return this->at(key).index();
        }

        /**
         * This method checks whether the contained answer to a command sent to the SRTMinorServo system was positive or not
         * @return true if the command was correctly accepted, false if the command was not accepted or the 'OUTPUT' key was not found (unlikely scenario)
         */
        const bool checkOutput() const
        {
            std::shared_lock<std::shared_mutex> lock(m_mutex);
            try
            {
                if(this->get<std::string>("OUTPUT") == "GOOD")
                {
                    return true;
                }
            }
            catch(std::out_of_range& ex)
            {
                // Key not found
            }

            return false;
        }

        /**
         * This method retrieves the ACS::Time associated with the received answer map. It converts the value from UNIX Epoch (double) to ACS::Time
         * @return the ACS::Time associated with the answer map
         */
        const ACS::Time getTimestamp() const
        {
            std::shared_lock<std::shared_mutex> lock(m_mutex);
            return IRA::CIRATools::UNIXEpoch2ACSTime(this->get<double>("TIMESTAMP"));
        }

    private:
        /**
         * Shared mutex to control read and write accesses. Multiple reading access are permitted and will only block writing access. Writing access will block all accesses
         */
        mutable std::shared_mutex m_mutex;
    };
}


#endif
+66 −62
Original line number Diff line number Diff line
@@ -7,15 +7,18 @@
 * Giuseppe Carboni (giuseppe.carboni@inaf.it)
 */

#include "SRTMinorServoAnswerMap.h"
#include <sstream>
#include <vector>
#include <map>
#include <variant>


#define CLOSER std::string("\r\n")

using SRTMinorServoAnswerMap = std::map<std::string, std::variant<long, double, std::string> >;

namespace MinorServo
{
    /**
     * SRT Minor Servo Command Library
     *
@@ -26,14 +29,14 @@ class SRTMinorServoCommandLibrary
    public:
        /**
         * Builds the command used to ask the status of the MSCU or, eventually, a single servo
     * @param servo_id the ID string of the eventual single servo to retrieve the status
         * @param servo_id the ID string of the eventual single servo to retrieve the status. Send no servo_id argument to retrieve the general status of the system
         * @return the composed message
         */
        static std::string status(std::string servo_id = "");

        /**
     * Builds the command used to configure the telescope for an observation
     * @param configuration the desired observing configuration
         * Builds the command used to configure the telescope for a specific focal path
         * @param configuration the desired focal path to command to the minor servo systems
         * @return the composed message
         */
        static std::string setup(std::string configuration);
@@ -54,7 +57,7 @@ public:
        static std::string stop(std::string servo_id);

        /*
     * Builds the command used to move a single servo to a given set of coordinates
         * Builds the command used to move a single servo to a given set of virtual coordinates
         * @param servo_id the ID string of the single servo to be moved
         * @param coordinates a vector containing the N coordinates to be sent to the servo
         * @return the composed message
@@ -62,18 +65,18 @@ public:
        static std::string preset(std::string servo_id, std::vector<double> coordinates);

        /*
     * Builds the command used to provide a single tracking set of coordinates to a single servo
         * Builds the command used to provide a single tracking point of virtual coordinates to a single servo
         * @param servo_id the ID string of the single servo to send the command to
         * @param trajectory_id the ID number of the trajectory the given set of coordinates belongs to
         * @param point_id the ID number of the given set of coordinates inside the trajectory
         * @param coordinates a vector containing the N coordinates the servo have to move to at the given time
     * @param start_time only mandatory for the first point in the trajectory, a double representing the UNIX epoch of the starting instant of the trajectory
         * @param start_time only mandatory for the first point in the trajectory, a double representing the UNIX Epoch of the starting instant of the trajectory
         * @return the composed message
         */
    static std::string programTrack(std::string servo_id, long unsigned int trajectory_id, unsigned int point_id, std::vector<double> coordinates, double start_time = 0);
        static std::string programTrack(std::string servo_id, unsigned long trajectory_id, unsigned long point_id, std::vector<double> coordinates, double start_time = 0);

        /*
     * Builds the command used to send a set of offset coordinates
         * Builds the command used to send a set of virtual offsets to a single servo
         * @param servo_id the ID string of the single servo to send the offsets to
         * @param coordinates a vector containing the N offsets to be added the servo coordinates
         * @return the composed message
@@ -81,11 +84,12 @@ public:
        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
         * Parses the received answer by splitting it and dynamically populating a SRTMinorServoAnswerMap object
         * @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.
         * @return a SRTMinorServoAnswerMap dictionary containing the answer splitted into keys and values. The keys are always std::string, the values can either be long, double or std::string.
         */
        static SRTMinorServoAnswerMap parseAnswer(std::string answer);
    };
}

#endif
+215 −0

File added.

Preview size limit exceeded, changes collapsed.

+7 −4
Original line number Diff line number Diff line
@@ -54,21 +54,24 @@ xxxxx_LIBS =
#
# Includes (.h) files (public only)
# ---------------------------------
INCLUDES        = hexlib.h SRTMinorServoCommandLibrary.h
INCLUDES        = hexlib.h SRTMinorServoCommandLibrary.h SRTMinorServoAnswerMap.h SRTMinorServoSocket.h

#
# Libraries (public and local)
# ----------------------------
LIBRARIES       = SRTMinorServoLibrary SRTMinorServoCommandLibrary PySRTMinorServoCommandLibrary
LIBRARIES       = SRTMinorServoLibrary SRTMinorServoCommandLibrary SRTMinorServoSocketLibrary PySRTMinorServoCommandLibrary
LIBRARIES_L     =
SRTMinorServoLibrary_OBJECTS = hexlib
SRTMinorServoLibrary_LIBS = gsl gslcblas m
SRTMinorServoCommandLibrary_OBJECTS = SRTMinorServoCommandLibrary
SRTMinorServoCommandLibrary_CFLAGS = -std=c++17
SRTMinorServoCommandLibrary_LIBS =
SRTMinorServoCommandLibrary_LIBS = pthread IRALibrary
PySRTMinorServoCommandLibrary_OBJECTS = PySRTMinorServoCommandLibrary
PySRTMinorServoCommandLibrary_CFLAGS = -std=c++17
PySRTMinorServoCommandLibrary_LIBS = SRTMinorServoCommandLibrary boost_python3
SRTMinorServoSocketLibrary_OBJECTS = SRTMinorServoSocket
SRTMinorServoSocketLibrary_LIBS = IRALibrary ComponentErrors MinorServoErrors SRTMinorServoCommandLibrary
SRTMinorServoSocket_CFLAGS = -std=c++17

#
# <brief description of lllll library>
@@ -95,7 +98,7 @@ PY_SCRIPTS_L =
PY_MODULES         =
PY_MODULES_L       =

PY_PACKAGES        = SRTMinorServoCommandLibrary
PY_PACKAGES        = #SRTMinorServoCommandLibrary
PY_PACKAGES_L      =
pppppp_MODULES	   =

Loading