This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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