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,64 @@
#######################################################################
# Software License Agreement (AGPL-3 License)
#
# OpenViBE SDK Test Software
# Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
# Copyright (C) Inria, 2015-2017,V1.0
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License version 3,
# as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#######################################################################
PROJECT(openvibe-scenario-player)
# ----------------------
# Project variables
# ----------------------
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
# ----------------------
# Add executable
# ----------------------
ADD_DEFINITIONS( -DPROJECT_VERSION="${PROJECT_VERSION}" )
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
# ----------------------
# External dependecies
# ----------------------
# To be done after add executable because FindOpenViBEXXX files need a target name
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
INCLUDE("FindOpenViBEModuleSystem")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyBoost")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
@@ -0,0 +1,157 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ovspCKernelFacade.h"
#include "ovspCCommand.h"
namespace OpenViBE {
EPlayerReturnCodes SInitCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
return kernelFacade.initialize();
}
void SInitCmd::doPrint(std::ostream& os) const
{
os << "command name: InitCommand" << std::endl;
os << "Benchmark: " << (this->benchmark ? std::to_string(this->benchmark.get()) : "not set") << std::endl;
}
EPlayerReturnCodes SLoadKernelCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
// default config file is an empty one so it is not problem to give it directly as param
return kernelFacade.loadKernel(*this);
}
void SLoadKernelCmd::doPrint(std::ostream& os) const
{
os << "command name: LoadKernelCommand" << std::endl;
os << "ConfigurationFile: " << (this->configFile ? this->configFile.get() : "not set") << std::endl;
}
EPlayerReturnCodes SLoadScenarioCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
if (!this->scenarioName || !this->scenarioFile)
{
std::cerr << "Missing required arguments for command" << std::endl;
return EPlayerReturnCodes::MissingMandatoryArgument;
}
return kernelFacade.loadScenario(*this);
}
void SLoadScenarioCmd::doPrint(std::ostream& os) const
{
os << "command name: LoadScenarioCommand" << std::endl;
os << "ScenarioName: " << (this->scenarioName ? this->scenarioName.get() : "not set") << std::endl;
os << "ScenarioFile: " << (this->scenarioFile ? this->scenarioFile.get() : "not set") << std::endl;
}
EPlayerReturnCodes SUpdateScenarioCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
if (!this->scenarioName || !this->scenarioFile)
{
std::cerr << "Missing required arguments for command" << std::endl;
return EPlayerReturnCodes::MissingMandatoryArgument;
}
return kernelFacade.updateScenario(*this);
}
void SUpdateScenarioCmd::doPrint(std::ostream& os) const
{
os << "command name: UpdateScenarioCommand" << std::endl;
os << "ScenarioName: " << (this->scenarioName ? this->scenarioName.get() : "not set") << std::endl;
os << "ScenarioFile: " << (this->scenarioFile ? this->scenarioFile.get() : "not set") << std::endl;
}
EPlayerReturnCodes SResetCmd::execute(CKernelFacade& /*kernelFacade*/) const
{
// to be implemented
std::cout << "About to execute:" << std::endl << *this << std::endl << "Not implemented yet" << std::endl;
return EPlayerReturnCodes::Success;
}
void SResetCmd::doPrint(std::ostream& os) const { os << "command name: ResetCommand" << std::endl; }
EPlayerReturnCodes SRunScenarioCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
if (!this->scenarioList)
{
std::cerr << "Missing required arguments for command: ScenarioList" << std::endl;
return EPlayerReturnCodes::MissingMandatoryArgument;
}
return kernelFacade.runScenarioList(*this);
}
void SRunScenarioCmd::doPrint(std::ostream& os) const
{
os << "command name: SRunScenarioCmd" << std::endl;
os << "ScenarioList:";
if (this->scenarioList) { for (auto& scenario : this->scenarioList.get()) { os << " " << scenario; } }
else { os << " not set"; }
os << std::endl;
os << "PlayMode: ";
if (this->playMode)
{
const std::string modeAsString = (this->playMode == EPlayerPlayMode::Fastfoward) ? "fastforward" : "standard";
os << modeAsString;
}
else { os << "not set"; }
os << std::endl;
os << "MaximumExecutionTime: " << (this->maximumExecutionTime ? std::to_string(this->maximumExecutionTime.get()) : "not set") << std::endl;
os << "TokenList:";
if (this->tokenList) { for (auto& token : this->tokenList.get()) { os << " (" << token.first << "," << token.second << ")"; } }
else { os << " not set"; }
os << std::endl;
}
EPlayerReturnCodes SSetupScenarioCmd::execute(CKernelFacade& kernelFacade) const
{
std::cout << "About to execute:" << std::endl << *this << std::endl;
if (!this->scenarioName)
{
std::cerr << "Missing required arguments for command" << std::endl;
return EPlayerReturnCodes::MissingMandatoryArgument;
}
return kernelFacade.setupScenario(*this);
}
void SSetupScenarioCmd::doPrint(std::ostream& os) const
{
os << "command name: SSetupScenarioCmd" << std::endl;
os << "ScenarioName: " << (this->scenarioName ? this->scenarioName.get() : "not set") << std::endl;
os << "TokenList:";
if (this->tokenList) { for (auto& token : this->tokenList.get()) { os << " (" << token.first << "," << token.second << ")"; } }
else { os << " not set"; }
os << std::endl;
}
} // namespace OpenViBE
@@ -0,0 +1,206 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovspICommand.h"
#include <boost/optional.hpp>
#include <string>
namespace OpenViBE {
/**
* \struct SInitCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives the initialization of the tool
* \ingroup ScenarioPlayer
*
* InitCommand class contains the following properties:
* - Benchmark: Flag to enable benchmark on execute command (optional).
*
*/
struct SInitCmd final : SCommand
{
// List of properties
boost::optional<bool> benchmark;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SLoadKernelCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives kernel loading
* \ingroup ScenarioPlayer
*
* LoadKernelCommand contains the following properties:
* - ConfigurationFile: Path to kernel configuration file (optional)
* .
*
*/
struct SLoadKernelCmd final : SCommand
{
// List of properties
boost::optional<std::string> configFile;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SLoadScenarioCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives scenario loading
* \ingroup ScenarioPlayer
*
* LoadScenarioCommand contains the following properties:
* - ScenarioFile: Path to xml scenario file (mandatory)
* - ScenarioName: Name of the scenario (mandatory)
* .
*
*/
struct SLoadScenarioCmd final : SCommand
{
// List of properties
boost::optional<std::string> scenarioFile;
boost::optional<std::string> scenarioName;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SUpdateScenarioCmd
* \author criou (INRIA)
* \date 2018-04-16
* \brief Command that drives scenario update and export
* \ingroup ScenarioPlayer
*
* UpdateScenarioCommand contains the following properties:
* - ScenarioFile: Path to xml scenario file (mandatory)
* .
*
*/
struct SUpdateScenarioCmd final : SCommand
{
// List of properties
boost::optional<std::string> scenarioFile;
boost::optional<std::string> scenarioName;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SResetCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives tool reset to its initial state
* \ingroup ScenarioPlayer
*/
struct SResetCmd final : SCommand
{
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SRunScenarioCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives the execution of a list of scenarios
* \ingroup ScenarioPlayer
*
* SRunScenarioCmd contains the following properties:
* - ScenarioList: Names of scenario that must be executed (mandatory)
* - PlayMode: 0 for standard, 1 for fastforward (optional)
* - MaximumExecutionTime: Scenarios playing execution time limit (optional)
* - TokenList: List of global (token,value) pairs (optional)
* .
*
*/
struct SRunScenarioCmd final : SCommand
{
using Token = std::pair<std::string, std::string>;
// List of properties
boost::optional<std::vector<std::string>> scenarioList;
boost::optional<EPlayerPlayMode> playMode;
boost::optional<double> maximumExecutionTime;
boost::optional<std::vector<Token>> tokenList;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
/**
* \struct SSetupScenarioCmd
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Command that drives the setup of a scenario
* \ingroup ScenarioPlayer
*
* SSetupScenarioCmd contains the following properties:
* - ScenarioName: name of the scenario to setup (mandatory)
* - TokenList: List of scenario specific tokens (optional)
* .
*
* The token list overwrites the previous token list if the command was already
* called on the same scenario (note that an empty token list is allowed).
*
*/
struct SSetupScenarioCmd final : SCommand
{
using Token = std::pair<std::string, std::string>;
// List of properties
boost::optional<std::string> scenarioName;
boost::optional<std::vector<Token>> tokenList;
EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const override;
protected:
void doPrint(std::ostream& os) const override;
};
} // namespace OpenViBE
@@ -0,0 +1,336 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ovspCCommand.h"
#include "ovspCCommandFileParser.h"
namespace OpenViBE {
// should be moved in a utility class/file
std::string CommandFileParser::trim(const std::string& str)
{
// remove leading and trailing space
// no use of regex here cause it might be too slow
auto begin = str.find_first_not_of(" \t"), end = str.find_last_not_of(" \t");
if (begin == std::string::npos) { begin = 0; }
if (end == std::string::npos) { end = str.size() - 1; }
return str.substr(begin, end - begin + 1);
}
// should be moved in a utility class/file
std::pair<std::string, std::string> CommandFileParser::tokenize(const std::string& str)
{
// given a string of type "token : value", return a pair of (token,value)
// split method is not used because only the first delimiter is of interest
const auto split = str.find_first_of(':');
if (split == std::string::npos) { throw std::runtime_error("Impossible to convert " + str + " to token/value pair"); }
auto token = trim(str.substr(0, split));
auto val = trim(str.substr(split + 1, str.size() - split - 1));
return std::make_pair(token, val);
}
// should be moved in a utility class/file
std::vector<std::string> CommandFileParser::split(const std::string& str, const char delimiter)
{
std::vector<std::string> vec;
size_t currentIdx = 0;
size_t delimiterIdx = str.find(delimiter);
auto trimmed = trim(str.substr(currentIdx, delimiterIdx));
if (!trimmed.empty()) { vec.push_back(trimmed); }
while (delimiterIdx != std::string::npos)
{
// abc,cde,fgh
// ^ -> current index points to the first element after a match
// ^ -> delimiter index points to next match
currentIdx = delimiterIdx + 1;
delimiterIdx = str.find(delimiter, currentIdx);
trimmed = trim(str.substr(currentIdx, delimiterIdx - currentIdx));
if (!trimmed.empty()) { vec.push_back(trimmed); }
}
return vec;
}
// should be moved in a utility class/file
bool CommandFileParser::toBool(const std::string& str)
{
// try to keep same behavior as stoi or stol
std::string lowerStr;
lowerStr.reserve(str.size());
std::transform(str.begin(), str.end(), lowerStr.begin(), tolower);
bool result;
if (str == "false" || str == "0") { result = false; }
else if (str == "true" || str == "1") { result = true; }
else { throw std::runtime_error("Impossible to convert " + str + " to bool"); }
return result;
}
// should be moved in a utility class/file
// input is expected to be trimmed (by a call to tolenize for example)
std::vector<std::string> CommandFileParser::toList(const std::string& str)
{
std::vector<std::string> vec;
// {token1, token2, token3 ...} pattern expected
if (str.size() >= 3 && str[0] == '{' && str[str.size() - 1] == '}') { vec = split(str.substr(1, str.length() - 2), ','); }
else { throw std::runtime_error("Impossible to convert " + str + " to list"); }
return vec;
}
// should be moved in a utility class/file
// part of this function duplicates code from ProgramOptions.hpp
std::vector<CommandFileParser::Token> CommandFileParser::toTokenList(const std::string& str)
{
std::vector<Token> vec;
for (auto& rawToken : toList(str))
{
// rawToken is expected to be trimmed
const auto split = rawToken.find_first_of(':');
const auto size = rawToken.size();
// (a:b) pattern expected
// minimal regex std::regex("\\(.+:.+\\)")
if (!(size >= 5 && rawToken[0] == '(' && rawToken[size - 1] == ')') || split == std::string::npos)
{
throw std::runtime_error("Failed to parse token pair from value: " + rawToken);
}
Token token;
token.first = trim(rawToken.substr(1, split - 1));
// magic 2 numbers is because substr takes a length as second parameter
// 2 = remove the last ) + account for the first one
token.second = trim(rawToken.substr(split + 1, size - split - 2));
vec.push_back(token);
}
return vec;
}
void CommandFileParser::initialize()
{
// using a callback mechanism allows us to implement the core parse() method
// very easily (no need to put some if/else blocks everywhere depending on which command is encountered)
m_callbacks["Init"] = std::bind(&CommandFileParser::initCommandCb, this, std::placeholders::_1);
m_callbacks["Reset"] = std::bind(&CommandFileParser::resetCommandCb, this, std::placeholders::_1);
m_callbacks["LoadKernel"] = std::bind(&CommandFileParser::loadKernelCommandCb, this, std::placeholders::_1);
m_callbacks["LoadScenario"] = std::bind(&CommandFileParser::loadScenarioCommandCb, this, std::placeholders::_1);
m_callbacks["SetupScenario"] = std::bind(&CommandFileParser::setupScenarioCommandCb, this, std::placeholders::_1);
m_callbacks["RunScenario"] = std::bind(&CommandFileParser::runScenarioCommandCb, this, std::placeholders::_1);
}
void CommandFileParser::uninitialize()
{
m_callbacks.clear();
m_cmdList.clear();
}
EPlayerReturnCodes CommandFileParser::parse()
{
std::ifstream fileStream(m_cmdFile);
if (!fileStream.is_open())
{
std::cerr << "ERROR: impossible to open file at location: " << m_cmdFile << std::endl;
return EPlayerReturnCodes::OpeningFileFailure;
}
std::string line;
bool isFillingSection{ false };
std::vector<std::string> sectionContent;
std::string sectionTag;
while (std::getline(fileStream, line))
{
auto trimmedLine = trim(line);
const auto size = trimmedLine.size();
// [a] pattern expected
// minimal regex std::regex("^(?!\\#)\\[.+\\])")
if (size >= 3 && trimmedLine[0] == '['
&& trimmedLine[size - 1] == ']')
{
if (isFillingSection) // flush the section that was beeing filled
{
const auto errorCode = this->flush(sectionTag, sectionContent);
if (errorCode != EPlayerReturnCodes::Success) { return errorCode; }
}
// use of regex to be confident on tag structure
// magic 2 numbers is because substr takes length as second parameter
// 2 = remove the last ] + account for the first one
sectionTag = trimmedLine.substr(1, size - 2);
if (m_callbacks.find(sectionTag) == m_callbacks.end())
{
std::cerr << "ERROR: Unknown command = " << sectionTag << std::endl;
return EPlayerReturnCodes::ParsingCommandFailure;
}
isFillingSection = true;
sectionContent.clear();
}
else { sectionContent.push_back(trimmedLine); }
}
if (isFillingSection)
{
const auto errorCode = this->flush(sectionTag, sectionContent);
if (errorCode != EPlayerReturnCodes::Success) { return errorCode; }
}
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::flush(const std::string& sectionTag, const std::vector<std::string>& sectionContent)
{
try // try block here as some conversions are made with the stl in the callback and might throw
{
const auto returnCode = m_callbacks[sectionTag](sectionContent);
if (returnCode != EPlayerReturnCodes::Success) { return returnCode; }
}
catch (const std::exception& e)
{
std::cerr << "ERROR: Caught exception while parsing command = " << sectionTag << std::endl;
std::cerr << "ERROR: Exception: " << e.what() << std::endl;
return EPlayerReturnCodes::ParsingCommandFailure;
}
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::initCommandCb(const std::vector<std::string>& sectionContent)
{
std::shared_ptr<SInitCmd> command = std::make_shared<SInitCmd>();
for (const auto& line : sectionContent)
{
// lines are expected to be trimmed
// a:b pattern expected
// minimal regex std::regex("^(?!\\#).+:.+")
if (!line.empty() && line[0] != '#')
{
auto param = tokenize(line);
if (param.first == "Benchmark") { command->benchmark = toBool(param.second); }
else { std::cout << "WARNING: Unknown parameter for Init command: " << param.first << std::endl; }
}
}
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::resetCommandCb(const std::vector<std::string>& /*sectionContent*/)
{
const std::shared_ptr<SResetCmd> command = std::make_shared<SResetCmd>();
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::loadKernelCommandCb(const std::vector<std::string>& sectionContent)
{
std::shared_ptr<SLoadKernelCmd> command = std::make_shared<SLoadKernelCmd>();
// cf. initCommandCb
for (const auto& line : sectionContent)
{
if (!line.empty() && line[0] != '#')
{
auto param = tokenize(line);
if (param.first == "ConfigurationFile") { command->configFile = param.second; }
else { std::cout << "WARNING: Unknown parameter for LoadKernel command: " << param.first << std::endl; }
}
}
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::loadScenarioCommandCb(const std::vector<std::string>& sectionContent)
{
std::shared_ptr<SLoadScenarioCmd> command = std::make_shared<SLoadScenarioCmd>();
// cf. initCommandCb
for (const auto& line : sectionContent)
{
if (!line.empty() && line[0] != '#')
{
auto param = tokenize(line);
if (param.first == "ScenarioName") { command->scenarioName = param.second; }
else if (param.first == "ScenarioFile") { command->scenarioFile = param.second; }
else { std::cout << "WARNING: Unknown parameter for LoadScenario command: " << param.first << std::endl; }
}
}
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::setupScenarioCommandCb(const std::vector<std::string>& sectionContent)
{
std::shared_ptr<SSetupScenarioCmd> command = std::make_shared<SSetupScenarioCmd>();
// cf. initCommandCb
for (const auto& line : sectionContent)
{
if (!line.empty() && line[0] != '#')
{
auto param = tokenize(line);
if (param.first == "ScenarioName") { command->scenarioName = param.second; }
else if (param.first == "TokenList") { command->tokenList = toTokenList(param.second); }
else { std::cout << "WARNING: Unknown parameter for SetupScenario command: " << param.first << std::endl; }
}
}
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CommandFileParser::runScenarioCommandCb(const std::vector<std::string>& sectionContent)
{
std::shared_ptr<SRunScenarioCmd> command = std::make_shared<SRunScenarioCmd>();
// cf. initCommandCb
for (const auto& line : sectionContent)
{
if (!line.empty() && line[0] != '#')
{
auto param = tokenize(line);
if (param.first == "ScenarioList") { command->scenarioList = toList(param.second); }
else if (param.first == "PlayMode") { command->playMode = EPlayerPlayMode(std::stoi(param.second)); }
else if (param.first == "MaximumExecutionTime") { command->maximumExecutionTime = std::stod(param.second); }
else if (param.first == "TokenList") { command->tokenList = (toTokenList(param.second)); }
else { std::cout << "WARNING: Unknown parameter for RunScenario command: " << param.first << std::endl; }
}
}
m_cmdList.push_back(command);
return EPlayerReturnCodes::Success;
}
} // namespace OpenViBE
@@ -0,0 +1,101 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
#include "ovspICommandParser.h"
#include <string>
#include <functional>
#include <fstream>
#include <vector>
#include <map>
namespace OpenViBE {
/**
* \class CommandFileParser
* \author cgarraud (INRIA)
* \date 2016-01-27
* \brief Parser implementation that parses command a file
* \ingroup ScenarioPlayer
*
* The current implementation retrieves the list of commands from a file.
*
* Input file format requirements:
* - Command names are defined within brackets [InitCommand] on a single line
* - Each command parameter is defined on a single line below the command name:
* + Single value parameters are defined as: parameterName: value
* + Simple list parameters are defined as: parameterName: {val1,val2,...}
* + Token list parameters are defined as: parameterName: { (token1:val1),(token2:val2) } (no ',' authorized in (token, value) pair)
* .
* .
*
* \todo The implementation uses a lot of string utility functions that need more
* testing
*
* \note Use of regex would simplify the implementation but boost::regex is not header-only and std::regex not implemented in gcc 4.8
*
*/
class CommandFileParser final : public ICommandParser
{
public:
/**
*
* \brief Constructor
* \param[in] file path to the command file
*
*/
explicit CommandFileParser(const std::string& file) : m_cmdFile(file) { }
void initialize() override;
void uninitialize() override;
std::vector<std::shared_ptr<SCommand>> getCommandList() const override { return m_cmdList; }
EPlayerReturnCodes parse() override;
private:
using CallbackType = std::function<EPlayerReturnCodes(std::vector<std::string>)>;
using Token = std::pair<std::string, std::string>;
static std::string trim(const std::string& str);
static std::pair<std::string, std::string> tokenize(const std::string& str);
static std::vector<std::string> split(const std::string& str, char delimiter);
static bool toBool(const std::string& str);
static std::vector<std::string> toList(const std::string& str);
static std::vector<Token> toTokenList(const std::string& str);
EPlayerReturnCodes flush(const std::string& sectionTag, const std::vector<std::string>& sectionContent);
EPlayerReturnCodes initCommandCb(const std::vector<std::string>& sectionContent);
EPlayerReturnCodes resetCommandCb(const std::vector<std::string>& sectionContent);
EPlayerReturnCodes loadKernelCommandCb(const std::vector<std::string>& sectionContent);
EPlayerReturnCodes loadScenarioCommandCb(const std::vector<std::string>& sectionContent);
EPlayerReturnCodes setupScenarioCommandCb(const std::vector<std::string>& sectionContent);
EPlayerReturnCodes runScenarioCommandCb(const std::vector<std::string>& sectionContent);
std::string m_cmdFile;
std::vector<std::shared_ptr<SCommand>> m_cmdList;
std::map<std::string, CallbackType> m_callbacks;
};
} // namespace OpenViBE
@@ -0,0 +1,128 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ovspCCommand.h"
#include "ovspCCommandLineOptionParser.h"
namespace OpenViBE {
CommandLineOptionParser::CommandLineOptionParser(ProgramOptionParser& parser) : m_parser(parser) { }
void CommandLineOptionParser::initialize() { } // nothing to do
void CommandLineOptionParser::uninitialize() { m_cmdList.clear(); }
std::vector<std::shared_ptr<SCommand>> CommandLineOptionParser::getCommandList() const { return m_cmdList; }
EPlayerReturnCodes CommandLineOptionParser::parse()
{
// parsing consists of building a straightfoward command workflow according to command-line
// options.
// commands have to be pushed in the right order
// choose a dumb name for the scenario
std::string scenarioName = "express-scenario";
// an init command is always needed
m_cmdList.push_back(std::make_shared<SInitCmd>());
// workflow must at least load the kernel
std::shared_ptr<SLoadKernelCmd> kernelCmd = std::make_shared<SLoadKernelCmd>();
if (m_parser.hasOption("config-file")) // optional
{
kernelCmd->configFile = m_parser.getOptionValue<std::string>("config-file");
}
m_cmdList.push_back(kernelCmd);
// scenario loading is a mandatory step
std::shared_ptr<SLoadScenarioCmd> scenarioCmd = std::make_shared<SLoadScenarioCmd>();
if (m_parser.hasOption("scenario-file")) // mandatory option
{
scenarioCmd->scenarioFile = m_parser.getOptionValue<std::string>("scenario-file");
// set dumb name as it used to recognize scenario in the application
scenarioCmd->scenarioName = scenarioName;
}
else
{
std::cerr << "ERROR: mandatory option 'scenario-file' not set" << std::endl;
return EPlayerReturnCodes::MissingMandatoryArgument;
}
m_cmdList.push_back(scenarioCmd);
// scenario update option
std::shared_ptr<SUpdateScenarioCmd> updateScenarioCmd = std::make_shared<SUpdateScenarioCmd>();
if (m_parser.hasOption("updated-scenario-file"))
{
// do not play scenario, just update it.
updateScenarioCmd->scenarioFile = m_parser.getOptionValue<std::string>("updated-scenario-file");
// set dumb name as it used to recognize scenario in the application
updateScenarioCmd->scenarioName = scenarioName;
m_cmdList.push_back(updateScenarioCmd);
}
else
{
// check if some scenario setup information has been set
if (m_parser.hasOption("ds"))
{
std::shared_ptr<SSetupScenarioCmd> setupCmd = std::make_shared<SSetupScenarioCmd>();
setupCmd->scenarioName = scenarioName;
setupCmd->tokenList = m_parser.getOptionValue<std::vector<SSetupScenarioCmd::Token>>("ds");
m_cmdList.push_back(setupCmd);
}
// last command in the workflow is the run command
std::shared_ptr<SRunScenarioCmd> runCmd = std::make_shared<SRunScenarioCmd>();
runCmd->scenarioList = std::vector<std::string>{ scenarioName };
if (m_parser.hasOption("play-mode"))
{
const auto playMode = m_parser.getOptionValue<std::string>("play-mode");
if (playMode != "ff" && playMode != "std")
{
std::cerr << "ERROR: option 'play-mode' must be ff or std" << std::endl;
return EPlayerReturnCodes::BadArg;
}
// permissive code here
// any other entry than ff leads to standard mode...
runCmd->playMode = ((playMode == "ff") ? EPlayerPlayMode::Fastfoward : EPlayerPlayMode::Standard);
}
if (m_parser.hasOption("max-time")) { runCmd->maximumExecutionTime = m_parser.getOptionValue<double>("max-time"); }
if (m_parser.hasOption("dg")) { runCmd->tokenList = m_parser.getOptionValue<std::vector<SSetupScenarioCmd::Token>>("dg"); }
m_cmdList.push_back(runCmd);
}
return EPlayerReturnCodes::Success;
}
} // namespace OpenViBE
@@ -0,0 +1,63 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
#include "ovspICommandParser.h"
namespace OpenViBE {
/**
* \class CommandLineOptionParser
* \author cgarraud (INRIA)
* \date 2016-01-27
* \brief Parser implementation that parses command from command-line arguments
* \ingroup ScenarioPlayer
*
* The current implementation retrieves the options from a ProgramOptions parser and
* simply builds the commands from the parsed options.
*
*/
class CommandLineOptionParser final : public ICommandParser
{
public:
/**
*
* \brief Constructor
* \param[in] parser Specific instantiation of ProgramOptions parser
*
*/
explicit CommandLineOptionParser(ProgramOptionParser& parser);
void initialize() override;
void uninitialize() override;
std::vector<std::shared_ptr<SCommand>> getCommandList() const override;
EPlayerReturnCodes parse() override;
private:
ProgramOptionParser& m_parser;
std::vector<std::shared_ptr<SCommand>> m_cmdList;
};
} // namespace OpenViBE
@@ -0,0 +1,364 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ovspCCommand.h"
#include "ovsp_base.h"
#include "ovspCKernelFacade.h"
#include <system/ovCTime.h>
#include <cstdio>
#include <map>
#include <limits>
#include <cassert>
namespace OpenViBE {
using TokenList = std::vector<std::pair<std::string, std::string>>;
static void setConfigTokens(Kernel::IConfigurationManager& configsManager, const TokenList& tokens)
{
for (const auto& token : tokens) { configsManager.addOrReplaceConfigurationToken(token.first.c_str(), token.second.c_str()); }
}
struct CKernelFacade::SKernelFacadeImpl
{
CKernelLoader loader;
Kernel::IKernelContext* ctx = nullptr;
std::map<std::string, CIdentifier> scenarios;
std::map<std::string, TokenList> scenarioTokens;
};
CKernelFacade::CKernelFacade() : m_impl(new SKernelFacadeImpl()) { }
// The destructor is needed in the .cpp file to implement pimpl idiom
// with unique_ptr. This is due to the fact that Kernel facade dtor
// has to call unique_ptr<KernelFacadeImpl> deleter function that calls the detete function
// on KernelFacadeImpl. Therefore it needs to know KernelFacadeImpl implementation.
CKernelFacade::~CKernelFacade()
{
this->unloadKernel();
uninitialize();
}
EPlayerReturnCodes CKernelFacade::loadKernel(const SLoadKernelCmd& command) const
{
if (m_impl->ctx)
{
std::cout << "WARNING: The kernel is already loaded" << std::endl;
return EPlayerReturnCodes::Success;
}
#if defined TARGET_OS_Windows
const CString kernelFile = Directories::getLibDir() + "/openvibe-kernel.dll";
#elif defined TARGET_OS_Linux
const CString kernelFile = Directories::getLibDir() + "/libopenvibe-kernel.so";
#elif defined TARGET_OS_MacOS
const CString kernelFile = Directories::getLibDir() + "/libopenvibe-kernel.dylib";
#endif
CKernelLoader& kernelLoader = m_impl->loader;
CString error;
if (!kernelLoader.load(kernelFile, &error))
{
std::cerr << "ERROR: impossible to load kernel from file located at: " << kernelFile << std::endl;
return EPlayerReturnCodes::KernelLoadingFailure;
}
kernelLoader.initialize();
Kernel::IKernelDesc* kernelDesc = nullptr;
kernelLoader.getKernelDesc(kernelDesc);
if (!kernelDesc)
{
std::cerr << "ERROR: impossible to retrieve kernel descriptor " << std::endl;
return EPlayerReturnCodes::KernelInvalidDesc;
}
CString configFile;
if (command.configFile && !command.configFile.get().empty()) { configFile = command.configFile.get().c_str(); }
else { configFile = CString(Directories::getDataDir() + "/kernel/openvibe.conf"); }
Kernel::IKernelContext* ctx = kernelDesc->createKernel("scenario-player", configFile);
if (!ctx)
{
std::cerr << "ERROR: impossible to create kernel context " << std::endl;
return EPlayerReturnCodes::KernelInvalidDesc;
}
ctx->initialize();
m_impl->ctx = ctx;
Toolkit::initialize(*ctx);
Kernel::IConfigurationManager& configurationManager = ctx->getConfigurationManager();
ctx->getPluginManager().addPluginsFromFiles(configurationManager.expand("${Kernel_Plugins}"));
ctx->getMetaboxManager().addMetaboxesFromFiles(configurationManager.expand("${Kernel_Metabox}"));
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CKernelFacade::unloadKernel() const
{
if (m_impl->ctx)
{
// not releasing the scenario before releasing the kernel
// causes a segfault on linux
auto& scenarioManager = m_impl->ctx->getScenarioManager();
for (auto& scenarioPair : m_impl->scenarios) { scenarioManager.releaseScenario(scenarioPair.second); }
Toolkit::uninitialize(*m_impl->ctx);
// m_impl->ctx->uninitialize();
Kernel::IKernelDesc* kernelDesc = nullptr;
m_impl->loader.getKernelDesc(kernelDesc);
kernelDesc->releaseKernel(m_impl->ctx);
m_impl->ctx = nullptr;
}
m_impl->loader.uninitialize();
m_impl->loader.unload();
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CKernelFacade::loadScenario(const SLoadScenarioCmd& command) const
{
assert(command.scenarioFile && command.scenarioName);
if (!m_impl->ctx)
{
std::cerr << "ERROR: Kernel is not loaded" << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
const std::string scenarioFile = command.scenarioFile.get();
const std::string scenarioName = command.scenarioName.get();
CIdentifier scenarioID;
auto& scenarioManager = m_impl->ctx->getScenarioManager();
if (!scenarioManager.importScenarioFromFile(scenarioID, scenarioFile.c_str(), OVP_GD_ClassId_Algorithm_XMLScenarioImporter))
{
std::cerr << "ERROR: failed to create scenario " << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
scenarioManager.getScenario(scenarioID).addAttribute(OV_AttributeId_ScenarioFilename, scenarioFile.c_str());
const auto it = m_impl->scenarios.find(scenarioName);
if (it != m_impl->scenarios.end()) { scenarioManager.releaseScenario(it->second); }
m_impl->scenarios[scenarioName] = scenarioID;
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CKernelFacade::updateScenario(const SUpdateScenarioCmd& command) const
{
assert(command.scenarioFile && command.scenarioName);
auto& scenarioManager = m_impl->ctx->getScenarioManager();
const auto scenarioName = command.scenarioName.get();
const auto scenarioFile = command.scenarioFile.get();
if (m_impl->scenarios.find(scenarioName) == m_impl->scenarios.end())
{
std::cerr << "ERROR: Trying to update a not loaded scenario " << scenarioName << std::endl;
return EPlayerReturnCodes::ScenarioNotLoaded;
}
auto& scenario = scenarioManager.getScenario(m_impl->scenarios[scenarioName]);
// check for boxes to be updated
// scenario.checkBoxesRequiringUpdate();
// update boxes to be updated
CIdentifier* listID = nullptr;
size_t elemCount = 0;
scenario.getOutdatedBoxIdentifierList(&listID, &elemCount);
for (size_t i = 0; i < elemCount; ++i) { scenario.updateBox(listID[i]); }
// export scenario to the destination file
if (!scenarioManager.exportScenarioToFile(scenarioFile.c_str(), m_impl->scenarios[scenarioName], OVP_GD_ClassId_Algorithm_XMLScenarioExporter))
{
std::cerr << "ERROR: failed to create scenario " << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CKernelFacade::setupScenario(const SSetupScenarioCmd& command) const
{
if (!m_impl->ctx)
{
std::cerr << "ERROR: Kernel is not loaded" << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
if (!command.scenarioName)
{
std::cerr << "ERROR: Missing scenario name for setup" << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
const auto name = command.scenarioName.get();
if (m_impl->scenarios.find(name) == m_impl->scenarios.end())
{
std::cerr << "ERROR: Trying to configure not loaded scenario " << name << std::endl;
return EPlayerReturnCodes::ScenarioNotLoaded;
}
// token list is just stored at this step for further use at runtime
// current token list overwrites the previous one
if (command.tokenList) { m_impl->scenarioTokens[name] = command.tokenList.get(); }
return EPlayerReturnCodes::Success;
}
EPlayerReturnCodes CKernelFacade::runScenarioList(const SRunScenarioCmd& command) const
{
assert(command.scenarioList);
if (!m_impl->ctx)
{
std::cerr << "ERROR: Kernel is not loaded" << std::endl;
return EPlayerReturnCodes::KernelInternalFailure;
}
auto scenarios = command.scenarioList.get();
// use of returnCode to store error and achive an RAII-like
// behavior by releasing all players at the end
EPlayerReturnCodes returnCode = EPlayerReturnCodes::Success;
// set up global token
if (command.tokenList) { setConfigTokens(m_impl->ctx->getConfigurationManager(), command.tokenList.get()); }
auto& playerManager = m_impl->ctx->getPlayerManager();
// Keep 2 different containers because identifier information is
// not relevant during the performance sensitive loop task.
// This might be premature optimization...
std::vector<Kernel::IPlayer*> players;
std::vector<CIdentifier> playerIDs;
// attach players to scenario
for (auto& pair : m_impl->scenarios)
{
auto name = pair.first;
if (std::find(scenarios.begin(), scenarios.end(), name) == scenarios.end()) { continue; } // not in the list of scenario to run
CIdentifier id;
if (!playerManager.createPlayer(id) || id == CIdentifier::undefined())
{
std::cerr << "ERROR: impossible to create player" << std::endl;
returnCode = EPlayerReturnCodes::KernelInternalFailure;
break;
}
Kernel::IPlayer* player = &playerManager.getPlayer(id);
// player identifier is pushed here to ensure a correct cleanup event if player initialization fails
playerIDs.push_back(id);
CNameValuePairList configTokens;
for (auto& token : m_impl->scenarioTokens[name]) { configTokens.setValue(token.first.c_str(), token.second.c_str()); }
// Scenario attachment with setup of local token
if (!player->setScenario(pair.second, &configTokens))
{
std::cerr << "ERROR: impossible to set player scenario " << name << std::endl;
returnCode = EPlayerReturnCodes::KernelInternalFailure;
break;
}
if (player->initialize() == Kernel::EPlayerReturnCodes::Success)
{
if (command.playMode && command.playMode.get() == EPlayerPlayMode::Fastfoward) { player->forward(); }
else { player->play(); }
players.push_back(player);
}
else
{
std::cerr << "ERROR: impossible to initialize player for scenario " << name << std::endl;
returnCode = EPlayerReturnCodes::KernelInternalFailure;
break;
}
}
if (returnCode == EPlayerReturnCodes::Success)
{
// loop until timeout
const uint64_t startTime = System::Time::zgetTime();
uint64_t lastLoopTime = startTime;
// cannot directly feed secondsToTime with parameters.m_MaximumExecutionTime
// because it could overflow
const double boundedMaxExecutionTimeInS = CTime::max().toSeconds();
uint64_t maxExecutionTimeInFixedPoint;
if (command.maximumExecutionTime && command.maximumExecutionTime.get() > 0 && command.maximumExecutionTime.get() < boundedMaxExecutionTimeInS)
{
maxExecutionTimeInFixedPoint = CTime(double(command.maximumExecutionTime.get())).time();
}
else { maxExecutionTimeInFixedPoint = std::numeric_limits<uint64_t>::max(); }
bool allStopped{ false };
while (!allStopped) // negative condition here because it is easier to reason about it
{
const uint64_t currentTime = System::Time::zgetTime();
allStopped = true;
for (auto* p : players)
{
if (p->getStatus() != Kernel::EPlayerStatus::Stop)
{
if (!p->loop(currentTime - lastLoopTime, maxExecutionTimeInFixedPoint)) { returnCode = EPlayerReturnCodes::KernelInternalFailure; }
}
if (p->getCurrentSimulatedTime() >= maxExecutionTimeInFixedPoint) { p->stop(); }
allStopped &= (p->getStatus() == Kernel::EPlayerStatus::Stop);
}
lastLoopTime = currentTime;
}
}
// release players
for (auto& id : playerIDs)
{
playerManager.getPlayer(id).uninitialize();
playerManager.releasePlayer(id);
}
return returnCode;
}
} // namespace OpenViBE
@@ -0,0 +1,109 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
#include <memory>
namespace OpenViBE {
struct SInitCmd;
struct SResetCmd;
struct SLoadKernelCmd;
struct SLoadScenarioCmd;
struct SUpdateScenarioCmd;
struct SSetupScenarioCmd;
struct SRunScenarioCmd;
/**
* \class CKernelFacade
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Wrapper class used to access Kernel features
* \ingroup ScenarioPlayer
*
* This class is one-to-many interface used as a central point
* to access a subset of Kernel features.
*
*/
class CKernelFacade final
{
public:
CKernelFacade();
~CKernelFacade();
/**
* \brief Initialize session parameters
*/
static EPlayerReturnCodes initialize() { return EPlayerReturnCodes::Success; }
/**
* \brief Reset session parameters
*/
static EPlayerReturnCodes uninitialize() { return EPlayerReturnCodes::Success; }
/**
* \brief Load kernel
* \param[in] command command containing all mandatory properties
*/
EPlayerReturnCodes loadKernel(const SLoadKernelCmd& command) const;
/**
* \brief Unload kernel
*/
EPlayerReturnCodes unloadKernel() const;
/**
* \brief Load scenario
* \param[in] command command containing all mandatory properties
*/
EPlayerReturnCodes loadScenario(const SLoadScenarioCmd& command) const;
/**
* \brief Update scenario
* \param[in] command command containing all mandatory properties
*/
EPlayerReturnCodes updateScenario(const SUpdateScenarioCmd& command) const;
/**
* \brief Configure scenario
* \param[in] command command containing all mandatory properties
*/
EPlayerReturnCodes setupScenario(const SSetupScenarioCmd& command) const;
/**
* \brief Run one or multiple scenarios
* \param[in] command command containing all mandatory properties
*/
EPlayerReturnCodes runScenarioList(const SRunScenarioCmd& command) const;
private:
// disable copy and assignment because it is not meant to used
// as a value class event if it is not inheritable
CKernelFacade(const CKernelFacade&) = delete;
CKernelFacade& operator=(const CKernelFacade&) = delete;
struct SKernelFacadeImpl;
std::unique_ptr<SKernelFacadeImpl> m_impl;
};
} // namespace OpenViBE
@@ -0,0 +1,72 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
namespace OpenViBE {
class CKernelFacade;
/**
* \struct SCommand
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Base abstract struct for commands
* \ingroup ScenarioPlayer
*
* A command is an object that encapsulates all necessary information to perform an action later one.
* Typically, a command implementation should contain an implementation of CommandInterface interface,
* and a list of properties.
*
*/
struct SCommand
{
SCommand() = default;
virtual ~SCommand() = default;
friend std::ostream& operator<<(std::ostream& os, const SCommand& cmd);
/**
* \brief Execute the command
* \param[in] kernelFacade the kernel facade that gives access to kernel features
*/
virtual EPlayerReturnCodes execute(CKernelFacade& kernelFacade) const = 0;
protected:
// use of the non-virtual interface pattern to implement printing in the class hierarchy
virtual void doPrint(std::ostream& os) const = 0;
private:
// disable copy and assignment because it is not meant to used
// as a value class
SCommand(const SCommand&) = delete;
SCommand& operator=(const SCommand&) = delete;
};
inline std::ostream& operator<<(std::ostream& os, const SCommand& cmd)
{
cmd.doPrint(os);
return os;
}
} // namespace OpenViBE
@@ -0,0 +1,82 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
#include <vector>
#include <memory>
namespace OpenViBE {
struct SCommand;
/**
* \class ICommandParser
* \author cgarraud (INRIA)
* \date 2016-01-27
* \brief Base abstract class for command parser
* \ingroup ScenarioPlayer
*
* Command parsers aim at parsing a list of commands from a specific input.
*
*/
class ICommandParser
{
public:
virtual ~ICommandParser() = default;
ICommandParser() = default;
/**
* \brief Initialize parser
*
*/
virtual void initialize() = 0;
/**
* \brief Unitialize parser
*
*/
virtual void uninitialize() = 0;
/**
* \brief Retrieve the list of commands
* \pre This method should be called after the parse() method
*
*/
virtual std::vector<std::shared_ptr<SCommand>> getCommandList() const = 0;
/**
* \brief Retrieve the list of commands
* \pre This method should be called after the initialize() method
*
*/
virtual EPlayerReturnCodes parse() = 0;
private:
// disable copy and assignment because it is not meant to used
// as a value class
ICommandParser(const ICommandParser&) = delete;
ICommandParser& operator=(const ICommandParser&) = delete;
};
} // namespace OpenViBE
@@ -0,0 +1,461 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovsp_defines.h"
#include <boost/variant.hpp>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#if defined TARGET_OS_Windows
#include <codecvt>
#include <locale>
#include <Windows.h>
#endif
namespace OpenViBE {
/**
* \struct SProgramOptionsTraits
* \author cgarraud (INRIA)
* \date 2016-01-26
* \brief Helper class for ProgramOptions type checking
* \ingroup ScenarioPlayer
*
* This class provides aliases to types currently handled by ProgramOptions class
* as well as type checking meta-programming features.
*
*/
struct SProgramOptionsTraits
{
// add a new type here (type must be default constructible)
// and add the corresponding callback in ProgramOptions
// types handled by ProgramOptions
using TokenPair = std::pair<std::string, std::string>;
using String = std::string;
using Integer = int;
using Float = double;
using StringList = std::vector<std::string>;
using IntegerList = std::vector<int>;
using FloatList = std::vector<double>;
using TokenPairList = std::vector<TokenPair>;
};
namespace ProgramOptionsUtils {
// template meta-programming features
/* Base type that represents a true value at compile-time */
struct STrueType
{
static const bool value = true;
};
/* Base type that represents a false value at compile-time */
struct SFalseType
{
static const bool value = false;
};
/* SIsCompliant is used to statically check if a type is compliant with the handled types */
template <typename T>
struct SIsCompliant : SFalseType { };
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::TokenPair> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::TokenPairList> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::String> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::StringList> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::Integer> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::IntegerList> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::Float> : STrueType {};
/* Specialization of the template for compliant types */
template <>
struct SIsCompliant<SProgramOptionsTraits::FloatList> : STrueType {};
/* SIsSignatureCompliant is used to statically checked a list of type is compliant with handled type */
template <typename... TList>
struct SIsSignatureCompliant;
/* Specialization for empty list */
template <>
struct SIsSignatureCompliant<> : STrueType {};
/* Specialization for non-empty list */
template <typename THead, typename... TTail>
struct SIsSignatureCompliant<THead, TTail...> :
std::conditional<SIsCompliant<THead>::value, SIsSignatureCompliant<TTail...>, SFalseType>::type { };
/* SIsIn is used to statically check if a type T is in a list of types TList*/
template <typename T, typename... TList>
struct SIsIn; // interface
/* Specialization for empty list */
template <typename T>
struct SIsIn<T> : SFalseType {};
/* Specialization for list where 1st element is a match */
template <typename T, typename... TTail>
struct SIsIn<T, T, TTail...> : STrueType {};
/* Specialization for list of many elements */
template <typename T, typename THead, typename... TTail>
struct SIsIn<T, THead, TTail...> : SIsIn<T, TTail...> {};
/* SHasDuplicate is used to statically check if a list of types has duplicates*/
template <typename... TList>
struct SHasDuplicate;
/* Specialization for empty list */
template <>
struct SHasDuplicate<> : SFalseType {};
/* Specialization for 1-element list */
template <typename T>
struct SHasDuplicate<T> : SFalseType {};
/* Specialization for list of many elements */
template <typename THead, typename... TTail>
struct SHasDuplicate<THead, TTail...> : std::conditional<SIsIn<THead, TTail...>::value, STrueType, SHasDuplicate<TTail...>>::type { };
} // namespace ProgramOptionsUtils
/**
* \class ProgramOptions
* \author cgarraud (INRIA)
* \date 2016-01-25
* \brief Command-line options parser
* \ingroup ScenarioPlayer
*
* This class is a basic class used to parse program options.
* Input format requirements:
* - Option prefix must be '-' or '--' e.g. cmd -help or --help
* - Option value assignment must be set with '=': e.g. cmd -config=myfile.txt
* - Option value that consists of pair are set with (): e.g. cmd -newToken=(key,value)
* .
*
* Template parameters are the list of option types that can be handled by the class.
* This list of options must comply to types defined in ProgramOptionsTrait.\n
*
* Parsing options occurs in 2 steps:
* - Populating the list of possible options with simple options (e.g. --help) and value options (e.g. --option=value)
* - Parsing options from command line
* .
*
* \todo The parser has only be tested for the player use. It needs more in-depth testing
* to be used in another context. Moreover, it should be extended to accept any type.
*
* \note The implementation is trivial. Prefer the use of robust and fully featured
* boost program_options compiled library if possible.
*
*/
template <typename TFirst, typename... TTypes>
class ProgramOptions final
{
public:
// static assert are used to raise understandable errors at compile time
static_assert(!ProgramOptionsUtils::SHasDuplicate<TFirst, TTypes...>::value, "Duplicates in the type list");
static_assert(ProgramOptionsUtils::SIsSignatureCompliant<TFirst, TTypes...>::value, "TTypes not handled by ProgramOptions");
/**
* Struct used to store used-defined option parameters.
*/
struct SOptionDesc // using a struct allows more extensibility than method parameters
{
/** Option shortname (e.g. h for help) */
std::string shortName;
/** Option description used for printing option list */
std::string desc;
};
/**
* \brief Add global description to the list of options
* \param[in] desc the global description in printable format
*
* The global description is used as additional printable documentation
* when printOptionsDesc() is called.
*
*/
void setGlobalDesc(const std::string& desc);
/**
* \brief Add a simple option to the internal dictionary
* \param[in] name the option name
* \param[in] optionDesc the option description
*
* Simple options are option withou value (e.g. --help or --version)
*
*/
void addSimpleOption(const std::string& name, const SOptionDesc& optionDesc);
/**
* \brief Add a value option to the internal dictionary
* \param[in] name the option name
* \param[in] optionDesc the option description
*
* Template paramter T: The type of the option to be added
*/
template <typename T>
void addValueOption(const std::string& name, const SOptionDesc& optionDesc);
/**
* \brief Parse command line options
* \pre addSimpleOption and addValueOption must be called to populate options dictionary
* \param[in] argc number of arguments
* \param[in] argv pointer to the list of arguments
* \return: false if an error occurred during parsing, true otherwise
*/
bool parse(int argc, char** argv);
/**
* \brief Check if an option was parsed or not
* \pre Must be called after parse()
* \param[in] name the option name
* \return: true if the option was parsed, false otherwise
*/
bool hasOption(const std::string& name) const;
/**
* \brief Get option value
* \pre hasOption() should be called to ensure the option is available
* \param[in] name the option name
* \return the option value (will be the default value if the option was not parsed)
*
* Template paramter T: the type of the option to retrieve (must match the type used to set
* the option with addValueOption())
*/
template <typename T>
T getOptionValue(const std::string& name) const;
/**
* \brief print all option descriptions
*/
void printOptionsDesc() const;
private:
// The visitor allows us to apply the correct parsing
// for any type (see boost::variant documentation for more details).
// Presently, it is pretty ugly. This should be refactored for
// as there is a lot of behavorial redundancy.
class OptionVisitor : public boost::static_visitor<>
{
public:
OptionVisitor(std::string& value) : m_value(value) { }
void operator()(SProgramOptionsTraits::Integer& operand) const { operand = std::stoi(m_value); }
void operator()(SProgramOptionsTraits::Float& operand) const { operand = std::stod(m_value); }
void operator()(SProgramOptionsTraits::String& operand) const { operand = m_value; }
void operator()(SProgramOptionsTraits::TokenPair& operand) const { operand = this->parsePair(m_value); }
void operator()(SProgramOptionsTraits::IntegerList& operand) const { operand.push_back(std::stoi(m_value)); }
void operator()(SProgramOptionsTraits::FloatList& operand) const { operand.push_back(std::stod(m_value)); }
void operator()(SProgramOptionsTraits::StringList& operand) const { operand.push_back(m_value); }
void operator()(SProgramOptionsTraits::TokenPairList& operand) const { operand.push_back(this->parsePair(m_value)); }
private:
static SProgramOptionsTraits::TokenPair parsePair(const std::string& str);
std::string& m_value;
};
using OptionValue = boost::variant<TFirst, TTypes...>;
// the pair contais a boolean to quickly know if an option
// is a simple option or a value option
using FullOptionDesc = std::pair<bool, SOptionDesc>;
std::string m_globalDesc;
std::map<std::string, FullOptionDesc> m_descs;
std::map<std::string, OptionValue> m_values;
std::vector<std::string> m_options;
};
///////////////////////////////////////////
/////// Definition ProgramOptions /////////
///////////////////////////////////////////
template <typename TFirst, typename... TTypes>
void ProgramOptions<TFirst, TTypes...>::setGlobalDesc(const std::string& desc) { m_globalDesc = desc; }
template <typename TFirst, typename... TTypes>
void ProgramOptions<TFirst, TTypes...>::addSimpleOption(const std::string& name, const SOptionDesc& optionDesc)
{
m_descs[name] = std::make_pair(true, optionDesc);
}
template <typename TFirst, typename... TTypes>
template <typename T>
void ProgramOptions<TFirst, TTypes...>::addValueOption(const std::string& name, const SOptionDesc& optionDesc)
{
m_descs[name] = std::make_pair(false, optionDesc);
T defaultValue{}; // with this implementation, only default constructible type can be added
m_values[name] = defaultValue;
}
template <typename TFirst, typename... TTypes>
bool ProgramOptions<TFirst, TTypes...>::hasOption(const std::string& name) const
{
return std::find(m_options.begin(), m_options.end(), name) != m_options.end();
}
template <typename TFirst, typename... TTypes>
template <typename T>
T ProgramOptions<TFirst, TTypes...>::getOptionValue(const std::string& name) const
{
T value{};
try { value = boost::get<T>(m_values.at(name)); }
catch (const std::exception& e) { std::cerr << "ERROR: Caught exception during option value retrieval: " << e.what() << std::endl; }
return value;
}
template <typename TFirst, typename... TTypes>
bool ProgramOptions<TFirst, TTypes...>::parse(const int argc, char** argv)
{
std::vector<std::string> args;
#if defined TARGET_OS_Windows
int nArg;
LPWSTR* argListUtf16 = CommandLineToArgvW(GetCommandLineW(), &nArg);
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
for (int i = 0; i < nArg; ++i) { args.push_back(converter.to_bytes(argListUtf16[i])); }
#else
args = std::vector<std::string>(argv, argv + argc);
#endif
for (int i = 1; i < argc; ++i)
{
std::string arg = args[i];
const auto argSplit = arg.find_first_of('='); // = is the separator for value option
std::string key;
if (argSplit == std::string::npos) { key = arg; } // simple option
else { key = arg.substr(0, argSplit); } // value option
// first check if the key exists
auto keyMatch = std::find_if(m_descs.begin(), m_descs.end(), [&](const std::pair<std::string, FullOptionDesc>& p)
{
const auto& desc = p.second.second;
return (("-" + p.first) == key) || (("--" + p.first) == key) || (("-" + desc.shortName) == key) || (
("--" + desc.shortName) == key);
}
);
if (keyMatch == m_descs.end())
{
std::cout << "WARNING: Found unknown option: " << key << std::endl;
std::cout << "Skipping..." << std::endl;
continue;
}
if (!keyMatch->second.first) // value option
{
if (key == arg)
{
std::cerr << "ERROR: No value set for argument: " << key << std::endl;
return false;
}
std::string val = arg.substr(argSplit + 1, arg.size() - argSplit - 1); // take value part of the arg
try { boost::apply_visitor(OptionVisitor(val), m_values[keyMatch->first]); }
catch (const std::exception& e)
{
std::cerr << "ERROR: Caught exception during option parsing: " << e.what() << std::endl;
std::cerr << "Could not parse option with key = " << key << " and value = " << val << std::endl;
return false;
}
}
m_options.push_back(keyMatch->first);
}
return true;
}
template <typename TFirst, typename... TTypes>
void ProgramOptions<TFirst, TTypes...>::printOptionsDesc() const
{
if (!m_globalDesc.empty()) { std::cout << m_globalDesc << std::endl; }
std::cout << "TList of available options:\n" << std::endl;
for (auto& option : m_descs)
{
std::cout << "Option: --" << option.first << std::endl;
const auto& desc = option.second.second;
if (!desc.shortName.empty()) { std::cout << "Shortname: --" << desc.shortName << std::endl; }
std::cout << "Description: " << std::endl;
std::cout << desc.desc << std::endl << std::endl;
}
}
///////////////////////////////////////////
/////// Definition Internal Visitor ///////
///////////////////////////////////////////
template <typename TFirst, typename... TTypes>
SProgramOptionsTraits::TokenPair ProgramOptions<TFirst, TTypes...>::OptionVisitor::parsePair(const std::string& str)
{
const auto split = str.find_first_of(':');
const auto size = str.size();
// (a:b) pattern expected
// minimal regex std::regex("\\(.+:.+\\)")
if (!(size >= 5 && str[0] == '(' && str[size - 1] == ')') || split == std::string::npos)
{
throw std::runtime_error("Failed to parse token pair from value: " + str);
}
// magic 2 numbers is because substr takes a length as second parameter
// 2 = remove the last ) + account for the first one
return std::make_pair(str.substr(1, split - 1), str.substr(split + 1, size - split - 2));
}
} // namespace OpenViBE
@@ -0,0 +1,27 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include "ovsp_defines.h"
@@ -0,0 +1,85 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ovspTProgramOptions.hpp"
namespace OpenViBE {
/**
* \defgroup ScenarioPlayer Scenario Player
*/
/**
* \class EPlayerReturnCodes
* \author cgarraud
* \date 2016-01-25
* \brief Scenario player list of potential return code
* \ingroup ScenarioPlayer
*/
enum class EPlayerReturnCodes
{
Success = 0, ///< No error during execution */
InvalidArg, ///< Invalid command-line options */
MissingMandatoryArgument, ///< A mandatory argument is missing */
BadArg, ///< An argument is given with a wrong value */
OpeningFileFailure, ///< A file could not be opened */
ParsingCommandFailure, ///< General parsing command error*/
UnkownFailure, ///< Error of unknown type*/
KernelLoadingFailure, ///< Kernel loading failed*/
KernelInvalidDesc, ///< Invalid kernel descriptor*/
KernelInvalidContext, ///< Invalid kernel context*/
KernelInternalFailure, ///< Generic error type for kernel internal error*/
ScenarioNotLoaded ///< Error triggered when an action is requested on an unknown scenario */
};
/**
* \class EPlayerPlayMode
* \author cgarraud
* \date 2016-01-25
* \brief Way of playing a scenario
* \ingroup ScenarioPlayer
*/
enum class EPlayerPlayMode { Standard = 0, Fastfoward };
/* Define the common parser to be used in the application */
using ProgramOptionParser = ProgramOptions<SProgramOptionsTraits::String, SProgramOptionsTraits::Float, SProgramOptionsTraits::TokenPairList>;
inline std::string toString(const EPlayerReturnCodes code)
{
switch (code)
{
case EPlayerReturnCodes::Success: return "Success";
case EPlayerReturnCodes::InvalidArg: return "Invalid Arg";
case EPlayerReturnCodes::MissingMandatoryArgument: return "Missing Mandatory Argument";
case EPlayerReturnCodes::BadArg: return "Bad Arg";
case EPlayerReturnCodes::OpeningFileFailure: return "Opening File Failure";
case EPlayerReturnCodes::ParsingCommandFailure: return "Parsing Command Failure";
case EPlayerReturnCodes::UnkownFailure: return "Unkown Failure";
case EPlayerReturnCodes::KernelLoadingFailure: return "Kernel Loading Failure";
case EPlayerReturnCodes::KernelInvalidDesc: return "Kernel Invalid Desc";
case EPlayerReturnCodes::KernelInvalidContext: return "Kernel Invalid Context";
case EPlayerReturnCodes::KernelInternalFailure: return "Kernel Internal Failure";
case EPlayerReturnCodes::ScenarioNotLoaded: return "Scenario Not Loaded";
default: return "Invalid Code";
}
}
} // namespace OpenViBE
@@ -0,0 +1,147 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE SDK Test Software
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <vector>
#include <memory>
#include "ovsp_defines.h"
#include "ovspCKernelFacade.h"
#include "ovspCCommand.h"
#include "ovspCCommandLineOptionParser.h"
#include "ovspCCommandFileParser.h"
namespace OpenViBE {
void initializeParser(ProgramOptionParser& parser)
{
const std::string desc =
R"d(Usage: program options
Program can be run in express mode to directly execute a scenario
Program can be run in command mode to execute list of commands from a file
)d";
parser.setGlobalDesc(desc);
parser.addSimpleOption("help", { "h", "Help" });
parser.addSimpleOption("version", { "v", "Program version" });
parser.addValueOption<SProgramOptionsTraits::String>("mode", { "m", "Execution mode: 'x' for express, 'c' for command [mandatory]" });
// express mode options
parser.addValueOption<SProgramOptionsTraits::String>("config-file", { "", "Path to configuration file (express mode only)" });
parser.addValueOption<SProgramOptionsTraits::String>("scenario-file", { "", "Path to scenario file (express mode only) [mandatory]" });
parser.addValueOption<SProgramOptionsTraits::String>("updated-scenario-file", {
"",
"Enable update process instead of playing scenario. Path to the updated scenario file (express mode only)."
});
parser.addValueOption<SProgramOptionsTraits::String>("play-mode",
{ "", "Play mode: std for standard and ff for fast-foward (express mode only) [default=std]" });
parser.addValueOption<SProgramOptionsTraits::Float>("max-time", { "", "Scenarios playing execution time limit (express mode only)" });
parser.addValueOption<SProgramOptionsTraits::TokenPairList>("dg", { "", "Global user-defined token: -dg=\"(token:value)\" (express mode only)" });
parser.addValueOption<SProgramOptionsTraits::TokenPairList>("ds", { "", "Scenario user-defined token: -ds=\"(token:value)\" (express mode only)" });
// command mode options
parser.addValueOption<SProgramOptionsTraits::String>("command-file", { "", "Path to command file (command mode only) [mandatory]" });
}
} // namespace OpenViBE
int main(int argc, char** argv)
{
OpenViBE::ProgramOptionParser optionParser;
initializeParser(optionParser);
if (!optionParser.parse(argc, argv))
{
std::cerr << "ERROR: Failed to parse arguments" << std::endl;
return int(OpenViBE::EPlayerReturnCodes::InvalidArg);
}
if (optionParser.hasOption("help"))
{
optionParser.printOptionsDesc();
return int(OpenViBE::EPlayerReturnCodes::Success);
}
if (optionParser.hasOption("version"))
{
// PROJECT_VERSION is added to definition from cmake
std::cout << "version: " << PROJECT_VERSION << std::endl;
return int(OpenViBE::EPlayerReturnCodes::Success);
}
if (optionParser.hasOption("mode") || optionParser.hasOption("updated-scenario-file"))
{
// command parser type is selected from mode
std::unique_ptr<OpenViBE::ICommandParser> commandParser{ nullptr };
const auto mode = optionParser.getOptionValue<OpenViBE::SProgramOptionsTraits::String>("mode");
if (mode == "c")
{
// check for the mandatory commad file
if (optionParser.hasOption("command-file"))
{
commandParser.reset(new OpenViBE::CommandFileParser(optionParser.getOptionValue<std::string>("command-file")));
}
else
{
std::cerr << "ERROR: mandatory option 'command-file' not set" << std::endl;
return int(OpenViBE::EPlayerReturnCodes::MissingMandatoryArgument);
}
}
else if ((mode == "x") || optionParser.hasOption("updated-scenario-file")) { commandParser.reset(new OpenViBE::CommandLineOptionParser(optionParser)); }
else
{
std::cerr << "ERROR: unknown mode set" << std::endl;
std::cerr << "Mode must be 'x' or 'c'" << std::endl;
return int(OpenViBE::EPlayerReturnCodes::InvalidArg);
}
commandParser->initialize();
try
{
auto returnCode = commandParser->parse();
if (returnCode == OpenViBE::EPlayerReturnCodes::Success)
{
OpenViBE::CKernelFacade kernel;
for (auto& cmd : commandParser->getCommandList())
{
returnCode = cmd->execute(kernel);
if (returnCode != OpenViBE::EPlayerReturnCodes::Success) { return int(returnCode); }
}
}
else { return int(returnCode); }
}
catch (const std::exception& e)
{
std::cerr << "ERROR: received unexpected exception: " << e.what() << std::endl;
return int(OpenViBE::EPlayerReturnCodes::UnkownFailure);
}
}
else
{
std::cerr << "ERROR: mandatory option 'mode' not set" << std::endl;
return int(OpenViBE::EPlayerReturnCodes::MissingMandatoryArgument);
}
return int(OpenViBE::EPlayerReturnCodes::Success);
}