diff --git a/doc/release/master.md b/doc/release/master.md index 44b5575ae6..3a4cc2f565 100644 --- a/doc/release/master.md +++ b/doc/release/master.md @@ -84,6 +84,10 @@ New Features * Network protocol now uses IDL thrift +#### FakePythonSpeechTranscription + +* Added new device `FakePythonSpeechTranscription`. The device is also an example which demonstrates the encapsulation of python code inside a c++ device implementing a Yarp interface. + ### GUIs #### yarpopencvdisplay diff --git a/src/devices/fake/fakePythonSpeechTranscription/CMakeLists.txt b/src/devices/fake/fakePythonSpeechTranscription/CMakeLists.txt new file mode 100644 index 0000000000..5b5164933a --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/CMakeLists.txt @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) +# SPDX-License-Identifier: BSD-3-Clause + +if (YARP_COMPILE_ALL_FAKE_DEVICES) + set(ENABLE_yarpmod_fakePythonSpeechTranscription ON CACHE BOOL "" FORCE) +endif() + +yarp_prepare_plugin(fakePythonSpeechTranscription + CATEGORY device + TYPE FakePythonSpeechTranscription + INCLUDE FakePythonSpeechTranscription.h + GENERATE_PARSER +) + +if(ENABLE_fakePythonSpeechTranscription) + find_package(Python REQUIRED Development) + yarp_add_plugin(yarp_fakePythonSpeechTranscription) + + target_sources(yarp_fakePythonSpeechTranscription + PRIVATE + FakePythonSpeechTranscription.cpp + FakePythonSpeechTranscription.h + FakePythonSpeechTranscription_ParamsParser.cpp + FakePythonSpeechTranscription_ParamsParser.h + ) + + target_link_libraries(yarp_fakePythonSpeechTranscription + PRIVATE + YARP::YARP_os + YARP::YARP_dev + Python::Python + ) + list(APPEND YARP_${YARP_PLUGIN_MASTER}_PRIVATE_DEPS + YARP_os + YARP_dev + ) + + yarp_install( + TARGETS yarp_fakePythonSpeechTranscription + EXPORT YARP_${YARP_PLUGIN_MASTER} + COMPONENT ${YARP_PLUGIN_MASTER} + LIBRARY DESTINATION ${YARP_DYNAMIC_PLUGINS_INSTALL_DIR} + ARCHIVE DESTINATION ${YARP_STATIC_PLUGINS_INSTALL_DIR} + YARP_INI DESTINATION ${YARP_PLUGIN_MANIFESTS_INSTALL_DIR} + ) + + yarp_install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/Module.py + DESTINATION ${YARP_CONTEXTS_INSTALL_DIR}/tests/fakePythonSpeechTranscription) + + set(YARP_${YARP_PLUGIN_MASTER}_PRIVATE_DEPS ${YARP_${YARP_PLUGIN_MASTER}_PRIVATE_DEPS} PARENT_SCOPE) + + set_property(TARGET yarp_fakePythonSpeechTranscription PROPERTY FOLDER "Plugins/Device/Fake") + + if(YARP_COMPILE_TESTS) + add_subdirectory(tests) + endif() + +endif() diff --git a/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.cpp b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.cpp new file mode 100644 index 0000000000..f82cef67a3 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.cpp @@ -0,0 +1,459 @@ +/* + * SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fakePythonSpeechTranscription.h" + +#include +#include +#include + +#include +#include + +using namespace yarp::os; +using namespace yarp::dev; + +namespace { +YARP_LOG_COMPONENT(FAKE_SPEECHTR, "yarp.device.fakePythonSpeechTranscription") +} + +FakePythonSpeechTranscription::FakePythonSpeechTranscription() +{ + m_classInstance=NULL; +} + +FakePythonSpeechTranscription::~FakePythonSpeechTranscription() +{ + // Clear references for each PyObject + if (m_classInstance!=NULL) + { + for (long int i = 0; i < m_classInstance->ob_refcnt; ++i) + { + Py_XDECREF(m_classInstance); + } + } + + Py_FinalizeEx(); +} + +bool FakePythonSpeechTranscription::open(yarp::os::Searchable& config) +{ + std::string func_name = "open"; + + m_moduleName = config.check("moduleName", yarp::os::Value("Module")).asString(); + m_path = config.find("modulePath").asString(); + m_className = config.check("className", yarp::os::Value("SpeechTranscriptor")).asString(); + + //Simple Function Calling without passing any parameters + + PyObject *pArgs = NULL, //Interpreted name of the Arguments + *pValue; //Interpreted return value from function call + + // Initialize the Python Interpreter + if (!Py_IsInitialized()) + { + yInfo() << "Calling Py_Initialize from open()"; + Py_Initialize(); + } + + if(!functionWrapper(m_moduleName, func_name, pArgs, pValue)) + { + yCError(FAKE_SPEECHTR) << "[open] Unable to call the " << func_name << " function from python \n"; + return false; + } + + if (pValue == NULL) + { + yCError(FAKE_SPEECHTR) << "[open] Returned null pointer from function call \n"; + //Py_DECREF(pValue); + return false; + } + + bool ret; //result from the function call on python side + if (!boolWrapper(pValue, ret)) + { + yCError(FAKE_SPEECHTR) << "[open] Unable to convert returned PyObject to bool \n"; + Py_DECREF(pValue); + return false; + } + Py_DECREF(pValue); + + /*------------------CLASS CREATION*------------------*/ + // This line instantiate the class in Module.py with two parameters: lang=auto and verbose=1 + PyObject* pClassArgs = Py_BuildValue("(si)", m_language.c_str(), 1); + + if (! classInstanceCreator(m_moduleName, m_className, pClassArgs, m_classInstance)) + { + yCError(FAKE_SPEECHTR) << "Failed to instantiate py class \n"; + Py_XDECREF(pClassArgs); + return false; + } + // Clear not needed PyObjects + Py_XDECREF(pClassArgs); + + return ret; +} + +bool FakePythonSpeechTranscription::close() +{ + std::string func_name = "close"; + + //Simple Function Calling without passing any parameters + + PyObject *pArgs = NULL, //Interpreted name of the Arguments + *pValue; //Interpreted return value from function call + + // Initialize the Python Interpreter + if (!Py_IsInitialized()) + { + yInfo() << "Calling Close Py_Initialize"; + Py_Initialize(); + } + + if(!functionWrapper(m_moduleName, func_name, pArgs, pValue)) + { + yCError(FAKE_SPEECHTR) << "[close] Unable to call the close() function from python \n"; + return false; + } + + if (pValue == NULL) + { + yCError(FAKE_SPEECHTR) << "[close] Returned null pointer from function call \n"; + //Py_DECREF(pValue); + return false; + } + + bool ret; //result from the function call on python side + if (!boolWrapper(pValue, ret)) + { + yCError(FAKE_SPEECHTR) << "[close] Unable to convert returned PyObject to bool \n"; + return false; + } + Py_DECREF(pValue); + + return ret; +} + +bool FakePythonSpeechTranscription::setLanguage(const std::string& language) +{ + if (!Py_IsInitialized()) + { + yInfo()<<"Calling setLanguage Py_Initialize"; + Py_Initialize(); + } + + PyObject* pRetVal; // Return Value from the class method + std::string methodName = "set_language"; + yCInfo(FAKE_SPEECHTR) << "[setLanguage] converting input: " << language.c_str(); + PyObject* pInput = PyUnicode_FromString(language.c_str()); // string to pass to the method + + yCInfo(FAKE_SPEECHTR) << "[setLanguage] calling class wrapper"; + if(! classWrapper(m_classInstance, methodName, pInput, pRetVal)) + { + yCError(FAKE_SPEECHTR) << "[setLanguage] Returned False at classWrapper \n"; + return false; + } + bool result; + + if (!boolWrapper(pRetVal, result)) + { + yCError(FAKE_SPEECHTR) << "[setLanguage] Unable to convert returned PyObject to bool \n"; + return false; + } + + if (pInput!=NULL) + { + for (long int i = 0; i < pInput->ob_refcnt; ++i) + { + Py_XDECREF(pInput); + } + } + Py_XDECREF(pRetVal); + yInfo() << "Returning from setLanguage: " << result; + + return true; +} + +bool FakePythonSpeechTranscription::getLanguage(std::string& language) +{ + if (!Py_IsInitialized()) + { + yInfo()<<"Calling getLanguage Py_Initialize"; + Py_Initialize(); + } + + PyObject* pRetVal; // Return Value from the class method + std::string methodName = "get_language"; + PyObject* pClassMethodArgs = NULL; // no arguments passed + + if(! classWrapper(m_classInstance, methodName, pClassMethodArgs, pRetVal)) + { + yCError(FAKE_SPEECHTR) << "[getLanguage] Returned False at classWrapper \n"; + return false; + } + + std::string ret; + if (!stringWrapper(pRetVal, ret)) + { + yCError(FAKE_SPEECHTR) << "[getLanguage] Unable to covert PyObject to string, Null value \n"; + return false; + } + + yInfo() << "Returning from getLanguage: " << ret; + language = ret; + Py_XDECREF(pRetVal); + + return true; +} + +bool FakePythonSpeechTranscription::transcribe(const yarp::sig::Sound& sound, std::string& transcription, double& score) +{ + if (sound.getSamples() == 0 || + sound.getChannels() == 0) + { + yCError(FAKE_SPEECHTR) << "Invalid Sound sample received"; + transcription = ""; + score = 0.0; + return false; + } + + transcription = "hello world"; + score = 1.0; + return true; +} + +bool FakePythonSpeechTranscription::functionWrapper(std::string moduleName, std::string functionName, PyObject* &pArgs, PyObject* &pValue) +{ + if (!Py_IsInitialized()) + { + yInfo()<<"Calling functionWrapper Py_Initialize"; + Py_Initialize(); + } + + PyObject *pName, //Interpreted name of the module + *pModule, //Imported py Module loaded in this object + *pFunc; //Interpreted name of the function + + // Build the name object (of the module) + PyObject* sysPath = PySys_GetObject((char*)"path"); // BORROWED REFERENCE + PyList_Append(sysPath, (PyUnicode_FromString(m_path.c_str()))); // sets where to look for the module + pName = PyUnicode_DecodeFSDefault(m_moduleName.c_str()); //The string "Module" should be the name of the python file: i.e. Module.py + + // Load the module object + pModule = PyImport_Import(pName); + // Destroy the pName object: not needed anymore + Py_DECREF(pName); + + if (pModule != NULL) // Check if the Module has been found and loaded + { + // Get the function inside the module + pFunc = PyObject_GetAttrString(pModule, functionName.c_str()); + + if (pFunc && PyCallable_Check(pFunc)) // Check if the function has been found and is callable + { + // Call the function + pValue = PyObject_CallObject(pFunc, pArgs); + // Check the return value + if (pValue != NULL) + { + yCInfo(FAKE_SPEECHTR) << "Returning object " << " \n"; + // Clear memory + Py_DECREF(pModule); + return true; + } + else + { // Call Failed + Py_DECREF(pModule); + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "Call failed"; + + return false; + } + } + else + { // Function not found in the py module + if (PyErr_Occurred()) + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "Cannot find function: " << functionName; + //Py_XDECREF(pFunc); + Py_XDECREF(pModule); + + return false; + } + } + else + { // Module not found or loaded + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "Failed to load: " << m_moduleName; + return false; + } +} + +bool FakePythonSpeechTranscription::classWrapper(PyObject* &pClassInstance, std::string methodName, PyObject* &pClassMethodArgs, PyObject* &pValue) +{ + if (!Py_IsInitialized()) + { + yInfo()<<"Calling classWrapper Py_Initialize"; + Py_Initialize(); + } + PyObject *pMethod; // Converted name of the class method to PyObject + + yCInfo(FAKE_SPEECHTR) << "[classWrapper] converting method from str"; + pMethod = PyUnicode_FromString(methodName.c_str()); + + if (pMethod == NULL) + { + yCError(FAKE_SPEECHTR) << "[classWrapper] Unable to convert methodName to python"; + if (PyErr_Occurred()) + PyErr_Print(); + Py_DECREF(pMethod); + return false; + } + + yCInfo(FAKE_SPEECHTR) << "[classWrapper] class instance check"; + if (pClassInstance==NULL) + { + yCError(FAKE_SPEECHTR) << "[classWrapper] Class instance NULL"; + if (PyErr_Occurred()) + PyErr_Print(); + Py_DECREF(pMethod); + return false; + } + + yCInfo(FAKE_SPEECHTR) << "[classWrapper] calling method class"; + pValue = PyObject_CallMethodObjArgs(pClassInstance, pMethod, pClassMethodArgs, NULL); + + if (pValue==NULL) + { + yCError(FAKE_SPEECHTR) << "[classWrapper] Returned NULL Value from Class call"; + if (PyErr_Occurred()) + PyErr_Print(); + Py_DECREF(pMethod); + return false; + } + Py_DECREF(pMethod); + return true; +} + +bool FakePythonSpeechTranscription::classInstanceCreator(std::string moduleName, std::string className, PyObject* &pClassArgs, PyObject* &pReturn) +{ + if (!Py_IsInitialized()) + { + yInfo()<<"Calling classWrapper Py_Initialize"; + Py_Initialize(); + } + PyObject *pName, // Interpreted name of the module + *pModule, // Imported py Module loaded in this object + *pClass, // Interpreted name of the class + *pDict; // Dictionary of the interpreter + + // Build the name object (of the module) + PyObject* sysPath = PySys_GetObject((char*)"path"); // BORROWED REFERENCE + PyList_Append(sysPath, (PyUnicode_FromString(m_path.c_str()))); // sets where to look for the module + pName = PyUnicode_DecodeFSDefault(m_moduleName.c_str()); //The string "Module" should be the name of the python file: i.e. Module.py + + // Load the module object + pModule = PyImport_Import(pName); + // Destroy the pName object: not needed anymore + Py_DECREF(pName); + + if (pModule==NULL) + { + if (PyErr_Occurred()) + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "[classWrapper] Unable to create module instance \n"; + return false; + } + Py_DECREF(pModule); + + // pDict is a borrowed reference + pDict = PyModule_GetDict(pModule); + // Build the name of a callable class -> borrowed reference + pClass = PyDict_GetItemString(pDict, className.c_str()); + + if (PyCallable_Check(pClass)) + { + //Create instance of the class + pReturn = PyObject_CallObject(pClass, pClassArgs); + + if (pReturn==NULL) + { + if (PyErr_Occurred()) + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "[classWrapper] Unable to instantiate Class with given class arguments \n"; + + if (pReturn->ob_refcnt>0) + { + Py_XDECREF(pReturn); + } + return false; + } + + return true; + } + else + { + if (PyErr_Occurred()) + PyErr_Print(); + yCError(FAKE_SPEECHTR) << "[classWrapper] Unable to find class in py module \n"; + return false; + } +} + +bool FakePythonSpeechTranscription::stringWrapper(PyObject* &pValue, std::string &ret) +{ + if (pValue!=NULL) + { + ret = std::string(PyUnicode_AsUTF8(pValue)); + return true; + } + else + { + yCError(FAKE_SPEECHTR) << "Null pValue passed to FakePythonSpeechTranscription::stringWrapper \n"; + return false; + } +} + +bool FakePythonSpeechTranscription::intWrapper(PyObject* &pValue, long &ret) +{ + if (pValue!=NULL) + { + ret = PyLong_AsLong(pValue); + return true; + } + else + { + yCError(FAKE_SPEECHTR) << "Null pValue passed to FakePythonSpeechTranscription::intWrapper \n"; + return false; + } +} + +bool FakePythonSpeechTranscription::doubleWrapper(PyObject* &pValue, double &ret) +{ + if (pValue!=NULL) + { + ret = PyFloat_AsDouble(pValue); + return true; + } + else + { + yCError(FAKE_SPEECHTR) << "Null pValue passed to FakePythonSpeechTranscription::doubleWrapper \n"; + return false; + } +} + +bool FakePythonSpeechTranscription::boolWrapper(PyObject* &pValue, bool &ret) +{ + if (pValue!=NULL) + { + ret = (bool)PyLong_AsLong(pValue); + return true; + } + else + { + yCError(FAKE_SPEECHTR) << "Null pValue passed to FakePythonSpeechTranscription::boolWrapper \n"; + return false; + } +} diff --git a/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.h b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.h new file mode 100644 index 0000000000..66c10f600e --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription.h @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FAKEPYTHONSPEECHTRANSCRIPTION_H +#define FAKEPYTHONSPEECHTRANSCRIPTION_H + +#include +#include +#include +#include +#include + +#include "Python.h" + +using namespace yarp::os; + +/** + * @ingroup dev_impl_other + * + * \brief `FakePythonSpeechTranscription`: A fake implementation of a speech transcriber plugin using python embedding. + */ +class FakePythonSpeechTranscription : + public yarp::dev::DeviceDriver, + public yarp::dev::ISpeechTranscription +{ +private: + bool m_verbose = true; + std::string m_language="auto"; + + std::string m_moduleName = "a"; // Name of the python file containing the functions/classes + std::string m_path = "b"; // Path where the file is located + std::string m_className = "c"; // Name of the class contained in @m_moduleName at @m_path + + PyObject * m_classInstance; // Python object of the created class + + /** + * @brief Calls a function present in a .py file and returns its values as python object. + * @param moduleName The name of the .py file + * @param functionName The name of the function contained in the .py file + * @param pArgs PyObject Arguments needed for creating the class (pass NULL if no argument is needed) + * @param pReturn The returned reference to the returned PyObject + * @return True in case of success, false otherwise. + */ + bool functionWrapper(std::string moduleName, std::string functionName, PyObject* &pArgs, PyObject* &pValue); + + /** + * @brief Wraps the call to a class method. + * @param pClassInstance The configuration parameters + * @param methodName The name of the method present in the class instance + * @param pClassMethodArgs PyObject Arguments needed for creating the class (pass NULL if no argument is needed) + * @param pValue PyObject returned by the method + * @return True in case of success, false otherwise. + */ + bool classWrapper(PyObject* &pClassInstance, std::string methodName, PyObject* &pClassMethodArgs, PyObject* &pValue); + + /** + * @brief Initialize python class described in the python file as an interpreted object. + * @param moduleName The name of the .py file + * @param className The name of the class contained in the .py file + * @param pClassArgs PyObject Arguments needed for creating the class (pass NULL if no argument is needed) + * @param pReturn The returned reference to the instance object + * @return True in case of success, false otherwise. + */ + bool classInstanceCreator(std::string moduleName, std::string className, PyObject* &pClassArgs, PyObject* &pReturn); + + /** + * @brief Converts a python object to string, returns NULL otherwise + * @param pValue PyObject to be converted + * @param ret The converted string + * @return True in case of success, false otherwise. + */ + bool stringWrapper(PyObject* &pValue, std::string &ret); + + /** + * @brief Converts a python object to long, returns NULL otherwise + * @param pValue PyObject to be converted + * @param ret The converted long + * @return True in case of success, false otherwise. + */ + bool intWrapper(PyObject* &pValue, long &ret); + + /** + * @brief Converts a python object to double, returns NULL otherwise + * @param pValue PyObject to be converted + * @param ret The converted double + * @return True in case of success, false otherwise. + */ + bool doubleWrapper(PyObject* &pValue, double &ret); + + /** + * @brief Converts a python object to bool, returns NULL otherwise + * @param pValue PyObject to be converted + * @param ret The converted boolean + * @return True in case of success, false otherwise. + */ + bool boolWrapper(PyObject* &pValue, bool &ret); + +public: + FakePythonSpeechTranscription(); + virtual ~FakePythonSpeechTranscription(); + FakePythonSpeechTranscription(const FakePythonSpeechTranscription&) = delete; + FakePythonSpeechTranscription(FakePythonSpeechTranscription&&) = delete; + FakePythonSpeechTranscription& operator=(const FakePythonSpeechTranscription&) = delete; + FakePythonSpeechTranscription& operator=(FakePythonSpeechTranscription&&) = delete; + + bool open(yarp::os::Searchable& config) override; + bool close() override; + + virtual bool setLanguage(const std::string& language) override; + virtual bool getLanguage(std::string& language) override; + virtual bool transcribe(const yarp::sig::Sound& sound, std::string& transcription, double& score) override; +}; + +#endif diff --git a/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.cpp b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.cpp new file mode 100644 index 0000000000..cb6ba0f9e9 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.cpp @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + + +// Generated by yarpDeviceParamParserGenerator (1.0) +// This is an automatically generated file. Please do not edit it. +// It will be re-generated if the cmake flag ALLOW_DEVICE_PARAM_PARSER_GERNERATION is ON. + +// Generated on: Wed Sep 11 16:54:49 2024 + + +#include "FakePythonSpeechTranscription_ParamsParser.h" +#include +#include + +namespace { + YARP_LOG_COMPONENT(FakePythonSpeechTranscriptionParamsCOMPONENT, "yarp.device.FakePythonSpeechTranscription") +} + + +FakePythonSpeechTranscription_ParamsParser::FakePythonSpeechTranscription_ParamsParser() +{ +} + + +std::vector FakePythonSpeechTranscription_ParamsParser::getListOfParams() const +{ + std::vector params; + params.push_back("language"); + return params; +} + + +bool FakePythonSpeechTranscription_ParamsParser::parseParams(const yarp::os::Searchable & config) +{ + //Check for --help option + if (config.check("help")) + { + yCInfo(FakePythonSpeechTranscriptionParamsCOMPONENT) << getDocumentationOfDeviceParams(); + } + + std::string config_string = config.toString(); + yarp::os::Property prop_check(config_string.c_str()); + //Parser of parameter language + { + if (config.check("language")) + { + m_language = config.find("language").asString(); + yCInfo(FakePythonSpeechTranscriptionParamsCOMPONENT) << "Parameter 'language' using value:" << m_language; + } + else + { + yCInfo(FakePythonSpeechTranscriptionParamsCOMPONENT) << "Parameter 'language' using DEFAULT value:" << m_language; + } + prop_check.unput("language"); + } + + /* + //This code check if the user set some parameter which are not check by the parser + //If the parser is set in strict mode, this will generate an error + if (prop_check.size() > 0) + { + bool extra_params_found = false; + for (auto it=prop_check.begin(); it!=prop_check.end(); it++) + { + if (m_parser_is_strict) + { + yCError(FakePythonSpeechTranscriptionParamsCOMPONENT) << "User asking for parameter: "<name <<" which is unknown to this parser!"; + extra_params_found = true; + } + else + { + yCWarning(FakePythonSpeechTranscriptionParamsCOMPONENT) << "User asking for parameter: "<< it->name <<" which is unknown to this parser!"; + } + } + + if (m_parser_is_strict && extra_params_found) + { + return false; + } + } + */ + return true; +} + + +std::string FakePythonSpeechTranscription_ParamsParser::getDocumentationOfDeviceParams() const +{ + std::string doc; + doc = doc + std::string("\n=============================================\n"); + doc = doc + std::string("This is the help for device: FakePythonSpeechTranscription\n"); + doc = doc + std::string("\n"); + doc = doc + std::string("This is the list of the parameters accepted by the device:\n"); + doc = doc + std::string("'language': language code\n"); + doc = doc + std::string("\n"); + doc = doc + std::string("Here are some examples of invocation command with yarpdev, with all params:\n"); + doc = doc + " yarpdev --device fakePythonSpeechTranscription --language auto\n"; + doc = doc + std::string("Using only mandatory params:\n"); + doc = doc + " yarpdev --device fakePythonSpeechTranscription\n"; + doc = doc + std::string("=============================================\n\n"); return doc; +} diff --git a/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.h b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.h new file mode 100644 index 0000000000..0f6a13e071 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_ParamsParser.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + + +// Generated by yarpDeviceParamParserGenerator (1.0) +// This is an automatically generated file. Please do not edit it. +// It will be re-generated if the cmake flag ALLOW_DEVICE_PARAM_PARSER_GERNERATION is ON. + +// Generated on: Wed Sep 11 16:54:49 2024 + + +#ifndef FAKEPYTHONSPEECHTRANSCRIPTION_PARAMSPARSER_H +#define FAKEPYTHONSPEECHTRANSCRIPTION_PARAMSPARSER_H + +#include +#include +#include +#include + +/** +* This class is the parameters parser for class FakePythonSpeechTranscription. +* +* These are the used parameters: +* | Group name | Parameter name | Type | Units | Default Value | Required | Description | Notes | +* |:----------:|:--------------:|:------:|:-----:|:-------------:|:--------:|:-------------:|:-----:| +* | - | language | string | - | auto | 0 | language code | - | +* +* The device can be launched by yarpdev using one of the following examples (with and without all optional parameters): +* \code{.unparsed} +* yarpdev --device fakePythonSpeechTranscription --language auto +* \endcode +* +* \code{.unparsed} +* yarpdev --device fakePythonSpeechTranscription +* \endcode +* +*/ + +class FakePythonSpeechTranscription_ParamsParser : public yarp::dev::IDeviceDriverParams +{ +public: + FakePythonSpeechTranscription_ParamsParser(); + ~FakePythonSpeechTranscription_ParamsParser() override = default; + +public: + const std::string m_device_classname = {"FakePythonSpeechTranscription"}; + const std::string m_device_name = {"fakePythonSpeechTranscription"}; + bool m_parser_is_strict = false; + struct parser_version_type + { + int major = 1; + int minor = 0; + }; + const parser_version_type m_parser_version = {}; + + const std::string m_language_defaultValue = {"auto"}; + + std::string m_language = {"auto"}; + + bool parseParams(const yarp::os::Searchable & config) override; + std::string getDeviceClassName() const override { return m_device_classname; } + std::string getDeviceName() const override { return m_device_name; } + std::string getDocumentationOfDeviceParams() const override; + std::vector getListOfParams() const override; + + std::string getDocumentationOfFakePythonSpeechTranscription() const; +}; + +#endif diff --git a/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_params.md b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_params.md new file mode 100644 index 0000000000..342fe2cdcb --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/FakePythonSpeechTranscription_params.md @@ -0,0 +1 @@ + * | | language | string | - | auto | No | language code | | diff --git a/src/devices/fake/fakePythonSpeechTranscription/Module.py b/src/devices/fake/fakePythonSpeechTranscription/Module.py new file mode 100755 index 0000000000..4f32385295 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/Module.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# SPDX-FileCopyrightText: 2024-2024 Istituto Italiano di Tecnologia (IIT) +# SPDX-License-Identifier: BSD-3-Clause + +def open(): + print("Calling open() function") + return True + +def close(): + print("Calling close() function") + return True + +class SpeechTranscriptor: + def __init__(self, lang, verbose): + self.lang = lang + self.verbose = verbose + print ('Created a SpeechTranscriptor with language: ' , self.lang, ' verbose: ', self.verbose) + + def set_language(self, new_lang): + self.lang = new_lang + print ('Setting Language: ', self.lang) + return True + + def get_language(self): + print ('Returning Language: ', self.lang) + return self.lang + + def set_verbose(self, verbose): + self.verbose = verbose + print ('Setting verbose: ', self.verbose) + return True + + def get_verbose(self): + print ('Getting verbose: ', self.verbose) + return self.verbose diff --git a/src/devices/fake/fakePythonSpeechTranscription/tests/CMakeLists.txt b/src/devices/fake/fakePythonSpeechTranscription/tests/CMakeLists.txt new file mode 100644 index 0000000000..fe00615471 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/tests/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2023 Istituto Italiano di Tecnologia (IIT) +# SPDX-License-Identifier: BSD-3-Clause + +create_device_test(fakePythonSpeechTranscription) diff --git a/src/devices/fake/fakePythonSpeechTranscription/tests/fakePythonSpeechTranscription_test.cpp b/src/devices/fake/fakePythonSpeechTranscription/tests/fakePythonSpeechTranscription_test.cpp new file mode 100644 index 0000000000..099d71a6d6 --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/tests/fakePythonSpeechTranscription_test.cpp @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2023-2023 Istituto Italiano di Tecnologia (IIT) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace yarp::dev; +using namespace yarp::os; + +TEST_CASE("dev::fakePythonSpeechTranscription", "[yarp::dev]") +{ + YARP_REQUIRE_PLUGIN("fakePythonSpeechTranscription", "device"); + + Network::setLocalMode(true); + + SECTION("Checking fakePythonSpeechTranscription device") + { + yarp::dev::ISpeechTranscription* istr=nullptr; + PolyDriver ddfake; + + //open the device + { + yarp::os::ResourceFinder res; + res.setDefaultContext("tests/fakePythonSpeechTranscription"); + std::string filepath = res.findFileByName("Module.py"); + std::string parentPath = filepath.substr(0, filepath.find_last_of("\\/")); + + Property pdev_cfg; + pdev_cfg.put("device", "fakePythonSpeechTranscription"); + pdev_cfg.put("moduleName", "Module"); + pdev_cfg.put("modulePath", parentPath); + pdev_cfg.put("className", "SpeechTranscriptor"); + REQUIRE(ddfake.open(pdev_cfg)); + REQUIRE(ddfake.view(istr)); + } + + std::string lang = "eng"; + CHECK(istr->getLanguage(lang)); // the default value initialized by the class is auto + CHECK(lang=="auto"); + + std::string new_lang = "eng"; + CHECK(istr->setLanguage(new_lang)); + CHECK(istr->getLanguage(lang)); + CHECK(lang == "eng"); + + std::string transcript; + double score; + yarp::sig::Sound snd; + snd.resize(100, 2); + CHECK(istr->transcribe(snd,transcript, score)); + CHECK(transcript=="hello world"); + CHECK(score > 0.99); + + //"Close all polydrivers and check" + { + yarp::os::Time::delay(0.1); + CHECK(ddfake.close()); + } + } + + Network::setLocalMode(false); +} diff --git a/src/devices/fake/fakePythonSpeechTranscription/tests/test_robotinterface.xml b/src/devices/fake/fakePythonSpeechTranscription/tests/test_robotinterface.xml new file mode 100644 index 0000000000..3c442ad5bc --- /dev/null +++ b/src/devices/fake/fakePythonSpeechTranscription/tests/test_robotinterface.xml @@ -0,0 +1,17 @@ + + + + + + + + + /home/ecub_docker/yarp/src/devices/fake/fakePythonSpeechTranscription + Module + SpeechTranscriptor + + +