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,4 @@
# Add all the subdirs as projects of the named branch
OV_ADD_PROJECTS("APPLICATIONS_DEVELOPER-TOOLS")
@@ -0,0 +1,36 @@
IF(OV_DISABLE_GTK)
MESSAGE(STATUS "Skipping Plugin Inspector, no GTK")
RETURN()
ENDIF(OV_DISABLE_GTK)
PROJECT(openvibe-plugin-inspector)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
INCLUDE("FindOpenViBEModuleEBML")
INCLUDE("FindOpenViBEModuleSystem")
INCLUDE("FindThirdPartyGTK")
# ----------------------
# Generate launch script
# ----------------------
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/applications/plugin-inspector)
@@ -0,0 +1,15 @@
#####################################################################################
# OpenViBE application plugin inspector configuration
#####################################################################################
# Note: The PluginInspector variables based on ${Path_Root} only make sense in dev environment and have been disabled for release
Path_Root = ..
PluginInspector_DumpGlobalDefinesDirectory = ${Path_Root}
PluginInspector_DumpAlgorithmSnapshotDirectory = ${Path_Root}/../documentation/src/algorithm-snapshots
PluginInspector_DumpAlgorithmDocTemplateDirectory = ${Path_Root}/../documentation/src/algorithm-doc
PluginInspector_DumpBoxAlgorithmSnapshotDirectory = ${Path_Root}/../documentation/src/box-algorithm-snapshots
PluginInspector_DumpBoxAlgorithmDocTemplateDirectory = ${Path_Root}/../documentation/src/box-algorithm-doc
#PluginInspector_DumpGlobalDefinesDirectory =
#PluginInspector_DumpAlgorithmSnapshotDirectory =
#PluginInspector_DumpAlgorithmDocTemplateDirectory =
#PluginInspector_DumpBoxAlgorithmSnapshotDirectory =
#PluginInspector_DumpBoxAlgorithmDocTemplateDirectory =
@@ -0,0 +1,121 @@
#include "CAlgorithmGlobalDefinesGen.hpp"
#include <iostream>
#include <functional>
namespace OpenViBE {
namespace PluginInspector {
CAlgorithmGlobalDefinesGen::CAlgorithmGlobalDefinesGen(const Kernel::IKernelContext& ctx, const std::string& filename)
: CPluginObjectDescEnum(ctx)
{
m_usedIDs.clear();
const std::string fullFilename = filename + "/ovp_global_defines.h";
getLogManager() << Kernel::LogLevel_Info << "Writing global defines to '" << fullFilename << "\n";
m_file.open(fullFilename.c_str());
OV_ERROR_UNLESS_K(m_file.good(), "Error opening '" << fullFilename << "for writing.", Kernel::ErrorType::BadFileRead,)
; // Please note the ",)" to simulate "return;"
m_file << "#pragma once\n\n// This file was automatically generated by Plugin Inspector\n\n";
}
CAlgorithmGlobalDefinesGen::~CAlgorithmGlobalDefinesGen()
{
if (!m_file.good()) { return; }
m_file << "\n";
m_file.close();
}
void CAlgorithmGlobalDefinesGen::addIdentifier(const std::string& objectName, const CIdentifier& candidate, const std::string& spaces)
{
if (m_usedIDs.find(candidate) == m_usedIDs.end())
{
m_usedIDs[candidate] = objectName;
m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << "\n";
}
else
{
OV_WARNING_K(" Object '" << objectName << "' has duplicate id " << candidate.str() << " with " << m_usedIDs[candidate]) ;
m_file << "#define " << objectName << spaces << "CIdentifier" << candidate.str() << " // Duplicate of " << m_usedIDs[candidate] << "\n";
}
}
bool CAlgorithmGlobalDefinesGen::callback(const Plugins::IPluginObjectDesc& pluginObjectDesc)
{
OV_ERROR_UNLESS_KRF(m_file.good(), "Cannot use file; Was it opened correctly ?", Kernel::ErrorType::BadProcessing);
int width = 120;
getLogManager() << Kernel::LogLevel_Info << " Dumping [" << pluginObjectDesc.getName() << "]\n";
Kernel::IAlgorithmManager& manager = m_kernelCtx.getAlgorithmManager();
const CIdentifier algorithmId = manager.createAlgorithm(pluginObjectDesc.getCreatedClass());
OV_ERROR_UNLESS_KRF(algorithmId != CIdentifier::undefined(), " Unable to create algorithm", Kernel::ErrorType::BadProcessing);
Kernel::IAlgorithmProxy& proxy = manager.getAlgorithm(algorithmId);
m_file << "// -----------------------------------------------------\n";
m_file << "// " << pluginObjectDesc.getName().toASCIIString() << "\n";
m_file << "// -----------------------------------------------------\n";
m_file << "\n";
proxy.initialize();
const auto addIdentifierFromParameters = [&width, &pluginObjectDesc, this](const char* nameStart, const char* nameEnd, const CIdentifier& id,
const std::string& toAppend)
{
std::string defName;
defName += nameStart;
defName += transform(pluginObjectDesc.getName().toASCIIString(), true);
defName += nameEnd;
if (!toAppend.empty()) { defName += transform(toAppend, true); }
std::string spaces;
spaces.resize(width - defName.length(), ' ');
addIdentifier(defName.c_str(), id, spaces.c_str());
};
addIdentifierFromParameters("OVP_GD_ClassId_Algorithm_", "", pluginObjectDesc.getCreatedClassIdentifier(), "");
addIdentifierFromParameters("OVP_GD_ClassId_Algorithm_", "Desc", pluginObjectDesc.getCreatedClassIdentifier(), "");
CIdentifier id = CIdentifier::undefined();
while ((id = proxy.getNextInputParameterIdentifier(id)) != CIdentifier::undefined())
{
addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputParameterId_", id, proxy.getInputParameterName(id).toASCIIString());
}
id = CIdentifier::undefined();
while ((id = proxy.getNextOutputParameterIdentifier(id)) != CIdentifier::undefined())
{
addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputParameterId_", id, proxy.getOutputParameterName(id).toASCIIString());
}
id = CIdentifier::undefined();
while ((id = proxy.getNextInputTriggerIdentifier(id)) != CIdentifier::undefined())
{
addIdentifierFromParameters("OVP_GD_Algorithm_", "_InputTriggerId_", id, proxy.getInputTriggerName(id).toASCIIString());
}
id = CIdentifier::undefined();
while ((id = proxy.getNextOutputTriggerIdentifier(id)) != CIdentifier::undefined())
{
addIdentifierFromParameters("OVP_GD_Algorithm_", "_OutputTriggerId_", id, proxy.getOutputTriggerName(id).toASCIIString());
}
proxy.uninitialize();
m_file << "\n";
manager.releaseAlgorithm(proxy);
return true;
}
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,27 @@
#pragma once
#include "CPluginObjectDescEnum.hpp"
#include <fstream>
#include <map>
namespace OpenViBE {
namespace PluginInspector {
class CAlgorithmGlobalDefinesGen final : public CPluginObjectDescEnum
{
public:
CAlgorithmGlobalDefinesGen(const Kernel::IKernelContext& ctx, const std::string& filename);
~CAlgorithmGlobalDefinesGen() override;
bool callback(const Plugins::IPluginObjectDesc& pluginObjectDesc) override;
protected:
std::ofstream m_file;
std::map<CIdentifier, std::string> m_usedIDs;
// Adds the define to m_usedIdentifiers and m_File
void addIdentifier(const std::string& objectName, const CIdentifier& candidate, const std::string& spaces);
};
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,296 @@
#include "CAlgorithmSnapshotGen.hpp"
#include <system/ovCTime.h>
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <algorithm>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <pango/pango.h>
#include <array>
namespace OpenViBE {
namespace PluginInspector {
// ------------------------------------------------------------------------------------------------------------------------------------
CAlgorithmSnapshotGen::CAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir)
: CPluginObjectDescEnum(ctx), m_snapshotDir(snapshotDir), m_docTemplateDir(docTemplateDir)
{
#define GDK_COLOR_SET(c, r, g, b) { c.pixel=0; c.red=r; c.green=g; c.blue=b; }
GDK_COLOR_SET(m_colors[EColors::BackgroundPlayerStarted], 32767, 32767, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundSelected], 65535, 65535, 49151);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundMissing], 49151, 32767, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundObsolete], 32767, 49151, 49151);
GDK_COLOR_SET(m_colors[EColors::BoxBackground], 65535, 65535, 65535);
GDK_COLOR_SET(m_colors[EColors::BoxBorderSelected], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::BoxBorder], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::BoxInputBackground], 65535, 49151, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxInputBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::BoxOutputBackground], 32767, 65535, 49151);
GDK_COLOR_SET(m_colors[EColors::BoxOutputBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::BoxSettingBackground], 49151, 32767, 65535);
GDK_COLOR_SET(m_colors[EColors::BoxSettingBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::Link], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::LinkSelected], 49151, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::SelectionArea], 0x3f00, 0x3f00, 0x3f00);
GDK_COLOR_SET(m_colors[EColors::SelectionAreaBorder], 0, 0, 0);
#undef GDK_COLOR_SET
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
m_widget = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(m_window), m_widget);
gtk_widget_set_size_request(m_widget, 512, 128);
gtk_widget_show_all(m_window);
gdk_flush();
System::Time::sleep(1000);
}
CAlgorithmSnapshotGen::~CAlgorithmSnapshotGen()
{
std::ofstream file;
file.open((m_docTemplateDir + "/Doc_Algorithms.dox").c_str());
file << "/**\n * \\page Doc_Algorithms Algorithms list\n\n * Available algorithms :\n";
size_t level = 0;
std::string lastCat;
std::vector<std::string> lastSplittedCat;
std::sort(m_categories.begin(), m_categories.end());
for (const auto& category : m_categories)
{
std::string cat = category.first;
std::string name = category.second;
if (lastCat != cat)
{
std::vector<std::string> splittedCat;
size_t i = size_t(-1);
bool finished = false;
while (!finished)
{
size_t j = cat.find('/', i + 1);
if (j == std::string::npos)
{
j = cat.length();
finished = true;
}
if (j != i + 1)
{
splittedCat.push_back(cat.substr(i + 1, j - i - 1));
i = j;
}
}
level = splittedCat.size();
auto it1 = lastSplittedCat.size() < splittedCat.size()
? std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin()).second
: std::mismatch(splittedCat.begin(), splittedCat.end(), lastSplittedCat.begin()).first;
for (; it1 != splittedCat.end(); ++it1) { file << " * " << std::string((it1 - splittedCat.begin()) * 3, ' ') << " - " << *it1 << " : \n"; }
lastCat = cat;
lastSplittedCat = splittedCat;
}
file << " * " << std::string(3 * (level + 1), ' ') << " - \\subpage Doc_algorithm_" << transform(cat + "/" + name) << " \"" << name << "\"\n";
}
file << " */\n";
file.close();
}
bool CAlgorithmSnapshotGen::callback(const Plugins::IPluginObjectDesc& pod)
{
const std::string fullName = std::string(pod.getCategory().toASCIIString()) + "/" + pod.getName().toASCIIString();
const std::string filename = m_snapshotDir + "/Algorithm_" + transform(fullName);
CIdentifier id;
if ((id = m_kernelCtx.getAlgorithmManager().createAlgorithm(pod.getCreatedClassIdentifier())) == CIdentifier::undefined())
{
m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Skipped [" << filename << "] (could not create corresponding algorithm)\n";
return true;
}
Kernel::IAlgorithmProxy& algorithm = m_kernelCtx.getAlgorithmManager().getAlgorithm(id);
m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Working on [" << filename << "]\n";
PangoContext* ctx = gtk_widget_get_pango_context(m_widget);
PangoLayout* layout = pango_layout_new(ctx);
PangoRectangle rectangle;
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_text(layout, pod.getName(), -1);
pango_layout_get_pixel_extents(layout, nullptr, &rectangle);
const int marginX = 5;
const int marginY = 5;
const int circleMargin = 5;
const int circleSize = 11;
const int circleSpace = 4;
const int w = rectangle.width + marginX * 2;
const int h = rectangle.height + marginY * 2;
const int x = 16;
const int y = 16;
gdk_window_invalidate_rect(m_widget->window, nullptr, true);
while (gtk_events_pending()) { gtk_main_iteration(); }
GdkGC* drawGC = gdk_gc_new(m_widget->window);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackground]);
gdk_draw_rectangle(m_widget->window, drawGC, TRUE, x, y, w, h);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBorder]);
gdk_draw_rectangle(m_widget->window, drawGC, FALSE, x, y, w, h);
size_t i = 0;
while ((id = algorithm.getNextInputParameterIdentifier(id)) != CIdentifier::undefined())
{
GdkColor color = m_colors[EColors(algorithm.getInputParameter(id)->getType())];
std::array<GdkPoint, 4> points;
points[0].x = circleSize >> 1;
points[0].y = circleSize;
points[1].x = 0;
points[1].y = 0;
points[2].x = circleSize - 1;
points[2].y = 0;
for (int j = 0; j < 3; ++j)
{
points[j].x += gint(x + circleMargin + i * (circleSpace + circleSize));
points[j].y += gint(y - (circleSize >> 1));
}
gdk_gc_set_rgb_fg_color(drawGC, &color);
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxInputBorder]);
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
i++;
}
i = 0;
while ((id = algorithm.getNextOutputParameterIdentifier(id)) != CIdentifier::undefined())
{
GdkColor color = m_colors[EColors(algorithm.getOutputParameter(id)->getType())];
std::array<GdkPoint, 4> points;
points[0].x = circleSize >> 1;
points[0].y = circleSize;
points[1].x = 0;
points[1].y = 0;
points[2].x = circleSize - 1;
points[2].y = 0;
for (int j = 0; j < 3; ++j)
{
points[j].x += gint(x + circleMargin + i * (circleSpace + circleSize));
points[j].y += gint(y - (circleSize >> 1) + h);
}
gdk_gc_set_rgb_fg_color(drawGC, &color);
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxOutputBorder]);
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
i++;
}
gdk_draw_layout(m_widget->window, m_widget->style->text_gc[GTK_WIDGET_STATE(m_widget)], x + marginX, y + marginY, layout);
g_object_unref(layout);
g_object_unref(drawGC);
GdkPixbuf* buf = gdk_pixbuf_get_from_drawable(nullptr, m_widget->window, nullptr, 0, 0, 0, 0, w + 32, h + 32);
OV_WARNING_UNLESS_K(gdk_pixbuf_save(buf, (filename+".png").c_str(), "png", nullptr, nullptr), "Failed saving " << (filename+".png"));
g_object_unref(buf);
// --------------------------------------------------------------------------------------------------------------------
#if 0
m_categories.push_back(pair < string, string >(pod.getCategory().toASCIIString(), pod.getName().toASCIIString()));
std::ofstream file;
file.open((filename+".dox").toASCIIString());
file << "/**\n"
<< " * \\page Doc_" << filename << " " << fullName.toASCIIString() << "\n"
<< " * \\section Doc_" << filename << "_summary Summary\n"
<< " * \\image html " << filename << ".png\n *\n"
<< " * - Plugin name : " << pod.getName() << "\n"
<< " * - Version : " << pod.getVersion() << "\n"
<< " * - Author : " << pod.getAuthorName() << "\n"
<< " * - Company : " << pod.getAuthorCompanyName() << "\n"
<< " * - Short description : " << pod.getShortDescription() << "\n"
<< " * - Documentation template generation date : " << __DATE__ << "\n *\n"
<< " * \\section Doc_" << filename << "_description Description\n"
<< " * " << pod.getDetailedDescription() << " TODO\n *\n";
file << " * \\section Doc_" << filename << "_inputs Inputs\n"
<< " * Number of inputs : " << box.getInputCount() << "\n *\n";
for (i = 0; i < box.getInputCount(); ++i)
{
CString name;
CIdentifier typeID;
box.getInputName(i, name);
box.getInputType(i, typeID);
file << " * - Input " << i+1 << "\n"
<< " * - Name : " << name.toASCIIString() << "\n"
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
<< " * - Description : TODO\n *\n";
}
file << " * \\section Doc_" << filename << "_outputs Outputs\n"
<< " * Number of outputs : " << box.getOutputCount() << "\n *\n";
for (i = 0; i < box.getOutputCount(); ++i)
{
CString name;
CIdentifier typeID;
box.getOutputName(i, name);
box.getOutputType(i, typeID);
file
<< " * - Output " << i+1 << "\n"
<< " * - Name : " << name.toASCIIString() << "\n"
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
<< " * - Description : TODO\n"
<< " *\n";
}
file << " * \\section Doc_" << filename << "_settings Settings\n"
<< " * Number of settings : " << box.getSettingCount() << "\n *\n";
for (i = 0; i < box.getSettingCount(); ++i)
{
CString name;
CIdentifier typeID;
CString defaultValue;
box.getSettingName(i, name);
box.getSettingType(i, typeID);
box.getSettingDefaultValue(i, defaultValue);
file
<< " * - Setting " << i+1 << "\n"
<< " * - Name : " << name.toASCIIString() << "\n"
<< " * - Type identifier : " << m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString() << " " << typeID.str() << "\n"
<< " * - Default value : " << defaultValue.toASCIIString() << "\n"
<< " * - Description : TODO\n *\n";
}
file << " * \\section Doc_" << filename << "_examples Examples\n * TODO\n *\n"
<< " * \\section Doc_" << filename << "_misc Miscellaneous\n * TODO\n */";
file.close();
#endif
m_kernelCtx.getAlgorithmManager().releaseAlgorithm(algorithm);
return true;
}
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,32 @@
#pragma once
#include "CPluginObjectDescEnum.hpp"
#include <map>
#include <vector>
#include <string>
#include <gtk/gtk.h>
namespace OpenViBE {
namespace PluginInspector {
// ------------------------------------------------------------------------------------------------------------------------------------
class CAlgorithmSnapshotGen final : public CPluginObjectDescEnum
{
public:
CAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir);
~CAlgorithmSnapshotGen() override;
bool callback(const Plugins::IPluginObjectDesc& pod) override;
protected:
std::string m_snapshotDir;
std::string m_docTemplateDir;
std::vector<std::pair<std::string, std::string>> m_categories;
std::map<EColors, GdkColor> m_colors;
GtkWidget* m_window = nullptr;
GtkWidget* m_widget = nullptr;
};
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,26 @@
#include "CBoxAlgorithmDumper.hpp"
#include <iostream>
#include <fstream>
namespace OpenViBE {
namespace PluginInspector {
// ------------------------------------------------------------------------------------------------------------------------------------
CBoxAlgorithmDumper::CBoxAlgorithmDumper(const Kernel::IKernelContext& ctx, const std::string& outFile)
: CPluginObjectDescEnum(ctx)
{
if (outFile.empty()) { m_writeToFile = false; }
if (m_writeToFile) { m_file.open(outFile.c_str()); }
}
// ------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------
bool CBoxAlgorithmDumper::callback(const Plugins::IPluginObjectDesc& pluginObjectDesc)
{
const CIdentifier boxID = pluginObjectDesc.getCreatedClassIdentifier();
(m_writeToFile ? m_file : std::cout) << "BoxAlgorithm " << transform(pluginObjectDesc.getName().toASCIIString()) << " " << boxID.str() << "\n";
return true;
}
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,31 @@
#pragma once
//
// Dumps all registered box algorithms to a text file or cout. The intended use case
// is to check the current scenarios to see if all boxes have at least one
// scenario where it is used. n.b. The checking was done by a shell script in
//
// test/check-scenario-coverage.sh
//
#include "CPluginObjectDescEnum.hpp"
#include <fstream>
namespace OpenViBE {
namespace PluginInspector {
// ------------------------------------------------------------------------------------------------------------------------------------
class CBoxAlgorithmDumper final : public CPluginObjectDescEnum
{
public:
CBoxAlgorithmDumper(const Kernel::IKernelContext& ctx, const std::string& outFile);
~CBoxAlgorithmDumper() override { if (m_writeToFile) { m_file.close(); } }
bool callback(const Plugins::IPluginObjectDesc& pluginObjectDesc) override;
protected:
std::ofstream m_file;
bool m_writeToFile = true; // if false, to console
};
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,426 @@
#include "CBoxAlgorithmSnapshotGen.hpp"
#include <system/ovCTime.h>
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <algorithm>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <pango/pango.h>
#include <array>
// ------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------
namespace OpenViBE {
namespace PluginInspector {
CBoxAlgorithmSnapshotGen::CBoxAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir)
: CPluginObjectDescEnum(ctx), m_snapshotDir(snapshotDir), m_docTemplateDir(docTemplateDir)
{
m_kernelCtx.getScenarioManager().createScenario(m_scenarioID);
m_scenario = &m_kernelCtx.getScenarioManager().getScenario(m_scenarioID);
#define GDK_COLOR_SET(c, r, g, b) { c.pixel=0; c.red=r; c.green=g; c.blue=b; }
GDK_COLOR_SET(m_colors[EColors::BackgroundPlayerStarted], 32767, 32767, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundSelected], 65535, 65535, 49151);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundMissing], 49151, 32767, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxBackgroundDeprecated], 16383, 24575, 24575);
GDK_COLOR_SET(m_colors[EColors::BoxBackground], 65535, 65535, 65535);
GDK_COLOR_SET(m_colors[EColors::BoxBorderSelected], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::BoxBorder], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::BoxInputBackground], 65535, 49151, 32767);
GDK_COLOR_SET(m_colors[EColors::BoxInputBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::BoxOutputBackground], 32767, 65535, 49151);
GDK_COLOR_SET(m_colors[EColors::BoxOutputBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::BoxSettingBackground], 49151, 32767, 65535);
GDK_COLOR_SET(m_colors[EColors::BoxSettingBorder], 16383, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::Link], 0, 0, 0);
GDK_COLOR_SET(m_colors[EColors::LinkSelected], 49151, 16383, 16383);
GDK_COLOR_SET(m_colors[EColors::SelectionArea], 0x3f00, 0x3f00, 0x3f00);
GDK_COLOR_SET(m_colors[EColors::SelectionAreaBorder], 0, 0, 0);
#undef GDK_COLOR_SET
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
m_widget = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(m_window), m_widget);
gtk_widget_set_size_request(m_widget, 512, 128);
gtk_widget_show_all(m_window);
gdk_flush();
System::Time::sleep(1000);
}
CBoxAlgorithmSnapshotGen::~CBoxAlgorithmSnapshotGen()
{
m_kernelCtx.getScenarioManager().releaseScenario(m_scenarioID);
std::ofstream file;
file.open((m_docTemplateDir + "/Doc_BoxAlgorithms.dox").c_str());
file << "/**\n * \\page Doc_BoxAlgorithms Box algorithms list\n *\n * Available box algorithms :\n";
size_t level = 0;
std::string lastCat;
std::vector<std::string> lastSplittedCat;
std::sort(m_categories.begin(), m_categories.end());
for (const auto& category : m_categories)
{
std::string cat = category.first;
std::string name = category.second;
if (lastCat != cat)
{
std::vector<std::string> splittedCat;
size_t i = size_t(-1);
bool finished = false;
while (!finished)
{
size_t j = cat.find('/', i + 1);
if (j == std::string::npos)
{
j = cat.length();
finished = true;
}
if (j != i + 1)
{
splittedCat.push_back(cat.substr(i + 1, j - i - 1));
i = j;
}
}
level = splittedCat.size();
// @FIXME C++14 : auto it1 = std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin(), splittedCat.end()).second;
auto it1 = lastSplittedCat.size() < splittedCat.size()
? std::mismatch(lastSplittedCat.begin(), lastSplittedCat.end(), splittedCat.begin()).second
: std::mismatch(splittedCat.begin(), splittedCat.end(), lastSplittedCat.begin()).first;
for (; it1 != splittedCat.end(); ++it1) { file << " * " << std::string((it1 - splittedCat.begin()) * 3, ' ') << " - " << *it1 << " : \n"; }
lastCat = cat;
lastSplittedCat = splittedCat;
}
file << " * " << std::string(3 * level, ' ') << " - \\subpage Doc_BoxAlgorithm_" << transform(name) << " \"" << name << "\"\n";
}
file << " */\n";
file.close();
}
bool CBoxAlgorithmSnapshotGen::callback(const Plugins::IPluginObjectDesc& pod)
{
//string fullName = string(rPluginObjectDesc.getCategory().toASCIIString()) + "/" + string(rPluginObjectDesc.getName().toASCIIString());
std::string filename = "BoxAlgorithm_" + transform(pod.getName().toASCIIString());
CIdentifier boxID;
if (!m_scenario->addBox(boxID, pod.getCreatedClassIdentifier(), CIdentifier::undefined()))
{
OV_WARNING_K("Skipped [" << filename << "] (could not create corresponding box)");
return true;
}
Kernel::IBox& box = *m_scenario->getBoxDetails(boxID);
getLogManager() << Kernel::LogLevel_Trace << "Working on [" << filename << "]\n";
PangoContext* ctx = gtk_widget_get_pango_context(m_widget);
PangoLayout* layout = pango_layout_new(ctx);
PangoRectangle rectangle;
pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
pango_layout_set_text(layout, pod.getName(), -1);
pango_layout_get_pixel_extents(layout, nullptr, &rectangle);
const int marginX = 5;
const int marginY = 5;
const int circleSize = 11;
const int circleSpace = 4;
int w = rectangle.width + marginX * 2;
int h = rectangle.height + marginY * 2;
int x = 16;
int y = 16;
gdk_window_invalidate_rect(m_widget->window, nullptr, true);
while (gtk_events_pending()) { gtk_main_iteration(); }
GdkGC* drawGC = gdk_gc_new(m_widget->window);
bool deprecated = m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsDeprecated(box.getAlgorithmClassIdentifier());
if (deprecated) { gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackgroundDeprecated]); }
else { gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBackground]); }
gdkDrawRoundedRectangle(m_widget->window, drawGC, TRUE, x, y, w, h);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxBorder]);
gdkDrawRoundedRectangle(m_widget->window, drawGC, FALSE, x, y, w, h);
int offset = int(w - box.getInputCount() * (circleSpace + circleSize) + circleSize / 2) / 2;
for (size_t i = 0; i < box.getInputCount(); ++i)
{
CIdentifier id;
box.getInputType(i, id);
GdkColor color = colorFromIdentifier(id);
std::array<GdkPoint, 4> points;
points[0].x = circleSize >> 1;
points[0].y = circleSize;
points[1].x = 0;
points[1].y = 0;
points[2].x = circleSize - 1;
points[2].y = 0;
for (size_t j = 0; j < 3; ++j)
{
points[j].x += gint(x + i * (circleSpace + circleSize) + offset);
points[j].y += gint(y - (circleSize >> 1));
}
gdk_gc_set_rgb_fg_color(drawGC, &color);
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxInputBorder]);
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
}
offset = int(w - box.getOutputCount() * (circleSpace + circleSize) + circleSize / 2) / 2;
for (size_t i = 0; i < box.getOutputCount(); ++i)
{
CIdentifier id;
box.getOutputType(i, id);
GdkColor color = colorFromIdentifier(id);
std::array<GdkPoint, 4> points;
points[0].x = circleSize >> 1;
points[0].y = circleSize;
points[1].x = 0;
points[1].y = 0;
points[2].x = circleSize - 1;
points[2].y = 0;
for (int j = 0; j < 3; ++j)
{
points[j].x += gint(x + i * (circleSpace + circleSize) + offset);
points[j].y += gint(y - (circleSize >> 1) + h);
}
gdk_gc_set_rgb_fg_color(drawGC, &color);
gdk_draw_polygon(m_widget->window, drawGC, TRUE, points.data(), 3);
gdk_gc_set_rgb_fg_color(drawGC, &m_colors[EColors::BoxOutputBorder]);
gdk_draw_polygon(m_widget->window, drawGC, FALSE, points.data(), 3);
}
gdk_draw_layout(m_widget->window, m_widget->style->text_gc[GTK_WIDGET_STATE(m_widget)], x + marginX, y + marginY, layout);
g_object_unref(layout);
g_object_unref(drawGC);
GdkPixbuf* buf = gdk_pixbuf_get_from_drawable(nullptr, m_widget->window, nullptr, 0, 0, 0, 0, w + 32, h + 32);
gdk_pixbuf_save(buf, (m_snapshotDir + "/Doc_" + filename + ".png").c_str(), "png", nullptr, nullptr);
g_object_unref(buf);
// --------------------------------------------------------------------------------------------------------------------
m_categories.push_back(std::pair<std::string, std::string>(pod.getCategory().toASCIIString(), pod.getName().toASCIIString()));
std::ofstream fileSkeleton;
std::ofstream filePart;
fileSkeleton.open((m_docTemplateDir + "/Doc_" + filename + ".dox-skeleton").c_str());
filePart.open((m_docTemplateDir + "/Doc_" + filename + ".dox-part-skeleton").c_str());
fileSkeleton << "/**\n"
<< " * \\page Doc_" << filename << " " << pod.getName() << "\n"
<< " * \\section Doc_" << filename << "_Summary Summary\n"
<< " * \\image html Doc_" << filename << ".png\n"
<< " *\n"
<< " * - Plugin name : " << pod.getName() << "\n"
<< " * - Version : " << pod.getVersion() << "\n"
<< " * - Author : " << pod.getAuthorName() << "\n"
<< " * - Company : " << pod.getAuthorCompanyName() << "\n"
<< " * - Short description : " << pod.getShortDescription() << "\n"
<< " * - Documentation template generation date : " << __DATE__ << "\n";
bool deprectated = m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsDeprecated(pod.getCreatedClassIdentifier());
if (deprectated)
{
fileSkeleton << " * - <b>WARNING : this box has been marked as DEPRECATED by the developer.</b>\n"
<< " * It will be removed soon or later, so you should consider not using this\n"
<< " * box and turn to another \"equivalent\" one.\n";
}
// @FIXME CERT
#pragma message("WARNING: 'Unstable' block commented out due to Certivibe")
/*
bool isUnstable=m_kernelCtx.getPluginManager().isPluginObjectFlaggedAsUnstable(rPluginObjectDesc.getCreatedClassIdentifier());
if(isUnstable)
{
fileSkeleton
<< " * - <b>WARNING : this box has been marked as UNSTABLE by the developer.\n"
<< " * It means that its implementation may be incomplete or that the box can only work\n"
<< " * under well known conditions. It may possibly crash or cause data loss.\n"
<< " * Use this box at your own risk, you've been warned.</b>\n";
}
*/
fileSkeleton << " *\n"
<< " * \\section Doc_" << filename << "_Description Description\n"
<< " * " << pod.getDetailedDescription() << "\n *\n"
<< " * @Doc_" << filename << "_Description_Content@\n *\n";
filePart << "/**\n"
<< " * \\page " << filename << " " << pod.getName() << "\n"
<< "__________________________________________________________________\n\n"
<< "Detailed description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Description|\n"
<< " * |OVP_DocEnd_" << filename << "_Description|\n";
if (box.getInputCount())
{
fileSkeleton << " * \\section Doc_" << filename << "_Inputs Inputs\n"
// << " * Number of inputs : " << box.getInputCount() << "\n"
<< " *\n"
<< " * @Doc_" << filename << "_Inputs_Content@\n";
filePart << "__________________________________________________________________\n\n"
<< "Inputs description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Inputs|\n"
<< " * |OVP_DocEnd_" << filename << "_Inputs|\n";
for (size_t i = 0; i < box.getInputCount(); ++i)
{
CString name;
CIdentifier typeID;
CString typeName;
box.getInputName(i, name);
box.getInputType(i, typeID);
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
fileSkeleton << " * \\subsection Doc_" << filename << "_Input_" << i + 1 << " " << i + 1 << ". " << name
<< "\n * @Doc_" << filename << "_Input" << i + 1 << "_Content@\n *\n * - Type identifier : \\ref Doc_Streams_"
<< transform(typeName.toASCIIString()) << " \"" << typeName << "\" <em>" << typeID.str() << "</em>\n *\n";
filePart << "\n * |OVP_DocBegin_" << filename << "_Input" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Input" << i + 1 <<
"|\n";
}
}
if (box.getOutputCount())
{
fileSkeleton << " * \\section Doc_" << filename << "_Outputs Outputs\n"
// << " * Number of outputs : " << box.getOutputCount() << "\n"
<< " *\n * @Doc_" << filename << "_Outputs_Content@\n";
filePart << "__________________________________________________________________\n\n"
<< "Outputs description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Outputs|\n * |OVP_DocEnd_" << filename << "_Outputs|\n";
for (size_t i = 0; i < box.getOutputCount(); ++i)
{
CString name;
CIdentifier typeID;
CString typeName;
box.getOutputName(i, name);
box.getOutputType(i, typeID);
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
fileSkeleton
<< " * \\subsection Doc_" << filename << "_Output_" << i + 1 << " " << i + 1 << ". " << name << "\n"
<< " * @Doc_" << filename << "_Output" << i + 1 << "_Content@\n *\n * - Type identifier : \\ref Doc_Streams_"
<< transform(typeName.toASCIIString()) << " \"" << typeName << "\" <em>" << typeID.str() << "</em>\n *\n";
filePart << "\n * |OVP_DocBegin_" << filename << "_Output" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Output" << i + 1 <<
"|\n";
}
}
if (box.getSettingCount())
{
fileSkeleton
<< " * \\section Doc_" << filename << "_Settings Settings\n"
// << " * Number of settings : " << box.getSettingCount() << "\n"
<< " *\n * @Doc_" << filename << "_Settings_Content@\n";
filePart
<< "__________________________________________________________________\n\n"
<< "Settings description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Settings|\n * |OVP_DocEnd_" << filename << "_Settings|\n";
for (size_t i = 0; i < box.getSettingCount(); ++i)
{
CString name;
CIdentifier typeID;
CString defaultValue;
CString typeName;
box.getSettingName(i, name);
box.getSettingType(i, typeID);
box.getSettingDefaultValue(i, defaultValue);
typeName = m_kernelCtx.getTypeManager().getTypeName(typeID);
fileSkeleton
<< " * \\subsection Doc_" << filename << "_Setting_" << i + 1 << " " << i + 1 << ". " << name << "\n"
<< " * @Doc_" << filename << "_Setting" << i + 1 << "_Content@\n"
<< " *\n"
// "\\ref Doc_Types_" << transform(typeName.toASCIIString())
<< " * - Type identifier : <em> " << typeName << " " << typeID.str() << "</em>\n"
<< " *\n"
<< " * - Default value : [ <em>" << defaultValue << "</em> ]\n"
<< " *\n";
filePart << "\n * |OVP_DocBegin_" << filename << "_Setting" << i + 1 << "|\n * |OVP_DocEnd_" << filename << "_Setting" << i + 1 << "|\n";
}
}
// @FIXME CERT
#pragma message("WARNING: PluginFunctionality_Visualization block commented out due to Certivibe")
/*
if(rPluginObjectDesc.hasFunctionality(PluginFunctionality_Visualization))
{
fileSkeleton
<< " * \\section Doc_" << filename << "_OnlineVisualizationSettings Online visualization settings\n"
<< " *\n"
<< " * @Doc_" << filename << "_OnlineVisualizationSettings_Content@\n";
filePart
<< "__________________________________________________________________\n"
<< "\n"
<< "Online visualization settings\n"
<< "__________________________________________________________________\n"
<< "\n"
<< " * |OVP_DocBegin_" << filename << "_OnlineVisualizationSettings|\n"
<< " * |OVP_DocEnd_" << filename << "_OnlineVisualizationSettings|\n";
}
*/
fileSkeleton
<< " *\n"
<< " * \\section Doc_" << filename << "_Examples Examples\n"
<< " * @Doc_" << filename << "_Examples_Content@\n"
<< " *\n"
<< " * \\section Doc_" << filename << "_Miscellaneous Miscellaneous\n"
<< " * @Doc_" << filename << "_Miscellaneous_Content@\n"
<< " */\n";
filePart
<< "__________________________________________________________________\n\n"
<< "Examples description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Examples|\n"
<< " * |OVP_DocEnd_" << filename << "_Examples|\n"
<< "__________________________________________________________________\n\n"
<< "Miscellaneous description\n"
<< "__________________________________________________________________\n\n"
<< " * |OVP_DocBegin_" << filename << "_Miscellaneous|\n"
<< " * |OVP_DocEnd_" << filename << "_Miscellaneous|\n"
<< " */\n";
fileSkeleton.close();
filePart.close();
return true;
}
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,34 @@
#pragma once
#include "CPluginObjectDescEnum.hpp"
#include <map>
#include <vector>
#include <string>
#include <gtk/gtk.h>
namespace OpenViBE {
namespace PluginInspector {
// ------------------------------------------------------------------------------------------------------------------------------------
class CBoxAlgorithmSnapshotGen final : public CPluginObjectDescEnum
{
public:
CBoxAlgorithmSnapshotGen(const Kernel::IKernelContext& ctx, const std::string& snapshotDir, const std::string& docTemplateDir);
~CBoxAlgorithmSnapshotGen() override;
bool callback(const Plugins::IPluginObjectDesc& pod) override;
protected:
std::string m_snapshotDir;
std::string m_docTemplateDir;
std::vector<std::pair<std::string, std::string>> m_categories;
std::map<EColors, GdkColor> m_colors;
GtkWidget* m_window = nullptr;
GtkWidget* m_widget = nullptr;
CIdentifier m_scenarioID = CIdentifier::undefined();
Kernel::IScenario* m_scenario = nullptr;
};
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,50 @@
#include "CPluginObjectDescEnum.hpp"
#include <cctype>
namespace OpenViBE {
namespace PluginInspector {
bool CPluginObjectDescEnum::enumeratePluginObjectDesc()
{
CIdentifier id;
while ((id = m_kernelCtx.getPluginManager().getNextPluginObjectDescIdentifier(id)) != CIdentifier::undefined())
{
this->callback(*m_kernelCtx.getPluginManager().getPluginObjectDesc(id));
}
return true;
}
bool CPluginObjectDescEnum::enumeratePluginObjectDesc(const CIdentifier& parentClassID)
{
CIdentifier id;
while ((id = m_kernelCtx.getPluginManager().getNextPluginObjectDescIdentifier(id, parentClassID)) !=
CIdentifier::undefined()) { this->callback(*m_kernelCtx.getPluginManager().getPluginObjectDesc(id)); }
return true;
}
std::string CPluginObjectDescEnum::transform(const std::string& in, const bool removeSlash)
{
std::string tmp(in);
std::string out;
bool lastWasSeparator = true;
for (std::string::size_type i = 0; i < tmp.length(); ++i)
{
if (std::isalpha(tmp[i]) || std::isdigit(tmp[i]) || (!removeSlash && tmp[i] == '/'))
{
if (tmp[i] == '/') { out += "_"; }
else { out += lastWasSeparator ? std::toupper(tmp[i]) : tmp[i]; }
lastWasSeparator = false;
}
else
{
//if(!lastWasSeparator) { dst+="_"; }
lastWasSeparator = true;
}
}
return out;
}
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,74 @@
#pragma once
#include "base.hpp"
#include <string>
namespace OpenViBE {
namespace PluginInspector {
class CPluginObjectDescEnum
{
public:
explicit CPluginObjectDescEnum(const Kernel::IKernelContext& ctx): m_kernelCtx(ctx) { }
virtual ~CPluginObjectDescEnum() { }
virtual bool enumeratePluginObjectDesc();
virtual bool enumeratePluginObjectDesc(const CIdentifier& parentClassID);
virtual bool callback(const Plugins::IPluginObjectDesc& pod) = 0;
static std::string transform(const std::string& in, const bool removeSlash = false);
protected:
enum class EColors
{
BackgroundPlayerStarted,
BoxBackground, BoxBackgroundSelected, BoxBackgroundMissing, BoxBackgroundObsolete, BoxBackgroundDeprecated,
BoxBorder, BoxBorderSelected,
BoxInputBackground, BoxInputBorder,
BoxOutputBackground, BoxOutputBorder,
BoxSettingBackground, BoxSettingBorder,
Link, LinkSelected,
SelectionArea, SelectionAreaBorder
};
virtual Kernel::ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
virtual Kernel::IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
const Kernel::IKernelContext& m_kernelCtx;
static GdkColor colorFromIdentifier(const CIdentifier& identifier)
{
GdkColor color;
const uint64_t res = identifier.id();
color.pixel = guint16(0);
color.red = guint16(((res) & 0xffff) | 0x8000);
color.green = guint16(((res >> 16) & 0xffff) | 0x8000);
color.blue = guint16(((res >> 32) & 0xffff) | 0x8000);
return color;
}
static void gdkDrawRoundedRectangle(GdkDrawable* drawable, GdkGC* gc, const gboolean fill, const gint x, const gint y, const gint w, const gint h,
const gint r = 8)
{
if (fill)
{
gdk_draw_rectangle(drawable, gc, TRUE, x + r, y, w - 2 * r, h);
gdk_draw_rectangle(drawable, gc, TRUE, x, y + r, w, h - 2 * r);
}
else
{
gdk_draw_line(drawable, gc, x + r, y, x + w - r, y);
gdk_draw_line(drawable, gc, x + r, y + h, x + w - r, y + h);
gdk_draw_line(drawable, gc, x, y + r, x, y + h - r);
gdk_draw_line(drawable, gc, x + w, y + r, x + w, y + h - r);
}
gdk_draw_arc(drawable, gc, fill, x + w - r * 2, y, r * 2, r * 2, 0 * 64, 90 * 64);
gdk_draw_arc(drawable, gc, fill, x, y, r * 2, r * 2, 90 * 64, 90 * 64);
gdk_draw_arc(drawable, gc, fill, x, y + h - r * 2, r * 2, r * 2, 180 * 64, 90 * 64);
gdk_draw_arc(drawable, gc, fill, x + w - r * 2, y + h - r * 2, r * 2, r * 2, 270 * 64, 90 * 64);
}
};
} // namespace PluginInspector
} // namespace OpenViBE
@@ -0,0 +1,7 @@
#pragma once
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
@@ -0,0 +1,263 @@
#include "CAlgorithmGlobalDefinesGen.hpp"
#include "CBoxAlgorithmDumper.hpp"
#include "CBoxAlgorithmSnapshotGen.hpp"
#include "CAlgorithmSnapshotGen.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <clocale> // std::setlocale
typedef struct SConfiguration
{
SConfiguration() { }
bool kernelPathOverload = false;
bool loadPluginsFromPaths = false;
bool dumpPathOverload = false;
bool configPathOverload = false;
bool listBoxes = false;
std::string kernelPath;
std::string dumpPath;
std::string configPath;
std::string listBoxesFile;
std::vector<std::string> pluginPaths;
} configuration_t;
bool parse_arguments(int argc, char** argv, configuration_t& config)
{
std::vector<std::string> argValues;
for (int i = 1; i < argc; ++i) { argValues.push_back(argv[i]); }
for (auto it = argValues.begin(); it != argValues.end(); ++it)
{
if (*it == "-h" || *it == "--help")
{
std::cout << "Usage: " << argv[0] << " [-p <dir1#dir2...>] [-d <dump_path>] [-l boxListFile]" << std::endl;
exit(0);
}
// get a list of folders to load plugins from
if (*it == "-p")
{
if (it + 1 != argValues.end())
{
std::string pluginDirectories = *++it;
// split the argument to separate paths, the delimiter is "#"
std::stringstream ss(pluginDirectories);
std::string item;
while (std::getline(ss, item, '#')) { config.pluginPaths.push_back(item); }
config.loadPluginsFromPaths = true;
}
else { return false; }
}
// Configuration path
else if (*it == "-c")
{
if (it + 1 != argValues.end())
{
config.configPathOverload = true;
config.configPath = *++it;
}
else { return false; }
}
// List path
else if (*it == "-l")
{
config.listBoxes = true;
if (it + 1 != argValues.end()) { config.listBoxesFile = *++it; }
else { config.listBoxesFile = ""; } // to cout
}
// Kernel path
else if (*it == "-k")
{
if (it + 1 != argValues.end())
{
config.kernelPathOverload = true;
config.kernelPath = *++it;
}
else { return false; }
}
// Dump path
else if (*it == "-d")
{
if (it + 1 != argValues.end())
{
config.dumpPathOverload = true;
config.dumpPath = *++it;
}
else { return false; }
}
}
return true;
}
int main(int argc, char** argv)
{
//___________________________________________________________________//
// //
configuration_t config;
if (!parse_arguments(argc, argv, config)) { std::cout << "Error parsing arguments" << std::endl; }
OpenViBE::CKernelLoader loader;
std::cout << "[ INF ] Created kernel loader, trying to load kernel module" << std::endl;
#if defined TARGET_OS_Windows
std::string kernelFile = "/openvibe-kernel.dll";
#else
std::string kernelFile = "/libopenvibe-kernel.so";
#endif
if (config.kernelPathOverload) { kernelFile = config.kernelPath + kernelFile; }
else { kernelFile = std::string(OpenViBE::Directories::getLibDir().toASCIIString()) + kernelFile; }
OpenViBE::CString error;
if (!loader.load(OpenViBE::CString(kernelFile.c_str()), &error))
{
std::cout << "[ FAILED ] Error loading kernel (" << error << ")" << " from [" << kernelFile << "]\n";
}
else
{
std::cout << "[ INF ] Kernel module loaded, trying to get kernel descriptor" << std::endl;
OpenViBE::Kernel::IKernelDesc* kernelDesc = nullptr;
OpenViBE::Kernel::IKernelContext* kernelCtx = nullptr;
loader.initialize();
loader.getKernelDesc(kernelDesc);
if (!kernelDesc) { std::cout << "[ FAILED ] No kernel descriptor" << std::endl; }
else
{
std::cout << "[ INF ] Got kernel descriptor, trying to create kernel" << std::endl;
std::string configPath = std::string(OpenViBE::Directories::getDataDir().toASCIIString()) + "/kernel/openvibe.conf";
if (config.configPathOverload) { configPath = config.configPath; }
kernelCtx = kernelDesc->createKernel("plugin-inspector", OpenViBE::CString(configPath.c_str()));
if (!kernelCtx || !kernelCtx->initialize()) { std::cout << "[ FAILED ] No kernel created by kernel descriptor" << std::endl; }
else
{
kernelCtx->getConfigurationManager().addConfigurationFromFile(OpenViBE::CString(configPath.c_str()));
kernelCtx->getConfigurationManager().addConfigurationFromFile(
OpenViBE::Directories::getDataDir() + "/applications/plugin-inspector/plugin-inspector.conf");
OpenViBE::Toolkit::initialize(*kernelCtx);
OpenViBE::Kernel::IConfigurationManager& configManager = kernelCtx->getConfigurationManager();
if (config.loadPluginsFromPaths)
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading plugins from specified folders\n";
std::string pluginPattern;
for (const auto& libpath : config.pluginPaths)
{
// Choose the right pattern for libraries to load depending on the OS
#if defined TARGET_OS_Windows
pluginPattern = libpath + "/openvibe-plugins-*.dll;";
#elif defined TARGET_OS_Linux
pluginPattern = libpath + "/libopenvibe-plugins-*.so;"; // + *it + "/lib???.so"
#endif
kernelCtx->getPluginManager().addPluginsFromFiles(pluginPattern.c_str());
}
}
else
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading plugins as specified by kernel\n";
kernelCtx->getPluginManager().addPluginsFromFiles(configManager.expand("${Kernel_Plugins}"));
}
//initialise Gtk before 3D context
gtk_init(&argc, &argv);
// gtk_rc_parse(Directories::getDataDir() + "/applications/designer/interface.gtkrc");
// We rely on this with 64bit/gtk 2.24, to roll back gtk_init() sometimes switching
// the locale to one where ',' is needed instead of '.' for separators of floats,
// causing issues, for example getConfigurationManager.expandAsFloat("0.05") -> 0;
// due to implementation by std::stod().
std::setlocale(LC_ALL, "C");
if (configManager.expandAsBoolean("${Kernel_3DVisualizationEnabled}"))
{
// kernelCtx->getVisualizationManager().initialize3DContext();
}
std::string globalDefinesDir = "";
std::string algorithmSnapshotDir = "";
std::string algorithmDocTemplateDir = "";
std::string boxAlgorithmSnapshotDir = "";
std::string boxAlgorithmDocTemplateDir = "";
if (config.dumpPathOverload)
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping stuff to [" << config.dumpPath << "]\n";
algorithmSnapshotDir = config.dumpPath + "/algorithm-snapshots";
algorithmDocTemplateDir = config.dumpPath + "/algorithm-doc";
boxAlgorithmSnapshotDir = config.dumpPath + "/box-algorithm-snapshots";
boxAlgorithmDocTemplateDir = config.dumpPath + "/box-algorithm-doc";
globalDefinesDir = config.dumpPath;
}
else
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Loading paths from Kernel configuration\n";
algorithmSnapshotDir = configManager.expand("${PluginInspector_DumpAlgorithmSnapshotDirectory}");
algorithmDocTemplateDir = configManager.expand("${PluginInspector_DumpAlgorithmDocTemplateDirectory}");
boxAlgorithmSnapshotDir = configManager.expand("${PluginInspector_DumpBoxAlgorithmSnapshotDirectory}");
boxAlgorithmDocTemplateDir = configManager.expand("${PluginInspector_DumpBoxAlgorithmDocTemplateDirectory}");
globalDefinesDir = configManager.expand("${PluginInspector_DumpGlobalDefinesDirectory}");
}
if (config.listBoxes)
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping box identifiers ...\n";
OpenViBE::PluginInspector::CBoxAlgorithmDumper boxIDGenerator(*kernelCtx, config.listBoxesFile);
boxIDGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_BoxAlgorithmDesc);
}
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping global defines...\n";
if (!globalDefinesDir.empty())
{
OpenViBE::PluginInspector::CAlgorithmGlobalDefinesGen globalDefinesGenerator(*kernelCtx, globalDefinesDir);
globalDefinesGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_AlgorithmDesc);
}
else
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
}
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping algorithm snapshots... to [" << algorithmSnapshotDir << "]\n";
if (!algorithmSnapshotDir.empty() && !algorithmDocTemplateDir.empty())
{
OpenViBE::PluginInspector::CAlgorithmSnapshotGen algorithmSnapshotGenerator(*kernelCtx, algorithmSnapshotDir, algorithmDocTemplateDir);
algorithmSnapshotGenerator.enumeratePluginObjectDesc(OV_ClassId_Plugins_AlgorithmDesc);
}
else
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
}
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Dumping box algorithm snapshots...\n";
if (!boxAlgorithmSnapshotDir.empty() && !boxAlgorithmDocTemplateDir.empty())
{
OpenViBE::PluginInspector::CBoxAlgorithmSnapshotGen generator(*kernelCtx, boxAlgorithmSnapshotDir, boxAlgorithmDocTemplateDir);
generator.enumeratePluginObjectDesc(OV_ClassId_Plugins_BoxAlgorithmDesc);
}
else
{
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Skipped, related PluginInspector tokens are empty in openvibe.conf\n";
}
kernelCtx->getLogManager() << OpenViBE::Kernel::LogLevel_Info << "Application terminated, releasing allocated objects \n";
OpenViBE::Toolkit::uninitialize(*kernelCtx);
kernelDesc->releaseKernel(kernelCtx);
}
}
loader.uninitialize();
loader.unload();
}
return 0;
}
@@ -0,0 +1,40 @@
IF(OV_DISABLE_GTK)
MESSAGE(STATUS "Skipping Skeleton Generator, no GTK")
RETURN()
ENDIF(OV_DISABLE_GTK)
PROJECT(openvibe-skeleton-generator)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
# ---------------------------------
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
INCLUDE("FindOpenViBEModuleEBML")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyGTK")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyBoost_Thread")
INCLUDE("FindThirdPartyBoost_Regex")
# ----------------------
# Generate launch script
# ----------------------
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/applications/skeleton-generator)
@@ -0,0 +1,147 @@
#include "ovpCBoxAlgorithm@@ClassName@@.h"
using namespace OpenViBE;
using namespace /*OpenViBE::*/Kernel;
using namespace /*OpenViBE::*/Plugins;
using namespace /*OpenViBE::Plugins::*/@@Namespace@@;
bool CBoxAlgorithm@@ClassName@@::initialize()
{
@@AlgorithmInitialisation@@
// If you need to retrieve setting values, use the FSettingValueAutoCast function.
// For example :
// - CString setting at index 0 in the setting list :
// CString sSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
// - unsigned int64 setting at index 1 in the setting list :
// uint64_t uiSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
// - double setting at index 2 in the setting list :
// double doubleSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
// ...
return true;
}
/*******************************************************************************/
bool CBoxAlgorithm@@ClassName@@::uninitialize()
{
@@AlgorithmUninitialisation@@
return true;
}
/*******************************************************************************/
@@ProcessClockCommentIn@@
bool CBoxAlgorithm@@ClassName@@::processClock(Kernel::CMessageClock& msg)
{
// some pre-processing code if needed...
// ready to process !
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
@@ProcessClockCommentOut@@
/*******************************************************************************/
@@ProcessClockCommentIn@@
uint64_t CBoxAlgorithm@@ClassName@@::getClockFrequency()
{
// Note that the time is coded on a 64 bits unsigned integer, fixed decimal point (32:32)
return @@ClockFrequency@@; // the box clock frequency
}
@@ProcessClockCommentOut@@
/*******************************************************************************/
@@ProcessInputCommentIn@@
bool CBoxAlgorithm@@ClassName@@::processInput(const size_t index)
{
// some pre-processing code if needed...
// ready to process !
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
@@ProcessInputCommentOut@@
/*******************************************************************************/
bool CBoxAlgorithm@@ClassName@@::process()
{
// the static box context describes the box inputs, outputs, settings structures
const IBox& staticBoxContext=this->getStaticBoxContext();
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
IBoxIO& boxContext = this->getDynamicBoxContext();
// here is some useful functions:
// - To get input/output/setting count:
// staticBoxContext.getInputCount();
// staticBoxContext.getOutputCount();
// - To get the number of chunks currently available on a particular input :
// boxContext.getInputChunkCount(input_index)
// - To send an output chunk :
// boxContext.markOutputAsReadyToSend(output_index, chunk_start_time, chunk_end_time);
// A typical process iteration may look like this.
// This example only iterate over the first input of type Signal, and output a modified Signal.
// thus, the box uses 1 decoder (m_signalDecoder) and 1 encoder (m_signalEncoder)
/*
IBoxIO& boxContext = this->getDynamicBoxContext();
//iterate over all chunk on input 0
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
// decode the chunk i
m_signalDecoder.decode(i);
// the decoder may have decoded 3 different parts : the header, a buffer or the end of stream.
if(m_signalDecoder.isHeaderReceived())
{
// Header received. This happens only once when pressing "play". For example with a StreamedMatrix input, you now know the dimension count, sizes, and labels of the matrix
// ... maybe do some process ...
// Pass the header to the next boxes, by encoding a header on the output 0:
m_signalEncoder.encodeHeader(0);
// send the output chunk containing the header. The dates are the same as the input chunk:
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
}
if(m_signalDecoder.isBufferReceived())
{
// Buffer received. For example the signal values
// Access to the buffer can be done thanks to :
CMatrix* matrix = m_signalDecoder.getOutputMatrix(); // the StreamedMatrix of samples.
uint64_t frequency = m_signalDecoder.getOutputSamplingRate(); // the sampling rate of the signal
// ... do some process on the matrix ...
// Encode the output buffer :
m_signalEncoder.encodeBuffer(0);
// and send it to the next boxes :
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
}
if(m_signalDecoder.isEndReceived())
{
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
m_signalEncoder.encodeEnd(0);
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
}
// The current input chunk has been processed, and automaticcaly discarded.
// you don't need to call "boxContext.markInputAsDeprecated(0, i);"
}
*/
// check the official developer documentation webpage for more example and information :
// Tutorials:
// http://openvibe.inria.fr/documentation/#Developer+Documentation
// Codec Toolkit page :
// http://openvibe.inria.fr/codec-toolkit-references/
// Feel free to ask experienced developers on the forum (http://openvibe.inria.fr/forum) and IRC (#openvibe on irc.freenode.net).
return true;
}
@@ -0,0 +1,148 @@
#include "ovpCBoxAlgorithm@@ClassName@@.h"
using namespace OpenViBE;
using namespace /*OpenViBE::*/Kernel;
using namespace /*OpenViBE::*/Plugins;
using namespace /*OpenViBE::Plugins::*/@@Namespace@@;
bool CBoxAlgorithm@@ClassName@@::initialize()
{
@@AlgorithmInitialisation@@
// If you need to retrieve setting values, use the FSettingValueAutoCast function.
// For example :
// - CString setting at index 0 in the setting list :
// CString sSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
// - unsigned int64 setting at index 1 in the setting list :
// uint64_t uiSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
// - double setting at index 2 in the setting list :
// double doubleSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
// ...
return true;
}
/*******************************************************************************/
bool CBoxAlgorithm@@ClassName@@::uninitialize()
{
@@AlgorithmUninitialisation@@
return true;
}
/*******************************************************************************/
@@ProcessClockCommentIn@@
bool CBoxAlgorithm@@ClassName@@::processClock(Kernel::CMessageClock& msg)
{
// some pre-processing code if needed...
// ready to process !
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
/*******************************************************************************/
@@ProcessClockCommentIn@@
uint64_t CBoxAlgorithm@@ClassName@@::getClockFrequency()
{
// Note that the time is coded on a 64 bits unsigned integer, fixed decimal point (32:32)
return @@ClockFrequency@@; // the box clock frequency
}
/*******************************************************************************/
@@ProcessInputCommentIn@@
bool CBoxAlgorithm@@ClassName@@::processInput(const size_t /*index*/)
{
// some pre-processing code if needed...
// ready to process !
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
/*******************************************************************************/
bool CBoxAlgorithm@@ClassName@@::process()
{
// the static box context describes the box inputs, outputs, settings structures
const IBox& staticBoxCtx = this->getStaticBoxContext();
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
IBoxIO& boxCtx = this->getDynamicBoxContext();
// here is some useful functions:
// - To get input/output/setting count:
// staticBoxCtx.getInputCount();
// staticBoxCtx.getOutputCount();
// staticBoxCtx.getSettingCount();
// - To get the chunks currently available on a particular input :
// boxCtx.getInputChunkCount(input_index)
// boxCtx.getInputChunk(input_index, chunk_index)
// - To get the chunk available on a particular output :
// boxCtx.getOutputChunk(output_index)
// - To send an output chunk :
// boxCtx.markOutputAsReadyToSend(output_index, chunk_start_time, chunk_end_time);
// A typical process iteration may look like this.
// This example only iterate over the first input.
/*
IBoxIO& boxCtx = this->getDynamicBoxContext();
//iterate over all chunk on input 0
for (size_t i = 0; i < boxCtx.getInputChunkCount(0); ++i)
{
// fill the decoder input with input chunk
ip_bufferToDecode = boxCtx.getInputChunk(0, i);
// link the encoder output to box output chunk
op_buffer = boxCtx.getOutputChunk(0);
// decode the input chunk
m_decoder->process();
// the decoder may have decoded 3 different parts : the header, a buffer or the end of stream.
// 3 corresponding output triggers may rise.
// the decoded block is now in the decoded output (let's say op_pDecodedMatrix)
if(m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_SignalDecoder_OutputTriggerId_ReceivedHeader))
{
// Header received. This happens only once when pressing "play". For example with a StreamedMatrix input, you now know the dimension count size, and label of the matrix
// ... do some process ...
// Pass the header to the next boxes, by encoding it :
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeHeader);
// send the output chunk containing the header :
boxCtx.markOutputAsReadyToSend(0, boxCtx.getInputChunkStartTime(0, i), boxCtx.getInputChunkEndTime(0, i));
}
if(m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_SignalDecoder_OutputTriggerId_ReceivedBuffer))
{
// Buffer received. For example the matrix values
// Access to the matrix buffer can be done thanks to :
// op_pDecodedMatrix->getBuffer(), which is a one-dimension vector of value.
// ... do some process ...
// Encode the output buffer :
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeBuffer);
// and send it to the next boxes :
boxCtx.markOutputAsReadyToSend(0, boxCtx.getInputChunkStartTime(0, i), boxCtx.getInputChunkEndTime(0, i));
}
if(m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_SignalDecoder_OutputTriggerId_ReceivedEnd))
{
// End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeEnd);
boxCtx.markOutputAsReadyToSend(0, boxCtx.getInputChunkStartTime(0, i), boxCtx.getInputChunkEndTime(0, i));
}
// The current input chunk has been processed, let's discard it :
boxCtx.markInputAsDeprecated(0, i);
}
*/
// check the official developer documentation webpage for more example and information :
// http://openvibe.inria.fr/documentation/#Developer+Documentation
// Feel free to ask experienced developers on the forum (http://openvibe.inria.fr/forum) and IRC (#openvibe on irc.freenode.net).
return true;
}
@@ -0,0 +1,149 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithm@@ClassName@@.h
/// \brief Classes of the Box @@BoxName@@.
/// \author @@Author@@ (@@Company@@).
/// \version @@Version@@.
/// \date @@Date@@.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
//You may have to change this path to match your folder organisation
#include "ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
// The unique identifiers for the box and its descriptor.
// Identifier are randomly chosen by the skeleton-generator.
#define OVP_ClassId_BoxAlgorithm_@@ClassName@@ OpenViBE::CIdentifier@@RandomIdentifierClass@@
#define OVP_ClassId_BoxAlgorithm_@@ClassName@@Desc OpenViBE::CIdentifier@@RandomIdentifierDescriptor@@
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
namespace OpenViBE
{
namespace Plugins
{
namespace @@Namespace@@
{
/// <summary> The class CBoxAlgorithm@@ClassName@@ describes the box @@BoxName@@. </summary>
class CBoxAlgorithm@@ClassName@@ final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
//Here is the different process callbacks possible
// - On clock ticks :
@@ProcessClockComment@@bool processClock(Kernel::CMessageClock& msg) override;
// - On new input received (the most common behaviour for signal processing) :
@@ProcessInputComment@@bool processInput(const size_t index) override;
// If you want to use processClock, you must provide the clock frequency.
@@ProcessClockComment@@uint64_t getClockFrequency() override;
bool process() override;
// As we do with any class in openvibe, we use the macro below to associate this box to an unique identifier.
// The inheritance information is also made available, as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_@@ClassName@@)
protected:
@@Algorithms@@
};
// If you need to implement a box Listener, here is a sekeleton for you.
// Use only the callbacks you need.
// For example, if your box has a variable number of input, but all of them must be stimulation inputs.
// The following listener callback will ensure that any newly added input is stimulations :
/*
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
box.setInputType(index, OV_TypeId_Stimulations);
};
*/
@@BoxListenerCommentIn@@
// The box listener can be used to call specific callbacks whenever the box structure changes : input added, name changed, etc.
// Please uncomment below the callbacks you want to use.
/// <summary> Listener of the box @@BoxName@@. </summary>
class CBoxAlgorithm@@ClassName@@Listener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
//bool onInitialized(Kernel::IBox& box) override { return true; };
//bool onNameChanged(Kernel::IBox& box) override { return true; };
@@BoxListenerOnInputConnectedComment@@bool onInputConnected(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnInputDisconnectedComment@@bool onInputDisconnected(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnInputAddedComment@@bool onInputAdded(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnInputRemovedComment@@bool onInputRemoved(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnInputTypeChangedComment@@bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnInputNameChangedComment@@bool onInputNameChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnOutputConnectedComment@@bool onOutputConnected(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnOutputDisconnectedComment@@bool onOutputDisconnected(Kernel::IBox& box, const size_t index) { return true; };
@@BoxListenerOnOutputAddedComment@@bool onOutputAdded(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnOutputRemovedComment@@bool onOutputRemoved(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnOutputTypeChangedComment@@bool onOutputTypeChanged(Kernel::IBox& box, const size_t index) override override { return true; };
@@BoxListenerOnOutputNameChangedComment@@bool onOutputNameChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingAddedComment@@bool onSettingAdded(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingRemovedComment@@bool onSettingRemoved(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingTypeChangedComment@@bool onSettingTypeChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingNameChangedComment@@bool onSettingNameChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingDefaultValueChangedComment@@bool onSettingDefaultValueChanged(Kernel::IBox& box, const size_t index) override { return true; };
@@BoxListenerOnSettingValueChangedComment@@bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override { return true; };
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
@@BoxListenerCommentOut@@
/// <summary> Descriptor of the box @@BoxName@@. </summary>
class CBoxAlgorithm@@ClassName@@Desc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("@@BoxName@@"); }
CString getAuthorName() const override { return CString("@@Author@@"); }
CString getAuthorCompanyName() const override { return CString("@@Company@@"); }
CString getShortDescription() const override { return CString("@@ShortDescription@@"); }
CString getDetailedDescription() const override { return CString("@@DetailedDescription@@"); }
CString getCategory() const override { return CString("@@Category@@"); }
CString getVersion() const override { return CString("@@Version@@"); }
CString getStockItemName() const override { return CString("@@StockItemName@@"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_@@ClassName@@; }
IPluginObject* create() override { return new CBoxAlgorithm@@ClassName@@; }
@@BoxListenerCommentIn@@
IBoxListener* createBoxListener() const override { return new CBoxAlgorithm@@ClassName@@Listener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
@@BoxListenerCommentOut@@
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
@@Inputs@@
@@InputFlagCanModify@@
@@InputFlagCanAdd@@
@@Outputs@@
@@OutputFlagCanModify@@
@@OutputFlagCanAdd@@
@@Settings@@
@@SettingFlagCanModify@@
@@SettingFlagCanAdd@@
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_@@ClassName@@Desc)
};
} // namespace @@Namespace@@
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,58 @@
#include "ovasCConfiguration@@ClassName@@.h"
using namespace OpenViBE;
using namespace /*OpenViBE::*/Kernel;
using namespace /*OpenViBE::*/AcquisitionServer;
/*_________________________________________________
Insert callback to specific widget here
Example with a button that launch a calibration of the device:
//Callback connected to a dedicated gtk button:
static void button_calibrate_pressed_cb(GtkButton* button, void* data)
{
CConfiguration@@ClassName@@* config = static_cast<CConfiguration@@ClassName@@*>(data);
config->buttonCalibratePressedCB();
}
//Callback actually called:
void CConfigurationGTecGUSBamp::buttonCalibratePressedCB()
{
// Connect to the hardware, ask for calibration, verify the return code, etc.
}
_________________________________________________*/
// If you added more reference attribute, initialize them here
CConfiguration@@ClassName@@::CConfiguration@@ClassName@@(IDriverContext& ctx, const char* gtkBuilderFilename)
: CConfigurationBuilder(gtkBuilderFilename), m_driverCtx(ctx) { }
bool CConfiguration@@ClassName@@::preConfigure()
{
if(! CConfigurationBuilder::preConfigure()) { return false; }
// Connect here all callbacks
// Example:
// g_signal_connect(gtk_builder_get_object(m_builder, "button_calibrate"), "pressed", G_CALLBACK(button_calibrate_pressed_cb), this);
// Insert here the pre-configure code.
// For example, you may want to check if a device is currently connected
// and if more than one are connected. Then you can list in a dedicated combo-box
// the device currently connected so the user can choose which one he wants to acquire from.
return true;
}
bool CConfiguration@@ClassName@@::postConfigure()
{
if(m_applyConfig)
{
// If the user pressed the "apply" button, you need to save the changes made in the configuration.
// For example, you can save the connection ID of the selected device:
// m_connectionID = <value-from-gtk-widget>
}
if(!CConfigurationBuilder::postConfigure()) { return false; } // normal header is filled (Subject ID, Age, Gender, channels, sampling frequency), ressources are realesed
return true;
}
@@ -0,0 +1,50 @@
#pragma once
#include "../ovasCConfigurationBuilder.h"
#include "ovasIDriver.h"
#include <gtk/gtk.h>
namespace OpenViBE
{
namespace AcquisitionServer
{
/**
* \class CConfiguration@@ClassName@@
* \author @@AuthorName@@ (@@CompanyName@@)
* \date @@Date@@
* \brief The CConfiguration@@ClassName@@ handles the configuration dialog specific to the @@DriverName@@ device.
*
* TODO: details
*
* \sa CDriver@@ClassName@@
*/
class CConfiguration@@ClassName@@ : public CConfigurationBuilder
{
public:
// you may have to add to your constructor some reference parameters
// for example, a connection ID:
//CConfiguration@@ClassName@@(IDriverContext& ctx, const char* gtkBuilderFilename, size_t& rConnectionId);
CConfiguration@@ClassName@@(IDriverContext& ctx, const char* gtkBuilderFilename);
virtual bool preConfigure();
virtual bool postConfigure();
protected:
IDriverContext& m_driverCtx;
private:
/*
* Insert here all specific attributes, such as a connection ID.
* use references to directly modify the corresponding attribute of the driver
* Example:
*/
// size_t& m_connectionID;
};
} //namespace AcquisitionServer
} //namespace OpenViBE
@@ -0,0 +1,149 @@
#include "ovasCDriver@@ClassName@@.h"
#include "ovasCConfiguration@@ClassName@@.h"
#include <toolkit/ovtk_all.h>
using namespace OpenViBE;
using namespace /*OpenViBE::*/AcquisitionServer;
using namespace /*OpenViBE::*/Kernel;
//___________________________________________________________________//
// //
CDriver@@ClassName@@::CDriver@@ClassName@@(IDriverContext& ctx)
:IDriver(ctx),m_settings("AcquisitionServer_Driver_@@ClassName@@", m_driverCtx.getConfigurationManager())
{
m_header.setSampling(@@Sampling@@);
m_header.setChannelCount(@@MaxChannel@@);
// The following class allows saving and loading driver settings from the acquisition server .conf file
m_settings.add("Header", &m_header);
// To save your custom driver settings, register each variable to the SettingsHelper
//m_settings.add("SettingName", &variable);
m_settings.load();
}
CDriver@@ClassName@@::~CDriver@@ClassName@@() { }
const char* CDriver@@ClassName@@::getName() { return "@@DriverName@@"; }
//___________________________________________________________________//
// //
bool CDriver@@ClassName@@::initialize(const size_t nSamplePerSentBlock, IDriverCallback& callback)
{
if(m_driverCtx.isConnected()) { return false; }
if(!m_header.isChannelCountSet()||!m_header.isSamplingSet()) { return false; }
// Builds up a buffer to store
// acquired samples. This buffer
// will be sent to the acquisition
// server later...
m_sample=new float[m_header.getChannelCount()*nSamplePerSentBlock];
if(!m_sample)
{
delete [] m_sample;
m_sample = nullptr;
return false;
}
// ...
// initialize hardware and get
// available header information
// from it
// Using for example the connection ID provided by the configuration (m_connectionID)
// ...
// Saves parameters
m_callback = &callback;
m_nSamplePerSentBlock = nSamplePerSentBlock;
return true;
}
bool CDriver@@ClassName@@::start()
{
if(!m_driverCtx.isConnected()) { return false; }
if(m_driverCtx.isStarted()) { return false; }
// ...
// request hardware to start
// sending data
// ...
return true;
}
bool CDriver@@ClassName@@::loop()
{
if(!m_driverCtx.isConnected()) { return false; }
if(!m_driverCtx.isStarted()) { return true; }
CStimulationSet stimSet;
// ...
// receive samples from hardware
// put them the correct way in the sample array
// whether the buffer is full, send it to the acquisition server
//...
m_callback->setSamples(m_sample);
// When your sample buffer is fully loaded,
// it is advised to ask the acquisition server
// to correct any drift in the acquisition automatically.
m_driverCtx.correctDriftSampleCount(m_driverCtx.getSuggestedDriftCorrectionSampleCount());
// ...
// receive events from hardware
// and put them the correct way in a CStimulationSet object
//...
m_callback->setStimulationSet(stimSet);
return true;
}
bool CDriver@@ClassName@@::stop()
{
if(!m_driverCtx.isConnected()) return false;
if(!m_driverCtx.isStarted()) return false;
// ...
// request the hardware to stop
// sending data
// ...
return true;
}
bool CDriver@@ClassName@@::uninitialize()
{
if(!m_driverCtx.isConnected()) return false;
if(m_driverCtx.isStarted()) return false;
// ...
// uninitialize hardware here
// ...
delete [] m_sample;
m_sample = nullptr;
m_callback = nullptr;
return true;
}
//___________________________________________________________________//
// //
bool CDriver@@ClassName@@::isConfigurable()
{
return true; // change to false if your device is not configurable
}
bool CDriver@@ClassName@@::configure()
{
// Change this line if you need to specify some references to your driver attribute that need configuration, e.g. the connection ID.
CConfiguration@@ClassName@@ config(m_driverCtx, Directories::getDataDir() + "/applications/acquisition-server/interface-@@ClassName@@.ui");
if(!config.configure(m_header)) { return false; }
m_settings.save();
return true;
}
@@ -0,0 +1,68 @@
#pragma once
#include "ovasIDriver.h"
#include "../ovasCHeader.h"
#include <openvibe/ov_all.h>
#include "../ovasCSettingsHelper.h"
#include "../ovasCSettingsHelperOperators.h"
namespace OpenViBE
{
namespace AcquisitionServer
{
/**
* \class CDriver@@ClassName@@
* \author @@AuthorName@@ (@@CompanyName@@)
* \date @@Date@@
* \brief The CDriver@@ClassName@@ allows the acquisition server to acquire data from a @@DriverName@@ device.
*
* TODO: details
*
* \sa CConfiguration@@ClassName@@
*/
class CDriver@@ClassName@@ : public IDriver
{
public:
CDriver@@ClassName@@(IDriverContext& ctx);
virtual ~CDriver@@ClassName@@();
virtual const char* getName();
virtual bool initialize(const size_t nSamplePerSentBlock, IDriverCallback& callback);
virtual bool uninitialize();
virtual bool start();
virtual bool stop();
virtual bool loop();
virtual bool isConfigurable();
virtual bool configure();
virtual const IHeader* getHeader() { return &m_header; }
virtual bool isFlagSet(const EDriverFlag flag) const { return flag==EDriverFlag::IsUnstable; }
protected:
SettingsHelper m_settings;
IDriverCallback* m_callback = nullptr;
// Replace this generic Header with any specific header you might have written
CHeader m_header;
size_t m_nSamplePerSentBlock = 0;
float* m_sample = nullptr;
private:
/*
* Insert here all specific attributes, such as USB port number or device ID.
* Example :
*/
// size_t m_connectionID = 0;
};
} //namespace AcquisitionServer
} //namespace OpenViBE
@@ -0,0 +1,323 @@
<?xml version="1.0"?>
<interface>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">100</property>
<property name="lower">0</property>
<property name="page_increment">10</property>
<property name="step_increment">1</property>
<property name="page_size">0</property>
<property name="value">18</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="upper">100</property>
<property name="lower">0</property>
<property name="page_increment">10</property>
<property name="step_increment">1</property>
<property name="page_size">0</property>
<property name="value">0</property>
</object>
<object class="GtkAdjustment" id="adjustment3">
<property name="upper">@@MaxChannel@@</property>
<property name="lower">@@MinChannel@@</property>
<property name="page_increment">10</property>
<property name="step_increment">1</property>
<property name="page_size">0</property>
<property name="value">@@MaxChannel@@</property>
</object>
<object class="GtkListStore" id="model1">
<columns>
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">unspecified</col>
</row>
<row>
<col id="0" translatable="yes">female</col>
</row>
<row>
<col id="0" translatable="yes">male</col>
</row>
<row>
<col id="0" translatable="yes">unknown</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model2">
<columns>
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">@@SamplingFrequencyList@@</col>
</row>
</data>
</object>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
<property name="border_width">5</property>
<property name="title" translatable="yes">Device configuration</property>
<property name="window_position">center</property>
<property name="type_hint">dialog</property>
<property name="gravity">center</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="spacing">8</property>
<child>
<object class="GtkLabel" id="label_title">
<property name="visible">True</property>
<property name="label" translatable="yes">@@DriverName@@</property>
<property name="justify">center</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox">
<property name="visible">True</property>
<property name="border_width">8</property>
<property name="spacing">8</property>
<child>
<object class="GtkHBox" id="hbox">
<property name="visible">True</property>
<property name="spacing">8</property>
<child>
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="n_rows">5</property>
<property name="n_columns">2</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkComboBox" id="combobox_gender">
<property name="visible">True</property>
<property name="model">model1</property>
<child>
<object class="GtkCellRendererText" id="renderer1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_age">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment1</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_identifier">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment2</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_sampling_frequency">
<property name="visible">True</property>
<property name="label" translatable="yes">Sampling frequency :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_number_of_channels">
<property name="visible">True</property>
<property name="label" translatable="yes">Number of channels :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_gender">
<property name="visible">True</property>
<property name="label" translatable="yes">Gender :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_age">
<property name="visible">True</property>
<property name="label" translatable="yes">Age :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label_identifier">
<property name="visible">True</property>
<property name="label" translatable="yes">Identifier :</property>
<property name="justify">right</property>
<property name="single_line_mode">True</property>
</object>
</child>
<child>
<object class="GtkComboBox" id="combobox_sampling_frequency">
<property name="visible">True</property>
<property name="active">0</property>
<property name="model">model2</property>
<child>
<object class="GtkCellRendererText" id="renderer2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="spinbutton_number_of_channels">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment3</property>
<property name="snap_to_ticks">True</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_change_channel_names">
<property name="label" translatable="yes">Change channel names</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">4</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button_apply">
<property name="label">gtk-apply</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-10">button_apply</action-widget>
<action-widget response="-6">button_cancel</action-widget>
</action-widgets>
</object>
</interface>
@@ -0,0 +1,51 @@
******************************************************
* Thanks for using the OpenViBE Skeleton Generator ! *
******************************************************
File generation completed
[@@Date@@]
-------------------------
The generator produced the following files:
The Box class:
- ovpCBoxAlgorithm@@ClassName@@.h
- ovpCBoxAlgorithm@@ClassName@@.cpp
Please put these files in your local repository, in the project of your choice (e.g. Signal Processing or Classification)
[openvibe-repository]/contrib/plugins/[plugin-project]/src/[my-box-folder]
You may have to change the file ovpCBoxAlgorithm@@ClassName@@.h to make it find the included file ovp_defines.h, whose path is related to the project used.
Don't forget to declare your box in ovp_main.cpp, in order to make it available in the Designer.
Look in one of the ovp_main.cpp file, you will find examples of such declarations (#include the header, then OVP_Declare_New macro).
This look something like this:
#include "[my-box-folder]/ovpCBoxAlgorithm@@ClassName@@.h"
...
OVP_Declare_Begin();
...
OVP_Declare_New(OpenViBE::Plugins::@@Namespace@@::CBoxAlgorithm@@ClassName@@Desc);
...
OVP_Declare_End()
If your box doesn't appear in the designer, maybe it's because you cannot see Unstable Boxes.
Try to set your configuration file (openvibe.conf on windows / .openviberc on Linux) with the following token :
>>>>>
Designer_ShowUnstable = true
>>>>>
For more information about implementing algorithms and boxes to fill your skeleton, please read the official tutorial:
http://openvibe.inria.fr/tutorial-1-implementing-a-signal-processing-box/
Feel free to propose your contribution on the forum !
http://openvibe.inria.fr/forum/
Enjoy OpenViBE !
- The development team -
@@ -0,0 +1,42 @@
******************************************************
* Thanks for using the OpenViBE Skeleton Generator ! *
******************************************************
File generation completed
[@@Date@@]
-------------------------
WARNING:
Writing a new driver for an EEG device is often dependent on hardware specifications, such as OS compatibility
or communication protocol. The generator gives you only the basic skeletons for easy integration with OpenViBE,
please refer to your device specifications (API, protocol description, etc.) for the next steps...
The generator produced the following files:
The Configuration and Driver classes:
- ovasCConfiguration@@ClassName@@.h
- ovasCConfiguration@@ClassName@@.cpp
- ovasCDriver@@ClassName@@.h
- ovasCDriver@@ClassName@@.cpp
You can put these files here in your local repository:
[openvibe-repository]/contrib/plugins/server-drivers/[my-device]/src/
The glade interface:
- interface-@@ClassName@@.glade
You can put this file in here in your local repository:
[openvibe-repository]/contrib/plugins/server-drivers/[my-device]/share/
Don't forget to declare your driver to the Acquisition Server application.
This happens in "./contrib/common/contribAcquisitionServer.inl", there you
will find examples of such declarations. You also need to register the source
files to CMake, modifying the corresponding "contribAcquisitionServer.cmake".
For more information about building a new driver and filling your skeleton, please read the official tutorial:
http://openvibe.inria.fr/tutorial-creating-a-new-driver-for-the-acquisition-server/
Feel free to propose your contribution on the forum !
http://openvibe.inria.fr/forum/
Enjoy OpenViBE !
- The development team -
@@ -0,0 +1,14 @@
######################################################################################
## OpenViBE configuration file, application specific values
##
## - The file consists of a list of "token name = token value"
## - Whatever space / tabulation at the begining or end of a line is removed
## - Comments start with #
## - Lines ending with \ continue on the next line (all ending \ are removed)
## - On Windows, use / in paths as \ is reserved
## - You can use ${token name} in a token value
## - You can use $Environment{env name} in a token value to get an environment variable
######################################################################################
CustomConfiguration = ${CustomConfigurationPrefix${OperatingSystem}}${CustomConfigurationSuffix${OperatingSystem}}
CustomConfigurationApplication = ${CustomConfigurationPrefix${OperatingSystem}}-${ApplicationName}${CustomConfigurationSuffix${OperatingSystem}}
@@ -0,0 +1,138 @@
#pragma once
#include "ovsgCSkeletonGenerator.h"
#include <map>
#include <vector>
namespace OpenViBE {
namespace SkeletonGenerator {
class CBoxAlgorithmSkeletonGenerator final : public CSkeletonGenerator
{
public:
CBoxAlgorithmSkeletonGenerator(Kernel::IKernelContext& ctx, GtkBuilder* builder) : CSkeletonGenerator(ctx, builder) { }
~CBoxAlgorithmSkeletonGenerator() override { }
bool initialize() override;
bool save(const std::string& filename) override;
bool load(const std::string& filename) override;
void getCurrentParameters() override;
// Box Description
std::string m_Name, m_Version, m_ClassName, m_Category, m_ShortDesc, m_DetailedDesc;
std::string m_GtkStockItemName;
int m_GtkStockItemIdx = 0;
struct SIOS
{
std::string name, type, typeID, defaultValue;
};
// Inputs
bool m_CanModifyInputs = false, m_CanAddInputs = false;
std::vector<SIOS> m_Inputs;
// Outputs
bool m_CanModifyOutputs = false, m_CanAddOutputs = false;
std::vector<SIOS> m_Outputs;
// Settings
bool m_CanModifySettings = false, m_CanAddSettings = false;
std::vector<SIOS> m_Settings;
//Algorithms
std::vector<std::string> m_Algorithms; // the algorithm selected by user
// Can be made non-const after '= false' produces working code
static const bool USE_CODEC_TOOLKIT = true; // use or not the codec toolkit for encoder and decoder algorithms
//the map between algorithm and corresponding header declaration (all variables algo/input/output).
std::map<std::string, std::string> m_AlgoDeclaration;
//the map between algorithm and corresponding initialisation
std::map<std::string, std::string> m_AlgoInitialisations;
//the map between algorithm and corresponding initialisation of ref targets
std::map<std::string, std::string> m_AlgoInitialisationReferenceTargets;
//the map between algorithm and corresponding uninitialisation
std::map<std::string, std::string> m_AlgoUninitialisations;
// Box Listener
bool m_UseBoxListener = false;
// input
bool m_HasOnInputAdded = false;
bool m_HasOnInputRemoved = false;
bool m_HasOnInputTypeChanged = false;
bool m_HasOnInputNameChanged = false;
bool m_HasOnInputConnected = false;
bool m_HasOnInputDisconnected = false;
// output
bool m_HasOnOutputAdded = false;
bool m_HasOnOutputRemoved = false;
bool m_HasOnOutputTypeChanged = false;
bool m_HasOnOutputNameChanged = false;
bool m_HasOnOutputConnected = false;
bool m_HasOnOutputDisconnected = false;
// setting
bool m_HasOnSettingAdded = false;
bool m_HasOnSettingRemoved = false;
bool m_HasOnSettingTypeChanged = false;
bool m_HasOnSettingNameChanged = false;
bool m_HasOnSettingDefaultValueChanged = false;
bool m_HasOnSettingValueChanged = false;
bool m_HasProcessInput = false;
bool m_HasProcessClock = false;
bool m_HasProcessMessage = false;
uint32_t m_ClockFrequency = 0;
void buttonCheckCB();
void buttonOkCB();
void toggleListenerCheckbuttonsStateCB(bool state) const;
void buttonTooltipCB(GtkButton* button);
void buttonExitCB();
void buttonAddInputCB() const;
void buttonAddOutputCB() const;
void buttonAddSettingCB() const;
void buttonAddAlgorithmCB() const;
void buttonRemoveGeneric(const char* buttonName) const;
void algorithmSelectedCB(int index);
void setSensitivity(const char* widgetName, bool isActive) const;
private:
Kernel::ILogManager& getLogManager() const override { return m_kernelCtx.getLogManager(); }
Kernel::IErrorManager& getErrorManager() const override { return m_kernelCtx.getErrorManager(); }
static std::string getRandomIdentifierString() { return CIdentifier::random().str(); }
// Sanity checks that a string is not empty or consist of spaces
static bool isStringValid(const std::string& str) { return !str.empty() && str.find_first_not_of(' ') != std::string::npos; }
void clearStore(const char* name) const;
void addDialogButttons(const char* name) const;
void setEntryToConfigValue(const char* name, const char* token) const;
void setActiveFromConf(const char* name, const char* token) const;
void setActiveFromConfTog(const char* name, const char* token) const;
const gchar* getText(const char* name) const;
void addCollectionToTree(const char* treeName, const std::vector<std::string>& collection) const;
void fillCollection(const char* treeName, std::vector<SIOS>& collection) const;
bool isActive(char* name) const;
std::map<GtkButton*, std::string> m_tooltips;
std::vector<std::string> m_typeCorrespondances;
};
class CDummyAlgoProto final : public Kernel::IAlgorithmProto
{
public:
std::map<std::string, Kernel::EParameterType> m_Inputs, m_Outputs;
std::vector<std::string> m_InputTriggers, m_OutputTriggers;
bool addInputParameter(const CIdentifier& id, const CString& name, Kernel::EParameterType typeID,
const CIdentifier& subTypeID = CIdentifier::undefined()) override;
bool addOutputParameter(const CIdentifier& id, const CString& name, Kernel::EParameterType typeID,
const CIdentifier& subTypeID = CIdentifier::undefined()) override;
bool addInputTrigger(const CIdentifier& id, const CString& name) override;
bool addOutputTrigger(const CIdentifier& id, const CString& name) override;
CIdentifier getClassIdentifier() const override { return CIdentifier::undefined(); }
};
} // namespace SkeletonGenerator
} // namespace OpenViBE
@@ -0,0 +1,504 @@
#include "ovsgCDriverSkeletonGenerator.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <glib/gstdio.h>
#include <cstdio>
#include <boost/regex.hpp>
namespace OpenViBE {
namespace SkeletonGenerator {
//-----------------------------------------------------------------------
static void CheckCB(GtkButton* /*button*/, void* data) { static_cast<CDriverSkeletonGenerator*>(data)->buttonCheckCB(); }
static void TooltipCB(GtkButton* button, void* data) { static_cast<CDriverSkeletonGenerator*>(data)->buttonTooltipCB(button); }
static void OkCB(GtkButton* /*button*/, void* data) { static_cast<CDriverSkeletonGenerator*>(data)->buttonOkCB(); }
static void ExitCB(GtkButton* /*button*/, void* data)
{
static_cast<CDriverSkeletonGenerator*>(data)->buttonExitCB();
gtk_exit(0);
}
//-----------------------------------------------------------------------
void CDriverSkeletonGenerator::buttonExitCB()
{
getCommonParameters();
getCurrentParameters();
if (!cleanConfigurationFile(m_configFile)) { return; }
saveCommonParameters(m_configFile);
save(m_configFile);
getLogManager() << Kernel::LogLevel_Info << "All entries saved in [" << m_configFile << "]. Exiting.\n";
}
void CDriverSkeletonGenerator::buttonCheckCB()
{
//Author and Company
getCommonParameters();
getCurrentParameters();
getLogManager() << Kernel::LogLevel_Info << "Checking values... \n";
bool success = true;
std::stringstream ss;
ss << "----- STATUS -----\n";
//-------------------------------------------------------------------------------------------------------------------------------------------//
//::GtkWidget * driverName = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_driver_name"));
//m_DriverName = gtk_entry_get_text(GTK_ENTRY(driverName));
const boost::regex regExpDriverName("([a-z]|[A-Z]|[0-9])+([a-z]|[A-Z]|[0-9]|[ \t\r\n]|[\\.-_\\(\\)])*", boost::regex::perl);
if (regex_match(std::string(m_DriverName), regExpDriverName) == false)
{
OV_WARNING_K("-- Driver Name: INVALID");
success = false;
ss << "[FAILED] Invalid driver name. Please use only characters (lower or uppercase) and numbers (blanck allowed).\n";
}
else
{
getLogManager() << Kernel::LogLevel_Info << "-- Driver Name: VALID (" << m_DriverName << ")\n";
ss << "[ OK ] Valid driver name.\n";
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
//::GtkWidget * className = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_class_name"));
//m_ClassName = gtk_entry_get_text(GTK_ENTRY(className));
const boost::regex regExpClassName("([a-z]|[A-Z]|[0-9])+", boost::regex::perl);
if (regex_match(std::string(m_ClassName), regExpClassName) == false)
{
OV_WARNING_K("-- Class Name: INVALID");
success = false;
ss << "[FAILED] Invalid class name. Please use only characters (lower or uppercase) and numbers (no blanck allowed).\n";
}
else
{
getLogManager() << Kernel::LogLevel_Info << "-- Class Name: VALID (" << m_ClassName << ")\n";
ss << "[ OK ] Valid class name.\n";
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
GtkWidget* spinMinChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_min_channel"));
//m_MinChannel = std::to_string(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinMinChannel)))).c_str();
GtkWidget* spinMaxChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_max_channel"));
//m_MaxChannel = std::to_string(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinMaxChannel)))).c_str();
if (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinMinChannel)) > gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinMaxChannel)))
{
OV_WARNING_K(Kernel::LogLevel_Warning << "-- Channels: INVALID");
success = false;
ss << "[FAILED] Invalid channel count. Be sure that Min <= Max.\n";
}
else
{
getLogManager() << Kernel::LogLevel_Info << "-- Channels: VALID (" << m_MinChannel << "/" << m_MaxChannel << ")\n";
ss << "[ OK ] Valid channel count.\n";
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
/*::GtkWidget * sf = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_sampling_frequencies"));
CString samplings = gtk_entry_get_text(GTK_ENTRY(sf));
*/
const boost::regex regExpSamplings("(([1-9][0-9]*);)*([1-9][0-9]*)", boost::regex::perl);
if (regex_match(m_Samplings, regExpSamplings) == false)
{
OV_WARNING_K("-- Sampling frequencies: INVALID");
success = false;
ss << "[FAILED] Invalid sampling frequencies. Please use only whole numbers separated with ';' (no blanck allowed).\n";
}
else
{
// Maximum 16 frequencies
size_t nSampling = 0;
size_t freq;
std::stringstream tmp(m_Samplings);
m_SamplingSeparate.clear();
while (tmp >> freq && nSampling < 16)
{
m_SamplingSeparate.push_back(std::to_string(freq));
if (tmp.peek() == ';') { tmp.ignore(); }
nSampling++;
}
getLogManager() << Kernel::LogLevel_Info << "-- Sampling frequencies: VALID\n";
for (const auto& s : m_SamplingSeparate) { std::cout << "- " << s << " Hz\n"; }
ss << "[ OK ] " << nSampling << " valid sampling frequencie(s).\n";
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
/*::GtkWidget * fileChooser = GTK_WIDGET(gtk_builder_get_object(m_builder, "filechooserbutton_target_directory"));
char * directory = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileChooser));
m_directory = CString(directory);
g_free(directory);*/
#ifdef TARGET_OS_Windows
const std::string space("%20");
if (m_directory.rfind(space) != std::string::npos)
{
ss << "[FAILED] Invalid destination folder :" << m_directory << ".\n";
getLogManager() << Kernel::LogLevel_Error << "Invalid destination folder :" << m_directory << ".\n";
success = false;
}
else
#endif
{
getLogManager() << Kernel::LogLevel_Info << "-- Target directory: " << m_directory << "\n";
ss << "[ OK ] Valid target directory: " << m_directory << "\n";
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
GtkWidget* textview = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-tooltips-textview"));
GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
if (success)
{
ss << "----- SUCCESS -----\nPress OK to generate the files. If you want to modify your choice(s), please press the \"Check\" button again.";
GtkWidget* buttonOk = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-ok-button"));
gtk_widget_set_sensitive(buttonOk, true);
}
else
{
ss << "----- PROCESS FAILED -----\nModify your choices and press the \"Check\" button again.";
GtkWidget* buttonOk = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-ok-button"));
gtk_widget_set_sensitive(buttonOk, false);
}
gtk_text_buffer_set_text(buffer, ss.str().c_str(), -1);
}
void CDriverSkeletonGenerator::buttonOkCB()
{
getLogManager() << Kernel::LogLevel_Info << "Generating files... \n";
std::string log = "Generating files...\n";
bool success = true;
const std::string date = getDate();
// we construct the map of substitutions
std::map<std::string, std::string> substitutions;
substitutions["@@AuthorName@@"] = m_author;
substitutions["@@CompanyName@@"] = m_company;
substitutions["@@Date@@"] = date;
substitutions["@@ClassName@@"] = m_ClassName;
substitutions["@@DriverName@@"] = m_DriverName;
substitutions["@@MinChannel@@"] = m_MinChannel;
substitutions["@@MaxChannel@@"] = m_MaxChannel;
substitutions["@@SamplingFrequency@@"] = m_SamplingSeparate[0];
GtkWidget* textview = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-tooltips-textview"));
GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
//-------------------------------------------------------------------------------------------------------------------------------------------//
// driver.h
std::string dst = m_directory + "/ovasCDriver" + m_ClassName + ".h";
std::string tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/driver.h-skeleton").toASCIIString();
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
// driver.cpp
dst = m_directory + "/ovasCDriver" + m_ClassName + ".cpp";
tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/driver.cpp-skeleton").toASCIIString();
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
// config.h
dst = m_directory + "/ovasCConfiguration" + m_ClassName + ".h";
tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/configuration.h-skeleton").toASCIIString();
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
// config.cpp
dst = m_directory + "/ovasCConfiguration" + m_ClassName + ".cpp";
tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/configuration.cpp-skeleton").toASCIIString();
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
// interface.ui
dst = m_directory + "/interface-" + m_ClassName + ".ui";
tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/interface.ui-skeleton").toASCIIString();
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
// the following substitution is done in a .ui file, and not in a cpp file.
// The SED primitive immplemented do not cover that case, and some typo problem happen with the character "
const std::string pattern("@@SamplingFrequencyList@@");
std::string substitute;
for (auto it = m_SamplingSeparate.begin(); it != m_SamplingSeparate.end();)
{
substitute += (*it++);
if (it != m_SamplingSeparate.end()) { substitute += "<\\/col><\\/row><row><col id=\\\"0\\\" translatable=\\\"yes\\\">"; }
}
success &= regexReplace(dst, pattern, substitute, "");
//-------------------------------------------------------------------------------------------------------------------------------------------//
// readme-driver.txt
dst = m_directory + "/README.txt";
tmplate = m_kernelCtx.getConfigurationManager().expand("${Path_Data}/applications/skeleton-generator/readme-driver.txt-skeleton");
if (!this->generate(tmplate, dst, substitutions, log))
{
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
success = false;
}
//-------------------------------------------------------------------------------------------------------------------------------------------//
if (success)
{
success &= cleanConfigurationFile(m_configFile);
success &= saveCommonParameters(m_configFile);
success &= save(m_configFile);
}
if (!success)
{
log += "Generation process did not completly succeed. Some files may have not been produced.\n";
OV_WARNING_K("Generation process did not completly succeed. Some files may have not been produced.");
}
else
{
log += "Generation process successful. All information saved in [" + m_configFile + "]\nPlease read the file [README.txt] !\n";
getLogManager() << Kernel::LogLevel_Info << "Generation process successful. All information saved in [" << m_configFile << "]\n";
}
// Launch the browser to display the produced files
const std::string browser = m_kernelCtx.getConfigurationManager().expand("${Designer_WebBrowserCommand_${OperatingSystem}}").toASCIIString();
#ifdef TARGET_OS_Windows
const std::string browserCmd = browser + " file:///" + m_directory; //otherwise the browser does not find the directory (problem with / and \ char)
#else
const std::string browserCmd = browser + " \"" + m_directory + "\"";
#endif
if (system(browserCmd.c_str())) { }
gtk_text_buffer_set_text(buffer, log.c_str(), -1);
}
void CDriverSkeletonGenerator::buttonTooltipCB(GtkButton* button)
{
const EWidgetName widgetName = m_widgetNames[button];
GtkWidget* textview = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-tooltips-textview"));
GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
if (widgetName == EWidgetName::DriverName)
{
gtk_text_buffer_set_text(buffer,
"Driver Name: \nThis name will be the one displayed in the Acquisition Server selection combobox.\nUsually, the driver is named according to the EEG device (with precisions such as a version number).\n------\nExample: OpenEEG Modular EEG (P2)\n\n\n",
-1);
}
else if (widgetName == EWidgetName::ClassName)
{
gtk_text_buffer_set_text(buffer,
"Class Name: \nThis name will be used to generate all source and GUI files.\nYou should choose a class name close to the device name (no blank allowed !).\n------\nExample: OpenEEGModularEEG will generate\n - ovasCDriverOpenEEGModularEEG.h/.cpp, the driver skeleton \n - ovasCConfigurationOpenEEGModularEEG.h/.cpp, the configuration class skeleton\n - interface-OpenEEG-ModularEEG.ui, the GUI description file",
-1);
}
else if (widgetName == EWidgetName::ChannelCount)
{
gtk_text_buffer_set_text(buffer,
"Channel count: \nEnter in the two fields the minimum and maximum number of channels the device is capable of.\nOf course you can still change it later in the source code.\n------\nExample: Min(1) Max(16)\n\n\n",
-1);
}
else if (widgetName == EWidgetName::Sampling)
{
gtk_text_buffer_set_text(buffer,
"Sampling frequencies: \nEnter in the text field the sampling frequencies your device is capable of.\nYou can specify a list of defined frequencies (value separator ';').\n------\nExample:\n\"128;256;512\" for three defined frequencies.\n\n",
-1);
}
else if (widgetName == EWidgetName::Directory)
{
gtk_text_buffer_set_text(buffer,
"Target directory: \nEnter the destination directory in which all files will be generated. \nAny existing files will be overwritten.\n------\nExample: ~/skeleton-generator/foobar-driver/\n\n\n",
-1);
}
else { }
}
bool CDriverSkeletonGenerator::initialize()
{
GtkWidget* driver = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-window"));
// Buttons and signals
GtkWidget* buttonCheck = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-check-button"));
GtkWidget* buttonOk = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-ok-button"));
g_signal_connect(buttonCheck, "pressed", G_CALLBACK(CheckCB), this);
g_signal_connect(buttonOk, "pressed", G_CALLBACK(OkCB), this);
////target directory
//::GtkWidget * fileChooser = GTK_WIDGET(gtk_builder_get_object(m_builder, "filechooserbutton_target_directory"));
//CString directory = m_kernelContext.getConfigurationManager().expand("${SkeletonGenerator_TargetDirectory}");
//if(!gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fileChooser), directory.c_str()))
//{
// gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fileChooser),"..");
//}
// Tooltips buttons and signal
GtkButton* buttonDriverName = GTK_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-driver-name-tooltip-button"));
GtkButton* buttonClassName = GTK_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-class-name-tooltip-button"));
GtkButton* buttonChannelCount = GTK_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-channel-count-tooltip-button"));
GtkButton* buttonSampling = GTK_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-sampling-frequencies-tooltip-button"));
GtkButton* buttonDirectory = GTK_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-target-directory-tooltip-button"));
m_widgetNames[buttonDriverName] = EWidgetName::DriverName;
m_widgetNames[buttonClassName] = EWidgetName::ClassName;
m_widgetNames[buttonChannelCount] = EWidgetName::ChannelCount;
m_widgetNames[buttonSampling] = EWidgetName::Sampling;
m_widgetNames[buttonDirectory] = EWidgetName::Directory;
g_signal_connect(buttonDriverName, "pressed", G_CALLBACK(TooltipCB), this);
g_signal_connect(buttonClassName, "pressed", G_CALLBACK(TooltipCB), this);
g_signal_connect(buttonChannelCount, "pressed", G_CALLBACK(TooltipCB), this);
g_signal_connect(buttonSampling, "pressed", G_CALLBACK(TooltipCB), this);
g_signal_connect(buttonDirectory, "pressed", G_CALLBACK(TooltipCB), this);
//Close with X and "cancel" button
g_signal_connect(G_OBJECT(driver), "delete_event", G_CALLBACK(gtk_exit), nullptr);
GtkWidget* buttonCancel = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-cancel-button"));
g_signal_connect(buttonCancel, "pressed", G_CALLBACK(ExitCB), this);
//load everything from file
load(m_configFile);
gtk_widget_show_all(driver);
return true;
}
bool CDriverSkeletonGenerator::save(const std::string& filename)
{
std::ofstream file;
file.open(filename, std::ios::app | std::ios::binary);
OV_ERROR_UNLESS_KRF(file.is_open(), "Saving the driver entries in [" << m_configFile << "] failed !", Kernel::ErrorType::BadFileWrite);
file << "# ----------------------DRIVER-------------------------\n";
std::string directory(m_directory);
for (auto it = directory.begin(); it < directory.end(); ++it) { if ((*it) == '\\') { directory.replace(it, it + 1, 1, '/'); } }
file << "SkeletonGenerator_Driver_DriverName = " << m_DriverName << std::endl;
file << "SkeletonGenerator_Driver_ClassName = " << m_ClassName << std::endl;
file << "SkeletonGenerator_Driver_MinChannel = " << m_MinChannel << std::endl;
file << "SkeletonGenerator_Driver_MaxChannel = " << m_MaxChannel << std::endl;
file << "SkeletonGenerator_Driver_SamplingFrequencies = " << m_Samplings << std::endl;
file << "SkeletonGenerator_Driver_TargetDirectory = " << directory << std::endl;
file << "# -----------------------------------------------------" << std::endl;
file.close();
getLogManager() << Kernel::LogLevel_Info << "Driver entries saved in [" << m_configFile << "]\n";
m_configFileLoaded = false;
return true;
}
bool CDriverSkeletonGenerator::load(const std::string& filename)
{
if (!m_configFileLoaded && !m_kernelCtx.getConfigurationManager().addConfigurationFromFile(filename.c_str()))
{
OV_WARNING_K("Driver: Configuration file [" << filename << "] could not be loaded. It will be automatically generated after first use.");
return false;
}
GtkWidget* driverName = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_driver_name"));
gtk_entry_set_text(GTK_ENTRY(driverName), m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Driver_DriverName}"));
GtkWidget* className = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_class_name"));
gtk_entry_set_text(GTK_ENTRY(className), m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Driver_ClassName}"));
GtkWidget* minChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_min_channel"));
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(minChannel), double(m_kernelCtx.getConfigurationManager().expandAsInteger("${SkeletonGenerator_Driver_MinChannel}")));
GtkWidget* maxChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_max_channel"));
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(maxChannel), double(m_kernelCtx.getConfigurationManager().expandAsInteger("${SkeletonGenerator_Driver_MaxChannel}")));
GtkWidget* sf = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_sampling_frequencies"));
gtk_entry_set_text(GTK_ENTRY(sf), m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Driver_SamplingFrequencies}"));
GtkWidget* fileChooser = GTK_WIDGET(gtk_builder_get_object(m_builder, "filechooserbutton_target_directory"));
// if the user specified a target directory, it has full priority
std::string directory = m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_TargetDirectory}").toASCIIString();
#ifdef TARGET_OS_Linux
bool needFilePrefix = false;
#endif
if (!directory.empty())
{
getLogManager() << Kernel::LogLevel_Debug << "Target dir user [" << directory << "]\n";
#ifdef TARGET_OS_Linux
needFilePrefix = true;
#endif
}
else
{
//previous entry
directory = m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Driver_TargetDirectory}");
if (!directory.empty())
{
getLogManager() << Kernel::LogLevel_Debug << "Target previous [" << directory << "]\n";
#ifdef TARGET_OS_Linux
needFilePrefix = true;
#endif
}
else
{
//default path = dist
getLogManager() << Kernel::LogLevel_Debug << "Target default [dist]\n";
#ifdef TARGET_OS_Linux
directory = std::string(gtk_file_chooser_get_current_folder_uri(GTK_FILE_CHOOSER(fileChooser)));
directory = directory + "/..";
#elif defined TARGET_OS_Windows
directory = "..";
#endif
}
}
#ifdef TARGET_OS_Linux
if(needFilePrefix) directory = "file://" + directory;
gtk_file_chooser_set_current_folder_uri(GTK_FILE_CHOOSER(fileChooser), directory.c_str());
#elif defined TARGET_OS_Windows
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fileChooser), directory.c_str());
#endif
getLogManager() << Kernel::LogLevel_Info << "Driver entries from [" << filename << "] loaded.\n";
return true;
}
void CDriverSkeletonGenerator::getCurrentParameters()
{
GtkWidget* driverName = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_driver_name"));
m_DriverName = gtk_entry_get_text(GTK_ENTRY(driverName));
GtkWidget* className = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_class_name"));
m_ClassName = gtk_entry_get_text(GTK_ENTRY(className));
GtkWidget* minChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_min_channel"));
m_MinChannel = std::to_string(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(minChannel))));
GtkWidget* maxChannel = GTK_WIDGET(gtk_builder_get_object(m_builder, "spinbutton_max_channel"));
m_MaxChannel = std::to_string(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(maxChannel))));
GtkWidget* sf = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_sampling_frequencies"));
m_Samplings = gtk_entry_get_text(GTK_ENTRY(sf));
GtkWidget* fileChooser = GTK_WIDGET(gtk_builder_get_object(m_builder, "filechooserbutton_target_directory"));
char* directory = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileChooser));
m_directory = CString(directory);
g_free(directory);
}
} // namespace SkeletonGenerator
} // namespace OpenViBE
@@ -0,0 +1,51 @@
#pragma once
#include "ovsgCSkeletonGenerator.h"
#include <vector>
#include <map>
namespace OpenViBE {
namespace SkeletonGenerator {
class CDriverSkeletonGenerator final : public CSkeletonGenerator
{
public:
CDriverSkeletonGenerator(Kernel::IKernelContext& ctx, GtkBuilder* builder) : CSkeletonGenerator(ctx, builder) { }
~CDriverSkeletonGenerator() override { }
bool initialize() override;
bool save(const std::string& filename) override;
bool load(const std::string& filename) override;
void getCurrentParameters() override;
std::string m_DriverName;
std::string m_ClassName;
std::string m_Samplings;
std::vector<std::string> m_SamplingSeparate;
std::string m_MinChannel;
std::string m_MaxChannel;
void buttonCheckCB();
void buttonOkCB();
void buttonTooltipCB(GtkButton* button);
void buttonExitCB();
private:
Kernel::ILogManager& getLogManager() const override { return m_kernelCtx.getLogManager(); }
Kernel::IErrorManager& getErrorManager() const override { return m_kernelCtx.getErrorManager(); }
enum class EWidgetName
{
DriverName,
ClassName,
ChannelCount,
Sampling,
Directory,
};
std::map<GtkButton*, EWidgetName> m_widgetNames;
};
} // namespace SkeletonGenerator
} // namespace OpenViBE
@@ -0,0 +1,249 @@
#include "ovsgCSkeletonGenerator.h"
#include <string>
#include <ctime>
#include <sstream>
#include <fstream>
#include <iostream>
#include <iterator>
#include <boost/regex.hpp>
namespace OpenViBE {
namespace SkeletonGenerator {
CSkeletonGenerator::CSkeletonGenerator(Kernel::IKernelContext& ctx, GtkBuilder* builder)
: m_kernelCtx(ctx), m_builder(builder)
{
m_configFile = m_kernelCtx.getConfigurationManager().expand("${CustomConfigurationApplication}");
m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Configuration file [" << m_configFile << "]\n";
loadCommonParameters(m_configFile);
}
void CSkeletonGenerator::getCommonParameters()
{
//Author and Company
GtkWidget* company = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_company_name"));
m_company = gtk_entry_get_text(GTK_ENTRY(company));
GtkWidget* author = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_author_name"));
m_author = gtk_entry_get_text(GTK_ENTRY(author));
}
bool CSkeletonGenerator::saveCommonParameters(const std::string& filename)
{
// we get the latest values
getCommonParameters();
std::ofstream file;
file.open(filename, std::ios::app | std::ios::binary);
OV_ERROR_UNLESS_KRF(file.is_open(), "Saving the common entries in [" << filename << "] failed !", Kernel::ErrorType::BadFileRead);
// generator selected
std::string active;
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "sg-driver-selection-radio-button")))) { active = "0"; }
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "sg-algo-selection-radio-button")))) { active = "1"; }
else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_builder, "sg-box-selection-radio-button")))) { active = "2"; }
file << "SkeletonGenerator_GeneratorSelected = " << active << std::endl;
file << "SkeletonGenerator_Common_Author = " << m_author << std::endl;
file << "SkeletonGenerator_Common_Company = " << m_company << std::endl;
file.close();
getLogManager() << Kernel::LogLevel_Info << "Common entries saved in [" << filename << "]\n";
//we can reload the file, it may have changed
m_configFileLoaded = false;
return true;
}
bool CSkeletonGenerator::cleanConfigurationFile(const std::string& filename) const
{
std::ofstream file;
file.open(filename, std::ios::binary);
OV_ERROR_UNLESS_KRF(file.is_open(), "Failed to clean [" << filename << "]", Kernel::ErrorType::BadFileRead);
getLogManager() << Kernel::LogLevel_Info << "Configuration file [" << filename << "] cleaned.\n";
file.close();
return true;
}
bool CSkeletonGenerator::loadCommonParameters(const std::string& filename)
{
OV_ERROR_UNLESS_KRF(m_configFileLoaded || m_kernelCtx.getConfigurationManager().addConfigurationFromFile(filename.c_str()),
"Common: Configuration file [" << filename << "] could not be loaded. \n", Kernel::ErrorType::BadFileRead);
GtkWidget* widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-driver-selection-radio-button"));
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(widget), (m_kernelCtx.getConfigurationManager().expandAsUInteger("${SkeletonGenerator_GeneratorSelected}") == 0));
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-algo-selection-radio-button"));
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(widget), (m_kernelCtx.getConfigurationManager().expandAsUInteger("${SkeletonGenerator_GeneratorSelected}") == 1));
widget = GTK_WIDGET(gtk_builder_get_object(m_builder, "sg-box-selection-radio-button"));
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(widget), (m_kernelCtx.getConfigurationManager().expandAsUInteger("${SkeletonGenerator_GeneratorSelected}") == 2));
GtkWidget* company = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_company_name"));
gtk_entry_set_text(GTK_ENTRY(company), m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Common_Company}"));
GtkWidget* author = GTK_WIDGET(gtk_builder_get_object(m_builder, "entry_author_name"));
gtk_entry_set_text(GTK_ENTRY(author), m_kernelCtx.getConfigurationManager().expand("${SkeletonGenerator_Common_Author}"));
getLogManager() << Kernel::LogLevel_Info << "Common entries from [" << filename << "] loaded.\n";
m_configFileLoaded = true;
return true;
}
std::string CSkeletonGenerator::ensureSedCompliancy(const std::string& expression)
{
std::string res(expression);
auto it = res.begin();
while (it < res.end())
{
if ((*it) == '\\')
{
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
#ifdef TARGET_OS_Linux
it = res.insert(it,'\\');
it = res.insert(it,'\\');
it+=2;
it = res.insert(it,'\\');
it = res.insert(it,'\\');
it+=2;
#endif
}
else if ((*it) == '/')
{
it = res.insert(it, '\\');
++it;
}
else if ((*it) == '"')
{
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
it = res.insert(it, '\\');
++it;
}
else if ((*it) == '\n')
{
it = res.erase(it);
#ifdef TARGET_OS_Linux
it = res.insert(it,'\\');
it = res.insert(it,'\\');
it+=2;
#endif
it = res.insert(it, '\\');
it = res.insert(it, '\\');
it += 2;
it = res.insert(it, 'n');
//++it;
}
++it;
}
return res;
}
bool CSkeletonGenerator::regexReplace(const std::string& src, const std::string& regEx, const std::string& substitute, const std::string& dst) const
{
try
{
// Read file to memory
std::ifstream in(src);
std::string buffer((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
in.close();
// Open output stream and set an iterator to it
std::string realDst((dst.empty() ? src : dst));
std::ofstream out(realDst);
const std::ostream_iterator<char> it(out);
// Do regex magic on the iterator
boost::regex exp;
exp.assign(regEx);
regex_replace(it, buffer.begin(), buffer.end(), exp, substitute, boost::match_default | boost::format_sed);
out.close();
}
catch (...)
{
std::cout << "Error occurred processing " << src << " to " << dst << "\n";
return false;
}
return true;
}
std::string CSkeletonGenerator::getDate()
{
time_t raw;
time(&raw);
struct tm* info = localtime(&raw);
std::string res(asctime(info));
res = res.substr(0, res.size() - 1); // the ascitime ends with a "\n"
return res;
}
bool CSkeletonGenerator::generate(const std::string& src, const std::string& dst, const std::map<std::string, std::string>& substitutions,
std::string& log) const
{
// we check if the template file is in place.
if (! g_file_test(src.c_str(), G_FILE_TEST_EXISTS))
{
log += "[FAILED] the template file '" + src + "' is missing.\n";
OV_ERROR_KRF("The template file '" << src << "' is missing.", Kernel::ErrorType::BadInput);
}
// we check the map
if (substitutions.empty())
{
log += "[WARNING] No substitution provided.\n";
OV_WARNING_K("No substitution provided.");
return false;
}
bool success = true;
log += "[ OK ] -- template file '" + src + "' found.\n";
getLogManager() << Kernel::LogLevel_Info << " -- template file '" << src << "' found.\n";
//we need to create the destination file by copying the template file, then do the first substitution
auto it = substitutions.cbegin();
success &= regexReplace(src, it->first, ensureSedCompliancy(it->second), dst);
++it;
//next substitutions are done on the - incomplete - destination file itself
while (it != substitutions.cend() && success)
{
getLogManager() << Kernel::LogLevel_Trace << "Executing substitution [" << it->first << "] ->[" << it->second << "]\n";
success &= regexReplace(dst, it->first, ensureSedCompliancy(it->second));
++it;
}
if (!success)
{
log += "[FAILED] -- " + dst + " cannot be written.\n";
OV_ERROR_KRF(" -- " << dst << " cannot be written.", Kernel::ErrorType::BadFileWrite);
}
log += "[ OK ] -- " + dst + " written.\n";
getLogManager() << Kernel::LogLevel_Info << " -- " << dst << " written.\n";
return true;
}
} // namespace SkeletonGenerator
} // namespace OpenViBE
@@ -0,0 +1,62 @@
#pragma once
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <openvibe/kernel/ovIKernelObject.h>
//#include <configuration/ovkCConfigurationManager.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <map>
namespace OpenViBE {
namespace SkeletonGenerator {
class CSkeletonGenerator
{
public:
CSkeletonGenerator(Kernel::IKernelContext& ctx, GtkBuilder* builder);
virtual ~CSkeletonGenerator() { }
protected:
Kernel::IKernelContext& m_kernelCtx;
GtkBuilder* m_builder = nullptr;
std::string m_author;
std::string m_company;
std::string m_directory;
std::string m_configFile; // basic application config file
bool m_configFileLoaded = false;
virtual bool initialize() = 0;
void getCommonParameters();
bool saveCommonParameters(const std::string& filename);
bool loadCommonParameters(const std::string& filename);
bool cleanConfigurationFile(const std::string& filename) const;
// returns a sed-compliant expression to be parsed in a substitution command
static std::string ensureSedCompliancy(const std::string& expression);
// executes a regex replace and builds a new file, by replacing the matching expressions by the substitute. If no destination file is provided, the template file is modified.
// Note that the input must be a valid sed format regex pattern, the function does not check.
bool regexReplace(const std::string& src, const std::string& regEx, const std::string& substitute, const std::string& dst = std::string("")) const;
// get the formatted string date
static std::string getDate();
// generate a new file, giving a template file, a destination file, and a map ofsubstitutions (Tag,Substitute)
// return false if an error occurred.
bool generate(const std::string& src, const std::string& dst, const std::map<std::string, std::string>& substitutions, std::string& log) const;
virtual void getCurrentParameters() = 0;
virtual bool save(const std::string& filename) = 0;
virtual bool load(const std::string& filename) = 0;
virtual Kernel::ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
virtual Kernel::IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
};
} // namespace SkeletonGenerator
} // namespace OpenViBE
@@ -0,0 +1,96 @@
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <iostream>
#include <clocale> // std::setlocale
#include "ovsgCDriverSkeletonGenerator.h"
#include "ovsgCBoxAlgorithmSkeletonGenerator.h"
int main(int argc, char** argv)
{
OpenViBE::CKernelLoader loader;
std::cout << "[ INF ] Created kernel loader, trying to load kernel module" << std::endl;
OpenViBE::CString err;
#if defined TARGET_OS_Windows
const OpenViBE::CString file = OpenViBE::Directories::getLibDir() + "/openvibe-kernel.dll";
#else
const OpenViBE::CString file = OpenViBE::Directories::getLibDir() + "/libopenvibe-kernel.so";
#endif
if (!loader.load(file, &err)) { std::cout << "[ FAILED ] Error loading kernel (" << err << ")" << " from [" << file << "]\n"; }
else
{
std::cout << "[ INF ] Kernel module loaded, trying to get kernel descriptor" << std::endl;
OpenViBE::Kernel::IKernelDesc* desc = nullptr;
loader.initialize();
loader.getKernelDesc(desc);
if (!desc) { std::cout << "[ FAILED ] No kernel descriptor" << std::endl; }
else
{
std::cout << "[ INF ] Got kernel descriptor, trying to create kernel" << std::endl;
OpenViBE::Kernel::IKernelContext* ctx = desc->createKernel("skeleton-generator", OpenViBE::Directories::getDataDir() + "/kernel/openvibe.conf");
if (!ctx || !ctx->initialize()) { std::cout << "[ FAILED ] No kernel created by kernel descriptor" << std::endl; }
else
{
ctx->getConfigurationManager().addConfigurationFromFile(OpenViBE::Directories::getDataDir() + "/kernel/openvibe.conf");
ctx->getConfigurationManager().addConfigurationFromFile(
OpenViBE::Directories::getDataDir() + "/applications/skeleton-generator/skeleton-generator.conf");
OpenViBE::Toolkit::initialize(*ctx);
OpenViBE::Kernel::IConfigurationManager& configMgr = ctx->getConfigurationManager();
ctx->getPluginManager().addPluginsFromFiles(configMgr.expand("${Kernel_Plugins}"));
gtk_init(&argc, &argv);
// We rely on this with 64bit/gtk 2.24, to roll back gtk_init() sometimes switching
// the locale to one where ',' is needed instead of '.' for separators of floats,
// causing issues, for example getConfigurationManager.expandAsFloat("0.05") -> 0;
// due to implementation by std::stod().
std::setlocale(LC_ALL, "C");
GtkBuilder* builder = gtk_builder_new();
const OpenViBE::CString filename = OpenViBE::Directories::getDataDir() + "/applications/skeleton-generator/generator-interface.ui";
OV_ERROR_UNLESS(gtk_builder_add_from_file(builder, filename.toASCIIString(), nullptr), "Problem loading [" << filename << "]",
OpenViBE::Kernel::ErrorType::BadFileRead, -1, ctx->getErrorManager(), ctx->getLogManager());
//gtk_builder_connect_signals(builder, nullptr);
GtkWidget* dialog = GTK_WIDGET(gtk_builder_get_object(builder, "sg-selection-dialog"));
gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
GtkWidget* radioDriver = GTK_WIDGET(gtk_builder_get_object(builder, "sg-driver-selection-radio-button"));
GtkWidget* radioAlgo = GTK_WIDGET(gtk_builder_get_object(builder, "sg-algo-selection-radio-button"));
GtkWidget* radioBox = GTK_WIDGET(gtk_builder_get_object(builder, "sg-box-selection-radio-button"));
OpenViBE::SkeletonGenerator::CBoxAlgorithmSkeletonGenerator boxGenerator(*ctx, builder);
OpenViBE::SkeletonGenerator::CDriverSkeletonGenerator driverGenerator(*ctx, builder);
const gint resp = gtk_dialog_run(GTK_DIALOG(dialog));
if (resp == GTK_RESPONSE_OK)
{
gtk_widget_hide(GTK_WIDGET(dialog));
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioDriver))) { driverGenerator.initialize(); }
OV_ERROR_UNLESS(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioAlgo)), "NOT YET AVAILABLE.",
OpenViBE::Kernel::ErrorType::Internal, 0, ctx->getErrorManager(), ctx->getLogManager());
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioBox))) { boxGenerator.initialize(); }
gtk_main();
}
else
{
std::cout << "User cancelled. Exit." << std::endl;
return 0;
}
}
}
}
loader.uninitialize();
loader.unload();
return 0;
}
@@ -0,0 +1,62 @@
IF(OV_DISABLE_GTK)
MESSAGE(STATUS "Skipping VRPN Simulator, no GTK")
RETURN()
ENDIF(OV_DISABLE_GTK)
PROJECT(openvibe-vrpn-simulator)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
INCLUDE("FindThirdPartyVRPN_Check")
IF(NOT PATH_VRPN)
MESSAGE(STATUS " --> Not building ${PROJECT_NAME}")
RETURN()
ENDIF(NOT PATH_VRPN)
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindThirdPartyGTK")
INCLUDE("FindThirdPartyVRPN")
# ---------------------------------
# Finds standard library pthread
# Adds library to target
# Adds include path
# ---------------------------------
IF(UNIX)
FIND_LIBRARY(LIB_STANDARD_MODULE_PTHREAD pthread)
IF(LIB_STANDARD_MODULE_PTHREAD)
MESSAGE(STATUS " Found pthread...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_PTHREAD})
ELSE(LIB_STANDARD_MODULE_PTHREAD)
MESSAGE(STATUS " FAILED to find pthread...")
ENDIF(LIB_STANDARD_MODULE_PTHREAD)
ENDIF(UNIX)
# ---------------------------------
# ---------------------------------
# ----------------------
# Generate launch script
# ----------------------
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/applications/vrpn-simulator)
@@ -0,0 +1,293 @@
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy toplevel-contextual -->
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment2">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment3">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment4">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment5">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment6">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment7">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkAdjustment" id="adjustment8">
<property name="lower">-100</property>
<property name="upper">100</property>
<property name="step_increment">0.01</property>
<property name="page_increment">1</property>
</object>
<object class="GtkWindow" id="window">
<child>
<object class="GtkVBox" id="vbox">
<property name="visible">True</property>
<child>
<object class="GtkHBox" id="hbox_analog">
<property name="visible">True</property>
<child>
<object class="GtkVScale" id="vscale_0">
<property name="height_request">256</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment1</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment2</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment3</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment4</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment5</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment6</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment7</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkVScale" id="vscale_7">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment8</property>
<property name="inverted">True</property>
</object>
<packing>
<property name="position">7</property>
</packing>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox_button">
<property name="visible">True</property>
<child>
<object class="GtkToggleButton" id="togglebutton_0">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_4">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_5">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_6">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image6">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">6</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="togglebutton_7">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage" id="image7">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
</object>
</child>
</object>
<packing>
<property name="position">7</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
@@ -0,0 +1,136 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include <clocale> // std::setlocale
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <openvibe/ov_directories.h>
#include <openvibe/ovCString.h>
#include <vrpn_Button.h>
#include <vrpn_Analog.h>
#include <vrpn_Connection.h>
#include <iostream>
#define _vrpn_peripheral_name_ "openvibe-vrpn@localhost"
// #define _DEBUG
vrpn_Connection* connection = nullptr;
vrpn_Button_Server* buttonServer = nullptr;
vrpn_Analog_Server* analogServer = nullptr;
long nAnalog = 0;
long nButton = 0;
typedef union
{
gpointer data;
int idx;
} user_data_t;
void fScrollCB(GtkRange* range, gpointer data)
{
user_data_t userData;
userData.data = data;
const gdouble value = gtk_range_get_value(range);
analogServer->channels()[userData.idx] = value;
#if defined _DEBUG
std::cout << "Channel " << int(data) << " value changed to " << value << "\n";
#endif
}
void fSwitchCB(GtkToggleButton* button, gpointer data)
{
user_data_t userData;
userData.data = data;
const gboolean value = gtk_toggle_button_get_active(button);
buttonServer->set_button(userData.idx, value);
#if defined _DEBUG
std::cout << "Channel " << userData.idx << " toggled to " << value << "\n";
#endif
}
void fConnectCB(GtkWidget* widget, gpointer /*data*/)
{
if (GTK_IS_RANGE(widget))
{
g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(fScrollCB), gpointer(nAnalog));
nAnalog++;
}
if (GTK_IS_TOGGLE_BUTTON(widget))
{
g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(fSwitchCB), reinterpret_cast<void*>(nButton));
nButton++;
}
}
gboolean fIdleApplicationLoop(gpointer /*data*/)
{
buttonServer->mainloop();
analogServer->report_changes();
analogServer->mainloop();
connection->mainloop();
return TRUE;
}
#endif // TARGET_HAS_ThirdPartyVRPN
int main(int argc, char** argv)
{
#if defined TARGET_HAS_ThirdPartyVRPN
const int nChannels = 8;
gtk_init(&argc, &argv);
// We rely on this with 64bit/gtk 2.24, to roll back gtk_init() sometimes switching
// the locale to one where ',' is needed instead of '.' for separators of floats,
// causing issues, for example getConfigurationManager.expandAsFloat("0.05") -> 0;
// due to implementation by std::stod().
std::setlocale(LC_ALL, "C");
// g_pConnection=new ::vrpn_Connection;
connection = vrpn_create_server_connection();
buttonServer = new vrpn_Button_Server(_vrpn_peripheral_name_, connection, nChannels);
analogServer = new vrpn_Analog_Server(_vrpn_peripheral_name_, connection, nChannels);
GtkBuilder* builder =
gtk_builder_new(); // glade_xml_new(OpenViBE::Directories::getDataDir() + "/applications/vrpn-simulator/interface.ui", "window", nullptr);
const OpenViBE::CString filename = OpenViBE::Directories::getDataDir() + "/applications/vrpn-simulator/interface.ui";
if (!gtk_builder_add_from_file(builder, filename, nullptr))
{
std::cout << "Problem loading [" << filename << "]\n";
return -1;
}
GtkWidget* mainWindow = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
GtkWidget* hBoxButton = GTK_WIDGET(gtk_builder_get_object(builder, "hbox_button"));
GtkWidget* hBoxAnalog = GTK_WIDGET(gtk_builder_get_object(builder, "hbox_analog"));
g_signal_connect(G_OBJECT(mainWindow), "destroy", gtk_main_quit, nullptr);
gtk_container_foreach(GTK_CONTAINER(hBoxButton), fConnectCB, nullptr);
gtk_container_foreach(GTK_CONTAINER(hBoxAnalog), fConnectCB, nullptr);
gtk_builder_connect_signals(builder, nullptr);
std::cout << "VRPN Stimulator\n";
std::cout << "Got " << nAnalog << " analogs...\n";
std::cout << "Got " << nButton << " buttons...\n";
std::cout << "Using " << nChannels << " VRPN channels...\n";
std::cout << "Signals will be sent to peripheral [" << _vrpn_peripheral_name_ << "]\n";
g_idle_add(fIdleApplicationLoop, nullptr);
gtk_widget_show(mainWindow);
gtk_main();
delete analogServer;
delete buttonServer;
delete connection;
#endif // TARGET_HAS_ThirdPartyVRPN
return 0;
}
@@ -0,0 +1,21 @@
PROJECT(xml-configuration-comparison)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
FILE(GLOB_RECURSE SRC_FILES *.cpp *.hpp)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
# ----------------------
# Generate launch script
# ----------------------
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
@@ -0,0 +1,104 @@
///-------------------------------------------------------------------------------------------------
///
/// \brief Classes of the Box Features Selection Trainer.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 14/02/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#include <fstream>
#include <iostream>
#include <string>
#include "tinyxml2.h"
#include <algorithm>
// validation Test take in the algorithm output file, the expected output file (reference) and a tolerance threshold
bool comparison(const char* file1, const char* file2, bool removeSpace)
{
// Load Files
tinyxml2::XMLDocument xmlDoc1, xmlDoc2;
if (xmlDoc1.LoadFile(file1) != 0) // Check File 1 Exist and Loading
{
std::cerr << "Error during loading File." << std::endl;
return false;
}
if (xmlDoc2.LoadFile(file2) != 0) // Check File 2 Exist and Loading
{
std::cerr << "Error during loading Reference." << std::endl;
return false;
}
// Load Roots
tinyxml2::XMLNode *root1 = xmlDoc1.FirstChild(), *root2 = xmlDoc2.FirstChild(); // Get Root Node
if (root1 == nullptr) // Check Root Node Exist
{
std::cerr << "File format not compatible." << std::endl;
return false;
}
if (root2 == nullptr) // Check Root Node Exist
{
std::cerr << "Reference format not compatible." << std::endl;
return false;
}
// Count Settings
size_t n1 = 0, n2 = 0;
for (tinyxml2::XMLElement* e = root1->FirstChildElement(); e != nullptr; e = e->NextSiblingElement()) { n1++; }
for (tinyxml2::XMLElement* e = root2->FirstChildElement(); e != nullptr; e = e->NextSiblingElement()) { n2++; }
if (n1 != n2)
{
std::cerr << "File and Reference are not same number of features." << std::endl;
return false;
}
// Load settings
tinyxml2::XMLElement *e1 = root1->FirstChildElement(), *e2 = root2->FirstChildElement();
for (size_t i = 0; i < n1; ++i, e1 = e1->NextSiblingElement(), e2 = e2->NextSiblingElement())
{
std::string s1(e1->GetText()), s2(e2->GetText());
std::cout << s1 << std::endl << s2 << std::endl;
if (removeSpace)
{
s1.erase(std::remove_if(s1.begin(), s1.end(), [](const unsigned char x) { return std::isspace(x); }), s1.end());
s2.erase(std::remove_if(s2.begin(), s2.end(), [](const unsigned char x) { return std::isspace(x); }), s2.end());
}
std::cout << s1 << std::endl << s2 << std::endl;
if (s1 != s2)
{
std::cerr << "Setting number " << i << " is different, For File : \n" << s1 << "\nfor Reference : \n" << s2 << std::endl;
return false;
}
}
return true;
}
int main(const int argc, char* argv[])
{
if (argc != 4)
{
std::cout << "Usage: " << argv[0] << " <file> <reference> <remove space (true or false)>\n";
return -1;
}
bool remove;
if (strcmp(argv[3], "true") == 0) { remove = true; }
else if (strcmp(argv[3], "false") == 0) { remove = false; }
else
{
std::cout << "Usage: <remove space> must be true or false not " << argv[3] << std::endl;
return -1;
}
if (!comparison(argv[1], argv[2], remove))
{
std::cout << "Algorithm failed validation test \n" << std::endl;
return 1;
}
std::cout << "Test passed\n" << std::endl;
return 0;
}