init
This commit is contained in:
+244
@@ -0,0 +1,244 @@
|
||||
#include "ovpCBoxAlgorithmEBMLStreamSpy.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
bool CBoxAlgorithmEBMLStreamSpy::initialize()
|
||||
{
|
||||
const Kernel::IBox& boxCtx = getStaticBoxContext();
|
||||
|
||||
m_reader = createReader(*this);
|
||||
m_helper = EBML::createReaderHelper();
|
||||
|
||||
bool expand;
|
||||
const CString fileName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
const uint64_t logLevel = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
if (boxCtx.getSettingCount() > 2)
|
||||
{
|
||||
expand = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
m_nExpandValues = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
expand = false;
|
||||
m_nExpandValues = 4;
|
||||
}
|
||||
|
||||
m_logLevel = Kernel::ELogLevel(logLevel);
|
||||
|
||||
std::ifstream file;
|
||||
FS::Files::openIFStream(file, fileName);
|
||||
while (file.good() && !file.eof())
|
||||
{
|
||||
uint32_t id1;
|
||||
uint32_t id2;
|
||||
std::string identifier1;
|
||||
std::string identifier2;
|
||||
std::string name;
|
||||
std::string type;
|
||||
|
||||
file >> name;
|
||||
file >> identifier1;
|
||||
file >> identifier2;
|
||||
file >> type;
|
||||
|
||||
sscanf(identifier1.c_str(), "EBML::CIdentifier(0x%08x", &id1);
|
||||
sscanf(identifier2.c_str(), "0x%08x)", &id2);
|
||||
|
||||
if (!expand)
|
||||
{
|
||||
if ((type == "binary(long double)") || (type == "binary(double)") || (type == "binary(float)")
|
||||
|| (type == "binary(integer8)") || (type == "binary(integer16)") || (type == "binary(integer32)")
|
||||
|| (type == "binary(integer64)") || (type == "binary(uinteger8)") || (type == "binary(uinteger16)")
|
||||
|| (type == "binary(uinteger32)") || (type == "binary(uinteger64)")) { type = "binary"; }
|
||||
}
|
||||
|
||||
// std::cout << "[" << identifier1 << "][" << identifier2 << "]" << std::endl;
|
||||
// printf("[EBML::CIdentifier(0x%08X,][0x%08X]\n", id1, id2);
|
||||
// std::cout << EBML::CIdentifier(id1, id2) << std::endl;
|
||||
|
||||
m_names[EBML::CIdentifier(id1, id2)] = name;
|
||||
m_types[EBML::CIdentifier(id1, id2)] = type;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmEBMLStreamSpy::uninitialize()
|
||||
{
|
||||
m_helper->release();
|
||||
m_helper = nullptr;
|
||||
|
||||
m_reader->release();
|
||||
m_reader = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmEBMLStreamSpy::isMasterChild(const EBML::CIdentifier& identifier)
|
||||
{
|
||||
const auto n = m_names.find(identifier);
|
||||
const auto t = m_types.find(identifier);
|
||||
if (n != m_names.end() && t != m_types.end()) { return (t->second == "master"); }
|
||||
return false;
|
||||
}
|
||||
|
||||
void CBoxAlgorithmEBMLStreamSpy::openChild(const EBML::CIdentifier& identifier)
|
||||
{
|
||||
const auto n = m_names.find(identifier);
|
||||
|
||||
getLogManager() << m_logLevel;
|
||||
|
||||
for (size_t i = 0; i <= m_nodes.size(); ++i) { getLogManager() << " "; }
|
||||
|
||||
getLogManager() << "Opened EBML node [id:" << identifier << "]-[name:" << (n != m_names.end() ? n->second : "unknown") << "]";
|
||||
|
||||
if (isMasterChild(identifier)) { getLogManager() << "\n"; }
|
||||
|
||||
m_nodes.push(identifier);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void CBoxAlgorithmEBMLStreamSpy::processBinaryBlock(const void* buffer, const size_t size)
|
||||
{
|
||||
const size_t n = (size / sizeof(T));
|
||||
const T* buf = static_cast<const T*>(buffer);
|
||||
for (size_t i = 0; i < std::min(m_nExpandValues, n); ++i) { getLogManager() << (i == 0 ? "" : " ") << buf[i]; }
|
||||
if (m_nExpandValues < n) { getLogManager() << " ..."; }
|
||||
}
|
||||
|
||||
void CBoxAlgorithmEBMLStreamSpy::processChildData(const void* buffer, const size_t size)
|
||||
{
|
||||
const auto t = m_types.find(m_nodes.top());
|
||||
|
||||
if (t != m_types.end())
|
||||
{
|
||||
if (t->second == "uinteger") { getLogManager() << "-[type:" << t->second << "]-[value:" << m_helper->getUInt(buffer, size) << "]"; }
|
||||
else if (t->second == "integer") { getLogManager() << "-[type:" << t->second << "]-[value:" << m_helper->getInt(buffer, size) << "]"; }
|
||||
else if (t->second == "float") { getLogManager() << "-[type:" << t->second << "]-[value:" << m_helper->getDouble(buffer, size) << "]"; }
|
||||
else if (t->second == "string") { getLogManager() << "-[type:" << t->second << "]-[value:" << m_helper->getStr(buffer, size) << "]"; }
|
||||
else if (t->second == "binary") { getLogManager() << "-[type:" << t->second << "]-[bytes:" << size << "]"; }
|
||||
else if (t->second == "binary(double)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<double>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(float)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<float>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(integer8)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<int8_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(integer16)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<int16_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(integer32)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<int>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(integer64)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<int64_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(uinteger8)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<uint8_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(uinteger16)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<uint16_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(uinteger32)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<uint32_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else if (t->second == "binary(uinteger64)")
|
||||
{
|
||||
getLogManager() << "-[type:" << t->second << "]-[values:";
|
||||
processBinaryBlock<uint64_t>(buffer, size);
|
||||
getLogManager() << "]";
|
||||
}
|
||||
else { getLogManager() << "-[type:unknown]-[bytes:" << size << "]"; }
|
||||
}
|
||||
getLogManager() << "\n";
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmEBMLStreamSpy::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmEBMLStreamSpy::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxCtx = getDynamicBoxContext();
|
||||
const Kernel::IBox& staticBoxCtx = getStaticBoxContext();
|
||||
|
||||
uint64_t tStart = 0;
|
||||
uint64_t tEnd = 0;
|
||||
size_t size = 0;
|
||||
const uint8_t* buffer = nullptr;
|
||||
|
||||
getLogManager() << m_logLevel << "\n";
|
||||
|
||||
for (size_t i = 0; i < staticBoxCtx.getInputCount(); ++i)
|
||||
{
|
||||
if (boxCtx.getInputChunkCount(i))
|
||||
{
|
||||
CString inputName;
|
||||
staticBoxCtx.getInputName(i, inputName);
|
||||
|
||||
CIdentifier inputType;
|
||||
staticBoxCtx.getInputType(i, inputType);
|
||||
|
||||
getLogManager() << m_logLevel << "For input " << inputName << " of type " << getTypeManager().getTypeName(inputType) << " :\n";
|
||||
|
||||
for (size_t j = 0; j < boxCtx.getInputChunkCount(i); ++j)
|
||||
{
|
||||
boxCtx.getInputChunk(i, j, tStart, tEnd, size, buffer);
|
||||
boxCtx.markInputAsDeprecated(i, j);
|
||||
|
||||
getLogManager() << m_logLevel << "For chunk [id:" << j << "] at [time:" << CIdentifier(tStart) << "," << CIdentifier(tEnd)
|
||||
<< " / " << CTime(tStart) << "," << CTime(tEnd) << "]\n";
|
||||
|
||||
m_reader->processData(buffer, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLogManager() << m_logLevel << "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <ebml/IReader.h>
|
||||
#include <ebml/IReaderHelper.h>
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
class CBoxAlgorithmEBMLStreamSpy final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>, virtual public EBML::IReaderCallback
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmEBMLStreamSpy() { }
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool isMasterChild(const EBML::CIdentifier& identifier) override;
|
||||
void openChild(const EBML::CIdentifier& identifier) override;
|
||||
void processChildData(const void* buffer, const size_t size) override;
|
||||
void closeChild() override { m_nodes.pop(); }
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EBMLStreamSpy)
|
||||
|
||||
protected:
|
||||
|
||||
template <class T>
|
||||
void processBinaryBlock(const void* buffer, size_t size);
|
||||
|
||||
std::stack<EBML::CIdentifier> m_nodes;
|
||||
std::map<EBML::CIdentifier, std::string> m_names;
|
||||
std::map<EBML::CIdentifier, std::string> m_types;
|
||||
size_t m_nExpandValues = 0;
|
||||
Kernel::ELogLevel m_logLevel = Kernel::ELogLevel::LogLevel_None;
|
||||
EBML::IReader* m_reader = nullptr;
|
||||
EBML::IReaderHelper* m_helper = nullptr;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmEBMLStreamSpyListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool check(Kernel::IBox& box) const
|
||||
{
|
||||
for (size_t i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
box.setInputName(i, ("Spied EBML stream " + std::to_string(i + 1)).c_str());
|
||||
box.setInputType(i, OV_TypeId_EBMLStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onInputRemoved(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmEBMLStreamSpyDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override { }
|
||||
CString getName() const override { return "EBML stream spy"; }
|
||||
CString getAuthorName() const override { return "Yann Renard"; }
|
||||
CString getAuthorCompanyName() const override { return "INRIA/IRISA"; }
|
||||
CString getShortDescription() const override { return "EBML stream tree viewer"; }
|
||||
|
||||
CString getDetailedDescription() const override { return "This sample EBML stream analyzer prints the EBML tree structure to the console"; }
|
||||
|
||||
CString getCategory() const override { return "Tools"; }
|
||||
CString getVersion() const override { return "1.0"; }
|
||||
CString getSoftwareComponent() const override { return "openvibe-sdk"; }
|
||||
CString getAddedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CString getUpdatedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EBMLStreamSpy; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmEBMLStreamSpy(); }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmEBMLStreamSpyListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Spied EBML stream 1", OV_TypeId_EBMLStream);
|
||||
prototype.addSetting("EBML nodes description", OV_TypeId_Filename, "${Path_Data}/plugins/tools/config-ebml-stream-spy.txt");
|
||||
prototype.addSetting("Log level to use", OV_TypeId_LogLevel, "Information");
|
||||
prototype.addSetting("Expand binary blocks", OV_TypeId_Boolean, "false");
|
||||
prototype.addSetting("Number of values in expanded blocks", OV_TypeId_Integer, "4");
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EBMLStreamSpyDesc)
|
||||
};
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+609
@@ -0,0 +1,609 @@
|
||||
#include "ovpCBoxAlgorithmExternalProcessing.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <stdlib.h>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
#include <process.h>
|
||||
#include <Windows.h>
|
||||
#include <ctime>
|
||||
#else
|
||||
#include <spawn.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
static Kernel::ELogLevel convertLogLevel(const Communication::ELogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Communication::LogLevel_Info: return Kernel::LogLevel_Info;
|
||||
case Communication::LogLevel_Warning: return Kernel::LogLevel_Warning;
|
||||
case Communication::LogLevel_Error: return Kernel::LogLevel_Error;
|
||||
case Communication::LogLevel_Fatal: return Kernel::LogLevel_Fatal;
|
||||
case Communication::LogLevel_Max:
|
||||
case Communication::LogLevel_Unknown:
|
||||
default: return Kernel::LogLevel_Info;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t CBoxAlgorithmExternalProcessing::getClockFrequency()
|
||||
{
|
||||
if (m_isGenerator)
|
||||
{
|
||||
// We slow down the generator type boxes by default, in order to limit syncing
|
||||
// In fast forward we limit the syncing even more by setting the frequency to 1Hz
|
||||
if (this->getPlayerContext().getStatus() == Kernel::EPlayerStatus::Forward) { return 1LL << 32; }
|
||||
return 16LL << 32;
|
||||
}
|
||||
return 128LL << 32;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::initialize()
|
||||
{
|
||||
m_port = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
|
||||
// Check that the port is not in the system range
|
||||
OV_ERROR_UNLESS_KRF((m_port >= 49152 && m_port <= 65535) || (m_port == 0),
|
||||
"Port [" << m_port << "] is invalid. It must be either 0 or a number in the range 49152-65535.", Kernel::ErrorType::BadConfig);
|
||||
|
||||
// Settings
|
||||
const Kernel::IBox& staticboxCtx = this->getStaticBoxContext();
|
||||
|
||||
for (size_t i = 8; i < staticboxCtx.getSettingCount(); ++i)
|
||||
{
|
||||
CString name;
|
||||
staticboxCtx.getSettingName(i, name);
|
||||
|
||||
CIdentifier type;
|
||||
staticboxCtx.getSettingType(i, type);
|
||||
|
||||
const CString value = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i);
|
||||
|
||||
OV_FATAL_UNLESS_K(m_messaging.addParameter(i, type.id(), name.toASCIIString(), value.toASCIIString()),
|
||||
"Failed to add a parameter: " << i, Kernel::ErrorType::Internal);
|
||||
}
|
||||
|
||||
// Inputs
|
||||
for (size_t i = 0; i < staticboxCtx.getInputCount(); ++i)
|
||||
{
|
||||
CIdentifier type;
|
||||
staticboxCtx.getInputType(i, type);
|
||||
|
||||
if (type == OV_TypeId_Stimulations) { m_decoders[i].initialize(*this, i); }
|
||||
|
||||
CString name;
|
||||
staticboxCtx.getInputName(i, name);
|
||||
|
||||
OV_FATAL_UNLESS_K(m_messaging.addInput(i, type.id(), name.toASCIIString()),
|
||||
"Failed to add an input: " << i, Kernel::ErrorType::Internal);
|
||||
}
|
||||
|
||||
// Outputs
|
||||
for (size_t i = 0; i < staticboxCtx.getOutputCount(); ++i)
|
||||
{
|
||||
CIdentifier type;
|
||||
staticboxCtx.getOutputType(i, type);
|
||||
|
||||
CString name;
|
||||
staticboxCtx.getOutputName(i, name);
|
||||
|
||||
if (!m_messaging.addOutput(i, type.id(), name.toASCIIString()))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Failed to add an output: " << i << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool mustGenerateConnectionID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4);
|
||||
|
||||
if (mustGenerateConnectionID) { m_connectionID = generateConnectionID(32); }
|
||||
else
|
||||
{
|
||||
const CString connectionID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5);
|
||||
m_connectionID = connectionID.toASCIIString();
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_messaging.listen(m_port),
|
||||
"Could not listen to TCP port: " << m_port << ". This port may be already used by another service. Please try another one.",
|
||||
Kernel::ErrorType::BadNetworkConnection);
|
||||
|
||||
if (m_port == 0)
|
||||
{
|
||||
m_messaging.getSocketPort(m_port);
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Box is now listening on TCP port [" << m_port << "].\n";
|
||||
}
|
||||
|
||||
m_shouldLaunchProgram = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
if (m_shouldLaunchProgram)
|
||||
{
|
||||
const CString programPath = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
const CString arguments = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
|
||||
if (!launchThirdPartyProgram(programPath.toASCIIString(), arguments.toASCIIString()))
|
||||
{
|
||||
// Error message in the function
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Third party program [" << programPath.toASCIIString() << "] started.\n";
|
||||
}
|
||||
|
||||
const auto startTime = System::Time::zgetTime();
|
||||
|
||||
bool clientConnected = false;
|
||||
m_hasReceivedEndMessage = false;
|
||||
|
||||
m_acceptTimeout = CTime(double(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 6))).time();
|
||||
m_isGenerator = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 7);
|
||||
|
||||
while (System::Time::zgetTime() - startTime < m_acceptTimeout)
|
||||
{
|
||||
if (m_messaging.accept())
|
||||
{
|
||||
clientConnected = true;
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Client connected to the server.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
const Communication::MessagingServer::ELibraryError error = m_messaging.getLastError();
|
||||
if (error == Communication::MessagingServer::ELibraryError::BadAuthenticationReceived)
|
||||
{
|
||||
OV_WARNING_K("A client sent a bad authentication.");
|
||||
break;
|
||||
}
|
||||
if (error == Communication::MessagingServer::ELibraryError::NoAuthenticationReceived) { OV_WARNING_K("The client has not sent authentication."); }
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(clientConnected, "No client connected before the timeout.", Kernel::ErrorType::Internal);
|
||||
|
||||
|
||||
// Now synchronize once with the client so it can perform its initialize before
|
||||
// the processing starts
|
||||
m_messaging.pushSync();
|
||||
|
||||
|
||||
while (m_messaging.isConnected() && !m_messaging.waitForSyncMessage())
|
||||
{
|
||||
if (!m_messaging.waitForSyncMessage()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
|
||||
}
|
||||
|
||||
m_syncTimeout = CTime(0.0625).time();
|
||||
m_lastSyncTime = System::Time::zgetTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::uninitialize()
|
||||
{
|
||||
for (auto& decoder : m_decoders) { decoder.second.uninitialize(); }
|
||||
|
||||
if (!m_hasReceivedEndMessage)
|
||||
{
|
||||
const bool result = m_messaging.close();
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
if (m_shouldLaunchProgram && m_extProcessId > 0)
|
||||
{
|
||||
DWORD exitCode;
|
||||
|
||||
// Wait for external process to stop by himself, terminate it if necessary
|
||||
const auto startTime = System::Time::zgetTime();
|
||||
while (System::Time::zgetTime() - startTime < m_acceptTimeout)
|
||||
{
|
||||
GetExitCodeProcess(HANDLE(m_extProcessId), &exitCode);
|
||||
|
||||
if (exitCode != STILL_ACTIVE) { break; }
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
|
||||
if (exitCode == STILL_ACTIVE)
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(TerminateProcess(HANDLE(m_extProcessId), EXIT_FAILURE), "Failed to kill third party program.",
|
||||
Kernel::ErrorType::Unknown);
|
||||
}
|
||||
else if (exitCode != 0) { OV_WARNING_K("Third party program [" << m_extProcessId << "] has terminated with exit code [" << int(exitCode) << "]"); }
|
||||
}
|
||||
#else
|
||||
if (m_shouldLaunchProgram && m_extProcessId != 0)
|
||||
{
|
||||
int status;
|
||||
pid_t pid = waitpid(m_extProcessId, &status, WNOHANG);
|
||||
|
||||
// Wait for external process to stop by himself, terminate it after 10s
|
||||
auto startTime = System::Time::zgetTime();
|
||||
while (pid == 0 && System::Time::zgetTime() - startTime < m_acceptTimeout)
|
||||
{
|
||||
// Check if the program has hung itself
|
||||
pid = waitpid(m_extProcessId, &status, WNOHANG);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
if (WIFEXITED(status)) { OV_WARNING_K("Third party program [" << m_extProcessId << "] has terminated with exit code [" << WEXITSTATUS(status) << "]"); }
|
||||
else if (WIFSIGNALED(status)) { OV_WARNING_K("Third party program [" << m_extProcessId << "] killed by signal [" << WTERMSIG(status) << "]"); }
|
||||
}
|
||||
else
|
||||
{
|
||||
kill(m_extProcessId, SIGTERM);
|
||||
waitpid(m_extProcessId, &status, 0);
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Third party program [" << m_extProcessId << "] exited with status [" << WEXITSTATUS(status) << "]\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::processClock(Kernel::CMessageClock& /*msg*/) { return this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess(); }
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::processInput(const size_t /*index*/)
|
||||
{
|
||||
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::process()
|
||||
{
|
||||
if (m_messaging.isInErrorState())
|
||||
{
|
||||
const std::string errorString = Communication::MessagingServer::getErrorString(m_messaging.getLastError());
|
||||
OV_ERROR_KRF("Error state connection: " << errorString << ".\n This may be due to a broken client connection.",
|
||||
Kernel::ErrorType::BadNetworkConnection);
|
||||
}
|
||||
|
||||
if (m_hasReceivedEndMessage == false && m_messaging.isEndReceived() == true)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "The third party program has ended the communication.\n";
|
||||
m_messaging.close();
|
||||
m_hasReceivedEndMessage = true;
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_messaging.pushTime(this->getPlayerContext().getCurrentTime()), "Failed to push Time.", Kernel::ErrorType::BadNetworkConnection);
|
||||
|
||||
const Kernel::IBox& staticboxCtx = this->getStaticBoxContext();
|
||||
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
|
||||
|
||||
// Send input EBML to the client
|
||||
|
||||
bool hasSentDataToClient = false;
|
||||
|
||||
for (size_t i = 0; i < staticboxCtx.getInputCount(); ++i)
|
||||
{
|
||||
auto maybeStimulationDecoder = m_decoders.find(i);
|
||||
for (size_t j = 0; j < boxCtx.getInputChunkCount(i); ++j)
|
||||
{
|
||||
if (!m_hasReceivedEndMessage)
|
||||
{
|
||||
uint64_t startTime = 0;
|
||||
uint64_t endTime = 0;
|
||||
size_t chunkSize = 0;
|
||||
const uint8_t* chunkBuffer = nullptr;
|
||||
|
||||
OV_FATAL_UNLESS_K(boxCtx.getInputChunk(i, j, startTime, endTime, chunkSize, chunkBuffer),
|
||||
"Failed to get input chunk [" << i << "][" << j << "]", Kernel::ErrorType::Internal);
|
||||
|
||||
std::shared_ptr<std::vector<uint8_t>> ebml(new std::vector<uint8_t>(chunkBuffer, chunkBuffer + chunkSize));
|
||||
|
||||
// We only encode stimulation stream chunks if they contain stimulations
|
||||
if (maybeStimulationDecoder != m_decoders.end())
|
||||
{
|
||||
maybeStimulationDecoder->second.decode(j, false); // The input will be marked as deprecated later
|
||||
|
||||
// Cache empty chunks, we will send them when a stimulation or a signal chunk arrives
|
||||
if (maybeStimulationDecoder->second.getOutputStimulationSet()->getStimulationCount() == 0)
|
||||
{
|
||||
m_packetHistory.emplace(startTime, endTime, i, ebml);
|
||||
OV_FATAL_UNLESS_K(boxCtx.markInputAsDeprecated(i, j), "Failed to mark input as deprecated", Kernel::ErrorType::Internal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Empty the history before to send useful data
|
||||
while (!m_packetHistory.empty())
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(m_messaging.pushEBML(m_packetHistory.front().index, m_packetHistory.front().startTime, m_packetHistory.front().endTime,
|
||||
m_packetHistory.front().EBML), "Failed to push EBML.", Kernel::ErrorType::BadNetworkConnection);
|
||||
|
||||
m_packetHistory.pop();
|
||||
}
|
||||
|
||||
// Push the last EBML
|
||||
OV_ERROR_UNLESS_KRF(m_messaging.pushEBML(i, startTime, endTime, ebml), "Failed to push EBML.", Kernel::ErrorType::BadNetworkConnection);
|
||||
hasSentDataToClient = true;
|
||||
}
|
||||
|
||||
OV_FATAL_UNLESS_K(boxCtx.markInputAsDeprecated(i, j), "Failed to mark input as deprecated", Kernel::ErrorType::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSentDataToClient || m_isGenerator || System::Time::zgetTime() - m_lastSyncTime > m_syncTimeout)
|
||||
{
|
||||
m_lastSyncTime = System::Time::zgetTime();
|
||||
// Here, we send a sync message to tell to the client that we have no more data to send.
|
||||
// Generators do not have input data, so the box never has to send data to the external program
|
||||
// and thus needs to perform syncing on each tick.
|
||||
|
||||
OV_ERROR_UNLESS_KRF(m_messaging.pushSync(), "Failed to push sync message.", Kernel::ErrorType::BadNetworkConnection);
|
||||
|
||||
if (!m_hasReceivedEndMessage)
|
||||
{
|
||||
this->log();
|
||||
|
||||
uint64_t packetId;
|
||||
size_t index;
|
||||
uint64_t startTime;
|
||||
uint64_t endTime;
|
||||
std::shared_ptr<const std::vector<uint8_t>> ebml;
|
||||
|
||||
bool receivedSync = false;
|
||||
while (!receivedSync && !m_hasReceivedEndMessage && !m_messaging.isInErrorState() && m_messaging.isConnected())
|
||||
{
|
||||
receivedSync = m_messaging.waitForSyncMessage();
|
||||
while (m_messaging.popEBML(packetId, index, startTime, endTime, ebml))
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(boxCtx.appendOutputChunkData(index, ebml->data(), ebml->size()),
|
||||
"Failed to append output chunk data.", Kernel::ErrorType::Internal);
|
||||
|
||||
OV_ERROR_UNLESS_KRF(boxCtx.markOutputAsReadyToSend(index, startTime, endTime),
|
||||
"Failed to mark output as ready to send.", Kernel::ErrorType::Internal);
|
||||
}
|
||||
if (!receivedSync) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CBoxAlgorithmExternalProcessing::generateConnectionID(const size_t size)
|
||||
{
|
||||
std::default_random_engine generator{ std::random_device()() };
|
||||
std::uniform_int_distribution<int> uni(0, 34);
|
||||
|
||||
std::string connectionID;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
const char c = char(uni(generator));
|
||||
connectionID.push_back((c < 26) ? ('A' + c) : '1' + (c - 26));
|
||||
}
|
||||
|
||||
return connectionID;
|
||||
}
|
||||
|
||||
static std::vector<std::string> splitCommandLine(const std::string& cmdLine)
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
std::string arg;
|
||||
bool escape = false;
|
||||
enum { Idle, Arg, QuotedArg } state = Idle;
|
||||
|
||||
for (const char c : cmdLine)
|
||||
{
|
||||
if (!escape && c == '\\')
|
||||
{
|
||||
escape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case Idle:
|
||||
if (!escape && c == '"')
|
||||
{
|
||||
state = QuotedArg;
|
||||
#if defined TARGET_OS_Windows
|
||||
arg += c;
|
||||
#endif
|
||||
}
|
||||
else if (escape || c != ' ')
|
||||
{
|
||||
arg += c;
|
||||
state = Arg;
|
||||
}
|
||||
break;
|
||||
|
||||
case Arg:
|
||||
if (!escape && c == '"')
|
||||
{
|
||||
state = QuotedArg;
|
||||
#if defined TARGET_OS_Windows
|
||||
arg += c;
|
||||
#endif
|
||||
}
|
||||
else if (escape || c != ' ') { arg += c; }
|
||||
else
|
||||
{
|
||||
list.push_back(arg);
|
||||
arg.clear();
|
||||
state = Idle;
|
||||
}
|
||||
break;
|
||||
|
||||
case QuotedArg:
|
||||
if (!escape && c == '"')
|
||||
{
|
||||
state = arg.empty() ? Idle : Arg;
|
||||
#if defined TARGET_OS_Windows
|
||||
arg += c;
|
||||
#endif
|
||||
}
|
||||
else { arg += c; }
|
||||
break;
|
||||
}
|
||||
|
||||
escape = false;
|
||||
}
|
||||
|
||||
if (!arg.empty()) { list.push_back(arg); }
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
char* strToChar(const std::string& s)
|
||||
{
|
||||
char* c = new char[s.size() + 1];
|
||||
std::copy(s.begin(), s.end(), c);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmExternalProcessing::launchThirdPartyProgram(const std::string& programPath, const std::string& arguments)
|
||||
{
|
||||
m_extProcessId = 0;
|
||||
|
||||
const std::vector<std::string> argumentsVector = splitCommandLine(arguments);
|
||||
|
||||
std::vector<std::string> programArguments = { programPath, "--connection-id", m_connectionID, "--port", std::to_string(m_port) };
|
||||
programArguments.insert(programArguments.begin() + 1, argumentsVector.cbegin(), argumentsVector.cend()); // Add the arguments after the program path.
|
||||
std::vector<char*> argv;
|
||||
|
||||
std::transform(programArguments.begin(), programArguments.end(), std::back_inserter(argv), strToChar);
|
||||
|
||||
argv.push_back(nullptr);
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
// _spawnp on Windows has a special case for empty program
|
||||
int status;
|
||||
if (programPath.empty())
|
||||
{
|
||||
status = -1;
|
||||
_set_errno(ENOENT);
|
||||
}
|
||||
else { status = int(_spawnvp(_P_NOWAIT, programPath.c_str(), argv.data())); }
|
||||
m_extProcessId = status;
|
||||
// _P_DETACH,
|
||||
#else
|
||||
posix_spawn_file_actions_t fileAction;
|
||||
int res = posix_spawn_file_actions_init(&fileAction);
|
||||
OV_ERROR_UNLESS_KRF(res==0, "File action could not be initialized. Got error [" << res << "]", Kernel::ErrorType::BadCall);
|
||||
res = posix_spawn_file_actions_addclose(&fileAction, STDOUT_FILENO);
|
||||
OV_ERROR_UNLESS_KRF(res==0, "File action 'close' could not be added. Got error [" << res << "]", Kernel::ErrorType::BadCall);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Run third party program [" << programPath << "] with arguments [" << arguments << "].\n";
|
||||
int status = posix_spawnp(&m_extProcessId, programPath.c_str(), &fileAction, nullptr, argv.data(), environ);
|
||||
|
||||
#ifdef TARGET_OS_Linux
|
||||
// On linux the glibc is bugged and posix_spawnp does not actually work as specified
|
||||
// we have to check if the program did not exit immediately with a 127 error code
|
||||
// for this we need to wait a bit because it could be that the process has not yet launched
|
||||
|
||||
// wait until the process exists or if it has failed
|
||||
|
||||
bool processExists = false;
|
||||
bool processHasFailed = false;
|
||||
while (!processExists && !processHasFailed)
|
||||
{
|
||||
System::Time::sleep(10);
|
||||
processExists = (kill(m_extProcessId, 0) == 0);
|
||||
|
||||
int childStatus;
|
||||
pid_t pid = waitpid(m_extProcessId, &childStatus, WNOHANG);
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
// The process is dead
|
||||
if (WIFEXITED(childStatus))
|
||||
{
|
||||
// If the exit status is 0 this means that maybe we will actually succeed in launching the program
|
||||
if (WEXITSTATUS(childStatus) != 0)
|
||||
{
|
||||
m_extProcessId = 0;
|
||||
status = WEXITSTATUS(childStatus);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OV_WARNING_K("The third party process died");
|
||||
m_extProcessId = 0;
|
||||
status = 1;
|
||||
}
|
||||
processHasFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < argv.size() - 1; ++i) { delete[] argv[i]; }
|
||||
|
||||
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
||||
if (status != 0)
|
||||
{
|
||||
const std::string errorMessage = "Failed to launch the program [" + programPath + "]: ";
|
||||
m_extProcessId = 0;
|
||||
OV_ERROR_KRF( errorMessage.c_str() << "[" << status << "]", Kernel::ErrorType::BadResourceCreation);
|
||||
}
|
||||
#else
|
||||
if (status == -1)
|
||||
{
|
||||
std::string errorMessage = "Failed to launch the program [" + programPath + "]:";
|
||||
|
||||
switch (errno)
|
||||
{
|
||||
case E2BIG:
|
||||
errorMessage += "Argument list exceeds 1024 bytes.\n";
|
||||
break;
|
||||
|
||||
case EINVAL:
|
||||
errorMessage += "Mode argument is invalid.\n";
|
||||
break;
|
||||
|
||||
case ENOENT:
|
||||
errorMessage += "File or path is not found.\n";
|
||||
break;
|
||||
|
||||
case ENOEXEC:
|
||||
errorMessage += "Specified file is not executable or has invalid executable-file format.\n";
|
||||
break;
|
||||
|
||||
case ENOMEM:
|
||||
errorMessage += "Not enough memory is available to execute the new process.\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
errorMessage += "Unknown error.\n";
|
||||
break;
|
||||
}
|
||||
|
||||
OV_ERROR_KRF(errorMessage.c_str(), Kernel::ErrorType::BadResourceCreation);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBoxAlgorithmExternalProcessing::log()
|
||||
{
|
||||
Communication::ELogLevel logLevel;
|
||||
std::string logMessage;
|
||||
uint64_t packetId;
|
||||
|
||||
while (m_messaging.popLog(packetId, logLevel, logMessage))
|
||||
{
|
||||
this->getLogManager() << convertLogLevel(logLevel) << "From third party program: " << logMessage << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <communication/ovCMessagingServer.h>
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
class CBoxAlgorithmExternalProcessing final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
CBoxAlgorithmExternalProcessing() : m_acceptTimeout(10ULL << 32) {}
|
||||
void release() override { delete this; }
|
||||
uint64_t getClockFrequency() override;
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processClock(Kernel::CMessageClock& msg) override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ExternalProcessing)
|
||||
|
||||
private:
|
||||
|
||||
struct SPacket
|
||||
{
|
||||
uint64_t startTime;
|
||||
uint64_t endTime;
|
||||
size_t index;
|
||||
std::shared_ptr<std::vector<uint8_t>> EBML;
|
||||
|
||||
SPacket(const uint64_t startTime, const uint64_t endTime, const size_t index, const std::shared_ptr<std::vector<uint8_t>>& ebml)
|
||||
: startTime(startTime), endTime(endTime), index(index), EBML(ebml) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief generate a connection identifier.
|
||||
*
|
||||
* \param size Size of the connection identifier.
|
||||
*
|
||||
* \return A string composed of size characters in the A-Z,0-9 range
|
||||
*/
|
||||
static std::string generateConnectionID(const size_t size);
|
||||
|
||||
/**
|
||||
* \brief Launch a third party program
|
||||
*
|
||||
* \param programPath Executable path
|
||||
* \param arguments Arguments to pass to the program
|
||||
*
|
||||
* \retval true
|
||||
* \retval false
|
||||
*/
|
||||
bool launchThirdPartyProgram(const std::string& programPath, const std::string& arguments);
|
||||
|
||||
/**
|
||||
* \brief Log in NeuroRT log from third party program
|
||||
*/
|
||||
void log();
|
||||
|
||||
Communication::MessagingServer m_messaging;
|
||||
size_t m_port = 0;
|
||||
std::string m_connectionID;
|
||||
std::string m_programPath;
|
||||
bool m_isGenerator = false;
|
||||
|
||||
int m_extProcessId = 0;
|
||||
|
||||
uint64_t m_acceptTimeout = 0;
|
||||
bool m_shouldLaunchProgram = false;
|
||||
bool m_hasReceivedEndMessage = false;
|
||||
// Synchronization timeout, and save time of last synchronization
|
||||
uint64_t m_syncTimeout = 0;
|
||||
uint64_t m_lastSyncTime = 0;
|
||||
|
||||
std::map<uint64_t, Toolkit::TStimulationDecoder<CBoxAlgorithmExternalProcessing>> m_decoders;
|
||||
std::queue<SPacket> m_packetHistory;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmExternalProcessingListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmExternalProcessingDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override {}
|
||||
CString getName() const override { return "External Processing"; }
|
||||
CString getAuthorName() const override { return "Alexis Placet"; }
|
||||
CString getAuthorCompanyName() const override { return "Mensia Technologies SA"; }
|
||||
CString getShortDescription() const override { return "This box can communicate via TCP with an other program."; }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return "Launches an external program which can then processes data. This box and the program communicate using TCP connection and a defined protocol.";
|
||||
}
|
||||
|
||||
CString getCategory() const override { return "Scripting"; }
|
||||
CString getVersion() const override { return "1.0"; }
|
||||
CString getSoftwareComponent() const override { return "openvibe-sdk"; }
|
||||
CString getAddedSoftwareVersion() const override { return "1.0.0"; }
|
||||
CString getUpdatedSoftwareVersion() const override { return "1.0.0"; }
|
||||
CString getStockItemName() const override { return "gtk-edit"; }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ExternalProcessing; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmExternalProcessing; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmExternalProcessingListener; }
|
||||
void releaseBoxListener(IBoxListener* boxListener) const override { delete boxListener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& boxAlgorithmPrototype) const override
|
||||
{
|
||||
boxAlgorithmPrototype.addSetting("Launch third party program", OV_TypeId_Boolean, "true"); // 0
|
||||
boxAlgorithmPrototype.addSetting("Executable path", OV_TypeId_Filename, ""); // 1
|
||||
boxAlgorithmPrototype.addSetting("Arguments", OV_TypeId_String, ""); // 2
|
||||
boxAlgorithmPrototype.addSetting("Port", OV_TypeId_Integer, "0"); // 3
|
||||
boxAlgorithmPrototype.addSetting("Automatic connection identifier", OV_TypeId_Boolean, "true"); // 4
|
||||
boxAlgorithmPrototype.addSetting("Custom connection identifier", OV_TypeId_String, ""); // 5
|
||||
boxAlgorithmPrototype.addSetting("Incoming connection timeout", OV_TypeId_Integer, "10"); // 6
|
||||
boxAlgorithmPrototype.addSetting("Generator", OV_TypeId_Boolean, "false"); // 7
|
||||
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanAddOutput);
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanModifyOutput);
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanAddSetting);
|
||||
boxAlgorithmPrototype.addFlag(Kernel::BoxFlag_CanModifySetting);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ExternalProcessingDesc)
|
||||
};
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
#include "ovpCBoxAlgorithmMatrixValidityChecker.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
bool CBoxAlgorithmMatrixValidityChecker::initialize()
|
||||
{
|
||||
const Kernel::IBox& boxCtx = this->getStaticBoxContext();
|
||||
|
||||
uint64_t logLevel = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_logLevel = Kernel::ELogLevel(logLevel);
|
||||
m_validityCheckerType = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
if (boxCtx.getSettingCount() == 1
|
||||
) { m_validityCheckerType = OVP_TypeId_ValidityCheckerType_LogWarning.id(); } // note that for boxes with one setting, we fallback to the old behavior
|
||||
|
||||
OV_ERROR_UNLESS_KRF(boxCtx.getSettingCount() <= 1 || boxCtx.getInputCount() == boxCtx.getOutputCount(),
|
||||
"Invalid input count [" << boxCtx.getInputCount() << "] (expected same value as output count [" << boxCtx.getOutputCount() <<
|
||||
"])",
|
||||
Kernel::ErrorType::BadConfig);
|
||||
|
||||
m_decoders.resize(boxCtx.getInputCount());
|
||||
m_encoders.resize(boxCtx.getInputCount());
|
||||
for (size_t i = 0; i < boxCtx.getInputCount(); ++i)
|
||||
{
|
||||
m_decoders[i].initialize(*this, i);
|
||||
m_encoders[i].initialize(*this, i);
|
||||
m_encoders[i].getInputMatrix().setReferenceTarget(m_decoders[i].getOutputMatrix());
|
||||
}
|
||||
|
||||
m_lastValidSamples.clear();
|
||||
m_lastValidSamples.resize(boxCtx.getInputCount());
|
||||
m_nTotalInterpolatedSample.clear();
|
||||
m_nTotalInterpolatedSample.resize(boxCtx.getInputCount());
|
||||
m_nTotalInterpolatedChunk.clear();
|
||||
m_nTotalInterpolatedChunk.resize(boxCtx.getInputCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixValidityChecker::uninitialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
m_decoders[i].uninitialize();
|
||||
m_encoders[i].uninitialize();
|
||||
}
|
||||
m_decoders.clear();
|
||||
m_encoders.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixValidityChecker::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixValidityChecker::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
const size_t nSetting = this->getStaticBoxContext().getSettingCount();
|
||||
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < boxCtx.getInputChunkCount(i); ++j)
|
||||
{
|
||||
m_decoders[i].decode(j);
|
||||
CMatrix* matrix = m_decoders[i].getOutputMatrix();
|
||||
|
||||
if (m_decoders[i].isHeaderReceived())
|
||||
{
|
||||
if (nSetting > 1) { m_encoders[i].encodeHeader(); }
|
||||
|
||||
if (m_validityCheckerType == OVP_TypeId_ValidityCheckerType_Interpolate.id())
|
||||
{
|
||||
m_nTotalInterpolatedSample[i] = 0;
|
||||
m_nTotalInterpolatedChunk[i] = 0;
|
||||
// for each channel, save of the last valid sample
|
||||
m_lastValidSamples[i].resize(matrix->getDimensionSize(0));
|
||||
}
|
||||
}
|
||||
if (m_decoders[i].isBufferReceived())
|
||||
{
|
||||
// log warning
|
||||
if (m_validityCheckerType == OVP_TypeId_ValidityCheckerType_LogWarning.id())
|
||||
{
|
||||
if (!matrix->isBufferValid())
|
||||
{
|
||||
getLogManager() << m_logLevel << "Matrix on input " << i << " either contains NAN or Infinity between " <<
|
||||
CTime(boxCtx.getInputChunkStartTime(i, j)) << " and " << CTime(boxCtx.getInputChunkEndTime(i, j)) << ".\n";
|
||||
}
|
||||
}
|
||||
// stop player
|
||||
else if (m_validityCheckerType == OVP_TypeId_ValidityCheckerType_StopPlayer.id())
|
||||
{
|
||||
if (!matrix->isBufferValid())
|
||||
{
|
||||
this->getPlayerContext().stop();
|
||||
OV_ERROR_KRF(
|
||||
"Invalid matrix content on input [" << i << "]: either contains NAN or Infinity between [" << CTime(boxCtx.
|
||||
getInputChunkStartTime(i, j)) << "] and [" << CTime(boxCtx.getInputChunkEndTime(i, j)) << "]",
|
||||
Kernel::ErrorType::BadInput);
|
||||
}
|
||||
}
|
||||
// interpolate
|
||||
else if (m_validityCheckerType == OVP_TypeId_ValidityCheckerType_Interpolate.id())
|
||||
{
|
||||
const size_t nChannel = matrix->getDimensionSize(0);
|
||||
const size_t nSample = matrix->getDimensionSize(1);
|
||||
double* buffer = matrix->getBuffer();
|
||||
size_t nInterpolatedSample = 0;
|
||||
|
||||
for (size_t k = 0; k < nChannel; ++k)
|
||||
{
|
||||
for (size_t l = 0; l < nSample; ++l)
|
||||
{
|
||||
if (std::isnan(buffer[l + k * nSample]) || std::isinf(buffer[l + k * nSample]))
|
||||
{
|
||||
// interpolation : order 0 (easiest for online interpolation)
|
||||
buffer[l + k * nSample] = m_lastValidSamples[i][k];
|
||||
nInterpolatedSample++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// save of the last valid sample of channel k
|
||||
m_lastValidSamples[i][k] = buffer[l + k * nSample];
|
||||
}
|
||||
}
|
||||
}
|
||||
m_nTotalInterpolatedSample[i] += nInterpolatedSample;
|
||||
|
||||
// log management
|
||||
if (nInterpolatedSample > 0 && m_nTotalInterpolatedSample[i] == nInterpolatedSample) // beginning of interpolation
|
||||
{
|
||||
getLogManager() << m_logLevel << "Matrix on input " << i << " either contains NAN or Infinity from " << CTime(
|
||||
boxCtx.getInputChunkStartTime(i, j)) << ": interpolation is enable.\n";
|
||||
}
|
||||
if (nInterpolatedSample > 0) // update of ChunkCount during interpolation
|
||||
{
|
||||
m_nTotalInterpolatedChunk[i]++;
|
||||
}
|
||||
if (nInterpolatedSample == 0 && m_nTotalInterpolatedSample[i] > 0) // end of interpolation
|
||||
{
|
||||
getLogManager() << m_logLevel << "Matrix on input " << i << " contained " << 100.0 * m_nTotalInterpolatedSample[i] / (
|
||||
m_nTotalInterpolatedChunk[i] * nSample * nChannel) << " % of NAN or Infinity. Interpolation disable from " << CTime(
|
||||
boxCtx.getInputChunkStartTime(i, j)) << ".\n";
|
||||
m_nTotalInterpolatedSample[i] = 0; // reset
|
||||
m_nTotalInterpolatedChunk[i] = 0;
|
||||
}
|
||||
}
|
||||
else { OV_WARNING_K("Invalid action type [" << m_validityCheckerType << "]"); }
|
||||
|
||||
if (nSetting > 1) { m_encoders[i].encodeBuffer(); }
|
||||
}
|
||||
if (m_decoders[i].isEndReceived()) { if (nSetting > 1) { m_encoders[i].encodeEnd(); } }
|
||||
if (nSetting > 1) { boxCtx.markOutputAsReadyToSend(i, boxCtx.getInputChunkStartTime(i, j), boxCtx.getInputChunkEndTime(i, j)); }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
class CBoxAlgorithmMatrixValidityChecker final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_MatrixValidityChecker)
|
||||
|
||||
protected:
|
||||
|
||||
std::vector<Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixValidityChecker>> m_decoders;
|
||||
std::vector<Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatrixValidityChecker>> m_encoders;
|
||||
Kernel::ELogLevel m_logLevel = Kernel::ELogLevel::LogLevel_None;
|
||||
uint64_t m_validityCheckerType = 0;
|
||||
|
||||
std::vector<size_t> m_nTotalInterpolatedSample;
|
||||
std::vector<size_t> m_nTotalInterpolatedChunk;
|
||||
std::vector<std::vector<double>> m_lastValidSamples;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmMatrixValidityCheckerListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool check(Kernel::IBox& box) const
|
||||
{
|
||||
for (size_t i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
box.setInputName(i, ("Stream " + std::to_string(i + 1)).c_str());
|
||||
box.setInputType(i, OV_TypeId_StreamedMatrix);
|
||||
}
|
||||
for (size_t i = 0; i < box.getOutputCount(); ++i)
|
||||
{
|
||||
box.setOutputName(i, ("Output stream " + std::to_string(i + 1)).c_str());
|
||||
box.setInputType(i, OV_TypeId_StreamedMatrix);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setInputType(index, OV_TypeId_StreamedMatrix);
|
||||
if (box.getSettingCount() > 1) { box.addOutput("", OV_TypeId_StreamedMatrix); }
|
||||
this->check(box);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onInputRemoved(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.removeOutput(index);
|
||||
this->check(box);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onOutputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setOutputType(index, OV_TypeId_StreamedMatrix);
|
||||
box.addInput("", OV_TypeId_StreamedMatrix);
|
||||
this->check(box);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onOutputRemoved(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.removeInput(index);
|
||||
this->check(box);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmMatrixValidityCheckerDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override { }
|
||||
CString getName() const override { return "Matrix validity checker"; }
|
||||
CString getAuthorName() const override { return "Yann Renard"; }
|
||||
CString getAuthorCompanyName() const override { return "INRIA/IRISA"; }
|
||||
|
||||
CString getShortDescription() const override { return "Checks if a matrix contains \"not a number\" or \"infinity\" elements"; }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return
|
||||
"This box is for debugging purposes and allows an author to check the validity of a streamed matrix and derived stream. This box can log a message, stop the player or interpolate data.";
|
||||
}
|
||||
|
||||
CString getCategory() const override { return "Tools"; }
|
||||
CString getVersion() const override { return "1.0"; }
|
||||
CString getSoftwareComponent() const override { return "openvibe-sdk"; }
|
||||
CString getAddedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CString getUpdatedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_MatrixValidityChecker; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmMatrixValidityChecker; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmMatrixValidityCheckerListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Stream 1", OV_TypeId_StreamedMatrix);
|
||||
prototype.addOutput("Output stream 1", OV_TypeId_StreamedMatrix);
|
||||
prototype.addSetting("Log level", OV_TypeId_LogLevel, "Warning");
|
||||
prototype.addSetting("Action to do", OVP_TypeId_ValidityCheckerType, OVP_TypeId_ValidityCheckerType_LogWarning.toString());
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddOutput);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_MatrixValidityCheckerDesc)
|
||||
};
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
#include "ovpCBoxAlgorithmStimulationListener.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
bool CBoxAlgorithmStimulationListener::initialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
for (size_t i = 0; i < nInput; ++i) { m_stimulationDecoders.push_back(new Toolkit::TStimulationDecoder<CBoxAlgorithmStimulationListener>(*this, i)); }
|
||||
|
||||
m_logLevel = Kernel::ELogLevel(uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmStimulationListener::uninitialize()
|
||||
{
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
m_stimulationDecoders[i]->uninitialize();
|
||||
delete m_stimulationDecoders[i];
|
||||
}
|
||||
m_stimulationDecoders.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmStimulationListener::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmStimulationListener::process()
|
||||
{
|
||||
const Kernel::IBox& staticboxCtx = this->getStaticBoxContext();
|
||||
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
|
||||
const size_t nInput = this->getStaticBoxContext().getInputCount();
|
||||
|
||||
for (size_t i = 0; i < nInput; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < boxCtx.getInputChunkCount(i); ++j)
|
||||
{
|
||||
m_stimulationDecoders[i]->decode(j);
|
||||
if (m_stimulationDecoders[i]->isHeaderReceived()) { }
|
||||
if (m_stimulationDecoders[i]->isBufferReceived())
|
||||
{
|
||||
const IStimulationSet* stimSet = m_stimulationDecoders[i]->getOutputStimulationSet();
|
||||
|
||||
CString inputName;
|
||||
staticboxCtx.getInputName(i, inputName);
|
||||
for (size_t k = 0; k < stimSet->getStimulationCount(); ++k)
|
||||
{
|
||||
this->getLogManager() << m_logLevel
|
||||
<< "For input " << i << " with name " << inputName
|
||||
<< " got stimulation " << stimSet->getStimulationIdentifier(k)
|
||||
<< "[" << getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, stimSet->getStimulationIdentifier(k)) << "]"
|
||||
<< " at date " << CTime(stimSet->getStimulationDate(k))
|
||||
<< " and duration " << CTime(stimSet->getStimulationDuration(k)) << "\n";
|
||||
|
||||
OV_WARNING_UNLESS_K(
|
||||
stimSet->getStimulationDate(k) >= boxCtx.getInputChunkStartTime(i, j)
|
||||
&& stimSet->getStimulationDate(k) <= boxCtx.getInputChunkEndTime(i, j),
|
||||
"Invalid out of range date [" << CTime(stimSet->getStimulationDate(k)) << "] (expected value between ["
|
||||
<< CTime(boxCtx.getInputChunkStartTime(i, j)) << "] and [" << CTime(boxCtx.getInputChunkEndTime(i, j)) << "])");
|
||||
}
|
||||
}
|
||||
if (m_stimulationDecoders[i]->isEndReceived()) { }
|
||||
boxCtx.markInputAsDeprecated(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
class CBoxAlgorithmStimulationListener final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_StimulationListener)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::ELogLevel m_logLevel = Kernel::LogLevel_None;
|
||||
std::vector<Toolkit::TStimulationDecoder<CBoxAlgorithmStimulationListener>*> m_stimulationDecoders;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmStimulationListenerListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool check(Kernel::IBox& box) const
|
||||
{
|
||||
for (size_t i = 0; i < box.getInputCount(); ++i)
|
||||
{
|
||||
box.setInputName(i, ("Stimulation stream " + std::to_string(i + 1)).c_str());
|
||||
box.setInputType(i, OV_TypeId_Stimulations);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onInputRemoved(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmStimulationListenerDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override { }
|
||||
CString getName() const override { return "Stimulation listener"; }
|
||||
CString getAuthorName() const override { return "Yann Renard"; }
|
||||
CString getAuthorCompanyName() const override { return "INRIA/IRISA"; }
|
||||
CString getShortDescription() const override { return "Prints stimulation codes in the log manager"; }
|
||||
|
||||
CString getDetailedDescription() const override { return "Prints each received stimulationto the log using the log level specified in the box config."; }
|
||||
|
||||
CString getCategory() const override { return "Tools"; }
|
||||
CString getVersion() const override { return "1.0"; }
|
||||
CString getSoftwareComponent() const override { return "openvibe-sdk"; }
|
||||
CString getAddedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CString getUpdatedSoftwareVersion() const override { return "0.0.0"; }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_StimulationListener; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmStimulationListener; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmStimulationListenerListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Stimulation stream 1", OV_TypeId_Stimulations);
|
||||
prototype.addSetting("Log level to use", OV_TypeId_LogLevel, "Information");
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_StimulationListenerDesc)
|
||||
};
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_EBMLStreamSpy OpenViBE::CIdentifier(0x0ED76695, 0x01A69CC3)
|
||||
#define OVP_ClassId_BoxAlgorithm_EBMLStreamSpyDesc OpenViBE::CIdentifier(0x354A6864, 0x06BC570C)
|
||||
#define OVP_ClassId_BoxAlgorithm_ExternalProcessing OpenViBE::CIdentifier(0x15422959, 0x16304449)
|
||||
#define OVP_ClassId_BoxAlgorithm_ExternalProcessingDesc OpenViBE::CIdentifier(0x63386942, 0x61D42502)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixValidityChecker OpenViBE::CIdentifier(0x60210579, 0x6F7519B6)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixValidityCheckerDesc OpenViBE::CIdentifier(0x6AFC2671, 0x1D8C493C)
|
||||
#define OVP_ClassId_BoxAlgorithm_StimulationListener OpenViBE::CIdentifier(0x65731E1D, 0x47DE5276)
|
||||
#define OVP_ClassId_BoxAlgorithm_StimulationListenerDesc OpenViBE::CIdentifier(0x0EC013FD, 0x5DD23E44)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OVP_TypeId_ValidityCheckerType OpenViBE::CIdentifier(0x32EA493A, 0x11E56D82)
|
||||
#define OVP_TypeId_ValidityCheckerType_LogWarning OpenViBE::CIdentifier(0x747A0F84, 0x1097253A)
|
||||
#define OVP_TypeId_ValidityCheckerType_StopPlayer OpenViBE::CIdentifier(0x4EC06D50, 0x5B131CE2)
|
||||
#define OVP_TypeId_ValidityCheckerType_Interpolate OpenViBE::CIdentifier(0x1DE96E02, 0x53767550)
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmEBMLStreamSpy.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmStimulationListener.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmMatrixValidityChecker.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmExternalProcessing.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// ValidityCheckerType: this type registration enables the choice between different action to do on an invalid stream
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_ValidityCheckerType, "Action to do");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_ValidityCheckerType, "Log warning", OVP_TypeId_ValidityCheckerType_LogWarning.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_ValidityCheckerType, "Stop player", OVP_TypeId_ValidityCheckerType_StopPlayer.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_ValidityCheckerType, "Interpolate", OVP_TypeId_ValidityCheckerType_Interpolate.id());
|
||||
|
||||
OVP_Declare_New(CBoxAlgorithmStimulationListenerDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmEBMLStreamSpyDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmMatrixValidityCheckerDesc);
|
||||
OVP_Declare_New(CBoxAlgorithmExternalProcessingDesc);
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Reference in New Issue
Block a user