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,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;
}