This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
PROJECT(openvibe-kernel)
OV_ADD_THIS_TO_PROJECT_LIST()
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})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl)
INCLUDE("FindSourceDependencyLepton")
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${KERNEL_FOLDER}
COMPILE_FLAGS "-DOVK_Exports -DOVK_Shared")
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindOpenViBEModuleSystem")
INCLUDE("FindOpenViBEModuleXML")
INCLUDE("FindThirdPartyBoost")
# ---------------------------------
# Finds standard library dl
# Adds library to target
# Adds include path
# ---------------------------------
IF(UNIX)
FIND_LIBRARY(LIB_STANDARD_MODULE_DL dl)
IF(LIB_STANDARD_MODULE_DL)
MESSAGE(STATUS " Found dl...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_DL})
ELSE(LIB_STANDARD_MODULE_PTHREAD)
MESSAGE(STATUS " FAILED to find dl...")
ENDIF(LIB_STANDARD_MODULE_DL)
ENDIF(UNIX)
# ---------------------------------
# Finds standard library pthread
# Adds library to target
# Adds include path
# ---------------------------------
IF(UNIX)
FIND_LIBRARY(LIB_STANDARD_MODULE_PTHREAD pthread)
IF(LIB_STANDARD_MODULE_PTHREAD)
MESSAGE(STATUS " Found pthread...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_PTHREAD})
ELSE(LIB_STANDARD_MODULE_PTHREAD)
MESSAGE(STATUS " FAILED to find pthread...")
ENDIF(LIB_STANDARD_MODULE_PTHREAD)
ENDIF(UNIX)
# ---------------------------------
# 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})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/kernel PATTERN "*-base" EXCLUDE)
# -----------------------------
# Install and configure .conf
# -----------------------------
# This construct is to fetch the documentation specific to this version.
# In addition, it tweaks the patch version to allow the "1.0.0+git" style version to
# fetch the "1.0.0" (base) version documentation as we don't have git version docs on the web.
STRING(REGEX REPLACE "[^0-9]" "" PROJECT_VERSION_PATCH_PRUNED ${PROJECT_VERSION_PATCH})
SET(OV_PROJECT_BASE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH_PRUNED})
@@ -0,0 +1,14 @@
material cone_ov_material
{
technique
{
pass
{
ambient 1 1 1
diffuse 1 1 1
// specular 0 0 0 10
// emissive 0 0 0
scene_blend alpha_blend
}
}
}
@@ -0,0 +1,28 @@
######################################################################################
## OpenViBE delayed configuration file
##
## === This file is loaded later when a scenario execution starts ===
##
## - 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
######################################################################################
#
# Add some tokens here
#
#####################################################################################
# Include custom configuration
#####################################################################################
CustomDelayedConfigurationPrefixWindows = ${Path_UserData}/openvibe-delayed
CustomDelayedConfigurationPrefixLinux = ${Path_UserData}/openvibe-delayed
CustomDelayedConfigurationSuffixWindows = .conf
CustomDelayedConfigurationSuffixLinux = rc
CustomDelayedConfiguration = ${CustomDelayedConfigurationPrefix${OperatingSystem}}${CustomDelayedConfigurationSuffix${OperatingSystem}}
Include = ${CustomDelayedConfiguration}
@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Base scenario schema for OpenViBE XML scenario -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Types definitions -->
<!-- Identifier Type -->
<xs:simpleType name="IdentifierType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Name Type -->
<xs:simpleType name="NameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- ParameterType (settings value, attributes values) -->
<xs:simpleType name="ParameterType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Index Type -->
<xs:simpleType name="IndexType">
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<!-- Scenario structure -->
<!-- Root node -->
<xs:element name="OpenViBE-Scenario">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" name="Creator" type="xs:string"/>
<xs:element minOccurs="0" name="CreatorVersion" type="xs:string"/>
<xs:element minOccurs="0" ref="Boxes"/>
<xs:element minOccurs="0" ref="Links"/>
<xs:element minOccurs="0" ref="Comments"/>
<xs:element minOccurs="0" ref="VisualisationTree"/>
<xs:element minOccurs="0" ref="MessageLinks"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Boxes -->
<xs:element name="Boxes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Box"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Box">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="AlgorithmClassIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Links -->
<xs:element name="Links">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Link"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Link">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element ref="Source"/>
<xs:element ref="Target"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Source">
<xs:complexType>
<xs:all>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:element name="BoxOutputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Target">
<xs:complexType>
<xs:all>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:element name="BoxInputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Comments -->
<xs:element name="Comments">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Comment"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Comment">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Text" type="xs:string"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Message Links -->
<xs:element name="MessageLinks">
<xs:complexType/>
</xs:element>
<!-- VisualisationTree -->
<xs:element name="VisualisationTree">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="VisualisationWidget"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="VisualisationWidget">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="Identifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="Name" type="NameType"/>
<xs:element minOccurs="0" name="Type" type="xs:integer"/>
<xs:element minOccurs="0" name="ParentIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="Index" type="IndexType"/>
<xs:element minOccurs="0" name="BoxIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="NumChildren" type="xs:integer"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Settings -->
<xs:element name="Settings">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Setting"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Setting">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="DefaultValue" type="ParameterType"/>
<xs:element minOccurs="0" name="Value" type="ParameterType"/>
<xs:element minOccurs="0" name="Modifiability" type="xs:boolean"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Inputs -->
<xs:element name="Inputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Input"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Input">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="LinkedBoxInputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Outputs -->
<xs:element name="Outputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Output"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Output">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="LinkedBoxOutputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Attributes -->
<xs:element name="Attributes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Attribute"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Attribute">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Value" type="ParameterType"/>
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
@@ -0,0 +1,221 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Base scenario schema for OpenViBE XML scenario -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Types definitions -->
<!-- Identifier Type -->
<xs:simpleType name="IdentifierType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Name Type -->
<xs:simpleType name="NameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- ParameterType (settings value, attributes values) -->
<xs:simpleType name="ParameterType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Index Type -->
<xs:simpleType name="IndexType">
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<!-- Scenario structure -->
<!-- Root node -->
<xs:element name="OpenViBE-Scenario">
<xs:complexType>
<xs:all>
<!-- Fixed format version number to implement a fallback validation mechanism -->
<xs:element name="FormatVersion" type="xs:string" fixed="1"/>
<!-- Version of the entity used to generate the scenario -->
<xs:element name="CreatorVersion" type="xs:string"/>
<!-- Name of the entity used to generate the scenario -->
<xs:element name="Creator" type="xs:string"/>
<xs:element minOccurs="0" ref="Boxes"/>
<xs:element minOccurs="0" ref="Links"/>
<xs:element minOccurs="0" ref="Comments"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
<xs:element minOccurs="0" ref="Metadata"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Boxes -->
<xs:element name="Boxes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Box"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Box">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="AlgorithmClassIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Links -->
<xs:element name="Links">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Link"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Link">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element ref="Source"/>
<xs:element ref="Target"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Source">
<xs:complexType>
<xs:all>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:element name="BoxOutputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Target">
<xs:complexType>
<xs:all>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:element name="BoxInputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Comments -->
<xs:element name="Comments">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Comment"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Comment">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Text" type="xs:string"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Settings -->
<xs:element name="Settings">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Setting"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Setting">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="DefaultValue" type="ParameterType"/>
<xs:element minOccurs="0" name="Value" type="ParameterType"/>
<xs:element minOccurs="0" name="Modifiability" type="xs:boolean"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Inputs -->
<xs:element name="Inputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Input"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Input">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="LinkedBoxInputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Outputs -->
<xs:element name="Outputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Output"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Output">
<xs:complexType>
<xs:all>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" name="LinkedBoxOutputIndex" type="IndexType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Attributes -->
<xs:element name="Attributes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Attribute"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Attribute">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Value" type="ParameterType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Metadata -->
<xs:element name="Metadata">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Entry"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Entry">
<xs:complexType>
<xs:sequence>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Type" type="IdentifierType"/>
<xs:element name="Data" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
@@ -0,0 +1,236 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Base scenario schema for OpenViBE XML scenario -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!-- Types definitions -->
<!-- Identifier Type -->
<xs:simpleType name="IdentifierType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Name Type -->
<xs:simpleType name="NameType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- ParameterType (settings value, attributes values) -->
<xs:simpleType name="ParameterType">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- Index Type -->
<xs:simpleType name="IndexType">
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<!-- Scenario structure -->
<!-- Root node -->
<xs:element name="OpenViBE-Scenario">
<xs:complexType>
<xs:all>
<!-- Fixed format version number to implement a fallback validation mechanism -->
<xs:element name="FormatVersion" type="xs:string" fixed="2"/>
<!-- Version of the entity used to generate the scenario -->
<xs:element name="CreatorVersion" type="xs:string"/>
<!-- Name of the entity used to generate the scenario -->
<xs:element name="Creator" type="xs:string"/>
<xs:element minOccurs="0" ref="Boxes"/>
<xs:element minOccurs="0" ref="Links"/>
<xs:element minOccurs="0" ref="Comments"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
<xs:element minOccurs="0" ref="Metadata"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Boxes -->
<xs:element name="Boxes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Box"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Box">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="AlgorithmClassIdentifier" type="IdentifierType"/>
<xs:element minOccurs="0" ref="Settings"/>
<xs:element minOccurs="0" ref="Inputs"/>
<xs:element minOccurs="0" ref="Outputs"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Links -->
<xs:element name="Links">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Link"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Link">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element ref="Source"/>
<xs:element ref="Target"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="Source">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:choice>
<xs:element name="BoxOutputIndex" type="IndexType"/>
<xs:element name="BoxOutputIdentifier" type="IdentifierType"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Target">
<xs:complexType>
<xs:sequence>
<xs:element name="BoxIdentifier" type="IdentifierType"/>
<xs:choice>
<xs:element name="BoxInputIndex" type="IndexType"/>
<xs:element name="BoxInputIdentifier" type="IdentifierType"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Comments -->
<xs:element name="Comments">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Comment"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Comment">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Text" type="xs:string"/>
<xs:element minOccurs="0" ref="Attributes"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Settings -->
<xs:element name="Settings">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Setting"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Setting">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" name="Identifier" type="IdentifierType"/>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element name="DefaultValue" type="ParameterType"/>
<xs:element minOccurs="0" name="Value" type="ParameterType"/>
<xs:element minOccurs="0" name="Modifiability" type="xs:boolean"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Inputs -->
<xs:element name="Inputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Input"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Input">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="Identifier" type="IdentifierType"/>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:choice>
<xs:element minOccurs="0" name="LinkedBoxInputIndex" type="IndexType"/>
<xs:element minOccurs="0" name="LinkedBoxInputIdentifier" type="IdentifierType"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Outputs -->
<xs:element name="Outputs">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Output"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Output">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="Identifier" type="IdentifierType"/>
<xs:element name="TypeIdentifier" type="IdentifierType"/>
<xs:element name="Name" type="NameType"/>
<xs:element minOccurs="0" name="LinkedBoxIdentifier" type="IdentifierType"/>
<xs:choice>
<xs:element minOccurs="0" name="LinkedBoxOutputIndex" type="IndexType"/>
<xs:element minOccurs="0" name="LinkedBoxOutputIdentifier" type="IdentifierType"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- Attributes -->
<xs:element name="Attributes">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Attribute"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Attribute">
<xs:complexType>
<xs:all>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Value" type="ParameterType"/>
</xs:all>
</xs:complexType>
</xs:element>
<!-- Metadata -->
<xs:element name="Metadata">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Entry"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Entry">
<xs:complexType>
<xs:sequence>
<xs:element name="Identifier" type="IdentifierType"/>
<xs:element name="Type" type="IdentifierType"/>
<xs:element name="Data" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
@@ -0,0 +1,99 @@
######################################################################################
## OpenViBE configuration file
##
## - 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
######################################################################################
######################################################################################
# OpenViBE configuration
#####################################################################################
# Note: Path tokens are set by default to the respective installation dirs. Defining a token here will override the default value.
#Path_Root = ..
#Path_Bin = ${Path_Root}/bin
#Path_Lib = ${Path_Root}/lib
#Path_Data = ${Path_Root}/share/openvibe
#Path_Samples = ${Path_Data}/scenarios
## By default, "Path_UserData" will point to $HOME/.config/openvibe on Linux, and to %APPDATA%/openvibe/ on Windows.
#Path_UserData = ${Path_Root}/openvibe-user
#Path_Log = ${Path_UserData}/log
#Path_Tmp = ${Path_UserData}/tmp
#####################################################################################
# System Generalization
#####################################################################################
Shell_ExtensionWindows = .cmd
Shell_ExtensionLinux = .sh
Shell_ExtensionMacOS = .sh
Shell_Extension = ${Shell_Extension${OperatingSystem}}
Program_Launcher_PrefixWindows = openvibe-
Program_Launcher_PrefixLinux = openvibe-
Program_Launcher_PrefixMacOS = openvibe-
Program_Launcher_Prefix = ${Program_Launcher_Prefix${OperatingSystem}}
External_Application_LauncherWindows = "${Path_Bin}/openvibe-external-application-launcher.cmd"
External_Application_LauncherLinux = "${Path_Bin}/openvibe-external-application-launcher"
External_Application_LauncherMacOS = "${Path_Bin}/openvibe-external-application-launcher"
External_Application_Launcher = ${External_Application_Launcher${OperatingSystem}}
VRPN_ExternalServerPort = 53883
#####################################################################################
# OpenViBE kernel configuration
#####################################################################################
Kernel_PluginsPatternMacOS = ${Path_Lib}/libopenvibe-plugins-*.dylib
Kernel_PluginsPatternLinux = ${Path_Lib}/libopenvibe-plugins-*.so
Kernel_PluginsPatternWindows = ${Path_Bin}/openvibe-plugins-*.dll
Kernel_Plugins = ${Kernel_PluginsPattern${OperatingSystem}}
Kernel_MainLogLevel = Trace
Kernel_ConsoleLogLevel = Information
Kernel_ConsoleLogWithHexa = False
Kernel_ConsoleLogTimeInSecond = True
Kernel_ConsoleLogTimePrecision = 3
Kernel_FileLogLevel = Debug
Kernel_FileLogWithHexa = True
Kernel_FileLogTimeInSecond = False
Kernel_FileLogTimePrecision = 3
Kernel_PlayerFrequency = 128
Kernel_DelayedConfiguration = ${Path_Data}/kernel/openvibe-delayed.conf
Kernel_AllowUnregisteredNumericalStimulationIdentifiers = false
Kernel_ExtendedStimulationLabels = true
Kernel_AbortPlayerWhenBoxIsOutdated = false
Kernel_AbortScenarioImportOnUnknownSetting = false
#####################################################################################
# OpenViBE plugin configuration
#####################################################################################
Plugin_Classification_RandomizeKFoldTestData = false
Box_ChannelSelector_EEGChannelNames = LPA;RPA;Nz;Fp1;Fpz;Fp2;AF9;AF7;AF5;AF3;AF1;AFz;AF2;AF4;AF6;AF8;AF10;F9;F7;F5;F3;F1;Fz;F2;F4;F6;F8;F10;FT9;FT7;FC5;FC3;FC1;FCz;FC2;FC4;FC6;FT8;FT10;T9;T7;C5;C3;C1;Cz;C2;C4;C6;T8;T10;TP9;TP7;CP5;CP3;CP1;CPz;CP2;CP4;CP6;TP8;TP10;P9;P7;P5;P3;P1;Pz;P2;P4;P6;P8;P10;PO9;PO7;PO5;PO3;PO1;POz;PO2;PO4;PO6;PO8;PO10;O1;Oz;O2;I1;Iz;I2;AFp9h;AFp7h;AFp5h;AFp3h;AFp1h;AFp2h;AFp4h;AFp6h;AFp8h;AFp10h;AFF9h;AFF7h;AFF5h;AFF3h;AFF1h;AFF2h;AFF4h;AFF6h;AFF8h;AFF10h;FFT9h;FFT7h;FFC5h;FFC3h;FFC1h;FFC2h;FFC4h;FFC6h;FFT8h;FFT10h;FTT9h;FTT7h;FCC5h;FCC3h;FCC1h;FCC2h;FCC4h;FCC6h;FTT8h;FTT10h;TTP9h;TTP7h;CCP5h;CCP3h;CCP1h;CCP2h;CCP4h;CCP6h;TTP8h;TTP10h;TPP9h;TPP7h;CPP5h;CPP3h;CPP1h;CPP2h;CPP4h;CPP6h;TPP8h;TPP10h;PPO9h;PPO7h;PPO5h;PPO3h;PPO1h;PPO2h;PPO4h;PPO6h;PPO8h;PPO10h;POO9h;POO7h;POO5h;POO3h;POO1h;POO2h;POO4h;POO6h;POO8h;POO10h;OI1h;OI2h;Fp1h;Fp2h;AF9h;AF7h;AF5h;AF3h;AF1h;AF2h;AF4h;AF6h;AF8h;AF10h;F9h;F7h;F5h;F3h;F1h;F2h;F4h;F6h;F8h;F10h;FT9h;FT7h;FC5h;FC3h;FC1h;FC2h;FC4h;FC6h;FT8h;FT10h;T9h;T7h;C5h;C3h;C1h;C2h;C4h;C6h;T8h;T10h;TP9h;TP7h;CP5h;CP3h;CP1h;CP2h;CP4h;CP6h;TP8h;TP10h;P9h;P7h;P5h;P3h;P1h;P2h;P4h;P6h;P8h;P10h;PO9h;PO7h;PO5h;PO3h;PO1h;PO2h;PO4h;PO6h;PO8h;PO10h;O1h;O2h;I1h;I2h;AFp9;AFp7;AFp5;AFp3;AFp1;AFpz;AFp2;AFp4;AFp6;AFp8;AFp10;AFF9;AFF7;AFF5;AFF3;AFF1;AFFz;AFF2;AFF4;AFF6;AFF8;AFF10;FFT9;FFT7;FFC5;FFC3;FFC1;FFCz;FFC2;FFC4;FFC6;FFT8;FFT10;FTT9;FTT7;FCC5;FCC3;FCC1;FCCz;FCC2;FCC4;FCC6;FTT8;FTT10;TTP9;TTP7;CCP5;CCP3;CCP1;CCPz;CCP2;CCP4;CCP6;TTP8;TTP10;TPP9;TPP7;CPP5;CPP3;CPP1;CPPz;CPP2;CPP4;CPP6;TPP8;TPP10;PPO9;PPO7;PPO5;PPO3;PPO1;PPOz;PPO2;PPO4;PPO6;PPO8;PPO10;POO9;POO7;POO5;POO3;POO1;POOz;POO2;POO4;POO6;POO8;POO10;OI1;OIz;OI2;T3;T5;T4;T6
Box_ChannelSelector_FailOnInvalidSelection = true
#####################################################################################
# Include custom configuration (from deprecated locations)
# Actual custom configuration below will override these, if its found.
#####################################################################################
CustomConfigurationPrefixWindows = ${Path_UserData}/openvibe
CustomConfigurationPrefixLinux = ${Path_UserData}/openvibe
CustomConfigurationPrefixMacOS = ${Path_UserData}/openvibe
CustomConfigurationSuffixWindows = .conf
CustomConfigurationSuffixLinux = rc
CustomConfigurationSuffixMacOS = rc
CustomConfigurationPluginsWildcard = ${Path_Data}/plugins/*.conf
Include = ${CustomConfigurationPluginsWildcard}
@@ -0,0 +1,7 @@
# Resource locations to be added to the default path
[Bootstrap]
Zip=${Path_Data}/kernel/OgreCore.zip
[General]
FileSystem=${Path_Data}/kernel/
@@ -0,0 +1,43 @@
material sphere_ov_material
{
technique
{
pass
{
ambient 1 1 1
diffuse 1 1 1
// specular 0 0 0 10
// emissive 0 0 0
scene_blend alpha_blend
}
}
}
//the following cg/diffuse shader allows controlling the diffuse component from code using parameter 1
//fragment_program set_color_fp cg
//{
// source diffuse.cg
// entry_point main_ps
// profiles ps_2_0 arbfp1
// default_params
// {
// param_named myColour float4 1 1 1 1
// }
//}
//material cg/diffuse
//{
// technique
// {
// pass
// {
//// lighting off
// scene_blend alpha_blend
// fragment_program_ref set_color_fp
// {
// param_named_auto myColour custom 1
// }
// }
// }
//}
@@ -0,0 +1,47 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkCAlgorithmProxy.h"
namespace OpenViBE {
namespace Kernel {
class CAlgorithmContext final : public TKernelObject<IAlgorithmContext>
{
public:
CAlgorithmContext(const IKernelContext& ctx, CAlgorithmProxy& algorithmProxy, const Plugins::IPluginObjectDesc& /*pluginObjectDesc*/)
: TKernelObject<IAlgorithmContext>(ctx), m_logManager(ctx.getLogManager()), m_algorithmProxy(algorithmProxy) {}
~CAlgorithmContext() override { }
IConfigurationManager& getConfigurationManager() const override { return getKernelContext().getConfigurationManager(); }
IAlgorithmManager& getAlgorithmManager() const override { return getKernelContext().getAlgorithmManager(); }
ILogManager& getLogManager() const override { return m_logManager; }
IErrorManager& getErrorManager() const override { return getKernelContext().getErrorManager(); }
ITypeManager& getTypeManager() const override { return getKernelContext().getTypeManager(); }
CIdentifier getNextInputParameterIdentifier(const CIdentifier& previous) const override
{
return m_algorithmProxy.getNextInputParameterIdentifier(previous);
}
IParameter* getInputParameter(const CIdentifier& identifier) override { return m_algorithmProxy.getInputParameter(identifier); }
CIdentifier getNextOutputParameterIdentifier(const CIdentifier& previous) const override
{
return m_algorithmProxy.getNextOutputParameterIdentifier(previous);
}
IParameter* getOutputParameter(const CIdentifier& identifier) override { return m_algorithmProxy.getOutputParameter(identifier); }
bool isInputTriggerActive(const CIdentifier& identifier) const override { return m_algorithmProxy.isInputTriggerActive(identifier); }
bool activateOutputTrigger(const CIdentifier& identifier, const bool state) override { return m_algorithmProxy.activateOutputTrigger(identifier, state); }
_IsDerivedFromClass_Final_(TKernelObject<IAlgorithmContext>, OVK_ClassId_Kernel_Algorithm_AlgorithmContext)
protected:
ILogManager& m_logManager;
CAlgorithmProxy& m_algorithmProxy;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,162 @@
#include "ovkCAlgorithmManager.h"
#include "ovkCAlgorithmProxy.h"
#include <system/ovCMath.h>
namespace OpenViBE {
namespace Kernel {
CAlgorithmManager::~CAlgorithmManager()
{
std::unique_lock<std::mutex> lock(m_oMutex);
for (auto& algorithm : m_algorithms)
{
CAlgorithmProxy* algorithmProxy = algorithm.second;
Plugins::IAlgorithm& tmpAlgorithm = algorithmProxy->getAlgorithm();
delete algorithmProxy;
getKernelContext().getPluginManager().releasePluginObject(&tmpAlgorithm);
}
m_algorithms.clear();
}
CIdentifier CAlgorithmManager::createAlgorithm(const CIdentifier& algorithmClassID)
{
const Plugins::IAlgorithmDesc* algorithmDesc = nullptr;
Plugins::IAlgorithm* algorithm = getKernelContext().getPluginManager().createAlgorithm(algorithmClassID, &algorithmDesc);
OV_ERROR_UNLESS_KRU(algorithm && algorithmDesc, "Algorithm creation failed, class identifier :" << algorithmClassID.str(),
ErrorType::BadResourceCreation);
getLogManager() << LogLevel_Debug << "Creating algorithm with class identifier " << algorithmClassID << "\n";
CIdentifier algorithmId = getUnusedIdentifier();
CAlgorithmProxy* algorithmProxy = new CAlgorithmProxy(getKernelContext(), *algorithm, *algorithmDesc);
{
std::unique_lock<std::mutex> lock(m_oMutex);
m_algorithms[algorithmId] = algorithmProxy;
}
return algorithmId;
}
CIdentifier CAlgorithmManager::createAlgorithm(const Plugins::IAlgorithmDesc& algorithmDesc)
{
std::unique_lock<std::mutex> lock(m_oMutex);
Plugins::IAlgorithm* algorithm = getKernelContext().getPluginManager().createAlgorithm(algorithmDesc);
OV_ERROR_UNLESS_KRU(algorithm, "Algorithm creation failed, class identifier :" << algorithmDesc.getClassIdentifier().str(),
ErrorType::BadResourceCreation);
getLogManager() << LogLevel_Debug << "Creating algorithm with class identifier " << algorithmDesc.getClassIdentifier() << "\n";
CIdentifier algorithmId = getUnusedIdentifier();
CAlgorithmProxy* algorithmProxy = new CAlgorithmProxy(getKernelContext(), *algorithm, algorithmDesc);
m_algorithms[algorithmId] = algorithmProxy;
return algorithmId;
}
bool CAlgorithmManager::releaseAlgorithm(const CIdentifier& rAlgorithmIdentifier)
{
std::unique_lock<std::mutex> lock(m_oMutex);
const auto itAlgorithm = m_algorithms.find(rAlgorithmIdentifier);
OV_ERROR_UNLESS_KRF(itAlgorithm != m_algorithms.end(),
"Algorithm release failed, identifier :" << rAlgorithmIdentifier.str(),
ErrorType::ResourceNotFound);
getLogManager() << LogLevel_Debug << "Releasing algorithm with identifier " << rAlgorithmIdentifier << "\n";
CAlgorithmProxy* algorithmProxy = itAlgorithm->second;
if (algorithmProxy)
{
Plugins::IAlgorithm& algorithm = algorithmProxy->getAlgorithm();
delete algorithmProxy;
algorithmProxy = nullptr;
getKernelContext().getPluginManager().releasePluginObject(&algorithm);
}
m_algorithms.erase(itAlgorithm);
return true;
}
bool CAlgorithmManager::releaseAlgorithm(IAlgorithmProxy& rAlgorithm)
{
std::unique_lock<std::mutex> lock(m_oMutex);
bool result = false;
for (auto& algorithm : m_algorithms)
{
CAlgorithmProxy* algorithmProxy = algorithm.second;
if (algorithmProxy == &rAlgorithm)
{
Plugins::IAlgorithm& tmpAlgorithm = algorithmProxy->getAlgorithm();
getLogManager() << LogLevel_Debug << "Releasing algorithm with class id " << tmpAlgorithm.getClassIdentifier() << "\n";
delete algorithmProxy;
algorithmProxy = nullptr;
m_algorithms.erase(algorithm.first);
getKernelContext().getPluginManager().releasePluginObject(&tmpAlgorithm);
result = true;
break;
}
}
OV_ERROR_UNLESS_KRF(result, "Algorithm release failed", Kernel::ErrorType::ResourceNotFound);
return result;
}
IAlgorithmProxy& CAlgorithmManager::getAlgorithm(const CIdentifier& rAlgorithmIdentifier)
{
std::unique_lock<std::mutex> lock(m_oMutex);
const auto itAlgorithm = m_algorithms.find(rAlgorithmIdentifier);
OV_FATAL_UNLESS_K(itAlgorithm != m_algorithms.end(), "Algorithm " << rAlgorithmIdentifier << " does not exist !", Kernel::ErrorType::ResourceNotFound);
return *itAlgorithm->second;
}
CIdentifier CAlgorithmManager::getNextAlgorithmIdentifier(const CIdentifier& previousID) const
{
std::unique_lock<std::mutex> lock(m_oMutex);
auto itAlgorithm = m_algorithms.begin();
if (previousID != CIdentifier::undefined())
{
itAlgorithm = m_algorithms.find(previousID);
if (itAlgorithm == m_algorithms.end()) { return CIdentifier::undefined(); }
++itAlgorithm;
}
return (itAlgorithm != m_algorithms.end() ? itAlgorithm->first : CIdentifier::undefined());
}
CIdentifier CAlgorithmManager::getUnusedIdentifier() const
{
std::unique_lock<std::mutex> lock(m_oMutex);
uint64_t identifier = CIdentifier::random().id();
CIdentifier result;
std::map<CIdentifier, CAlgorithmProxy*>::const_iterator i;
do
{
identifier++;
result = CIdentifier(identifier);
i = m_algorithms.find(result);
} while (i != m_algorithms.end() || result == CIdentifier::undefined());
return result;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,34 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
#include <mutex>
namespace OpenViBE {
namespace Kernel {
class CAlgorithmProxy;
class CAlgorithmManager final : public TKernelObject<IAlgorithmManager>
{
public:
explicit CAlgorithmManager(const IKernelContext& ctx) : TKernelObject<IAlgorithmManager>(ctx) {}
~CAlgorithmManager() override;
CIdentifier createAlgorithm(const CIdentifier& algorithmClassID) override;
CIdentifier createAlgorithm(const Plugins::IAlgorithmDesc& algorithmDesc) override;
bool releaseAlgorithm(const CIdentifier& rAlgorithmIdentifier) override;
bool releaseAlgorithm(IAlgorithmProxy& rAlgorithm) override;
IAlgorithmProxy& getAlgorithm(const CIdentifier& rAlgorithmIdentifier) override;
CIdentifier getNextAlgorithmIdentifier(const CIdentifier& previousID) const override;
_IsDerivedFromClass_Final_(TKernelObject<IAlgorithmManager>, OVK_ClassId_Kernel_Algorithm_AlgorithmManager)
protected:
CIdentifier getUnusedIdentifier() const;
std::map<CIdentifier, CAlgorithmProxy*> m_algorithms;
mutable std::mutex m_oMutex;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,33 @@
#include "ovkCAlgorithmProto.h"
#include "ovkCAlgorithmProxy.h"
namespace OpenViBE {
namespace Kernel {
CAlgorithmProto::CAlgorithmProto(const IKernelContext& ctx, CAlgorithmProxy& rAlgorithmProxy)
: TKernelObject<IAlgorithmProto>(ctx), m_algorithmProxy(rAlgorithmProxy) {}
bool CAlgorithmProto::addInputParameter(const CIdentifier& inputParameterID, const CString& sInputName,
const EParameterType eParameterType, const CIdentifier& subTypeID)
{
return m_algorithmProxy.addInputParameter(inputParameterID, sInputName, eParameterType, subTypeID);
}
bool CAlgorithmProto::addOutputParameter(const CIdentifier& outputParameterID, const CString& sOutputName,
const EParameterType eParameterType, const CIdentifier& subTypeID)
{
return m_algorithmProxy.addOutputParameter(outputParameterID, sOutputName, eParameterType, subTypeID);
}
bool CAlgorithmProto::addInputTrigger(const CIdentifier& inputTriggerID, const CString& rInputTriggerName)
{
return m_algorithmProxy.addInputTrigger(inputTriggerID, rInputTriggerName);
}
bool CAlgorithmProto::addOutputTrigger(const CIdentifier& outputTriggerID, const CString& rOutputTriggerName)
{
return m_algorithmProxy.addOutputTrigger(outputTriggerID, rOutputTriggerName);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,28 @@
#pragma once
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CAlgorithmProxy;
class CAlgorithmProto final : public TKernelObject<IAlgorithmProto>
{
public:
CAlgorithmProto(const IKernelContext& ctx, CAlgorithmProxy& rAlgorithmProxy);
bool addInputParameter(const CIdentifier& inputParameterID, const CString& sInputName, EParameterType eParameterType,
const CIdentifier& subTypeID) override;
bool addOutputParameter(const CIdentifier& outputParameterID, const CString& sOutputName, EParameterType eParameterType,
const CIdentifier& subTypeID) override;
bool addInputTrigger(const CIdentifier& inputTriggerID, const CString& rInputTriggerName) override;
bool addOutputTrigger(const CIdentifier& outputTriggerID, const CString& rOutputTriggerName) override;
_IsDerivedFromClass_Final_(TKernelObject<IAlgorithmProto>, OVK_ClassId_Kernel_Algorithm_AlgorithmProto)
protected:
CAlgorithmProxy& m_algorithmProxy;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,285 @@
#include <cassert>
#include "ovkCAlgorithmProxy.h"
#include "ovkCAlgorithmContext.h"
#include "ovkCAlgorithmProto.h"
#include "../../ovk_tools.h"
#include <openvibe/ovExceptionHandler.h>
namespace OpenViBE {
namespace Kernel {
CAlgorithmProxy::CAlgorithmProxy(const IKernelContext& ctx, Plugins::IAlgorithm& rAlgorithm, const Plugins::IAlgorithmDesc& algorithmDesc)
: TKernelObject<IAlgorithmProxy>(ctx), m_algorithmDesc(algorithmDesc), m_algorithm(rAlgorithm)
{
m_iConfigurable = dynamic_cast<IConfigurable*>(getKernelContext().getKernelObjectFactory().createObject(OV_ClassId_Kernel_Configurable));
m_oConfigurable = dynamic_cast<IConfigurable*>(getKernelContext().getKernelObjectFactory().createObject(OV_ClassId_Kernel_Configurable));
// FIXME
CAlgorithmProto algorithmProto(ctx, *this);
algorithmDesc.getAlgorithmPrototype(algorithmProto);
}
CAlgorithmProxy::~CAlgorithmProxy()
{
getKernelContext().getKernelObjectFactory().releaseObject(m_oConfigurable);
getKernelContext().getKernelObjectFactory().releaseObject(m_iConfigurable);
}
bool CAlgorithmProxy::addInputParameter(const CIdentifier& parameterID, const CString& name, const EParameterType parameterType,
const CIdentifier& subTypeID)
{
OV_ERROR_UNLESS_KRF(m_iConfigurable->getParameter(parameterID) == nullptr,
"For algorithm " << m_algorithmDesc.getName() << " : Input parameter id " << parameterID.str() << " already exists",
ErrorType::BadResourceCreation);
m_iConfigurable->createParameter(parameterID, parameterType, subTypeID);
m_iParameterNames[parameterID] = name;
return true;
}
CIdentifier CAlgorithmProxy::getNextInputParameterIdentifier(const CIdentifier& parameterID) const
{
return m_iConfigurable->getNextParameterIdentifier(parameterID);
}
IParameter* CAlgorithmProxy::getInputParameter(const CIdentifier& parameterID)
{
IParameter* parameter = m_iConfigurable->getParameter(parameterID);
OV_ERROR_UNLESS_KRN(
parameter, "For algorithm " << m_algorithmDesc.getName() << " : Requested null input parameter id " << parameterID.str(),
ErrorType::ResourceNotFound);
return parameter;
}
EParameterType CAlgorithmProxy::getInputParameterType(const CIdentifier& parameterID) const
{
IParameter* parameter = m_iConfigurable->getParameter(parameterID);
if (!parameter) { return ParameterType_None; }
return parameter->getType();
}
CString CAlgorithmProxy::getInputParameterName(const CIdentifier& parameterID) const
{
const auto itName = m_iParameterNames.find(parameterID);
if (itName == m_iParameterNames.end()) { return ""; }
return itName->second;
}
bool CAlgorithmProxy::removeInputParameter(const CIdentifier& parameterID)
{
if (!m_iConfigurable->removeParameter(parameterID)) { return false; }
m_iParameterNames.erase(m_iParameterNames.find(parameterID));
return true;
}
bool CAlgorithmProxy::addOutputParameter(const CIdentifier& parameterID, const CString& name, const EParameterType parameterType,
const CIdentifier& subTypeID)
{
OV_ERROR_UNLESS_KRF(m_oConfigurable->getParameter(parameterID) == nullptr,
"For algorithm " << m_algorithmDesc.getName() << " : Output parameter id " << parameterID.str() <<
" already exists",
ErrorType::BadResourceCreation);
m_oConfigurable->createParameter(parameterID, parameterType, subTypeID);
m_oParameterNames[parameterID] = name;
return true;
}
CIdentifier CAlgorithmProxy::getNextOutputParameterIdentifier(const CIdentifier& parameterID) const
{
return m_oConfigurable->getNextParameterIdentifier(parameterID);
}
IParameter* CAlgorithmProxy::getOutputParameter(const CIdentifier& parameterID)
{
IParameter* parameter = m_oConfigurable->getParameter(parameterID);
OV_ERROR_UNLESS_KRN(
parameter, "For algorithm " << m_algorithmDesc.getName() << " : Requested null output parameter id " << parameterID.str(),
ErrorType::ResourceNotFound);
return parameter;
}
EParameterType CAlgorithmProxy::getOutputParameterType(const CIdentifier& parameterID) const
{
IParameter* parameter = m_oConfigurable->getParameter(parameterID);
if (!parameter) { return ParameterType_None; }
return parameter->getType();
}
CString CAlgorithmProxy::getOutputParameterName(const CIdentifier& parameterID) const
{
const auto itName = m_oParameterNames.find(parameterID);
if (itName == m_oParameterNames.end()) { return ""; }
return itName->second;
}
bool CAlgorithmProxy::removeOutputParameter(const CIdentifier& parameterID)
{
if (!m_oConfigurable->removeParameter(parameterID)) { return false; }
m_oParameterNames.erase(m_oParameterNames.find(parameterID));
return true;
}
bool CAlgorithmProxy::addInputTrigger(const CIdentifier& triggerID, const CString& name)
{
if (m_iTriggers.find(triggerID) != m_iTriggers.end()) { return false; }
m_iTriggers[triggerID].first = name;
m_iTriggers[triggerID].second = false;
return true;
}
CIdentifier CAlgorithmProxy::getNextInputTriggerIdentifier(const CIdentifier& triggerID) const
{
return getNextIdentifier<std::pair<CString, bool>>(m_iTriggers, triggerID);
}
CString CAlgorithmProxy::getInputTriggerName(const CIdentifier& triggerID) const
{
const auto itTrigger = m_iTriggers.find(triggerID);
if (itTrigger == m_iTriggers.end()) { return ""; }
return itTrigger->second.first;
}
bool CAlgorithmProxy::isInputTriggerActive(const CIdentifier& triggerID) const
{
const auto itTrigger = m_iTriggers.find(triggerID);
if (itTrigger == m_iTriggers.end()) { return false; }
return itTrigger->second.second;
}
bool CAlgorithmProxy::activateInputTrigger(const CIdentifier& triggerID, const bool /*triggerState*/)
{
const auto itTrigger = m_iTriggers.find(triggerID);
if (itTrigger == m_iTriggers.end()) { return false; }
itTrigger->second.second = true;
return true;
}
bool CAlgorithmProxy::removeInputTrigger(const CIdentifier& triggerID)
{
const auto itTrigger = m_iTriggers.find(triggerID);
if (itTrigger == m_iTriggers.end()) { return false; }
m_iTriggers.erase(itTrigger);
return true;
}
bool CAlgorithmProxy::addOutputTrigger(const CIdentifier& triggerID, const CString& name)
{
if (m_oTriggers.find(triggerID) != m_oTriggers.end()) { return false; }
m_oTriggers[triggerID].first = name;
m_oTriggers[triggerID].second = false;
return true;
}
CIdentifier CAlgorithmProxy::getNextOutputTriggerIdentifier(const CIdentifier& triggerID) const
{
return getNextIdentifier<std::pair<CString, bool>>(m_oTriggers, triggerID);
}
CString CAlgorithmProxy::getOutputTriggerName(const CIdentifier& triggerID) const
{
const auto itTrigger = m_oTriggers.find(triggerID);
if (itTrigger == m_oTriggers.end()) { return ""; }
return itTrigger->second.first;
}
bool CAlgorithmProxy::isOutputTriggerActive(const CIdentifier& triggerID) const
{
const auto itTrigger = m_oTriggers.find(triggerID);
if (itTrigger == m_oTriggers.end()) { return false; }
return itTrigger->second.second;
}
bool CAlgorithmProxy::activateOutputTrigger(const CIdentifier& triggerID, const bool /*state*/)
{
const auto itTrigger = m_oTriggers.find(triggerID);
if (itTrigger == m_oTriggers.end()) { return false; }
itTrigger->second.second = true;
return true;
}
bool CAlgorithmProxy::removeOutputTrigger(const CIdentifier& triggerID)
{
const auto itTrigger = m_oTriggers.find(triggerID);
if (itTrigger == m_oTriggers.end()) { return false; }
m_oTriggers.erase(itTrigger);
return true;
}
bool CAlgorithmProxy::initialize()
{
assert(!m_isInitialized);
return translateException([&]()
{
CAlgorithmContext context(getKernelContext(), *this, m_algorithmDesc);
// The dual state initialized or not does not take into account
// a partially initialized state. Thus, we have to trust algorithms to implement
// their initialization routine as a rollback transaction mechanism
m_isInitialized = m_algorithm.initialize(context);
return m_isInitialized;
},
std::bind(&CAlgorithmProxy::handleException, this, "Algorithm initialization", std::placeholders::_1));
}
bool CAlgorithmProxy::uninitialize()
{
assert(m_isInitialized);
return translateException([&]()
{
CAlgorithmContext context(getKernelContext(), *this, m_algorithmDesc);
return m_algorithm.uninitialize(context);
},
std::bind(&CAlgorithmProxy::handleException, this, "Algorithm uninitialization", std::placeholders::_1));
}
bool CAlgorithmProxy::process()
{
assert(m_isInitialized);
return translateException([&]()
{
CAlgorithmContext context(getKernelContext(), *this, m_algorithmDesc);
this->setAllOutputTriggers(false);
const bool result = m_algorithm.process(context);
this->setAllInputTriggers(false);
return result;
},
std::bind(&CAlgorithmProxy::handleException, this, "Algorithm processing", std::placeholders::_1));
}
bool CAlgorithmProxy::process(const CIdentifier& triggerID)
{
assert(m_isInitialized);
if (!this->activateInputTrigger(triggerID, true)) { return false; }
return this->process();
}
void CAlgorithmProxy::setAllInputTriggers(const bool status) { for (auto& trigger : m_iTriggers) { trigger.second.second = status; } }
void CAlgorithmProxy::setAllOutputTriggers(const bool status) { for (auto& trigger : m_oTriggers) { trigger.second.second = status; } }
bool CAlgorithmProxy::isAlgorithmDerivedFrom(const CIdentifier& classID) { return m_algorithm.isDerivedFromClass(classID); }
void CAlgorithmProxy::handleException(const char* errorHint, const std::exception& exception)
{
this->getLogManager() << LogLevel_Error << "Exception caught in algorithm\n";
this->getLogManager() << LogLevel_Error << " [name: " << this->getAlgorithmDesc().getName() << "]\n";
this->getLogManager() << LogLevel_Error << " [class identifier: " << this->getAlgorithmDesc().getCreatedClass() << "]\n";
this->getLogManager() << LogLevel_Error << " [hint: " << (errorHint ? errorHint : "no hint") << "]\n";
this->getLogManager() << LogLevel_Error << " [cause: " << exception.what() << "]\n";
OV_ERROR_KRV("Caught exception: " << exception.what(), Kernel::ErrorType::ExceptionCaught);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,76 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
namespace OpenViBE {
namespace Kernel {
class CAlgorithmProxy final : public TKernelObject<IAlgorithmProxy>
{
public:
CAlgorithmProxy(const IKernelContext& ctx, Plugins::IAlgorithm& rAlgorithm, const Plugins::IAlgorithmDesc& algorithmDesc);
~CAlgorithmProxy() override;
Plugins::IAlgorithm& getAlgorithm() { return m_algorithm; }
const Plugins::IAlgorithm& getAlgorithm() const { return m_algorithm; }
const Plugins::IAlgorithmDesc& getAlgorithmDesc() const { return m_algorithmDesc; }
bool addInputParameter(const CIdentifier& parameterID, const CString& name, EParameterType parameterType, const CIdentifier& subTypeID);
CIdentifier getNextInputParameterIdentifier(const CIdentifier& parameterID) const override;
IParameter* getInputParameter(const CIdentifier& parameterID) override;
EParameterType getInputParameterType(const CIdentifier& parameterID) const;
CString getInputParameterName(const CIdentifier& parameterID) const override;
bool removeInputParameter(const CIdentifier& parameterID);
bool addOutputParameter(const CIdentifier& parameterID, const CString& name, EParameterType parameterType, const CIdentifier& subTypeID);
CIdentifier getNextOutputParameterIdentifier(const CIdentifier& parameterID) const override;
IParameter* getOutputParameter(const CIdentifier& parameterID) override;
EParameterType getOutputParameterType(const CIdentifier& parameterID) const;
CString getOutputParameterName(const CIdentifier& parameterID) const override;
bool removeOutputParameter(const CIdentifier& parameterID);
bool addInputTrigger(const CIdentifier& triggerID, const CString& name);
CIdentifier getNextInputTriggerIdentifier(const CIdentifier& triggerID) const override;
CString getInputTriggerName(const CIdentifier& triggerID) const override;
bool isInputTriggerActive(const CIdentifier& triggerID) const;
bool activateInputTrigger(const CIdentifier& triggerID, bool triggerState) override;
bool removeInputTrigger(const CIdentifier& triggerID);
bool addOutputTrigger(const CIdentifier& triggerID, const CString& name);
CIdentifier getNextOutputTriggerIdentifier(const CIdentifier& triggerID) const override;
CString getOutputTriggerName(const CIdentifier& triggerID) const override;
bool isOutputTriggerActive(const CIdentifier& triggerID) const override;
bool activateOutputTrigger(const CIdentifier& triggerID, const bool triggerState);
bool removeOutputTrigger(const CIdentifier& triggerID);
bool initialize() override;
bool uninitialize() override;
bool process() override;
bool process(const CIdentifier& triggerID) override;
bool isAlgorithmDerivedFrom(const CIdentifier& classID) override;
_IsDerivedFromClass_Final_(TKernelObject<IAlgorithmProxy>, OVK_ClassId_Kernel_Algorithm_AlgorithmProxy)
protected:
IConfigurable* m_iConfigurable = nullptr;
IConfigurable* m_oConfigurable = nullptr;
std::map<CIdentifier, CString> m_iParameterNames;
std::map<CIdentifier, CString> m_oParameterNames;
std::map<CIdentifier, std::pair<CString, bool>> m_iTriggers;
std::map<CIdentifier, std::pair<CString, bool>> m_oTriggers;
void setAllInputTriggers(bool status);
void setAllOutputTriggers(bool status);
private:
void handleException(const char* errorHint, const std::exception& exception);
const Plugins::IAlgorithmDesc& m_algorithmDesc;
Plugins::IAlgorithm& m_algorithm;
bool m_isInitialized = false;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,744 @@
#include "ovkCConfigurationManager.h"
#include <openvibe/kernel/configuration/ovIConfigurationKeywordExpandCallback.h>
#include <fs/IEntryEnumerator.h>
#include <fs/Files.h>
#include <system/ovCTime.h>
#include <system/ovCMath.h>
#include <stack>
#include <string>
#include <fstream>
#include <string>
#include <algorithm>
#include <functional>
#include <cctype>
#include <ctime>
#include <climits>
#include <cstdlib>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <unistd.h> // for getpid
#elif defined TARGET_OS_Windows
#include <windows.h> // for GetCurrentProcessId
#else
#endif
namespace OpenViBE {
namespace Kernel {
// because std::tolower has multiple signatures,
// it can not be easily used in std::transform
// this workaround is taken from http://www.gcek.net/ref/books/sw/cpp/ticppv2/
template <class TCharT>
static TCharT ToLower(TCharT c) { return std::tolower(c); }
class CConfigurationManagerEntryEnumeratorCallBack final : public FS::IEntryEnumeratorCallBack
{
public:
CConfigurationManagerEntryEnumeratorCallBack(ILogManager& logManager, IErrorManager& errorManager, IConfigurationManager& configManger)
: m_logManager(logManager), m_errorManager(errorManager), m_configManager(configManger) { }
static std::string reduce(const std::string& value)
{
if (value.length() == 0) { return ""; }
size_t i = 0;
size_t j = value.length() - 1;
while (i < value.length() && (value[i] == '\t' || value[i] == ' ')) { i++; }
while (j >= i && (value[j] == '\t' || value[j] == ' ')) { j--; }
return value.substr(i, j - i + 1);
}
bool callback(FS::IEntryEnumerator::IEntry& rEntry, FS::IEntryEnumerator::IAttributes& /*attributes*/) override
{
std::ifstream file;
FS::Files::openIFStream(file, rEntry.getName());
OV_ERROR_UNLESS(file.good(), "Could not open file " << rEntry.getName(), Kernel::ErrorType::ResourceNotFound, false, m_errorManager, m_logManager);
m_logManager << LogLevel_Trace << "Processing configuration file " << rEntry.getName() << "\n";
do
{
std::string line;
std::string linePart;
size_t eq;
while (!file.eof() && (line.length() == 0 || line[line.length() - 1] == '\\'))
{
while (line.length() != 0 && line[line.length() - 1] == '\\')
{
line.resize(line.length() - 1); // removes ending backslashes
}
std::getline(file, linePart, '\n');
line += reduce(linePart);
}
// process everything except empty line or comment
if (!line.empty() && line[0] != '\0' && line[0] != '#')
{
OV_ERROR_UNLESS((eq=line.find('=')) != std::string::npos,
"Invalid syntax in configuration file " << CString(rEntry.getName()) << " : " << line,
ErrorType::BadFileParsing, false, m_errorManager, m_logManager);
std::string name(reduce(line.substr(0, eq)));
std::string value(reduce(line.substr(eq + 1, line.length() - eq)));
if (name == "Include")
{
CString wildCard = m_configManager.expand(value.c_str());
m_logManager << LogLevel_Trace << "Including configuration file " << wildCard << "...\n";
m_configManager.addConfigurationFromFile(wildCard);
m_logManager << LogLevel_Trace << "Including configuration file " << wildCard << " done...\n";
}
else
{
CIdentifier tokenID = m_configManager.lookUpConfigurationTokenIdentifier(name.c_str());
if (tokenID == CIdentifier::undefined())
{
m_logManager << LogLevel_Trace << "Adding configuration token " << name << " : " << value << "\n";
m_configManager.createConfigurationToken(name.c_str(), value.c_str());
}
else
{
m_logManager << LogLevel_Trace << "Changing configuration token " << name << " to " << value << "\n";
// warning if base token are overwritten here
OV_WARNING_UNLESS(name != "Path_UserData" && name != "Path_Log" && name != "Path_Tmp"
&& name != "Path_Lib" && name != "Path_Bin" && name != "OperatingSystem",
"Overwriting critical token " + name, m_logManager);
m_configManager.setConfigurationTokenValue(tokenID, value.c_str());
}
}
}
} while (!file.eof());
m_logManager << LogLevel_Trace << "Processing configuration file " << CString(rEntry.getName()) << " finished\n";
return true;
}
protected:
ILogManager& m_logManager;
IErrorManager& m_errorManager;
IConfigurationManager& m_configManager;
};
CConfigurationManager::CConfigurationManager(const IKernelContext& ctx, IConfigurationManager* parentConfigManager)
: TKernelObject<IConfigurationManager>(ctx), m_parentConfigManager(parentConfigManager)
{
m_idx = 0;
m_startTime = System::Time::getTime();
}
void CConfigurationManager::clear()
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
m_ConfigTokens.clear();
}
bool CConfigurationManager::addConfigurationFromFile(const CString& rFileNameWildCard)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
this->getLogManager() << LogLevel_Trace << "Adding configuration file(s) [" << rFileNameWildCard << "]\n";
CConfigurationManagerEntryEnumeratorCallBack cb(getKernelContext().getLogManager(), getKernelContext().getErrorManager(), *this);
FS::IEntryEnumerator* entryEnumerator = createEntryEnumerator(cb);
const bool res = entryEnumerator->enumerate(rFileNameWildCard);
entryEnumerator->release();
return res;
}
// ----------------------------------------------------------------------------------------------------------------------------
//
CIdentifier CConfigurationManager::createConfigurationToken(const CString& name, const CString& value)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(this->lookUpConfigurationTokenIdentifier(name, false) == CIdentifier::undefined(),
"Configuration token name " << name << " already exists", Kernel::ErrorType::BadResourceCreation);
CIdentifier id = this->getUnusedIdentifier();
m_ConfigTokens[id].name = name;
m_ConfigTokens[id].value = value;
return id;
}
bool CConfigurationManager::releaseConfigurationToken(const CIdentifier& identifier)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_ConfigTokens.find(identifier);
OV_ERROR_UNLESS_KRF(it != m_ConfigTokens.end(), "Configuration token not found " << identifier.str(), Kernel::ErrorType::ResourceNotFound);
m_ConfigTokens.erase(it);
return true;
}
CIdentifier CConfigurationManager::getNextConfigurationTokenIdentifier(const CIdentifier& prevConfigTokenID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
std::map<CIdentifier, config_token_t>::const_iterator it;
if (prevConfigTokenID == CIdentifier::undefined()) { it = m_ConfigTokens.begin(); }
else
{
it = m_ConfigTokens.find(prevConfigTokenID);
if (it == m_ConfigTokens.end()) { return CIdentifier::undefined(); }
++it;
}
return it != m_ConfigTokens.end() ? it->first : CIdentifier::undefined();
}
// ----------------------------------------------------------------------------------------------------------------------------
CString CConfigurationManager::getConfigurationTokenName(const CIdentifier& identifier) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_ConfigTokens.find(identifier);
if (it != m_ConfigTokens.end()) { return it->second.name; }
return "";
}
CString CConfigurationManager::getConfigurationTokenValue(const CIdentifier& identifier) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_ConfigTokens.find(identifier);
if (it != m_ConfigTokens.end()) { return it->second.value; }
return "";
}
// ----------------------------------------------------------------------------------------------------------------------------
bool CConfigurationManager::setConfigurationTokenName(const CIdentifier& identifier, const CString& name)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(this->lookUpConfigurationTokenIdentifier(name, false) == CIdentifier::undefined(),
"Configuration token name " << name << " already exists", Kernel::ErrorType::BadResourceCreation);
auto it = m_ConfigTokens.find(identifier);
OV_ERROR_UNLESS_KRF(it != m_ConfigTokens.end(), "Configuration token " << identifier.str() << " does not exist",
ErrorType::BadResourceCreation);
it->second.name = name;
return true;
}
bool CConfigurationManager::setConfigurationTokenValue(const CIdentifier& identifier, const CString& value)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto itConfigurationToken = m_ConfigTokens.find(identifier);
OV_ERROR_UNLESS_KRF(itConfigurationToken != m_ConfigTokens.end(), "Configuration token " << identifier.str() << " does not exist",
ErrorType::BadResourceCreation);
itConfigurationToken->second.value = value;
return true;
}
bool CConfigurationManager::addOrReplaceConfigurationToken(const CString& name, const CString& value)
{
const CIdentifier oldID = this->lookUpConfigurationTokenIdentifier(name, false);
if (oldID == CIdentifier::undefined()) { return CIdentifier::undefined() != this->createConfigurationToken(name, value); }
return this->setConfigurationTokenValue(oldID, value);
}
// ----------------------------------------------------------------------------------------------------------------------------
CIdentifier CConfigurationManager::lookUpConfigurationTokenIdentifier(const CString& name, const bool recursive) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto it = m_ConfigTokens.begin();
while (it != m_ConfigTokens.end())
{
if (it->second.name == name) { return it->first; }
++it;
}
if (recursive && m_parentConfigManager) { return m_parentConfigManager->lookUpConfigurationTokenIdentifier(name, recursive); }
return CIdentifier::undefined();
}
CString CConfigurationManager::lookUpConfigurationTokenValue(const CString& name) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto it = m_ConfigTokens.begin();
while (it != m_ConfigTokens.end())
{
if (it->second.name == name) { return it->second.value; }
++it;
}
if (m_parentConfigManager) { return m_parentConfigManager->lookUpConfigurationTokenValue(name); }
return "";
}
// ----------------------------------------------------------------------------------------------------------------------------
bool CConfigurationManager::registerKeywordParser(const CString& keyword, const IConfigurationKeywordExpandCallback& callback)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(keyword != CString("") && keyword != CString("core") && keyword != CString("environment"),
"Trying to overwrite internal keyword " << keyword,
ErrorType::BadResourceCreation);
m_keywordOverrides[keyword] = &callback;
return true;
}
bool CConfigurationManager::unregisterKeywordParser(const CString& keyword)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(m_keywordOverrides.count(keyword), "Override for keyword [" << keyword << "] was not found", Kernel::ErrorType::ResourceNotFound);
m_keywordOverrides.erase(keyword);
return true;
}
bool CConfigurationManager::unregisterKeywordParser(const IConfigurationKeywordExpandCallback& callback)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto it = m_keywordOverrides.begin();
bool res = false;
while (it != m_keywordOverrides.end())
{
if (it->second == &callback)
{
m_keywordOverrides.erase(it);
res = true;
break;
}
++it;
}
OV_ERROR_UNLESS_KRF(res, "Override for the callback was not found", Kernel::ErrorType::ResourceNotFound);
return res;
}
// ----------------------------------------------------------------------------------------------------------------------------
CString CConfigurationManager::expand(const CString& expression) const
{
const std::string value(expression.toASCIIString());
std::string res;
if (this->internalExpand(value, res)) { return res.c_str(); }
return value.c_str();
}
// ----------------------------------------------------------------------------------------------------------------------------
CIdentifier CConfigurationManager::getUnusedIdentifier() const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
uint64_t id = (uint64_t(rand()) << 32) + uint64_t(rand());
CIdentifier res;
std::map<CIdentifier, config_token_t>::const_iterator i;
do
{
id++;
res = CIdentifier(id);
i = m_ConfigTokens.find(res);
} while (i != m_ConfigTokens.end() || res == CIdentifier::undefined());
return res;
}
// ----------------------------------------------------------------------------------------------------------------------------
enum class ENodeType { Value, NamePrefix, NamePostfix, };
bool CConfigurationManager::internalExpand(const std::string& sValue, std::string& result) const
{
std::stack<std::pair<ENodeType, std::string>> children;
children.push(std::make_pair(ENodeType::Value, std::string()));
std::string prefix;
std::string postfix;
std::string lowerPrefix;
std::string lowerPostfix;
std::string value;
std::string expandedValue;
for (size_t i = 0; i < sValue.length(); ++i)
{
bool shouldExpand;
switch (sValue[i])
{
case '$':
children.push(std::make_pair(ENodeType::NamePrefix, std::string()));
break;
case '{':
OV_ERROR_UNLESS_KRF(children.top().first == ENodeType::NamePrefix,
"Could not expand token with syntax error while expanding " << sValue, Kernel::ErrorType::BadFileParsing);
children.push(std::make_pair(ENodeType::NamePostfix, std::string()));
break;
case '}':
OV_ERROR_UNLESS_KRF(children.top().first == ENodeType::NamePostfix,
"Could not expand token with syntax error while expanding " << sValue, Kernel::ErrorType::BadFileParsing);
postfix = children.top().second;
lowerPostfix.resize(postfix.size());
std::transform(postfix.begin(), postfix.end(), lowerPostfix.begin(), ToLower<std::string::value_type>);
children.pop();
prefix = children.top().second;
lowerPrefix.resize(prefix.size());
std::transform(prefix.begin(), prefix.end(), lowerPrefix.begin(), ToLower<std::string::value_type>);
children.pop();
shouldExpand = true;
if (lowerPrefix.empty())
{
// value = this->getConfigurationTokenValue(this->lookUpConfigurationTokenIdentifier(postfix.c_str()));
// this->internalGetConfigurationTokenValueFromName(postfix, value);
value = this->lookUpConfigurationTokenValue(postfix.c_str()).toASCIIString();
}
else if (lowerPrefix == "environment" || lowerPrefix == "env")
{
char* envValue = getenv(postfix.c_str());
value = (envValue ? envValue : "");
shouldExpand = false;
}
else if (lowerPrefix == "core")
{
if (lowerPostfix == "random") { value = std::to_string(this->getRandom()); }
else if (lowerPostfix == "index") { value = std::to_string(this->getIndex()); }
else if (lowerPostfix == "time") { value = this->getTime(); }
else if (lowerPostfix == "date") { value = this->getDate(); }
else if (lowerPostfix == "real-time") { value = std::to_string(this->getRealTime()); }
else if (lowerPostfix == "process-id") { value = std::to_string(this->getProcessId()); }
else
{
OV_ERROR_KRF("Could not expand token with " << prefix << " prefix and " << postfix << " postfix while expanding " << sValue,
ErrorType::BadFileParsing);
}
}
else
{
if (m_keywordOverrides.count(lowerPrefix.c_str()))
{
CString overridenValue("");
OV_ERROR_UNLESS_KRF((m_keywordOverrides.find(lowerPrefix.c_str())->second)->expand(CString(postfix.c_str()), overridenValue),
"Could not expand $" << lowerPrefix << "{" << lowerPostfix << "}",
ErrorType::BadFileParsing);
value = overridenValue;
}
else
{
OV_ERROR_UNLESS_KRF(m_parentConfigManager, "Could not expand token with " << prefix << " prefix while expanding " << sValue,
ErrorType::BadFileParsing);
std::string keyword = "$" + lowerPrefix + "{" + lowerPostfix + "}";
value = m_parentConfigManager->expand(CString(keyword.c_str()));
if (value == sValue)
{
value = "";
OV_ERROR_KRF("Could not expand token with " << prefix << " prefix while expanding " << sValue,
ErrorType::BadFileParsing);
}
}
}
if (shouldExpand)
{
OV_ERROR_UNLESS_KRF(this->internalExpand(value, expandedValue),
"Could not expand " << value << " while expanding " << sValue,
ErrorType::BadFileParsing);
children.top().second += expandedValue;
}
else { children.top().second += value; }
break;
case '\\':
i++;
OV_ERROR_UNLESS_KRF(i < sValue.length(),
"Could not expand token with unterminated string while expanding " << sValue,
ErrorType::BadFileParsing);
default:
children.top().second += sValue[i];
break;
}
}
/* This will merge all NamePrefix Node on top of the pile into the first which isn't.
* In case of handling an UNC path, pile should look like this, eventually with multiple ENodeType::NamePrefix :
* ENodeType::NamePrefix, value2
* ENodeType::Value, value1
*
* This will merge all of them into the Node below, like this :
* ENodeType::Value, value1 + '$' + value2 + ( '$' + value3 ...)
* Using this method, tokens are not reinterpreted, which reduce risks introduced by introducing parser leniency.
*/
while (children.top().first == ENodeType::NamePrefix && children.size() > 1)
{
const std::string topVal = children.top().second;
children.pop();
children.top().second += "$" + topVal;
}
OV_ERROR_UNLESS_KRF(children.size() == 1, "Could not expand token with unterminated string while expanding " << sValue,
ErrorType::BadFileParsing);
result = children.top().second;
return true;
}
bool CConfigurationManager::internalExpandOnlyKeyword(const std::string& sKeyword, const std::string& sValue, std::string& sResult,
bool preserveBackslashes = false) const
{
std::stack<std::pair<ENodeType, std::string>> childrens;
childrens.push(std::make_pair(ENodeType::Value, std::string()));
std::string prefix;
std::string postfix;
std::string lowerPrefix;
std::string lowerPostfix;
std::string value;
std::string expandedValue;
for (size_t i = 0; i < sValue.length(); ++i)
{
bool shouldExpand;
switch (sValue[i])
{
case '$':
childrens.push(std::make_pair(ENodeType::NamePrefix, std::string()));
break;
case '{':
OV_ERROR_UNLESS_KRF(childrens.top().first == ENodeType::NamePrefix,
"Could not expand token with syntax error while expanding " << sValue,
ErrorType::BadFileParsing);
childrens.push(std::make_pair(ENodeType::NamePostfix, std::string()));
break;
case '}':
OV_ERROR_UNLESS_KRF(childrens.top().first == ENodeType::NamePostfix,
"Could not expand token with syntax error while expanding " << sValue,
ErrorType::BadFileParsing);
postfix = childrens.top().second;
childrens.pop();
prefix = childrens.top().second;
childrens.pop();
shouldExpand = true;
lowerPrefix = prefix;
lowerPostfix = postfix;
std::transform(lowerPrefix.begin(), lowerPrefix.end(), lowerPrefix.begin(), ToLower<std::string::value_type>);
std::transform(lowerPostfix.begin(), lowerPostfix.end(), lowerPostfix.begin(), ToLower<std::string::value_type>);
if (lowerPrefix == sKeyword)
{
OV_ERROR_UNLESS_KRF(m_keywordOverrides.count(lowerPrefix.c_str()),
"Could not expand token with " << prefix << " prefix while expanding " << sValue,
ErrorType::BadFileParsing);
CString overridenValue("");
OV_ERROR_UNLESS_KRF((m_keywordOverrides.find(lowerPrefix.c_str())->second)->expand(CString(postfix.c_str()), overridenValue),
"Could not expand $" << lowerPrefix << "{" << lowerPostfix << "}",
ErrorType::BadFileParsing);
value = overridenValue;
}
else
{
// If the previous token was not something we want to parse we will simply put it back
value = "$" + prefix + "{" + postfix + "}";
shouldExpand = false;
}
if (shouldExpand)
{
OV_ERROR_UNLESS_KRF(this->internalExpandOnlyKeyword(sKeyword, value, expandedValue),
"Could not expand " << value << " while expanding " << sValue,
ErrorType::BadFileParsing);
childrens.top().second += expandedValue;
}
else { childrens.top().second += value; }
break;
case '\\':
if (preserveBackslashes) { childrens.top().second += sValue[i]; }
i++;
OV_ERROR_UNLESS_KRF(i < sValue.length(),
"Could not expand token with unterminated string while expanding " << sValue,
ErrorType::BadFileParsing);
childrens.top().second += sValue[i];
break;
default:
childrens.top().second += sValue[i];
break;
}
}
OV_ERROR_UNLESS_KRF(childrens.size() == 1, "Could not expand token with unterminated string while expanding " << sValue,
ErrorType::BadFileParsing);
sResult = childrens.top().second;
return true;
}
bool CConfigurationManager::internalGetConfigurationTokenValueFromName(const std::string& name, std::string& value) const
{
const CIdentifier tokenID = this->lookUpConfigurationTokenIdentifier(name.c_str(), false);
if (tokenID == CIdentifier::undefined())
{
OV_ERROR_UNLESS_KRF(m_parentConfigManager,
"Could not expand token [" << name <<
"]. This token does not exist. If this is expected behavior, please add \"" << name <<
" = \" to your configuration file",
ErrorType::ResourceNotFound);
const std::string str = std::string("${") + name + ("}");
value = m_parentConfigManager->expand(str.c_str());
}
else { value = this->getConfigurationTokenValue(tokenID); }
return true;
}
CString CConfigurationManager::expandOnlyKeyword(const CString& rKeyword, const CString& expression, const bool preserveBackslashes) const
{
const std::string value(expression.toASCIIString());
std::string res;
if (this->internalExpandOnlyKeyword(rKeyword.toASCIIString(), value, res, preserveBackslashes)) { return res.c_str(); }
return value.c_str();
}
double CConfigurationManager::expandAsFloat(const CString& expression, const double fallbackValue) const
{
const CString result = this->expand(expression);
double res;
try { res = std::stod(result.toASCIIString()); }
catch (const std::exception&) { res = fallbackValue; }
return res;
}
int64_t CConfigurationManager::expandAsInteger(const CString& expression, const int64_t fallbackValue) const
{
const CString result = this->expand(expression);
int64_t res;
try { res = std::stoll(result.toASCIIString()); }
catch (const std::exception&) { res = fallbackValue; }
return res;
}
uint64_t CConfigurationManager::expandAsUInteger(const CString& expression, const uint64_t fallbackValue) const
{
const CString result = this->expand(expression);
uint64_t res;
try { res = std::stoull(result.toASCIIString()); }
catch (const std::exception&) { res = fallbackValue; }
return res;
}
bool CConfigurationManager::expandAsBoolean(const CString& expression, const bool fallbackValue) const
{
std::string res = this->expand(expression).toASCIIString();
std::transform(res.begin(), res.end(), res.begin(), ToLower<std::string::value_type>);
if (res == "true" || res == "on" || res == "1") { return true; }
if (res == "false" || res == "off" || res == "0") { return false; }
return fallbackValue;
}
uint64_t CConfigurationManager::expandAsEnumerationEntryValue(const CString& expression, const CIdentifier& enumTypeID, const uint64_t fallbackValue) const
{
const CString str = this->expand(expression);
const uint64_t result = this->getTypeManager().getEnumerationEntryValueFromName(enumTypeID, str);
if (result != ULLONG_MAX) { return result; }
return fallbackValue;
}
size_t CConfigurationManager::getRandom() { return size_t(System::Math::randomI()); }
size_t CConfigurationManager::getIndex() const { return m_idx++; }
CString CConfigurationManager::getTime()
{
char res[1024];
time_t rawTime;
time(&rawTime);
struct tm* timeInfo = localtime(&rawTime);
sprintf(res, "%02i.%02i.%02i", timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
return res;
}
CString CConfigurationManager::getDate()
{
char res[1024];
time_t rawTime;
time(&rawTime);
struct tm* timeInfo = localtime(&rawTime);
sprintf(res, "%04i.%02i.%02i", timeInfo->tm_year + 1900, timeInfo->tm_mon + 1, timeInfo->tm_mday);
return res;
}
size_t CConfigurationManager::getRealTime() const { return System::Time::getTime() - m_startTime; }
size_t CConfigurationManager::getProcessId()
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
return size_t(getpid());
#elif defined TARGET_OS_Windows
return size_t(GetCurrentProcessId());
#else
#error TODO
#endif
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,74 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
#include <string>
#include <mutex>
namespace OpenViBE {
namespace Kernel {
class IConfigurationKeywordExpandCallback;
typedef struct
{
CString name;
CString value;
} config_token_t;
class CConfigurationManager final : public TKernelObject<IConfigurationManager>
{
public:
CConfigurationManager(const IKernelContext& ctx, IConfigurationManager* parentConfigManager = nullptr);
void clear() override;
bool addConfigurationFromFile(const CString& rFileNameWildCard) override;
CIdentifier createConfigurationToken(const CString& name, const CString& value) override;
bool releaseConfigurationToken(const CIdentifier& identifier) override;
CIdentifier getNextConfigurationTokenIdentifier(const CIdentifier& prevConfigTokenID) const override;
CString getConfigurationTokenName(const CIdentifier& identifier) const override;
CString getConfigurationTokenValue(const CIdentifier& identifier) const override;
bool setConfigurationTokenName(const CIdentifier& identifier, const CString& name) override;
bool setConfigurationTokenValue(const CIdentifier& identifier, const CString& value) override;
bool addOrReplaceConfigurationToken(const CString& name, const CString& value) override;
CIdentifier lookUpConfigurationTokenIdentifier(const CString& name, bool recursive) const override;
CString lookUpConfigurationTokenValue(const CString& name) const override;
bool registerKeywordParser(const CString& keyword, const IConfigurationKeywordExpandCallback& callback) override;
bool unregisterKeywordParser(const CString& keyword) override;
bool unregisterKeywordParser(const IConfigurationKeywordExpandCallback& callback) override;
CString expand(const CString& expression) const override;
_IsDerivedFromClass_Final_(TKernelObject<IConfigurationManager>, OVK_ClassId_Kernel_Config_ConfigManager)
CString expandOnlyKeyword(const CString& rKeyword, const CString& expression, bool preserveBackslashes) const override;
double expandAsFloat(const CString& expression, double fallbackValue) const override;
int64_t expandAsInteger(const CString& expression, int64_t fallbackValue) const override;
uint64_t expandAsUInteger(const CString& expression, uint64_t fallbackValue) const override;
bool expandAsBoolean(const CString& expression, bool fallbackValue) const override;
uint64_t expandAsEnumerationEntryValue(const CString& expression, const CIdentifier& enumTypeID, uint64_t fallbackValue) const override;
protected:
CIdentifier getUnusedIdentifier() const;
bool internalExpand(const std::string& sValue, std::string& result) const;
bool internalExpandOnlyKeyword(const std::string& sKeyword, const std::string& sValue, std::string& sResult, bool preserveBackslashes) const;
bool internalGetConfigurationTokenValueFromName(const std::string& name, std::string& value) const;
IConfigurationManager* m_parentConfigManager = nullptr;
mutable size_t m_idx;
mutable size_t m_startTime;
static size_t getRandom();
size_t getIndex() const;
static CString getTime();
static CString getDate();
size_t getRealTime() const;
static size_t getProcessId();
std::map<CIdentifier, config_token_t> m_ConfigTokens;
std::map<CString, const IConfigurationKeywordExpandCallback*> m_keywordOverrides;
mutable std::recursive_mutex m_mutex;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,92 @@
/*********************************************************************
* 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 "ovkCErrorManager.h"
// Error manager IError internal implementation
namespace OpenViBE {
namespace Kernel {
class CError final : public IError
{
public:
CError(ErrorType type, const char* description, IError* nestedError, const char* filename, const size_t line)
: m_errorType(type), m_nestedError(nestedError), m_description(description), m_location(std::string(filename) + ":" + std::to_string(line)) { }
~CError() override { }
const char* getErrorString() const override { return m_description.c_str(); }
const char* getErrorLocation() const override { return m_location.c_str(); }
ErrorType getErrorType() const override { return m_errorType; }
const IError* getNestedError() const override { return m_nestedError.get(); }
_IsDerivedFromClass_Final_(IError, OVK_ClassId_Kernel_Error_Error)
private:
ErrorType m_errorType;
std::unique_ptr<IError> m_nestedError;
std::string m_description;
std::string m_location;
};
// Error manager implementation
void CErrorManager::pushErrorAtLocation(const ErrorType type, const char* description, const char* filename, const size_t line)
{
std::lock_guard<std::mutex> lock(m_managerGuard);
const auto lastTopError = m_topError.release();
m_topError.reset(new CError(type, description, lastTopError, filename, line));
}
void CErrorManager::releaseErrors()
{
std::lock_guard<std::mutex> lock(m_managerGuard);
m_topError.reset(nullptr);
}
bool CErrorManager::hasError() const
{
std::lock_guard<std::mutex> lock(m_managerGuard);
return (m_topError != nullptr);
}
const IError* CErrorManager::getLastError() const
{
std::lock_guard<std::mutex> lock(m_managerGuard);
return m_topError.get();
}
const char* CErrorManager::getLastErrorString() const
{
std::lock_guard<std::mutex> lock(m_managerGuard);
return (m_topError ? m_topError->getErrorString() : "");
}
ErrorType CErrorManager::getLastErrorType() const
{
std::lock_guard<std::mutex> lock(m_managerGuard);
return (m_topError ? m_topError->getErrorType() : ErrorType::NoErrorFound);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,60 @@
/*********************************************************************
* 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 <mutex>
#include <memory>
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
/**
* \class CErrorManager
* \author Charles Garraud (Inria)
* \date 2016-07-13
* \brief Error manager kernel default implementation
*/
class CErrorManager final : public TKernelObject<IErrorManager>
{
public:
explicit CErrorManager(const IKernelContext& context) : TKernelObject<IErrorManager>(context), m_topError(nullptr) { }
~CErrorManager() override { this->releaseErrors(); }
void pushError(ErrorType type, const char* description) override { this->pushErrorAtLocation(type, description, "NoLocationInfo", 0); }
void pushErrorAtLocation(ErrorType type, const char* description, const char* filename, size_t line) override;
void releaseErrors() override;
bool hasError() const override;
const IError* getLastError() const override;
const char* getLastErrorString() const override;
ErrorType getLastErrorType() const override;
_IsDerivedFromClass_Final_(TKernelObject<IErrorManager>, OVK_ClassId_Kernel_Error_ErrorManager)
private:
mutable std::mutex m_managerGuard;
std::unique_ptr<IError> m_topError;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,409 @@
#include "ovkCLogListenerConsole.h"
#include <iostream>
#include <sstream>
#if defined TARGET_OS_Windows
#include <Windows.h>
#endif
namespace OpenViBE {
namespace Kernel {
CLogListenerConsole::CLogListenerConsole(const IKernelContext& ctx, const CString& sApplicationName)
: TKernelObject<ILogListener>(ctx), m_color(LogColor_Default), m_applicationName(sApplicationName), m_timeInSeconds(true), m_timePrecision(3),
m_useColor(true)
{
#if defined TARGET_OS_Windows
SetConsoleOutputCP(CP_UTF8);
#endif
}
void CLogListenerConsole::configure(const IConfigurationManager& configManager)
{
m_timeInSeconds = configManager.expandAsBoolean("${Kernel_ConsoleLogTimeInSecond}", true);
m_logWithHexa = configManager.expandAsBoolean("${Kernel_ConsoleLogWithHexa}", false);
m_timePrecision = configManager.expandAsUInteger("${Kernel_ConsoleLogTimePrecision}", 3);
m_useColor = configManager.expandAsBoolean("${Kernel_ConsoleLogUseColor}", true);
}
bool CLogListenerConsole::isActive(const ELogLevel level)
{
const auto itLogLevel = m_activeLevels.find(level);
if (itLogLevel == m_activeLevels.end()) { return true; }
return itLogLevel->second;
}
bool CLogListenerConsole::activate(const ELogLevel level, const bool active)
{
m_activeLevels[level] = active;
return true;
}
bool CLogListenerConsole::activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active)
{
for (int i = startLevel; i <= endLevel; ++i) { m_activeLevels[ELogLevel(i)] = active; }
return true;
}
bool CLogListenerConsole::activate(const bool active) { return activate(LogLevel_First, LogLevel_Last, active); }
void CLogListenerConsole::log(const CTime value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << value.str(m_timeInSeconds, m_logWithHexa);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const uint64_t value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
const std::ios_base::fmtflags fmt = std::cout.flags();
std::cout << std::dec << value;
if (m_logWithHexa) { std::cout << " (0x" << std::hex << value << ")"; }
std::cout.flags(fmt);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const uint32_t value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
const std::ios_base::fmtflags fmt = std::cout.flags();
std::cout << std::dec << value;
if (m_logWithHexa) { std::cout << " (0x" << std::hex << value << ")"; }
std::cout.flags(fmt);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const int64_t value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
const std::ios_base::fmtflags fmt = std::cout.flags();
std::cout << std::dec << value;
if (m_logWithHexa) { std::cout << " (0x" << std::hex << value << ")"; }
std::cout.flags(fmt);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const int value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
const std::ios_base::fmtflags fmt = std::cout.flags();
std::cout << std::dec << value;
if (m_logWithHexa) { std::cout << " (0x" << std::hex << value << ")"; }
std::cout.flags(fmt);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const double value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << value;
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const bool value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << (value ? "true" : "false");
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const CIdentifier& value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << value.str();
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const CString& value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << value;
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const std::string& value)
{
this->log(LogColor_PushStateBit);
this->log(LogColor_ForegroundMagenta);
std::cout << value;
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const char* value) { std::cout << value << std::flush; }
void CLogListenerConsole::log(const ELogLevel level)
{
this->log(LogColor_PushStateBit);
switch (level)
{
case LogLevel_Debug: this->log(LogColor_ForegroundBlue);
break;
case LogLevel_Benchmark: this->log(LogColor_ForegroundMagenta);
break;
case LogLevel_Trace: this->log(LogColor_ForegroundYellow);
break;
case LogLevel_Info: this->log(LogColor_ForegroundGreen);
break;
case LogLevel_Warning: this->log(LogColor_ForegroundCyan);
break;
case LogLevel_ImportantWarning:
case LogLevel_Error:
case LogLevel_Fatal: this->log(LogColor_ForegroundRed);
break;
default: break;
}
std::cout << toString(level);
this->log(LogColor_PopStateBit);
}
void CLogListenerConsole::log(const ELogColor color)
{
if (m_useColor)
{
// Tests 'push state' bit
if (color & LogColor_PushStateBit) { m_colors.push(m_color); }
// Tests 'pop state' bit
if (color & LogColor_PopStateBit)
{
if (!m_colors.empty())
{
m_color = m_colors.top();
m_colors.pop();
}
else { m_color = LogColor_Default; }
}
// Tests 'reset' bit
if (color & LogColor_ResetBit) { m_color = LogColor_Default; }
// Tests 'foreground' bit
if (color & LogColor_ForegroundBit)
{
// Tests 'color' bit
if (color & LogColor_ForegroundColorBit)
{
const ELogColor colorMask = ELogColor(LogColor_ForegroundColorRedBit | LogColor_ForegroundColorGreenBit | LogColor_ForegroundColorBlueBit);
m_color = ELogColor(m_color & (~colorMask));
m_color = ELogColor(m_color | (color & colorMask) | LogColor_ForegroundBit | LogColor_ForegroundColorBit);
}
// Test 'light' bit
if (color & LogColor_ForegroundLightBit)
{
const ELogColor lightMask = ELogColor(LogColor_ForegroundLightBit | LogColor_ForegroundLightStateBit);
m_color = ELogColor(m_color & (~lightMask));
m_color = ELogColor(m_color | (color & lightMask) | LogColor_ForegroundBit);
}
// Test 'blink' bit
if (color & LogColor_ForegroundBlinkBit)
{
const ELogColor blinkMask = ELogColor(LogColor_ForegroundBlinkBit | LogColor_ForegroundBlinkStateBit);
m_color = ELogColor(m_color & (~blinkMask));
m_color = ELogColor(m_color | (color & blinkMask) | LogColor_ForegroundBit);
}
// Test 'bold' bit
if (color & LogColor_ForegroundBoldBit)
{
const ELogColor boldMask = ELogColor(LogColor_ForegroundBoldBit | LogColor_ForegroundBoldStateBit);
m_color = ELogColor(m_color & (~boldMask));
m_color = ELogColor(m_color | (color & boldMask) | LogColor_ForegroundBit);
}
// Test 'underline' bit
if (color & LogColor_ForegroundUnderlineBit)
{
const ELogColor underlineMask = ELogColor(LogColor_ForegroundBlinkBit | LogColor_ForegroundBlinkStateBit);
m_color = ELogColor(m_color & (~underlineMask));
m_color = ELogColor(m_color | (color & underlineMask) | LogColor_ForegroundBit);
}
}
// Tests 'background' bit
if (color & LogColor_BackgroundBit)
{
// Tests 'color' bit
if (color & LogColor_BackgroundColorBit)
{
const ELogColor colorMask = ELogColor(LogColor_BackgroundColorRedBit | LogColor_BackgroundColorGreenBit | LogColor_BackgroundColorBlueBit);
m_color = ELogColor(m_color & (~colorMask));
m_color = ELogColor(m_color | (color & colorMask) | LogColor_BackgroundBit | LogColor_BackgroundColorBit);
}
// Test 'light' bit
if (color & LogColor_BackgroundLightBit)
{
const ELogColor lightMask = ELogColor(LogColor_BackgroundLightBit | LogColor_BackgroundLightStateBit);
m_color = ELogColor(m_color & (~lightMask));
m_color = ELogColor(m_color | (color & lightMask) | LogColor_BackgroundBit);
}
// Test 'blink' bit
if (color & LogColor_BackgroundBlinkBit)
{
const ELogColor blinkMask = ELogColor(LogColor_BackgroundBlinkBit | LogColor_BackgroundBlinkStateBit);
m_color = ELogColor(m_color & (~blinkMask));
m_color = ELogColor(m_color | (color & blinkMask) | LogColor_BackgroundBit);
}
}
// Finally applies current color
applyColor();
}
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
void CLogListenerConsole::applyColor()
{
int gotACommand=0;
#define _command_separator_ (gotACommand++?";":"")
std::cout << "\033[00m";
if(m_color!=LogColor_Default)
{
std::cout << "\033[";
if(m_color&LogColor_ForegroundBit)
{
if(m_color&LogColor_ForegroundLightBit && m_color&LogColor_ForegroundLightStateBit) { } // No function to do that
if(m_color&LogColor_ForegroundBoldBit && m_color&LogColor_ForegroundBoldStateBit)
{
std::cout << _command_separator_ << "01";
}
if(m_color&LogColor_ForegroundUnderlineBit && m_color&LogColor_ForegroundUnderlineStateBit)
{
std::cout << _command_separator_ << "04";
}
if(m_color&LogColor_ForegroundBlinkBit && m_color&LogColor_ForegroundBlinkStateBit)
{
std::cout << _command_separator_ << "05";
}
if(m_color&LogColor_ForegroundColorBit)
{
ELogColor color=ELogColor(m_color&(LogColor_ForegroundBit|LogColor_ForegroundColorBit|LogColor_ForegroundColorRedBit|LogColor_ForegroundColorGreenBit|LogColor_ForegroundColorBlueBit));
switch(color)
{
case LogColor_ForegroundBlack: std::cout << _command_separator_ << "30"; break;
case LogColor_ForegroundRed: std::cout << _command_separator_ << "31"; break;
case LogColor_ForegroundGreen: std::cout << _command_separator_ << "32"; break;
case LogColor_ForegroundYellow: std::cout << _command_separator_ << "33"; break;
case LogColor_ForegroundBlue: std::cout << _command_separator_ << "34"; break;
case LogColor_ForegroundMagenta: std::cout << _command_separator_ << "35"; break;
case LogColor_ForegroundCyan: std::cout << _command_separator_ << "36"; break;
case LogColor_ForegroundWhite: std::cout << _command_separator_ << "37"; break;
default: break;
}
}
}
if(m_color&LogColor_BackgroundBit)
{
if(m_color&LogColor_BackgroundColorBit)
{
ELogColor color=ELogColor(m_color&(LogColor_BackgroundBit|LogColor_BackgroundColorBit|LogColor_BackgroundColorRedBit|LogColor_BackgroundColorGreenBit|LogColor_BackgroundColorBlueBit));
switch(color)
{
case LogColor_BackgroundBlack: std::cout << _command_separator_ << "40"; break;
case LogColor_BackgroundRed: std::cout << _command_separator_ << "41"; break;
case LogColor_BackgroundGreen: std::cout << _command_separator_ << "42"; break;
case LogColor_BackgroundYellow: std::cout << _command_separator_ << "43"; break;
case LogColor_BackgroundBlue: std::cout << _command_separator_ << "44"; break;
case LogColor_BackgroundMagenta: std::cout << _command_separator_ << "45"; break;
case LogColor_BackgroundCyan: std::cout << _command_separator_ << "46"; break;
case LogColor_BackgroundWhite: std::cout << _command_separator_ << "47"; break;
default: break;
}
}
if(m_color&LogColor_BackgroundLightBit && m_color&LogColor_BackgroundLightStateBit) { } // No function to do that
if(m_color&LogColor_BackgroundBlinkBit && m_color&LogColor_BackgroundBlinkStateBit) { } // No function to do that
}
std::cout << "m";
}
#undef _command_separator_
}
#elif defined TARGET_OS_Windows
void CLogListenerConsole::applyColor()
{
WORD attribute = 0;
if (m_color & LogColor_ForegroundBit)
{
if (m_color & LogColor_ForegroundColorBit)
{
attribute |= ((m_color & LogColor_ForegroundColorRedBit) ? FOREGROUND_RED : 0);
attribute |= ((m_color & LogColor_ForegroundColorGreenBit) ? FOREGROUND_GREEN : 0);
attribute |= ((m_color & LogColor_ForegroundColorBlueBit) ? FOREGROUND_BLUE : 0);
}
else
{
attribute |= FOREGROUND_RED;
attribute |= FOREGROUND_GREEN;
attribute |= FOREGROUND_BLUE;
}
if (m_color & LogColor_ForegroundLightBit && m_color & LogColor_ForegroundLightStateBit) { attribute |= FOREGROUND_INTENSITY; }
if (m_color & LogColor_ForegroundBoldBit && m_color & LogColor_ForegroundBoldStateBit) { } // No function to do that
if (m_color & LogColor_ForegroundUnderlineBit && m_color & LogColor_ForegroundUnderlineStateBit) { attribute |= COMMON_LVB_UNDERSCORE; }
if (m_color & LogColor_ForegroundBlinkBit && m_color & LogColor_ForegroundBlinkStateBit) { } // No function to do that
}
else
{
attribute |= FOREGROUND_RED;
attribute |= FOREGROUND_GREEN;
attribute |= FOREGROUND_BLUE;
}
if (m_color & LogColor_BackgroundBit)
{
if (m_color & LogColor_BackgroundColorBit)
{
attribute |= ((m_color & LogColor_BackgroundColorRedBit) ? BACKGROUND_RED : 0);
attribute |= ((m_color & LogColor_BackgroundColorGreenBit) ? BACKGROUND_GREEN : 0);
attribute |= ((m_color & LogColor_BackgroundColorBlueBit) ? BACKGROUND_BLUE : 0);
}
if (m_color & LogColor_BackgroundLightBit && m_color & LogColor_BackgroundLightStateBit) { attribute |= BACKGROUND_INTENSITY; }
if (m_color & LogColor_BackgroundBlinkBit && m_color & LogColor_BackgroundBlinkStateBit)
{
// No function to do that
}
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attribute);
}
#else
void CLogListenerConsole::applyColor() { }
#endif
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,54 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <stack>
#include <map>
namespace OpenViBE {
namespace Kernel {
class CLogListenerConsole final : public TKernelObject<ILogListener>
{
public:
CLogListenerConsole(const IKernelContext& ctx, const CString& sApplicationName);
bool isActive(const ELogLevel level) override;
bool activate(const ELogLevel level, const bool active) override;
bool activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active) override;
bool activate(const bool active) override;
void configure(const IConfigurationManager& configManager);
void log(const CTime value) override;
void log(const uint64_t value) override;
void log(const uint32_t value) override;
void log(const int64_t value) override;
void log(const int value) override;
void log(const double value) override;
void log(const bool value) override;
void log(const CIdentifier& value) override;
void log(const CString& value) override;
void log(const std::string& value) override;
void log(const char* value) override;
void log(const ELogLevel level) override;
void log(const ELogColor color) override;
_IsDerivedFromClass_Final_(TKernelObject<ILogListener>, OVK_ClassId_Kernel_Log_LogListenerConsole)
protected:
void applyColor();
std::map<ELogLevel, bool> m_activeLevels;
std::stack<ELogColor> m_colors;
ELogColor m_color;
CString m_applicationName;
bool m_logWithHexa = false;
bool m_timeInSeconds = false;
uint64_t m_timePrecision = 0;
bool m_useColor = false;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,56 @@
#include "ovkCLogListenerFile.h"
#include <sstream>
#include <iostream>
#include <fs/Files.h>
namespace OpenViBE {
namespace Kernel {
CLogListenerFile::CLogListenerFile(const IKernelContext& ctx, const CString& applicationName, const CString& logFilename)
: TKernelObject<ILogListener>(ctx), m_applicationName(applicationName), m_logFilename(logFilename)
{
// Create the path to the log file
FS::Files::createParentPath(logFilename.toASCIIString());
FS::Files::openFStream(m_fsFileStream, logFilename.toASCIIString(), std::ios_base::out);
if (!m_fsFileStream.is_open())
{
std::cout << "[ ERR ] Error while creating FileLogListener into '" << logFilename << "'" << std::endl;
return;
}
m_fsFileStream << std::flush;
}
void CLogListenerFile::configure(const IConfigurationManager& configurationManager)
{
m_timeInSeconds = configurationManager.expandAsBoolean("${Kernel_FileLogTimeInSecond}", false);
m_logWithHexa = configurationManager.expandAsBoolean("${Kernel_FileLogWithHexa}", true);
m_timePrecision = configurationManager.expandAsUInteger("${Kernel_FileLogTimePrecision}", 3);
}
bool CLogListenerFile::isActive(const ELogLevel level)
{
const auto it = m_activeLevels.find(level);
if (it == m_activeLevels.end()) { return true; }
return it->second;
}
bool CLogListenerFile::activate(const ELogLevel level, const bool active)
{
m_activeLevels[level] = active;
return true;
}
bool CLogListenerFile::activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active)
{
for (int i = startLevel; i <= endLevel; ++i) { m_activeLevels[ELogLevel(i)] = active; }
return true;
}
bool CLogListenerFile::activate(const bool active) { return activate(LogLevel_First, LogLevel_Last, active); }
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,68 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <sstream>
#include <iostream>
#include <map>
#include <fstream>
namespace OpenViBE {
namespace Kernel {
class CLogListenerFile final : public TKernelObject<ILogListener>
{
public:
CLogListenerFile(const IKernelContext& ctx, const CString& applicationName, const CString& logFilename);
~CLogListenerFile() override { m_fsFileStream.close(); }
bool isActive(const ELogLevel level) override;
bool activate(const ELogLevel level, const bool active) override;
bool activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active) override;
bool activate(const bool active) override;
void configure(const IConfigurationManager& configurationManager);
void log(const CTime value) override { m_fsFileStream << value.str(m_timeInSeconds, m_logWithHexa); }
void log(const uint64_t value) override { logInteger(value); }
void log(const uint32_t value) override { logInteger(value); }
void log(const int64_t value) override { logInteger(value); }
void log(const int value) override { logInteger(value); }
void log(const double value) override { m_fsFileStream << value; }
void log(const bool value) override { m_fsFileStream << (value ? "true" : "false"); }
void log(const CIdentifier& value) override { m_fsFileStream << value.str(); }
void log(const CString& value) override { m_fsFileStream << value << std::flush; }
void log(const std::string& value) override { m_fsFileStream << value << std::flush; }
void log(const char* value) override { m_fsFileStream << value << std::flush; }
void log(const ELogLevel level) override { m_fsFileStream << toString(level); }
void log(const ELogColor /*color*/) override { }
_IsDerivedFromClass_Final_(TKernelObject<ILogListener>, OVK_ClassId_Kernel_Log_LogListenerFile)
protected:
std::map<ELogLevel, bool> m_activeLevels;
CString m_applicationName;
CString m_logFilename;
std::fstream m_fsFileStream;
// Log Settings
bool m_timeInSeconds = true;
bool m_logWithHexa = false;
uint64_t m_timePrecision = 3;
private:
template <typename T>
void logInteger(T value)
{
m_fsFileStream << value << " ";
if (m_logWithHexa)
{
m_fsFileStream.setf(std::ios::hex);
m_fsFileStream << "(" << value << ")";
m_fsFileStream.unsetf(std::ios::hex);
}
}
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,86 @@
#include "ovkCLogManager.h"
namespace OpenViBE {
namespace Kernel {
bool CLogManager::isActive(const ELogLevel level)
{
const auto it = m_activeLevels.find(level);
if (it == m_activeLevels.end()) { return true; }
return it->second;
}
bool CLogManager::activate(const ELogLevel level, const bool active)
{
m_activeLevels[level] = active;
return true;
}
bool CLogManager::activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active)
{
for (int i = startLevel; i <= endLevel; ++i) { m_activeLevels[ELogLevel(i)] = active; }
return true;
}
void CLogManager::log(const char* value)
{
logForEach<const char*>(value);
{
GRAB_OWNERSHIP;
std::string copy(value);
if (copy.length() > 0 && copy[copy.length() - 1] == '\n')
{
// we are done, release
m_owner = std::thread::id();
m_condition.notify_one();
}
}
}
void CLogManager::log(const ELogLevel level)
{
{
GRAB_OWNERSHIP;
m_currentLogLevel = level;
}
logForEach<ELogLevel>(level);
}
bool CLogManager::addListener(ILogListener* listener)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (listener == nullptr) { return false; }
auto it = m_listeners.begin();
while (it != m_listeners.end())
{
if ((*it) == listener) { return false; }
++it;
}
m_listeners.push_back(listener);
return true;
}
bool CLogManager::removeListener(ILogListener* listener)
{
std::unique_lock<std::mutex> lock(m_mutex);
auto it = m_listeners.begin();
while (it != m_listeners.end())
{
if ((*it) == listener)
{
m_listeners.erase(it);
return true; // due to constraint in addListener(), listener can be in the array only once, so we can return
}
++it;
}
return false;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,75 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <vector>
#include <map>
#include <mutex>
#include <condition_variable>
#include <thread>
namespace OpenViBE {
namespace Kernel {
class CLogManager final : public TKernelObject<ILogManager>
{
public:
explicit CLogManager(const IKernelContext& ctx) : TKernelObject<ILogManager>(ctx) {}
bool isActive(const ELogLevel level) override;
bool activate(const ELogLevel level, const bool active) override;
bool activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active) override;
bool activate(const bool active) override { return activate(LogLevel_First, LogLevel_Last, active); }
void log(const CTime value) override { logForEach<const CTime>(value); }
void log(const uint64_t value) override { logForEach<const uint64_t>(value); }
void log(const uint32_t value) override { logForEach<const uint32_t>(value); }
void log(const int64_t value) override { logForEach<const int64_t>(value); }
void log(const int value) override { logForEach<const int>(value); }
void log(const double value) override { logForEach<const double>(value); }
void log(const bool value) override { logForEach<const bool>(value); }
void log(const CIdentifier& value) override { logForEach<const CIdentifier&>(value); }
void log(const CString& value) override { log(value.toASCIIString()); }
void log(const std::string& value) override { log(value.c_str()); }
void log(const char* value) override;
void log(const ELogLevel level) override;
void log(const ELogColor color) override { logForEach<ELogColor>(color); }
bool addListener(ILogListener* listener) override;
bool removeListener(ILogListener* listener) override;
_IsDerivedFromClass_Final_(TKernelObject<ILogManager>, OVK_ClassId_Kernel_Log_LogManager)
protected:
// This macro waits until m_oHolder is either the current thread id or unassigned
#define GRAB_OWNERSHIP std::unique_lock<std::mutex> lock(m_mutex); \
m_condition.wait(lock, [this]() { return (this->m_owner == std::thread::id() || this->m_owner == std::this_thread::get_id() ); } ); \
m_owner = std::this_thread::get_id();
template <class T>
void logForEach(T tValue)
{
GRAB_OWNERSHIP;
if (m_currentLogLevel != LogLevel_None && this->isActive(m_currentLogLevel))
{
for (auto i = m_listeners.begin(); i != m_listeners.end(); ++i) { if ((*i)->isActive(m_currentLogLevel)) { (*i)->log(tValue); } }
}
}
std::vector<ILogListener*> m_listeners;
std::map<ELogLevel, bool> m_activeLevels;
ELogLevel m_currentLogLevel = LogLevel_Info;
// Variables to make sure only one thread writes to the LogManager at a time.
// Concurrency control operating logic:
// Calling log() will wait until it obtains logmanager ownership for the calling thread
// Only thread having the ownership can write to the log
// After a thread logs an entry ending in an EOL character, the ownership is freed.
std::mutex m_mutex;
std::condition_variable m_condition;
std::thread::id m_owner;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,131 @@
#include "ovkCMetaboxManager.h"
#include <fs/IEntryEnumerator.h>
#include <map>
#include <random>
#include "ovp_global_defines.h"
#include "ovkCMetaboxObjectDesc.h"
namespace OpenViBE {
namespace Kernel {
class CMetaboxManagerEntryEnumeratorCallBack final : public TKernelObject<IObject>, public FS::IEntryEnumeratorCallBack
{
public:
CMetaboxManagerEntryEnumeratorCallBack(const IKernelContext& ctx, CMetaboxManager& metaboxManager)
: TKernelObject<IObject>(ctx), m_manager(metaboxManager) { m_n = 0; }
bool callback(FS::IEntryEnumerator::IEntry& rEntry, FS::IEntryEnumerator::IAttributes& rAttributes) override
{
if (rAttributes.isFile())
{
const char* fullFileName = rEntry.getName();
CIdentifier scenarioID, metaboxId, metaboxHash;
this->getKernelContext().getScenarioManager().
importScenarioFromFile(scenarioID, OV_ScenarioImportContext_OnLoadMetaboxImport, fullFileName);
if (scenarioID != CIdentifier::undefined())
{
IScenario& metaboxScenario = this->getKernelContext().getScenarioManager().getScenario(scenarioID);
const bool isValid = metaboxId.fromString(metaboxScenario.getAttributeValue(OVP_AttributeId_Metabox_ID));
if (isValid && metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Name) != CString())
{
const bool hasHash = metaboxHash.fromString(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_MetaboxHash));
if (!hasHash)
{
this->getKernelContext().getLogManager() << LogLevel_Warning << "The metabox " << metaboxId.str() <<
" has no Hash in the scenario " << fullFileName << "\n";
}
m_manager.setMetaboxFilePath(metaboxId, CString(fullFileName));
m_manager.setMetaboxHash(metaboxId, metaboxHash);
m_manager.setMetaboxObjectDesc(metaboxId, new Metabox::CMetaboxObjectDesc(metaboxId.str().c_str(), metaboxScenario));
m_n++;
}
else
{
this->getKernelContext().getLogManager() << LogLevel_Warning << "The metabox file " << fullFileName <<
" is missing elements. Please check it.\n";
}
}
this->getKernelContext().getScenarioManager().releaseScenario(scenarioID);
}
return true;
}
size_t resetMetaboxCount()
{
const size_t res = m_n;
m_n = 0;
return res;
}
_IsDerivedFromClass_Final_(TKernelObject<IObject>, CIdentifier::undefined())
protected:
CMetaboxManager& m_manager;
size_t m_n;
};
CMetaboxManager::CMetaboxManager(const IKernelContext& ctx) : TKernelObject<IMetaboxManager>(ctx)
{
this->TKernelObject<IMetaboxManager>::getScenarioManager().registerScenarioImporter(
OV_ScenarioImportContext_OnLoadMetaboxImport, ".mxb", OVP_GD_ClassId_Algorithm_XMLScenarioImporter);
this->TKernelObject<IMetaboxManager>::getScenarioManager().registerScenarioImporter(
OV_ScenarioImportContext_OnLoadMetaboxImport, ".xml", OVP_GD_ClassId_Algorithm_XMLScenarioImporter);
}
bool CMetaboxManager::addMetaboxesFromFiles(const CString& fileNameWildCard)
{
this->getLogManager() << LogLevel_Info << "Adding metaboxes from [" << fileNameWildCard << "]\n";
CMetaboxManagerEntryEnumeratorCallBack callBack(this->getKernelContext(), *this); //, m_pluginModules, m_pluginObjectDescs, haveAllPluginsLoadedCorrectly);
FS::IEntryEnumerator* entryEnumerator = createEntryEnumerator(callBack);
std::stringstream ss(fileNameWildCard.toASCIIString());
std::string path;
while (getline(ss, path, ';'))
{
bool result = false; // Used to output imported metabox count
CString ext("");
while ((ext = this->getScenarioManager().getNextScenarioImporter(OV_ScenarioImportContext_OnLoadMetaboxImport, ext)) != CString(""))
{
result |= entryEnumerator->enumerate((path + "*" + ext.toASCIIString()).c_str());
}
if (result) { this->getLogManager() << LogLevel_Info << "Added " << callBack.resetMetaboxCount() << " metaboxes from [" << path << "]\n"; }
}
entryEnumerator->release();
return true;
}
CIdentifier CMetaboxManager::getNextMetaboxObjectDescIdentifier(const CIdentifier& previousID) const
{
if (m_objectDesc.empty()) { return CIdentifier::undefined(); }
if (previousID == CIdentifier::undefined()) { return m_objectDesc.begin()->first; }
const auto result = m_objectDesc.find(previousID);
if (result == m_objectDesc.end() || std::next(result, 1) == m_objectDesc.end()) { return CIdentifier::undefined(); }
return std::next(result, 1)->first;
}
const Plugins::IPluginObjectDesc* CMetaboxManager::getMetaboxObjectDesc(const CIdentifier& metaboxID) const
{
const auto result = m_objectDesc.find(metaboxID);
return result != m_objectDesc.end() ? result->second : nullptr;
}
CString CMetaboxManager::getMetaboxFilePath(const CIdentifier& metaboxID) const
{
const auto resultIt = m_filepath.find(metaboxID);
return resultIt != m_filepath.end() ? resultIt->second : CString();
}
CIdentifier CMetaboxManager::getMetaboxHash(const CIdentifier& metaboxID) const
{
const auto resultIt = m_hash.find(metaboxID);
return resultIt != m_hash.end() ? resultIt->second : CIdentifier::undefined();
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,31 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
namespace OpenViBE {
namespace Kernel {
class CMetaboxManager final : public TKernelObject<IMetaboxManager>
{
public:
explicit CMetaboxManager(const IKernelContext& ctx);
~CMetaboxManager() override { for (auto& desc : m_objectDesc) { delete desc.second; } }
bool addMetaboxesFromFiles(const CString& fileNameWildCard) override;
CIdentifier getNextMetaboxObjectDescIdentifier(const CIdentifier& previousID) const override;
const Plugins::IPluginObjectDesc* getMetaboxObjectDesc(const CIdentifier& metaboxID) const override;
void setMetaboxObjectDesc(const CIdentifier& metaboxID, Plugins::IPluginObjectDesc* metaboxDesc) override { m_objectDesc[metaboxID] = metaboxDesc; }
CString getMetaboxFilePath(const CIdentifier& metaboxID) const override;
void setMetaboxFilePath(const CIdentifier& metaboxID, const CString& filePath) override { m_filepath[metaboxID] = filePath; }
CIdentifier getMetaboxHash(const CIdentifier& metaboxID) const override;
void setMetaboxHash(const CIdentifier& metaboxID, const CIdentifier& hash) override { m_hash[metaboxID] = hash; }
_IsDerivedFromClass_Final_(TKernelObject<IMetaboxManager>, OVK_ClassId_Kernel_Metaboxes_MetaboxManager)
protected:
std::map<CIdentifier, const Plugins::IPluginObjectDesc*> m_objectDesc;
std::map<CIdentifier, CString> m_filepath;
std::map<CIdentifier, CIdentifier> m_hash;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,71 @@
#include "ovkCMetaboxObjectDesc.h"
namespace OpenViBE {
namespace Metabox {
CMetaboxObjectDesc::CMetaboxObjectDesc(const CString& rMetaboxDescriptor, Kernel::IScenario& metaboxScenario)
: m_metaboxDesc(rMetaboxDescriptor)
, m_name(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Name))
, m_authorName(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Author))
, m_authorCompanyName(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Company))
, m_shortDesc(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_ShortDescription))
, m_detailedDesc(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_DetailedDescription))
, m_category(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Category))
, m_version(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_Version))
, m_stockItemName("")
, m_addedSoftwareVersion(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_AddedSoftwareVersion))
, m_updatedSoftwareVersion(metaboxScenario.getAttributeValue(OV_AttributeId_Scenario_UpdatedSoftwareVersion))
, m_metaboxID(metaboxScenario.getAttributeValue(OVP_AttributeId_Metabox_ID))
{
for (size_t scenarioInputIdx = 0; scenarioInputIdx < metaboxScenario.getInputCount(); ++scenarioInputIdx)
{
CString name;
CIdentifier typeID;
CIdentifier id;
metaboxScenario.getInputType(scenarioInputIdx, typeID);
metaboxScenario.getInputName(scenarioInputIdx, name);
metaboxScenario.getInterfacorIdentifier(Kernel::EBoxInterfacorType::Input, scenarioInputIdx, id);
m_inputs.push_back(io_stream_t(name, typeID, id));
}
for (size_t scenarioOutputIdx = 0; scenarioOutputIdx < metaboxScenario.getOutputCount(); ++scenarioOutputIdx)
{
CString name;
CIdentifier typeID;
CIdentifier id;
metaboxScenario.getOutputType(scenarioOutputIdx, typeID);
metaboxScenario.getOutputName(scenarioOutputIdx, name);
metaboxScenario.getInterfacorIdentifier(Kernel::EBoxInterfacorType::Output, scenarioOutputIdx, id);
m_outputs.push_back(io_stream_t(name, typeID, id));
}
for (size_t scenarioSettingIdx = 0; scenarioSettingIdx < metaboxScenario.getSettingCount(); ++scenarioSettingIdx)
{
CString name;
CIdentifier typeID;
CString defaultValue;
CIdentifier id;
metaboxScenario.getSettingName(scenarioSettingIdx, name);
metaboxScenario.getSettingType(scenarioSettingIdx, typeID);
metaboxScenario.getSettingDefaultValue(scenarioSettingIdx, defaultValue);
metaboxScenario.getInterfacorIdentifier(Kernel::EBoxInterfacorType::Setting, scenarioSettingIdx, id);
m_settings.push_back(setting_t(name, typeID, defaultValue, id));
}
}
bool CMetaboxObjectDesc::getBoxPrototype(Kernel::IBoxProto& prototype) const
{
for (const auto& input : m_inputs) { prototype.addInput(input.m_name, input.m_typeID, input.m_id); }
for (const auto& output : m_outputs) { prototype.addOutput(output.m_name, output.m_typeID, output.m_id); }
for (const auto& setting : m_settings) { prototype.addSetting(setting.m_name, setting.m_typeID, setting.m_defaultValue, false, setting.m_id); }
return true;
}
} // namespace Metabox
} // namespace OpenViBE
@@ -0,0 +1,94 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <vector>
namespace OpenViBE {
namespace Metabox {
/**
* \brief The CMetaboxObjectDesc virtual BoxAlgorithmDesc for metaboxes
*
* This class provides a virtual algorithm descriptor for metaboxes. Each metabox-scenario
* will result in one of these descriptors. The prototype is created from scenario inputs,
* outputs and settings.
*
* Variables such as name, author etc are pulled from scenario information.
*/
class CMetaboxObjectDesc final : virtual public IMetaboxObjectDesc
{
public:
CMetaboxObjectDesc() { }
CMetaboxObjectDesc(const CString& rMetaboxDescriptor, Kernel::IScenario& metaboxScenario);
void release() override { }
CString getMetaboxDescriptor() const override { return m_metaboxDesc; }
CString getName() const override { return m_name; }
CString getAuthorName() const override { return m_authorName; }
CString getAuthorCompanyName() const override { return m_authorCompanyName; }
CString getShortDescription() const override { return m_shortDesc; }
CString getDetailedDescription() const override { return m_detailedDesc; }
CString getCategory() const override { return m_category; }
CString getVersion() const override { return m_version; }
CString getStockItemName() const override { return m_stockItemName; }
CString getAddedSoftwareVersion() const override { return m_addedSoftwareVersion; }
CString getUpdatedSoftwareVersion() const override { return m_updatedSoftwareVersion; }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Metabox; }
Plugins::IPluginObject* create() override { return nullptr; }
// Handling of the virtual prototype
// Since we have to construct a prototype on the fly, the special Metabox descriptor
// will also hold the information about the settings, inputs and outputs of the box
typedef struct SStream
{
SStream() : m_name(""), m_typeID(CIdentifier::undefined()), m_id(CIdentifier::undefined()) {}
SStream(const CString& name, const CIdentifier& typeID, const CIdentifier& identifier)
: m_name(name), m_typeID(typeID), m_id(identifier) {}
CString m_name;
CIdentifier m_typeID = CIdentifier::undefined();
CIdentifier m_id = CIdentifier::undefined();
} io_stream_t;
typedef struct SSetting
{
SSetting()
: m_name(""), m_typeID(CIdentifier::undefined()), m_defaultValue(""), m_id(CIdentifier::undefined()) {}
SSetting(const CString& name, const CIdentifier& typeID, const CString& value, const CIdentifier& id)
: m_name(name), m_typeID(typeID), m_defaultValue(value), m_id(id) { }
CString m_name;
CIdentifier m_typeID = CIdentifier::undefined();
CString m_defaultValue;
CIdentifier m_id = CIdentifier::undefined();
} setting_t;
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override;
_IsDerivedFromClass_Final_(IMetaboxObjectDesc, OVP_ClassId_BoxAlgorithm_MetaboxDesc)
private:
CString m_metaboxDesc;
CString m_name;
CString m_authorName;
CString m_authorCompanyName;
CString m_shortDesc;
CString m_detailedDesc;
CString m_category;
CString m_version;
CString m_stockItemName;
CString m_addedSoftwareVersion;
CString m_updatedSoftwareVersion;
CString m_metaboxID;
std::vector<io_stream_t> m_inputs;
std::vector<io_stream_t> m_outputs;
std::vector<setting_t> m_settings;
};
} // namespace Metabox
} // namespace OpenViBE
@@ -0,0 +1,19 @@
#pragma once
#include "ovkTKernelObject.h"
#include "ovkTConfigurable.h"
namespace OpenViBE {
namespace Kernel {
typedef TBaseConfigurable<TKernelObject<IConfigurable>> configurable;
class CConfigurable final : public configurable
{
public:
explicit CConfigurable(const IKernelContext& ctx) : TBaseConfigurable<TKernelObject<IConfigurable>>(ctx) { }
_IsDerivedFromClass_Final_(configurable, OVK_ClassId_Kernel_Configurable)
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,316 @@
#include "ovkCKernelContext.h"
#include "ovkCKernelObjectFactory.h"
#include "ovkCTypeManager.h"
#include <openvibe/CIdentifier.hpp>
#include "algorithm/ovkCAlgorithmManager.h"
#include "configuration/ovkCConfigurationManager.h"
#include "player/ovkCPlayerManager.h"
#include "plugins/ovkCPluginManager.h"
#include "metabox/ovkCMetaboxManager.h"
#include "scenario/ovkCScenarioManager.h"
#include "log/ovkCLogManager.h"
#include "log/ovkCLogListenerConsole.h"
#include "log/ovkCLogListenerFile.h"
#include "error/ovkCErrorManager.h"
#include <cassert>
#include <string>
#include <algorithm>
#include <functional>
#include <cctype>
#include <fs/Files.h>
namespace OpenViBE {
namespace Kernel {
CKernelContext::CKernelContext(const IKernelContext* masterKernelCtx, const CString& applicationName, const CString& configFile)
: m_masterKernelCtx(masterKernelCtx ? *masterKernelCtx : *this), m_algorithmManager(nullptr), m_configManager(nullptr),
m_kernelObjectFactory(nullptr), m_playerManager(nullptr), m_pluginManager(nullptr), m_metaboxManager(nullptr), m_scenarioManager(nullptr),
m_typeManager(nullptr), m_logManager(nullptr), m_errorManager(nullptr), m_applicationName(applicationName), m_configFile(configFile),
m_logListenerConsole(nullptr), m_logListenerFile(nullptr) {}
CKernelContext::~CKernelContext() { this->uninitialize(); }
bool CKernelContext::initialize(const char* const* tokenList, size_t nToken)
{
std::map<std::string, std::string> initializationTokens;
const auto tokens = tokenList;
while (tokens && nToken > 0)
{
const auto key = tokenList++;
const auto value = tokenList++;
nToken--;
initializationTokens[*key] = *value;
}
m_errorManager.reset(new CErrorManager(m_masterKernelCtx));
m_kernelObjectFactory.reset(new CKernelObjectFactory(m_masterKernelCtx));
m_logManager.reset(new CLogManager(m_masterKernelCtx));
m_logManager->activate(true);
m_configManager.reset(new CConfigurationManager(m_masterKernelCtx));
// We create the configuration manager very soon to be able to deactivate the console log listener
if (initializationTokens.count("Kernel_SilentConsole"))
{
m_configManager->createConfigurationToken("Kernel_SilentConsole", initializationTokens.at("Kernel_SilentConsole").c_str());
}
if (!m_configManager->expandAsBoolean("${Kernel_SilentConsole}", false))
{
m_logListenerConsole.reset(new CLogListenerConsole(m_masterKernelCtx, m_applicationName));
m_logListenerConsole->activate(false);
m_logListenerConsole->activate(LogLevel_Info, LogLevel_Last, true);
this->getLogManager().addListener(m_logListenerConsole.get());
}
m_configManager->createConfigurationToken("ApplicationName", m_applicationName);
m_configManager->createConfigurationToken("Path_UserData", Directories::getUserDataDir());
m_configManager->createConfigurationToken("Path_Log", Directories::getLogDir());
m_configManager->createConfigurationToken("Path_Tmp", "${Path_UserData}/tmp");
m_configManager->createConfigurationToken("Path_Lib", Directories::getLibDir());
m_configManager->createConfigurationToken("Path_Bin", Directories::getBinDir());
m_configManager->createConfigurationToken("Path_Data", Directories::getDataDir());
#if defined TARGET_OS_Windows
m_configManager->createConfigurationToken("OperatingSystem", "Windows");
#elif defined TARGET_OS_Linux
m_configManager->createConfigurationToken("OperatingSystem", "Linux");
#elif defined TARGET_OS_MacOS
m_configManager->createConfigurationToken("OperatingSystem", "MacOS");
#else
m_configManager->createConfigurationToken("OperatingSystem", "Unknown");
#endif
m_configManager->createConfigurationToken("Kernel_PluginsPatternMacOS", "libopenvibe-plugins-*.dylib");
m_configManager->createConfigurationToken("Kernel_PluginsPatternLinux", "libopenvibe-plugins-*.so");
m_configManager->createConfigurationToken("Kernel_PluginsPatternWindows", "openvibe-plugins-*.dll");
m_configManager->createConfigurationToken("Kernel_Plugins", "${Path_Lib}/${Kernel_PluginsPattern${OperatingSystem}}");
m_configManager->createConfigurationToken("Kernel_Metabox", "${Path_Data}/metaboxes/;${Path_UserData}/metaboxes/");
m_configManager->createConfigurationToken("Kernel_MainLogLevel", "Debug");
m_configManager->createConfigurationToken("Kernel_ConsoleLogLevel", "Information");
m_configManager->createConfigurationToken("Kernel_FileLogLevel", "Debug");
m_configManager->createConfigurationToken("Kernel_PlayerFrequency", "128");
// Add this two tokens to be used to know what documentation should be loaded
m_configManager->createConfigurationToken("Brand_Name", BRAND_NAME);
m_configManager->createConfigurationToken("Application_Name", OV_PROJECT_NAME);
m_configManager->createConfigurationToken("Application_Version", OV_VERSION_MAJOR "." OV_VERSION_MINOR "." OV_VERSION_PATCH);
for (auto& token : initializationTokens) { m_configManager->addOrReplaceConfigurationToken(token.first.c_str(), token.second.c_str()); }
this->getLogManager() << LogLevel_Info << "Adding kernel configuration file [" << m_configFile << "]\n";
OV_ERROR_UNLESS_KRF(m_configManager->addConfigurationFromFile(m_configFile),
"Problem parsing config file [" << m_configFile << "]", Kernel::ErrorType::Internal);
CString pathTmp = m_configManager->expand("${Path_UserData}");
FS::Files::createPath(pathTmp.toASCIIString());
pathTmp = m_configManager->expand("${Path_Tmp}");
FS::Files::createPath(pathTmp.toASCIIString());
pathTmp = m_configManager->expand("${Path_Log}");
FS::Files::createPath(pathTmp);
const CString logFile = pathTmp + "/openvibe-" + m_applicationName + ".log";
// We do this here to allow user to set the Path_Log in the .conf. The downside is that earlier log messages will not appear in the file log.
m_logListenerFile.reset(new CLogListenerFile(m_masterKernelCtx, m_applicationName, logFile));
m_logListenerFile->activate(true);
this->getLogManager().addListener(m_logListenerFile.get());
const ELogLevel mainLogLevel = this->earlyGetLogLevel(m_configManager->expand("${Kernel_MainLogLevel}"));
const ELogLevel consoleLogLevel = this->earlyGetLogLevel(m_configManager->expand("${Kernel_ConsoleLogLevel}"));
const ELogLevel fileLogLevel = this->earlyGetLogLevel(m_configManager->expand("${Kernel_FileLogLevel}"));
m_logManager->activate(false);
m_logManager->activate(mainLogLevel, LogLevel_Last, true);
m_logListenerFile->activate(false);
m_logListenerFile->activate(fileLogLevel, LogLevel_Last, true);
m_logListenerFile->configure(*m_configManager);
m_logListenerFile->configure(*m_configManager);
if (m_logListenerConsole)
{
m_logListenerConsole->activate(false);
m_logListenerConsole->activate(consoleLogLevel, LogLevel_Last, true);
m_logListenerConsole->configure(*m_configManager);
}
m_algorithmManager.reset(new CAlgorithmManager(m_masterKernelCtx));
m_playerManager.reset(new CPlayerManager(m_masterKernelCtx));
m_typeManager.reset(new CTypeManager(m_masterKernelCtx));
m_typeManager->registerType(OV_TypeId_Boolean, "Boolean");
m_typeManager->registerType(OV_TypeId_Integer, "Integer");
m_typeManager->registerType(OV_TypeId_Float, "Float");
m_typeManager->registerType(OV_TypeId_String, "String");
m_typeManager->registerType(OV_TypeId_Filename, "Filename");
m_typeManager->registerType(OV_TypeId_Script, "Script");
m_typeManager->registerEnumerationType(OV_TypeId_Stimulation, "Stimulation");
m_typeManager->registerEnumerationType(OV_TypeId_MeasurementUnit, "Measurement unit");
m_typeManager->registerEnumerationType(OV_TypeId_Factor, "Factor");
m_typeManager->registerEnumerationType(OV_TypeId_LogLevel, "Log level");
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "None", LogLevel_None);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Debug", LogLevel_Debug);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Benchmarking / Profiling", LogLevel_Benchmark);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Trace", LogLevel_Trace);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Information", LogLevel_Info);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Warning", LogLevel_Warning);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Important warning", LogLevel_ImportantWarning);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Error", LogLevel_Error);
m_typeManager->registerEnumerationEntry(OV_TypeId_LogLevel, "Fatal error", LogLevel_Fatal);
m_typeManager->registerStreamType(OV_TypeId_EBMLStream, "EBML stream", CIdentifier::undefined());
m_typeManager->registerStreamType(OV_TypeId_ExperimentInfo, "Experiment information", OV_TypeId_EBMLStream);
m_typeManager->registerStreamType(OV_TypeId_Stimulations, "Stimulations", OV_TypeId_EBMLStream);
m_typeManager->registerStreamType(OV_TypeId_StreamedMatrix, "Streamed matrix", OV_TypeId_EBMLStream);
m_typeManager->registerStreamType(OV_TypeId_ChannelLocalisation, "Channel localisation", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_ChannelUnits, "Channel units", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_FeatureVector, "Feature vector", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_Signal, "Signal", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_Spectrum, "Spectrum", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_TimeFrequency, "Time-frequency", OV_TypeId_StreamedMatrix);
m_typeManager->registerStreamType(OV_TypeId_CovarianceMatrix, "Covariance Matrix", OV_TypeId_StreamedMatrix);
m_typeManager->registerType(OV_TypeId_Message, "Message");
m_scenarioManager.reset(new CScenarioManager(m_masterKernelCtx));
m_pluginManager.reset(new CPluginManager(m_masterKernelCtx));
m_metaboxManager.reset(new CMetaboxManager(m_masterKernelCtx));
return true;
}
bool CKernelContext::uninitialize()
{
// As releaseScenario() can call into Plugin Manager we have to clear the scenario manager
// before destroying the Plugin Manager. We can not destroy the Scenario Manager first
// before Plugin Manager destructor needs it.
CIdentifier scenarioID;
while ((scenarioID = m_scenarioManager->getNextScenarioIdentifier(CIdentifier::undefined())) != CIdentifier::undefined())
{
m_scenarioManager->releaseScenario(scenarioID);
}
CIdentifier algorithmIdentifier;
while ((algorithmIdentifier = m_algorithmManager->getNextAlgorithmIdentifier(CIdentifier::undefined())) != CIdentifier::undefined())
{
m_algorithmManager->releaseAlgorithm(algorithmIdentifier);
}
m_pluginManager.reset();
m_metaboxManager.reset();
m_scenarioManager.reset();
m_typeManager.reset();
m_playerManager.reset();
m_algorithmManager.reset();
m_configManager.reset();
this->getLogManager().removeListener(m_logListenerConsole.get());
this->getLogManager().removeListener(m_logListenerFile.get());
m_logManager.reset();
m_logListenerConsole.reset();
m_logListenerFile.reset();
m_kernelObjectFactory.reset();
m_errorManager.reset();
return true;
}
IAlgorithmManager& CKernelContext::getAlgorithmManager() const
{
assert(m_algorithmManager);
return *m_algorithmManager;
}
IConfigurationManager& CKernelContext::getConfigurationManager() const
{
assert(m_configManager);
return *m_configManager;
}
IKernelObjectFactory& CKernelContext::getKernelObjectFactory() const
{
assert(m_kernelObjectFactory);
return *m_kernelObjectFactory;
}
IPlayerManager& CKernelContext::getPlayerManager() const
{
assert(m_playerManager);
return *m_playerManager;
}
IPluginManager& CKernelContext::getPluginManager() const
{
assert(m_pluginManager);
return *m_pluginManager;
}
IMetaboxManager& CKernelContext::getMetaboxManager() const
{
assert(m_metaboxManager);
return *m_metaboxManager;
}
IScenarioManager& CKernelContext::getScenarioManager() const
{
assert(m_scenarioManager);
return *m_scenarioManager;
}
ITypeManager& CKernelContext::getTypeManager() const
{
assert(m_typeManager);
return *m_typeManager;
}
ILogManager& CKernelContext::getLogManager() const
{
assert(m_logManager);
return *m_logManager;
}
IErrorManager& CKernelContext::getErrorManager() const
{
assert(m_errorManager);
return *m_errorManager;
}
ELogLevel CKernelContext::earlyGetLogLevel(const CString& rLogLevelName)
{
assert(m_logManager);
std::string value(rLogLevelName.toASCIIString());
std::transform(value.begin(), value.end(), value.begin(), [](const char c) { return char(std::tolower(c)); });
if (value == "none") { return LogLevel_None; }
if (value == "debug") { return LogLevel_Debug; }
if (value == "benchmarking / profiling") { return LogLevel_Benchmark; }
if (value == "trace") { return LogLevel_Trace; }
if (value == "information") { return LogLevel_Info; }
if (value == "warning") { return LogLevel_Warning; }
if (value == "important warning") { return LogLevel_ImportantWarning; }
if (value == "error") { return LogLevel_Error; }
if (value == "fatal error") { return LogLevel_Fatal; }
OV_WARNING("Invalid log level " << rLogLevelName << " specified in configuration file, falling back to " << CString("Debug"), (*m_logManager));
return LogLevel_Debug;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,116 @@
#pragma once
#include <memory>
#include "ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CLogListenerConsole;
class CLogListenerFile;
class CKernelContext final : public IKernelContext
{
public:
CKernelContext(const IKernelContext* masterKernelCtx, const CString& applicationName, const CString& configFile);
~CKernelContext() override;
bool initialize(const char* const * tokenList, size_t nToken) override;
bool uninitialize() override;
IAlgorithmManager& getAlgorithmManager() const override;
IConfigurationManager& getConfigurationManager() const override;
IKernelObjectFactory& getKernelObjectFactory() const override;
IPlayerManager& getPlayerManager() const override;
IPluginManager& getPluginManager() const override;
IMetaboxManager& getMetaboxManager() const override;
IScenarioManager& getScenarioManager() const override;
ITypeManager& getTypeManager() const override;
ILogManager& getLogManager() const override;
IErrorManager& getErrorManager() const override;
_IsDerivedFromClass_Final_(IKernelContext, OVK_ClassId_Kernel_KernelContext)
protected:
ELogLevel earlyGetLogLevel(const CString& rLogLevelName);
private:
const IKernelContext& m_masterKernelCtx;
std::unique_ptr<IAlgorithmManager> m_algorithmManager;
std::unique_ptr<IConfigurationManager> m_configManager;
std::unique_ptr<IKernelObjectFactory> m_kernelObjectFactory;
std::unique_ptr<IPlayerManager> m_playerManager;
std::unique_ptr<IPluginManager> m_pluginManager;
std::unique_ptr<IMetaboxManager> m_metaboxManager;
std::unique_ptr<IScenarioManager> m_scenarioManager;
std::unique_ptr<ITypeManager> m_typeManager;
std::unique_ptr<ILogManager> m_logManager;
std::unique_ptr<IErrorManager> m_errorManager;
CString m_applicationName;
CString m_configFile;
std::unique_ptr<CLogListenerConsole> m_logListenerConsole;
std::unique_ptr<CLogListenerFile> m_logListenerFile;
CKernelContext() = delete;
};
class CKernelContextBridge final : public IKernelContext
{
public:
explicit CKernelContextBridge(const IKernelContext& ctx) : m_kernelCtx(ctx) { }
bool initialize() { return true; }
bool uninitialize() override { return true; }
void setAlgorithmManager(IAlgorithmManager* manager) { m_algorithmManager = manager; }
void setConfigurationManager(IConfigurationManager* manager) { m_configManager = manager; }
void setKernelObjectFactory(IKernelObjectFactory* kernelObjectFactory) { m_kernelObjectFactory = kernelObjectFactory; }
void setPlayerManager(IPlayerManager* manager) { m_playerManager = manager; }
void setPluginManager(IPluginManager* manager) { m_pluginManager = manager; }
void setMetaboxManager(IMetaboxManager* manager) { m_metaboxManager = manager; }
void setScenarioManager(IScenarioManager* manager) { m_scenarioManager = manager; }
void setTypeManager(ITypeManager* manager) { m_typeManager = manager; }
void setLogManager(ILogManager* manager) { m_logManager = manager; }
void setErrorManager(IErrorManager* manager) { m_errorManager = manager; }
IAlgorithmManager& getAlgorithmManager() const override { return m_algorithmManager ? *m_algorithmManager : m_kernelCtx.getAlgorithmManager(); }
IConfigurationManager& getConfigurationManager() const override { return m_configManager ? *m_configManager : m_kernelCtx.getConfigurationManager(); }
IKernelObjectFactory& getKernelObjectFactory() const override
{
return m_kernelObjectFactory ? *m_kernelObjectFactory : m_kernelCtx.getKernelObjectFactory();
}
IPlayerManager& getPlayerManager() const override { return m_playerManager ? *m_playerManager : m_kernelCtx.getPlayerManager(); }
IPluginManager& getPluginManager() const override { return m_pluginManager ? *m_pluginManager : m_kernelCtx.getPluginManager(); }
IMetaboxManager& getMetaboxManager() const override { return m_metaboxManager ? *m_metaboxManager : m_kernelCtx.getMetaboxManager(); }
IScenarioManager& getScenarioManager() const override { return m_scenarioManager ? *m_scenarioManager : m_kernelCtx.getScenarioManager(); }
ITypeManager& getTypeManager() const override { return m_typeManager ? *m_typeManager : m_kernelCtx.getTypeManager(); }
ILogManager& getLogManager() const override { return m_logManager ? *m_logManager : m_kernelCtx.getLogManager(); }
IErrorManager& getErrorManager() const override { return m_errorManager ? *m_errorManager : m_kernelCtx.getErrorManager(); }
_IsDerivedFromClass_Final_(IKernelContext, OVK_ClassId_Kernel_KernelContext)
protected:
const IKernelContext& m_kernelCtx;
mutable IAlgorithmManager* m_algorithmManager = nullptr;
mutable IConfigurationManager* m_configManager = nullptr;
mutable IKernelObjectFactory* m_kernelObjectFactory = nullptr;
mutable IPlayerManager* m_playerManager = nullptr;
mutable IPluginManager* m_pluginManager = nullptr;
mutable IMetaboxManager* m_metaboxManager = nullptr;
mutable IScenarioManager* m_scenarioManager = nullptr;
mutable ITypeManager* m_typeManager = nullptr;
mutable ILogManager* m_logManager = nullptr;
mutable IErrorManager* m_errorManager = nullptr;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,55 @@
#include "ovkCKernelObjectFactory.h"
#include "ovkCConfigurable.h"
#include "plugins/ovkCPluginModule.h"
#include <algorithm>
namespace OpenViBE {
namespace Kernel {
#define create(rcid,cid,sptr,cl) \
if(rcid==cid) \
{ \
sptr=new cl(getKernelContext()); \
if(sptr) { m_oCreatedObjects.push_back(sptr); } \
}
IObject* CKernelObjectFactory::createObject(const CIdentifier& classID)
{
std::unique_lock<std::mutex> lock(m_oMutex);
IObject* res = nullptr;
create(classID, OV_ClassId_Kernel_Plugins_PluginModule, res, Kernel::CPluginModule);
create(classID, OV_ClassId_Kernel_Configurable, res, Kernel::CConfigurable);
OV_ERROR_UNLESS_KRN(res, "Unable to allocate object with class id " << classID.str(), Kernel::ErrorType::BadAlloc);
return res;
}
bool CKernelObjectFactory::releaseObject(IObject* pObject)
{
std::unique_lock<std::mutex> lock(m_oMutex);
if (!pObject) { return true; }
const CIdentifier classID = pObject->getClassIdentifier();
const auto i = find(m_oCreatedObjects.begin(), m_oCreatedObjects.end(), pObject);
OV_ERROR_UNLESS_KRF(i != m_oCreatedObjects.end(),
"Can not release object with final class id " << classID.str() << " - it is not owned by this fatory",
ErrorType::ResourceNotFound);
m_oCreatedObjects.erase(i);
delete pObject;
this->getLogManager() << LogLevel_Debug << "Released object with final class id " << classID << "\n";
return true;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,27 @@
#pragma once
#include "ovkTKernelObject.h"
#include <vector>
#include <mutex>
namespace OpenViBE {
namespace Kernel {
class CKernelObjectFactory final : public TKernelObject<IKernelObjectFactory>
{
public:
explicit CKernelObjectFactory(const IKernelContext& ctx) : TKernelObject<IKernelObjectFactory>(ctx) {}
IObject* createObject(const CIdentifier& classID) override;
bool releaseObject(IObject* pObject) override;
_IsDerivedFromClass_Final_(TKernelObject<IKernelObjectFactory>, OVK_ClassId_Kernel_KernelObjectFactory)
protected:
std::vector<IObject*> m_oCreatedObjects;
std::mutex m_oMutex;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,16 @@
#include "ovkCObjectVisitorContext.h"
namespace OpenViBE {
namespace Kernel {
CObjectVisitorContext::CObjectVisitorContext(const IKernelContext& ctx) : TKernelObject<IObjectVisitorContext>(ctx) {}
CObjectVisitorContext::~CObjectVisitorContext() {}
IAlgorithmManager& CObjectVisitorContext::getAlgorithmManager() const { return TKernelObject<IObjectVisitorContext>::getAlgorithmManager(); }
IConfigurationManager& CObjectVisitorContext::getConfigurationManager() const { return TKernelObject<IObjectVisitorContext>::getConfigurationManager(); }
ITypeManager& CObjectVisitorContext::getTypeManager() const { return TKernelObject<IObjectVisitorContext>::getTypeManager(); }
ILogManager& CObjectVisitorContext::getLogManager() const { return TKernelObject<IObjectVisitorContext>::getLogManager(); }
IErrorManager& CObjectVisitorContext::getErrorManager() const { return TKernelObject<IObjectVisitorContext>::getErrorManager(); }
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,22 @@
#pragma once
#include "ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CObjectVisitorContext final : public TKernelObject<IObjectVisitorContext>
{
public:
explicit CObjectVisitorContext(const IKernelContext& ctx);
~CObjectVisitorContext() override;
IAlgorithmManager& getAlgorithmManager() const override;
IConfigurationManager& getConfigurationManager() const override;
ITypeManager& getTypeManager() const override;
ILogManager& getLogManager() const override;
IErrorManager& getErrorManager() const override;
_IsDerivedFromClass_Final_(TKernelObject<IObjectVisitorContext>, OVK_ClassId_Kernel_ObjectVisitorContext)
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,85 @@
#pragma once
#include "ovkTKernelObject.h"
#include "ovkTParameter.h"
#include <cstring>
namespace OpenViBE {
namespace Kernel {
#define _parameter_template_instance_simple_type_(_CName_, CType, IType, oClassId) \
typedef TBaseParameter<TKernelObject<IParameter>, IType> _Base_##_CName_; \
class _CName_ : public _Base_##_CName_ \
{ \
public: \
_CName_(const IKernelContext& ctx, EParameterType eParameterType, const CIdentifier& subTypeID = CIdentifier::undefined()) \
:TBaseParameter < TKernelObject < IParameter >, IType >(ctx, eParameterType, subTypeID) \
{ \
m_defaultValue = 0; \
memcpy(&m_value, &m_defaultValue, sizeof(IType)); \
} \
_IsDerivedFromClass_Final_(_Base_##_CName_, oClassId) \
protected: \
CType m_defaultValue; \
};
#define _parameter_template_instance_object_(_CName_, CType, IType, oClassId) \
typedef TBaseParameter<TKernelObject<IParameter>, IType > _Base_##_CName_; \
class _CName_ : public _Base_##_CName_ \
{ \
public: \
_CName_(const IKernelContext& ctx, EParameterType eParameterType) \
:TBaseParameter < TKernelObject < IParameter >, IType >(ctx, eParameterType) \
{ \
IType defaultValue=&m_defaultValue; \
memcpy(&m_value, &defaultValue, sizeof(IType)); \
} \
_IsDerivedFromClass_Final_(_Base_##_CName_, oClassId) \
protected: \
CType m_defaultValue; \
};
#define _parameter_template_instance_pointer_(_CName_, CType, IType, oClassId) \
typedef TBaseParameter<TKernelObject<IParameter>, IType > _Base_##_CName_; \
class _CName_ : public _Base_##_CName_ \
{ \
public: \
_CName_(const IKernelContext& ctx, EParameterType eParameterType) \
:TBaseParameter < TKernelObject < IParameter >, IType >(ctx, eParameterType) \
{ \
m_defaultValue = nullptr; \
IType defaultValue=&m_defaultValue; \
memcpy(&m_value, &defaultValue, sizeof(IType)); \
} \
_IsDerivedFromClass_Final_(_Base_##_CName_, oClassId) \
protected: \
CType m_defaultValue; \
};
_parameter_template_instance_simple_type_(CIntegerParameter, int64_t, int64_t, OVK_ClassId_Kernel_IntegerParameter)
_parameter_template_instance_simple_type_(CUIntegerParameter, uint64_t, uint64_t, OVK_ClassId_Kernel_UIntegerParameter)
_parameter_template_instance_simple_type_(CEnumerationParameter, uint64_t, uint64_t, OVK_ClassId_Kernel_EnumerationParameter)
_parameter_template_instance_simple_type_(CBooleanParameter, bool, bool, OVK_ClassId_Kernel_BooleanParameter)
_parameter_template_instance_simple_type_(CFloatParameter, double, double, OVK_ClassId_Kernel_FloatParameter)
_parameter_template_instance_object_(CStringParameter, CString, CString*, OVK_ClassId_Kernel_StringParameter)
_parameter_template_instance_object_(CIdentifierParameter, CIdentifier, CIdentifier*, OVK_ClassId_Kernel_IdentifierParameter)
_parameter_template_instance_object_(CMatrixParameter, CMatrix, CMatrix*, OVK_ClassId_Kernel_MatrixParameter)
_parameter_template_instance_object_(CStimulationSetParameter, CStimulationSet, IStimulationSet*, OVK_ClassId_Kernel_StimulationSetParameter)
_parameter_template_instance_object_(CMemoryBufferParameter, CMemoryBuffer, IMemoryBuffer*, OVK_ClassId_Kernel_MemoryBufferParameter)
_parameter_template_instance_object_(CObjectParameter, CNullObject, IObject*, OVK_ClassId_Kernel_ObjectParameter)
_parameter_template_instance_pointer_(CPointerParameter, void*, void*, OVK_ClassId_Kernel_PointerParameter)
#undef _instance_
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,463 @@
#include "ovkCTypeManager.h"
#include "lepton/Lepton.h"
#include <string>
#include <algorithm>
#include <functional>
#include <cctype>
#include <algorithm>
#define OV_TRACE_K(message) this->getLogManager() << OpenViBE::Kernel::LogLevel_Trace << message << "\n";
#define OV_DEBUG_K(message) this->getLogManager() << OpenViBE::Kernel::LogLevel_Debug << message << "\n";
#define OV_DEBUG_UNLESS_K(expression, message) if (!(expression)) { OV_DEBUG_K(message); }
namespace OpenViBE {
namespace Kernel {
namespace {
struct SAInfB
{
bool operator()(const std::pair<CIdentifier, CString> a, const std::pair<CIdentifier, CString>& b) const { return a.second < b.second; }
};
} // namespace
// because std::tolower has multiple signatures,
// it can not be easily used in std::transform
// this workaround is taken from http://www.gcek.net/ref/books/sw/cpp/ticppv2/
template <class TCharT>
static TCharT ToLower(TCharT c) { return std::tolower(c); }
CTypeManager::CTypeManager(const IKernelContext& ctx)
: TKernelObject<ITypeManager>(ctx)
{
m_names[CIdentifier::undefined()] = "undefined";
this->registerEnumerationType(OV_TypeId_BoxAlgorithmFlag, "BoxFlags");
}
CIdentifier CTypeManager::getNextTypeIdentifier(const CIdentifier& previousID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
return getNextIdentifier<CString>(m_names, previousID);
}
std::vector<std::pair<CIdentifier, CString>> CTypeManager::getSortedTypes() const
{
std::vector<std::pair<CIdentifier, CString>> sorted;
for (const auto& element : m_names) { sorted.push_back(std::pair<CIdentifier, CString>(element.first, element.second)); }
std::sort(sorted.begin(), sorted.end(), SAInfB());
return sorted;
}
bool CTypeManager::registerType(const CIdentifier& typeID, const CString& name)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(!isRegistered(typeID), "Trying to register type " << typeID.str() << " that already exists.",
ErrorType::BadArgument);
OV_DEBUG_UNLESS_K(m_takenNames.find(name) == m_takenNames.end(),
"Trying to register type " << typeID << " with a name that already exists ( " << name << ")");
m_names[typeID] = name;
OV_TRACE_K("Registered type id " << typeID << " - " << name);
return true;
}
bool CTypeManager::registerStreamType(const CIdentifier& typeID, const CString& name, const CIdentifier& parentTypeID)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(!isRegistered(typeID), "Trying to register stream type " << typeID.str() << " that already exists.",
ErrorType::BadArgument);
OV_DEBUG_UNLESS_K(m_takenNames.find(name) == m_takenNames.end(),
"Trying to register stream type " << typeID << " with a name that already exists ( " << name << ")");
OV_ERROR_UNLESS_KRF(parentTypeID == CIdentifier::undefined() || isStream(parentTypeID),
"Trying to register an invalid stream type [" << name << "] " << typeID.str() << ", parent : " << parentTypeID.str() << ".",
ErrorType::BadArgument);
m_names[typeID] = name;
m_takenNames.insert(name);
m_streams[typeID] = parentTypeID;
OV_TRACE_K("Registered stream type id " << typeID << "::" << parentTypeID << " - " << name);
return true;
}
bool CTypeManager::registerEnumerationType(const CIdentifier& typeID, const CString& name)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
if (isRegistered(typeID))
{
if (m_names[typeID] != name)
{
OV_ERROR_KRF(
"Trying to register enum type " << typeID.str() << " that already exists with different value (" << m_names[typeID] << " != " << name <<
")",
ErrorType::BadArgument);
}
OV_DEBUG_K("Trying to register enum type " << typeID.str() << " that already exists.");
}
OV_DEBUG_UNLESS_K(m_takenNames.find(name) == m_takenNames.end(),
"Trying to register enum type " << typeID << " with a name that already exists ( " << name << ")");
m_names[typeID] = name;
m_takenNames.insert(name);
m_enumerations[typeID];
OV_TRACE_K("Registered enumeration type id " << typeID << " - " << name);
return true;
}
bool CTypeManager::registerEnumerationEntry(const CIdentifier& typeID, const CString& name, const uint64_t value)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto itEnumeration = m_enumerations.find(typeID);
OV_ERROR_UNLESS_KRF(itEnumeration != m_enumerations.end(), "Enumeration type [" << typeID.str() << "] does not exist." << name,
ErrorType::BadArgument);
const auto itElem = itEnumeration->second.find(value);
if (itElem != itEnumeration->second.end())
{
if (std::string(itElem->second) != std::string(name))
{
OV_WARNING_K(
"Enumeration type [" + typeID.str() + "] already has element [" + std::to_string(value) + "]. Value will be overriden : "
+ itElem->second.toASCIIString() + " => " + name.toASCIIString());
}
else { OV_DEBUG_K("Enumeration type [" << typeID.str() << "] already has element [" << value << "]."); }
}
itEnumeration->second[value] = name;
return true;
}
bool CTypeManager::registerBitMaskType(const CIdentifier& typeID, const CString& name)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
OV_ERROR_UNLESS_KRF(!isRegistered(typeID), "Trying to register bitmask type " << typeID.str() << " that already exists.",
ErrorType::BadArgument);
OV_DEBUG_UNLESS_K(m_takenNames.find(name) == m_takenNames.end(),
"Trying to register bitmask type " << typeID << " with a name that already exists ( " << name << ")");
m_names[typeID] = name;
m_bitMasks[typeID];
OV_TRACE_K("Registered bitmask type id " << typeID << " - " << name);
return true;
}
bool CTypeManager::registerBitMaskEntry(const CIdentifier& typeID, const CString& name, const uint64_t value)
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto itBitMask = m_bitMasks.find(typeID);
OV_ERROR_UNLESS_KRF(itBitMask != m_bitMasks.end(), "Bitmask type [" << typeID.str() << "] does not exist.", Kernel::ErrorType::BadArgument);
const auto itElem = itBitMask->second.find(value);
if (itElem != itBitMask->second.end())
{
if (std::string(itElem->second) != std::string(name))
{
OV_WARNING_K(
"Bitmask type [" + typeID.str() + "] already has element [" + std::to_string(value) + "]. Value will be overriden : "
+ itElem->second.toASCIIString() + " => " + name.toASCIIString());
}
else { OV_DEBUG_K("Bitmask type [" << typeID.str() << "] already has element [" << value << "]."); }
}
for (size_t nBit = 0, i = 0; i < 64; ++i)
{
if (value & (1LL << i))
{
nBit++;
OV_ERROR_UNLESS_KRF(nBit <= 1,
"Discarded bitmask entry (" << m_names[typeID] << ":" << name << ") because value " << value << " contains more than one bit",
ErrorType::Overflow);
}
}
itBitMask->second[value] = name;
return true;
}
bool CTypeManager::isRegistered(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
return m_names.find(typeID) != m_names.end();
}
bool CTypeManager::isStream(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
return m_streams.find(typeID) != m_streams.end();
}
bool CTypeManager::isDerivedFromStream(const CIdentifier& typeID, const CIdentifier& parentTypeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
auto it = m_streams.find(typeID);
const auto itParent = m_streams.find(parentTypeID);
if (it == m_streams.end() || itParent == m_streams.end()) { return false; }
while (it != m_streams.end())
{
if (it->first == parentTypeID) { return true; }
it = m_streams.find(it->second);
}
return false;
}
bool CTypeManager::isEnumeration(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
return m_enumerations.find(typeID) != m_enumerations.end();
}
bool CTypeManager::isBitMask(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
return m_bitMasks.find(typeID) != m_bitMasks.end();
}
CString CTypeManager::getTypeName(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
if (!isRegistered(typeID)) { return CString(""); }
return m_names.find(typeID)->second;
}
CIdentifier CTypeManager::getStreamParentType(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
if (!isStream(typeID)) { return CIdentifier::undefined(); }
return m_streams.find(typeID)->second;
}
size_t CTypeManager::getEnumerationEntryCount(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itEnumeration = m_enumerations.find(typeID);
if (itEnumeration == m_enumerations.end()) { return 0; }
return itEnumeration->second.size();
}
bool CTypeManager::getEnumerationEntry(const CIdentifier& typeID, const uint64_t index, CString& name, uint64_t& value) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_enumerations.find(typeID);
if (it == m_enumerations.end()) { return false; }
if (index >= it->second.size()) { return false; }
auto itEntry = it->second.begin();
for (size_t i = 0; i < index && itEntry != it->second.end(); i++, ++itEntry) { }
value = itEntry->first;
name = itEntry->second;
return true;
}
CString CTypeManager::getEnumerationEntryNameFromValue(const CIdentifier& typeID, const uint64_t value) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_enumerations.find(typeID);
if (it == m_enumerations.end()) { return ""; }
const auto itEntry = it->second.find(value);
if (itEntry == it->second.end()) { return ""; }
return it->second.find(value)->second;
}
uint64_t CTypeManager::getEnumerationEntryValueFromName(const CIdentifier& typeID, const CString& name) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_enumerations.find(typeID);
if (it == m_enumerations.end()) { return OV_IncorrectStimulation; }
// first looks at the exact std::string match
for (const auto& entry : it->second) { if (entry.second == name) { return entry.first; } }
// then looks at the caseless std::string match
std::string nameLower = name.toASCIIString();
std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ToLower<std::string::value_type>);
for (const auto& entry : it->second)
{
std::string tmp = entry.second.toASCIIString();
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ToLower<std::string::value_type>);
if (tmp == nameLower) { return entry.first; }
}
// then looks at the std::string being the value itself
try
{
const uint64_t value = std::stoull(name.toASCIIString());
if ((it->second.find(value) != it->second.end()) ||
(typeID == OV_TypeId_Stimulation && this->getConfigurationManager().expandAsBoolean("Kernel_AllowUnregisteredNumericalStimulationIdentifiers")))
{
return value;
}
}
catch (const std::exception&) { return OV_IncorrectStimulation; }
return OV_IncorrectStimulation;
}
size_t CTypeManager::getBitMaskEntryCount(const CIdentifier& typeID) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itBitMask = m_bitMasks.find(typeID);
if (itBitMask == m_bitMasks.end()) { return 0; }
return itBitMask->second.size();
}
bool CTypeManager::getBitMaskEntry(const CIdentifier& typeID, const uint64_t index, CString& name, uint64_t& value) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itBitMask = m_bitMasks.find(typeID);
if (itBitMask == m_bitMasks.end()) { return false; }
if (index >= itBitMask->second.size()) { return false; }
auto itBitMaskEntry = itBitMask->second.begin();
for (size_t i = 0; i < index && itBitMaskEntry != itBitMask->second.end(); i++, ++itBitMaskEntry) { }
value = itBitMaskEntry->first;
name = itBitMaskEntry->second;
return true;
}
CString CTypeManager::getBitMaskEntryNameFromValue(const CIdentifier& typeID, const uint64_t value) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itBitMask = m_bitMasks.find(typeID);
if (itBitMask == m_bitMasks.end()) { return ""; }
const auto itBitMaskEntry = itBitMask->second.find(value);
if (itBitMaskEntry == itBitMask->second.end()) { return ""; }
return itBitMask->second.find(value)->second;
}
uint64_t CTypeManager::getBitMaskEntryValueFromName(const CIdentifier& typeID, const CString& name) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itBitMask = m_bitMasks.find(typeID);
if (itBitMask == m_bitMasks.end()) { return 0xffffffffffffffffLL; }
// first looks at the exact std::string match
for (const auto& mask : itBitMask->second) { if (mask.second == name) { return mask.first; } }
// then looks at the caseless std::string match
std::string entryNameLower = name.toASCIIString();
std::transform(entryNameLower.begin(), entryNameLower.end(), entryNameLower.begin(), ToLower<std::string::value_type>);
for (const auto& mask : itBitMask->second)
{
std::string itEntryNameLower = mask.second.toASCIIString();
std::transform(itEntryNameLower.begin(), itEntryNameLower.end(), itEntryNameLower.begin(), ToLower<std::string::value_type>);
if (itEntryNameLower == entryNameLower) { return mask.first; }
}
// then looks at the std::string being the value itself
try
{
const uint64_t value = std::stoll(name.toASCIIString());
if (itBitMask->second.find(value) != itBitMask->second.end()) { return value; }
}
catch (const std::exception&) { return 0xffffffffffffffffLL; }
return 0xffffffffffffffffLL;
}
CString CTypeManager::getBitMaskEntryCompositionNameFromValue(const CIdentifier& typeID, const uint64_t value) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto itBitMask = m_bitMasks.find(typeID);
if (itBitMask == m_bitMasks.end()) { return ""; }
std::string res;
for (size_t i = 0; i < 64; ++i)
{
if (value & (1LL << i))
{
const auto itBitMaskEntry = itBitMask->second.find(value & (1LL << i));
if (itBitMaskEntry == itBitMask->second.end()) { return ""; }
if (res.empty()) { res = itBitMaskEntry->second.toASCIIString(); }
else
{
res += std::string(1, OV_Value_EnumeratedStringSeparator);
res += itBitMaskEntry->second.toASCIIString();
}
}
}
return CString(res.c_str());
}
uint64_t CTypeManager::getBitMaskEntryCompositionValueFromName(const CIdentifier& typeID, const CString& name) const
{
std::unique_lock<std::recursive_mutex> lock(m_mutex);
const auto it = m_bitMasks.find(typeID);
if (it == m_bitMasks.end()) { return 0; }
uint64_t res = 0;
const std::string entryCompositionName = name.toASCIIString();
size_t i = 0;
size_t j = 0;
do
{
i = entryCompositionName.find(OV_Value_EnumeratedStringSeparator, i);
if (i == std::string::npos) { i = entryCompositionName.length(); }
if (i != j)
{
std::string entryName;
entryName.assign(entryCompositionName, j, i - j);
bool found = false;
for (const auto& mask : it->second)
{
if (mask.second == CString(entryName.c_str()))
{
res |= mask.first;
found = true;
}
}
if (!found) { return 0; }
}
i++;
j = i;
} while (i < entryCompositionName.length());
return res;
}
bool CTypeManager::evaluateSettingValue(const CString value, double& result) const
{
// parse and expression with no variables or functions
try { result = Lepton::Parser::parse(value.toASCIIString()).evaluate(); }
catch (...) { return false; }
return true;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,57 @@
#pragma once
#include "ovkTKernelObject.h"
#include <map>
#include <set>
#include <vector>
#include <mutex>
namespace OpenViBE {
namespace Kernel {
class CTypeManager final : public TKernelObject<ITypeManager>
{
public:
explicit CTypeManager(const IKernelContext& ctx);
CIdentifier getNextTypeIdentifier(const CIdentifier& previousID) const override;
std::vector<std::pair<CIdentifier, CString>> getSortedTypes() const override;
bool registerType(const CIdentifier& typeID, const CString& name) override;
bool registerStreamType(const CIdentifier& typeID, const CString& name, const CIdentifier& parentTypeID) override;
bool registerEnumerationType(const CIdentifier& typeID, const CString& name) override;
bool registerEnumerationEntry(const CIdentifier& typeID, const CString& name, uint64_t value) override;
bool registerBitMaskType(const CIdentifier& typeID, const CString& name) override;
bool registerBitMaskEntry(const CIdentifier& typeID, const CString& name, uint64_t value) override;
bool isRegistered(const CIdentifier& typeID) const override;
bool isStream(const CIdentifier& typeID) const override;
bool isDerivedFromStream(const CIdentifier& typeID, const CIdentifier& parentTypeID) const override;
bool isEnumeration(const CIdentifier& typeID) const override;
bool isBitMask(const CIdentifier& typeID) const override;
CString getTypeName(const CIdentifier& typeID) const override;
CIdentifier getStreamParentType(const CIdentifier& typeID) const override;
size_t getEnumerationEntryCount(const CIdentifier& typeID) const override;
bool getEnumerationEntry(const CIdentifier& typeID, uint64_t index, CString& name, uint64_t& value) const override;
CString getEnumerationEntryNameFromValue(const CIdentifier& typeID, uint64_t value) const override;
uint64_t getEnumerationEntryValueFromName(const CIdentifier& typeID, const CString& name) const override;
size_t getBitMaskEntryCount(const CIdentifier& typeID) const override;
bool getBitMaskEntry(const CIdentifier& typeID, uint64_t index, CString& name, uint64_t& value) const override;
CString getBitMaskEntryNameFromValue(const CIdentifier& typeID, uint64_t value) const override;
uint64_t getBitMaskEntryValueFromName(const CIdentifier& typeID, const CString& name) const override;
CString getBitMaskEntryCompositionNameFromValue(const CIdentifier& typeID, uint64_t value) const override;
uint64_t getBitMaskEntryCompositionValueFromName(const CIdentifier& typeID, const CString& name) const override;
bool evaluateSettingValue(CString value, double& result) const override;
_IsDerivedFromClass_Final_(TKernelObject<ITypeManager>, OVK_ClassId_Kernel_TypeManager)
protected:
std::map<CIdentifier, CString> m_names;
std::set<CString> m_takenNames;
std::map<CIdentifier, std::map<uint64_t, CString>> m_enumerations;
std::map<CIdentifier, std::map<uint64_t, CString>> m_bitMasks;
std::map<CIdentifier, CIdentifier> m_streams;
mutable std::recursive_mutex m_mutex;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,114 @@
#pragma once
#include "ovkTKernelObject.h"
#include "ovkCParameter.h"
#include <map>
namespace OpenViBE {
namespace Kernel {
class IParameter;
template <class TBase>
class TBaseConfigurable : public TBase
{
public:
explicit TBaseConfigurable(const IKernelContext& ctx) : TBase(ctx) { }
~TBaseConfigurable() override
{
auto itParameter = m_parameters.begin();
while (itParameter != m_parameters.end())
{
// @FIXME is this really as intended, test the first, delete the second?
if (itParameter->second.first)
{
delete itParameter->second.second;
itParameter->second.second = nullptr;
}
++itParameter;
}
}
CIdentifier getNextParameterIdentifier(const CIdentifier& previousID) const override
{
return getNextIdentifier<std::pair<bool, IParameter*>>(m_parameters, previousID);
}
IParameter* getParameter(const CIdentifier& parameterID) override
{
const auto it = m_parameters.find(parameterID);
if (it == m_parameters.end()) { return nullptr; }
return it->second.second;
}
bool setParameter(const CIdentifier& parameterID, IParameter& parameter) override
{
this->removeParameter(parameterID);
m_parameters[parameterID] = std::pair<bool, IParameter*>(false, &parameter);
return true;
}
IParameter* createParameter(const CIdentifier& parameterID, const EParameterType parameterType, const CIdentifier& subTypeID) override
{
const auto it = m_parameters.find(parameterID);
if (it != m_parameters.end()) { return nullptr; }
IParameter* parameter = nullptr;
switch (parameterType)
{
case ParameterType_UInteger: parameter = new CUIntegerParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Integer: parameter = new CIntegerParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Enumeration: parameter = new CEnumerationParameter(this->getKernelContext(), parameterType, subTypeID);
break;
case ParameterType_Boolean: parameter = new CBooleanParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Float: parameter = new CFloatParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_String: parameter = new CStringParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Identifier: parameter = new CIdentifierParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Matrix: parameter = new CMatrixParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_StimulationSet: parameter = new CStimulationSetParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_MemoryBuffer: parameter = new CMemoryBufferParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_Object: parameter = new CObjectParameter(this->getKernelContext(), parameterType);
break;
case ParameterType_None:
case ParameterType_Pointer: parameter = new CPointerParameter(this->getKernelContext(), parameterType);
break;
}
if (parameter != nullptr) { m_parameters[parameterID] = std::pair<bool, IParameter*>(true, parameter); }
return parameter;
}
bool removeParameter(const CIdentifier& rParameterIdentifier) override
{
auto itParameter = m_parameters.find(rParameterIdentifier);
if (itParameter == m_parameters.end()) { return false; }
if (itParameter->second.first) { delete itParameter->second.second; }
m_parameters.erase(itParameter);
return true;
}
_IsDerivedFromClass_Final_(TBase, OVK_ClassId_Kernel_ConfigurableT)
private:
std::map<CIdentifier, std::pair<bool, IParameter*>> m_parameters;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,41 @@
#pragma once
#include "../ovk_base.h"
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Kernel {
template <class T>
class TKernelObject : public T
{
public:
virtual ~TKernelObject() = default;
explicit TKernelObject(const IKernelContext& ctx) : m_kernelCtx(ctx) { }
TKernelObject(const TKernelObject&) = delete;
TKernelObject& operator=(const TKernelObject&) = delete;
const IKernelContext& getKernelContext() const { return m_kernelCtx; }
virtual IAlgorithmManager& getAlgorithmManager() const { return m_kernelCtx.getAlgorithmManager(); }
virtual IConfigurationManager& getConfigurationManager() const { return m_kernelCtx.getConfigurationManager(); }
virtual IKernelObjectFactory& getKernelObjectFactory() const { return m_kernelCtx.getKernelObjectFactory(); }
virtual IPlayerManager& getPlayerManager() const { return m_kernelCtx.getPlayerManager(); }
virtual IPluginManager& getPluginManager() const { return m_kernelCtx.getPluginManager(); }
virtual IMetaboxManager& getMetaboxManager() const { return m_kernelCtx.getMetaboxManager(); }
virtual IScenarioManager& getScenarioManager() const { return m_kernelCtx.getScenarioManager(); }
virtual ITypeManager& getTypeManager() const { return m_kernelCtx.getTypeManager(); }
virtual ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
virtual IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
_IsDerivedFromClass_(T, OVK_ClassId_Kernel_KernelObjectT)
private:
const IKernelContext& m_kernelCtx;
// TKernelObject();
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,81 @@
#pragma once
#include "ovkTKernelObject.h"
#include <cstring>
namespace OpenViBE {
namespace Kernel {
template <class TBase, class TType>
class TBaseParameter : public TBase
{
public:
TBaseParameter(const IKernelContext& ctx, const EParameterType type, const CIdentifier& subTypeID = CIdentifier::undefined())
: TBase(ctx), m_valueRef(nullptr), m_value(0), m_parameterType(type), m_subTypeID(subTypeID) { }
uint64_t getParameterSize() const override { return sizeof(TType); }
EParameterType getType() const override { return m_parameterType; }
CIdentifier getSubTypeIdentifier() const override { return m_subTypeID; }
bool clearReferenceTarget() override
{
m_valueRef = NULL;
m_parameterRef = nullptr;
return true;
}
bool getReferenceTarget(IParameter*& pParameterRef) const override
{
pParameterRef = m_parameterRef;
return true;
}
bool setReferenceTarget(IParameter* pParameterRef) override
{
if (m_valueRef) { m_valueRef = NULL; }
m_parameterRef = pParameterRef;
return true;
}
bool getReferenceTarget(void* pValue) const override
{
memcpy(&pValue, &m_valueRef, sizeof(TType*));
return true;
}
bool setReferenceTarget(const void* pValue) override
{
if (m_parameterRef) { m_parameterRef = nullptr; }
memcpy(&m_valueRef, &pValue, sizeof(TType*));
return true;
}
bool getValue(void* pValue) const override
{
if (m_parameterRef) { return m_parameterRef->getValue(pValue); }
if (m_valueRef) { memcpy(pValue, m_valueRef, sizeof(TType)); }
else { memcpy(pValue, &m_value, sizeof(TType)); }
return true;
}
bool setValue(const void* pValue) override
{
if (m_parameterRef) { return m_parameterRef->setValue(pValue); }
if (m_valueRef) { memcpy(m_valueRef, pValue, sizeof(TType)); }
else { memcpy(&m_value, pValue, sizeof(TType)); }
return true;
}
_IsDerivedFromClass_(TBase, OVK_ClassId_Kernel_ParameterT)
protected:
IParameter* m_parameterRef = nullptr;
TType* m_valueRef = nullptr;
TType m_value;
EParameterType m_parameterType;
CIdentifier m_subTypeID = CIdentifier::undefined();
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,19 @@
#include "ovkCBoxAlgorithmContext.h"
#include "ovkCPlayerContext.h"
#include "ovkCSimulatedBox.h"
namespace OpenViBE {
namespace Kernel {
CBoxAlgorithmCtx::CBoxAlgorithmCtx(const IKernelContext& ctx, CSimulatedBox* simulatedBox, const IBox* box)
: TKernelObject<IBoxAlgorithmContext>(ctx), m_pStaticBoxContext(box), m_pDynamicBoxContext(simulatedBox), m_oPlayerContext(ctx, simulatedBox) {}
bool CBoxAlgorithmCtx::markAlgorithmAsReadyToProcess()
{
m_bReadyToProcess = true;
return true;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,35 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkCPlayerContext.h"
namespace OpenViBE {
namespace Kernel {
class CSimulatedBox;
class CBoxAlgorithmCtx final : public TKernelObject<IBoxAlgorithmContext>
{
public:
CBoxAlgorithmCtx(const IKernelContext& ctx, CSimulatedBox* simulatedBox, const IBox* box);
~CBoxAlgorithmCtx() override {}
const IBox* getStaticBoxContext() override { return m_pStaticBoxContext; }
IBoxIO* getDynamicBoxContext() override { return m_pDynamicBoxContext; }
IPlayerContext* getPlayerContext() override { return &m_oPlayerContext; }
bool markAlgorithmAsReadyToProcess() override;
_IsDerivedFromClass_Final_(TKernelObject<IBoxAlgorithmContext>, OVK_ClassId_Kernel_Player_BoxAlgorithmContext)
bool isAlgorithmReadyToProcess() const { return m_bReadyToProcess; }
protected:
const IBox* m_pStaticBoxContext = nullptr;
IBoxIO* m_pDynamicBoxContext = nullptr;
// here we prefer value type over reference/pointer
// in order to improve performance at runtime (no heap allocation)
CPlayerContext m_oPlayerContext;
bool m_bReadyToProcess = false;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,17 @@
#include "ovkCPlayerContext.h"
#include "ovkCSimulatedBox.h"
namespace OpenViBE {
namespace Kernel {
void CBoxAlgorithmLogManager::log(const ELogLevel logLevel)
{
CIdentifier boxId;
m_simulatedBox.getBoxIdentifier(boxId);
m_logManager << logLevel << "At time " << CTime(m_playerCtx.getCurrentTime()) << " <" << LogColor_PushStateBit << LogColor_ForegroundBlue
<< "Box algorithm" << LogColor_PopStateBit << "::" << boxId << " aka " << m_simulatedBox.getName() << "> ";
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,52 @@
#pragma once
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CSimulatedBox;
class CBoxAlgorithmLogManager final : public ILogManager
{
public:
CBoxAlgorithmLogManager(const IPlayerContext& playerContext, ILogManager& logManager, CSimulatedBox& simulatedBox)
: m_playerCtx(playerContext), m_logManager(logManager), m_simulatedBox(simulatedBox) {}
bool isActive(const ELogLevel level) override { return m_logManager.isActive(level); }
bool activate(const ELogLevel level, const bool active) override { return m_logManager.activate(level, active); }
bool activate(const ELogLevel startLevel, const ELogLevel endLevel, const bool active) override
{
return m_logManager.activate(startLevel, endLevel, active);
}
bool activate(const bool active) override { return m_logManager.activate(active); }
bool addListener(ILogListener* listener) override { return m_logManager.addListener(listener); }
bool removeListener(ILogListener* listener) override { return m_logManager.removeListener(listener); }
void log(const CTime value) override { m_logManager.log(value); }
void log(const uint64_t value) override { m_logManager.log(value); }
void log(const uint32_t value) override { m_logManager.log(value); }
void log(const int64_t value) override { m_logManager.log(value); }
void log(const int value) override { m_logManager.log(value); }
void log(const double value) override { m_logManager.log(value); }
void log(const bool value) override { m_logManager.log(value); }
void log(const CString& value) override { m_logManager.log(value); }
void log(const std::string& value) override { m_logManager.log(value); }
void log(const char* value) override { m_logManager.log(value); }
void log(const CIdentifier& value) override { m_logManager.log(value); }
void log(const ELogColor value) override { m_logManager.log(value); }
void log(const ELogLevel logLevel) override;
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
private:
const IPlayerContext& m_playerCtx;
ILogManager& m_logManager;
CSimulatedBox& m_simulatedBox;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,168 @@
#include "ovkCBoxSettingModifierVisitor.h"
#include "ovkCPlayer.h"
#include <xml/IReader.h>
#include <fs/Files.h>
#include "../../tools/ovk_setting_checker.h"
#include <string>
#include <iostream>
#include <fstream>
#define OVD_AttributeId_SettingOverrideFilename OpenViBE::CIdentifier(0x8D21FF41, 0xDF6AFE7E)
#define OV_AttributeId_Box_Disabled OpenViBE::CIdentifier(0x341D3912, 0x1478DE86)
namespace OpenViBE {
namespace Kernel {
void CBoxSettingModifierVisitor::openChild(const char* name, const char** /*sAttributeName*/, const char** /*sAttributeValue*/, const size_t /*nAttribute*/)
{
if (!m_IsParsingSettingOverride) { if (std::string(name) == std::string("OpenViBE-SettingsOverride")) { m_IsParsingSettingOverride = true; } }
else if (std::string(name) == std::string("SettingValue")) { m_IsParsingSettingValue = true; }
else { m_IsParsingSettingValue = false; }
}
void CBoxSettingModifierVisitor::processChildData(const char* data)
{
if (m_IsParsingSettingValue)
{
m_ObjectVisitorCtx->getLogManager() << LogLevel_Debug << "Using [" << data << "] as setting " << m_SettingIdx << "...\n";
m_Box->setSettingValue(m_SettingIdx, data);
}
}
void CBoxSettingModifierVisitor::closeChild()
{
//We need to count it here because we need to take in account the empty value
if (m_IsParsingSettingValue) { m_SettingIdx++; }
m_IsParsingSettingValue = false;
}
bool CBoxSettingModifierVisitor::processBegin(IObjectVisitorContext& visitorCtx, IBox& box)
{
m_ObjectVisitorCtx = &visitorCtx;
// checks if this box should override
// settings from external file
if (box.hasAttribute(OVD_AttributeId_SettingOverrideFilename))
{
const CString settingOverrideFilename = box.getAttributeValue(OVD_AttributeId_SettingOverrideFilename);
CString settingOverrideFilenameFinal;
if (m_ConfigManager == nullptr) { settingOverrideFilenameFinal = visitorCtx.getConfigurationManager().expand(settingOverrideFilename); }
else { settingOverrideFilenameFinal = m_ConfigManager->expand(settingOverrideFilename); }
// message
visitorCtx.getLogManager() << LogLevel_Trace << "Trying to override [" << box.getName() << "] box settings with file [" <<
settingOverrideFilename << " which expands to " << settingOverrideFilenameFinal << "] !\n";
// creates XML reader
XML::IReader* reader = createReader(*this);
// adds new box settings
m_Box = &box;
m_SettingIdx = 0;
m_IsParsingSettingValue = false;
m_IsParsingSettingOverride = false;
auto cleanup = [&]()
{
// cleans up internal state
m_Box = nullptr;
m_SettingIdx = 0;
m_IsParsingSettingValue = false;
m_IsParsingSettingOverride = false;
// releases XML reader
reader->release();
reader = nullptr;
};
// 1. Open settings file (binary because read would conflict with tellg for text files)
// 2. Loop until end of file, reading it
// and sending what is read to the XML parser
// 3. Close the settings file
std::ifstream file;
FS::Files::openIFStream(file, settingOverrideFilenameFinal.toASCIIString(), std::ios::binary);
if (file.is_open())
{
char buffer[1024];
bool statusOk = true;
file.seekg(0, std::ios::end);
std::streamoff fileLen = file.tellg();
file.seekg(0, std::ios::beg);
while (fileLen && statusOk)
{
// File length is always positive so this is safe
const std::streamoff bufferLen = (unsigned(fileLen) > sizeof(buffer) ? sizeof(buffer) : fileLen);
file.read(buffer, bufferLen);
fileLen -= bufferLen;
statusOk = reader->processData(buffer, size_t(bufferLen));
}
file.close();
// message
if (m_SettingIdx == box.getSettingCount())
{
visitorCtx.getLogManager() << LogLevel_Trace << "Overrode " << m_SettingIdx << " setting(s) with this configuration file...\n";
for (size_t i = 0; i < m_SettingIdx; ++i)
{
CString settingName = "";
CString rawSettingvalue = "";
box.getSettingName(i, settingName);
box.getSettingValue(i, rawSettingvalue);
CString value = rawSettingvalue;
value = m_ConfigManager->expand(value);
CIdentifier settingType;
box.getSettingType(i, settingType);
if (!checkSettingValue(value, settingType, visitorCtx.getTypeManager()))
{
const auto settingTypeName = visitorCtx.getTypeManager().getTypeName(settingType);
cleanup();
OV_ERROR("<" << box.getName() << "> The following value: [" << rawSettingvalue << "] expanded as [" << value
<< "] given as setting is not a valid [" << settingTypeName << "] value.",
ErrorType::BadArgument, false, m_ObjectVisitorCtx->getErrorManager(), m_ObjectVisitorCtx->getLogManager());
}
}
}
else
{
cleanup();
OV_ERROR(
"Overrode " << m_SettingIdx << " setting(s) with configuration file [" << settingOverrideFilenameFinal <<
"]. That does not match the box setting count " << box.getSettingCount(),
ErrorType::OutOfBound, false, m_ObjectVisitorCtx->getErrorManager(), m_ObjectVisitorCtx->getLogManager());
}
}
else
{
if (box.hasAttribute(OV_AttributeId_Box_Disabled))
{
// if the box is disabled do not stop the scenario execution when configuration fails
}
else
{
cleanup();
OV_ERROR(
"Could not override [" << box.getName() << "] settings because configuration file [" << settingOverrideFilenameFinal <<
"] could not be opened",
ErrorType::ResourceNotFound, false, m_ObjectVisitorCtx->getErrorManager(), m_ObjectVisitorCtx->getLogManager());
}
}
cleanup();
}
return true;
}
bool CBoxSettingModifierVisitor::processEnd(IObjectVisitorContext& visitorCtx, IBox& /*box*/)
{
m_ObjectVisitorCtx = &visitorCtx;
return true;
}
} //namespace Kernel
} //namespace OpenViBE
@@ -0,0 +1,38 @@
#pragma once
#include "ovkCSimulatedBox.h"
#include "ovkCPlayer.h"
#include <openvibe/ovIObjectVisitor.h>
#include <openvibe/kernel/ovIObjectVisitorContext.h>
#include <xml/IReader.h>
#define OVD_AttributeId_SettingOverrideFilename OpenViBE::CIdentifier(0x8D21FF41, 0xDF6AFE7E)
namespace OpenViBE {
namespace Kernel {
class CBoxSettingModifierVisitor final : public IObjectVisitor, public XML::IReaderCallback
{
public:
explicit CBoxSettingModifierVisitor(IConfigurationManager* pConfigurationManager = nullptr)
: IObjectVisitor(), m_ConfigManager(pConfigurationManager) {}
void openChild(const char* name, const char** sAttributeName, const char** sAttributeValue, const size_t nAttribute) override;
void processChildData(const char* data) override;
void closeChild() override;
bool processBegin(IObjectVisitorContext& visitorCtx, IBox& box) override;
bool processEnd(IObjectVisitorContext& visitorCtx, IBox& box) override;
IObjectVisitorContext* m_ObjectVisitorCtx = nullptr;
IBox* m_Box = nullptr;
size_t m_SettingIdx = 0;
bool m_IsParsingSettingValue = false;
bool m_IsParsingSettingOverride = false;
IConfigurationManager* m_ConfigManager = nullptr;
_IsDerivedFromClass_Final_(IObjectVisitor, CIdentifier::undefined())
};
} //namespace Kernel
} //namespace OpenViBE
@@ -0,0 +1,20 @@
#include "ovkCBuffer.h"
namespace OpenViBE {
namespace Kernel {
CBuffer::CBuffer(const CBuffer& buffer)
{
this->CMemoryBuffer::setSize(buffer.getSize(), true);
memcpy(this->CMemoryBuffer::getDirectPointer(), buffer.getDirectPointer(), buffer.getSize());
}
CBuffer& CBuffer::operator=(const CBuffer& buffer)
{
this->CMemoryBuffer::setSize(buffer.getSize(), true);
memcpy(this->CMemoryBuffer::getDirectPointer(), buffer.getDirectPointer(), buffer.getSize());
return *this;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,17 @@
#pragma once
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Kernel {
class CBuffer final : public CMemoryBuffer
{
public:
CBuffer() { }
explicit CBuffer(const CBuffer& buffer);
CBuffer& operator=(const CBuffer& buffer);
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,356 @@
#include "ovkCPlayer.h"
#include "ovkCSimulatedBox.h"
#include "ovkCScheduler.h"
#include "../configuration/ovkCConfigurationManager.h"
#include "../scenario/ovkCScenarioManager.h"
#include "../scenario/ovkCScenarioSettingKeywordParserCallback.h"
#include <system/ovCTime.h>
#include <xml/IReader.h>
#include <string>
#include <iostream>
#include <fstream>
namespace OpenViBE {
namespace Kernel {
const uint64_t SCHEDULER_DEFAULT_FREQUENCY = 128;
const uint64_t SCHEDULER_MAXIMUM_LOOPS_DURATION = (100LL << 22); /* 100/1024 seconds, approx 100ms */
//___________________________________________________________________//
// //
CPlayer::CPlayer(const IKernelContext& ctx)
: TKernelObject<IPlayer>(ctx), m_kernelCtxBridge(ctx), m_scheduler(m_kernelCtxBridge, *this)
{
uint64_t schedulerFrequency = this->TKernelObject<IPlayer>::getConfigurationManager().expandAsUInteger("${Kernel_PlayerFrequency}");
if (schedulerFrequency == 0)
{
OV_WARNING_K(std::string("Invalid frequency configuration Kernel_PlayerFrequency = ")
+ this->TKernelObject<IPlayer>::getConfigurationManager().expand("${Kernel_PlayerFrequency}").toASCIIString() + " restored to default "
+ std::to_string(SCHEDULER_DEFAULT_FREQUENCY));
schedulerFrequency = SCHEDULER_DEFAULT_FREQUENCY;
}
else { TKernelObject<IPlayer>::getLogManager() << LogLevel_Trace << "Player frequency set to " << schedulerFrequency << "\n"; }
m_scheduler.setFrequency(schedulerFrequency);
}
CPlayer::~CPlayer()
{
if (this->isHoldingResources()) { this->uninitialize(); }
delete m_runtimeConfigManager;
delete m_runtimeScenarioManager;
}
//___________________________________________________________________//
// //
bool CPlayer::setScenario(const CIdentifier& scenarioID, const CNameValuePairList* localConfigurationTokens)
{
OV_ERROR_UNLESS_KRF(!this->isHoldingResources(), "Trying to configure a player with non-empty resources", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Debug << "Player setScenario\n";
// Create a fresh runtime configuration manager which will handle scenario-specific
// configuration, such as the scenario settings and local settings (workspace)
delete m_runtimeConfigManager;
m_runtimeConfigManager = new CConfigurationManager(this->getKernelContext(), &this->getKernelContext().getConfigurationManager());
m_runtimeConfigManager->addConfigurationFromFile(this->getKernelContext().getConfigurationManager().expand("${Kernel_DelayedConfiguration}"));
IScenario& originalScenario = this->getScenarioManager().getScenario(scenarioID);
delete m_runtimeScenarioManager;
m_runtimeScenarioManager = new CScenarioManager(this->getKernelContext());
m_runtimeScenarioManager->cloneScenarioImportersAndExporters(this->getKernelContext().getScenarioManager());
OV_ERROR_UNLESS_KRF(m_runtimeScenarioManager->createScenario(m_runtimeScenarioID),
"Fail to create a scenario duplicate for the current runtime session", Kernel::ErrorType::BadResourceCreation);
IScenario& runtimeScenario = m_runtimeScenarioManager->getScenario(m_runtimeScenarioID);
runtimeScenario.merge(originalScenario, nullptr, true, true);
if (runtimeScenario.hasAttribute(OV_AttributeId_ScenarioFilename))
{
const std::string filename = runtimeScenario.getAttributeValue(OV_AttributeId_ScenarioFilename).toASCIIString();
std::string directoryName = ".";
m_runtimeConfigManager->createConfigurationToken("Player_ScenarioFilename", filename.c_str());
const size_t iDir = filename.rfind('/');
if (iDir != std::string::npos) { directoryName = filename.substr(0, iDir); }
m_runtimeConfigManager->createConfigurationToken("Player_ScenarioDirectory", directoryName.c_str());
m_runtimeConfigManager->createConfigurationToken("__volatile_ScenarioDir", directoryName.c_str());
const std::string workspaceConfigurationFile = directoryName + "/" + std::string("openvibe-workspace.conf");
this->getLogManager() << LogLevel_Trace << "Player adds workspace configuration file [" << workspaceConfigurationFile <<
"] to runtime configuration manager\n";
m_runtimeConfigManager->addConfigurationFromFile(CString(workspaceConfigurationFile.c_str()));
std::string scenarioConfigurationFile = directoryName + "/" + std::string("scenario.conf");
this->getLogManager() << LogLevel_Trace << "Player adds scenario configuration file [" << scenarioConfigurationFile <<
"] to runtime configuration manager\n";
m_runtimeConfigManager->addConfigurationFromFile(CString(scenarioConfigurationFile.c_str()));
const size_t ext = filename.rfind('.');
if (ext != std::string::npos)
{
scenarioConfigurationFile = filename.substr(0, ext) + std::string(".conf");
this->getLogManager() << LogLevel_Trace << "Player adds scenario configuration file [" << scenarioConfigurationFile <<
"] to runtime configuration manager\n";
m_runtimeConfigManager->addConfigurationFromFile(CString(scenarioConfigurationFile.c_str()));
}
}
// Sets configuration tokens for this player
// Once every token file, applies the configuration tokens coming from an external application defining its own scenario specific tokens
if (localConfigurationTokens != nullptr)
{
this->getLogManager() << LogLevel_Trace << "Player setScenario: add local configuration token from map.\n";
for (size_t i = 0; i < localConfigurationTokens->getSize(); ++i)
{
CString name;
CString value;
if (localConfigurationTokens->getValue(i, name, value))
{
this->getLogManager() << LogLevel_Debug << "Player setScenario: add local configuration token: [" << name << "] = [" << value << "].\n";
CIdentifier tokenID = m_runtimeConfigManager->lookUpConfigurationTokenIdentifier(name);
if (tokenID == CIdentifier::undefined()) { m_runtimeConfigManager->createConfigurationToken(name, value); }
else { m_runtimeConfigManager->setConfigurationTokenValue(tokenID, value); }
}
// This should not happen
else
{
this->getLogManager() << LogLevel_Trace << "Player setScenario: Could not acces to value of localConfigurationTokens at index " << i <<
".\n";
}
}
}
OV_ERROR_UNLESS_KRF(runtimeScenario.checkSettings(m_runtimeConfigManager),
"Checking settings failed for scenario duplicate instantiated for the current runtime session", Kernel::ErrorType::BadArgument);
return m_scheduler.setScenario(m_runtimeScenarioID);
}
IConfigurationManager& CPlayer::getRuntimeConfigurationManager() const { return *m_runtimeConfigManager; }
IScenarioManager& CPlayer::getRuntimeScenarioManager() const { return *m_runtimeScenarioManager; }
CIdentifier CPlayer::getRuntimeScenarioIdentifier() const { return m_runtimeScenarioID; }
EPlayerReturnCodes CPlayer::initialize()
{
OV_ERROR_UNLESS_K(!this->isHoldingResources(), "Trying to configure a player with non-empty resources", Kernel::ErrorType::BadCall,
EPlayerReturnCodes::Failed);
this->getLogManager() << LogLevel_Trace << "Player initialized.\n";
m_fastForwardMaximumFactor = 0;
// At this point we've inserted the bridge as a stand-in for Kernel context to the local CConfigurationManager, but the manager in the bridge is still the
// 'global' one. Now lets change the config manager in the bridge to point to the local manager in order to load configurations into the local manager.
m_kernelCtxBridge.setConfigurationManager(m_runtimeConfigManager);
const ESchedulerInitialization code = m_scheduler.initialize();
if (code == ESchedulerInitialization::Failed) { OV_ERROR_K("Failed to initialize player", Kernel::ErrorType::Internal, EPlayerReturnCodes::Failed); }
if (code == ESchedulerInitialization::BoxInitializationFailed)
{
OV_ERROR_K("Failed to initialize player", Kernel::ErrorType::Internal, EPlayerReturnCodes::BoxInitializationFailed);
}
m_benchmarkChrono.reset(size_t(m_scheduler.getFrequency()));
m_currentTimeToReach = 0;
m_lateness = 0;
m_innerLateness = 0;
m_status = EPlayerStatus::Stop;
return EPlayerReturnCodes::Success;
}
bool CPlayer::uninitialize()
{
this->getLogManager() << LogLevel_Trace << "Player uninitialize\n";
m_scheduler.uninitialize();
m_kernelCtxBridge.setConfigurationManager(nullptr);
m_scenarioConfigFile = "";
m_workspaceConfigFile = "";
if (m_runtimeConfigManager != nullptr)
{
delete m_runtimeConfigManager;
m_runtimeConfigManager = nullptr;
}
return true;
}
bool CPlayer::stop()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Trace << "Player stop\n";
m_status = EPlayerStatus::Stop;
return true;
}
bool CPlayer::pause()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Trace << "Player pause\n";
m_status = EPlayerStatus::Pause;
return true;
}
bool CPlayer::step()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Trace << "Player step\n";
m_status = EPlayerStatus::Step;
return true;
}
bool CPlayer::play()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Trace << "Player play\n";
m_status = EPlayerStatus::Play;
return true;
}
bool CPlayer::forward()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
this->getLogManager() << LogLevel_Trace << "Player forward\n";
m_status = EPlayerStatus::Forward;
return true;
}
EPlayerStatus CPlayer::getStatus() const { return m_status; }
bool CPlayer::setFastForwardMaximumFactor(const double fastForwardFactor)
{
m_fastForwardMaximumFactor = (fastForwardFactor < 0 ? 0 : fastForwardFactor);
return true;
}
double CPlayer::getFastForwardMaximumFactor() const { return m_fastForwardMaximumFactor; }
double CPlayer::getCPUUsage() const { return m_scheduler.getCPUUsage(); }
bool CPlayer::isHoldingResources() const { return m_scheduler.isHoldingResources(); }
bool CPlayer::loop(const uint64_t elapsedTime, const uint64_t maximumTimeToReach)
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized player", Kernel::ErrorType::BadCall);
if (m_status == EPlayerStatus::Stop) { return true; }
bool hasTimeToReach = false;
switch (m_status)
{
// Calls a single controller loop and goes back to pause state
case EPlayerStatus::Step:
m_currentTimeToReach += CTime(m_scheduler.getFrequency(), 1LL).time();
hasTimeToReach = true;
m_status = EPlayerStatus::Pause;
break;
// Calls multiple controller loops
case EPlayerStatus::Forward:
// We can't know what m_currentTimeToReach should be in advance
// We will try to do as many scheduler loops as possible until
// SCHEDULER_MAXIMUM_LOOPS_DURATION seconds elapsed
if (m_fastForwardMaximumFactor != 0)
{
m_currentTimeToReach += uint64_t(m_fastForwardMaximumFactor * elapsedTime);
hasTimeToReach = true;
}
break;
// Simply updates time according to delta time
case EPlayerStatus::Play:
m_currentTimeToReach += elapsedTime;
hasTimeToReach = true;
break;
default: return true;
}
#if defined CPlayer_Debug_Time
::printf("---\n");
::printf("Factor : %lf\n", m_fastForwardMaximumFactor);
::printf("Current time : %llx\n", m_scheduler.getCurrentTime());
::printf("Time to reach : %llx\n", m_currentTimeToReach);
#endif // CPlayer_Debug_Time
const uint64_t schedulerStepDuration = m_scheduler.getStepDuration();
const uint64_t tStart = System::Time::zgetTime();
bool finished = false;
while (!finished)
{
const uint64_t nextSchedulerTime = m_scheduler.getCurrentTime() + schedulerStepDuration;
#if defined CPlayer_Debug_Time
::printf(" Next time : %llx\n", nextSchedulerTime);
#endif // CPlayer_Debug_Time
if (m_status == EPlayerStatus::Stop) { finished = true; }
if ((hasTimeToReach && (nextSchedulerTime > m_currentTimeToReach)) || (
(m_status == EPlayerStatus::Forward || hasTimeToReach) && (m_scheduler.getCurrentTime() >= maximumTimeToReach)))
{
finished = true;
#if defined CPlayer_Debug_Time
::printf("Breaks because would get over time to reach\n");
#endif // CPlayer_Debug_Time
}
else
{
if (hasTimeToReach) { m_innerLateness = (m_currentTimeToReach > nextSchedulerTime ? m_currentTimeToReach - nextSchedulerTime : 0); }
else { m_innerLateness = 0; }
if (!m_scheduler.loop())
{
m_status = EPlayerStatus::Stop;
this->getLogManager() << LogLevel_Error << "Scheduler loop failed.\n";
return false;
}
#if defined CPlayer_Debug_Time
::printf("Iterates (%f / %f - %s)\n", (m_scheduler.getCurrentTime()>>22)/1024., (maximumTimeToReach>>22)/1024., (m_status==EPlayerStatus::Forward?"true":"false"));
#endif // CPlayer_Debug_Time
}
if (System::Time::zgetTime() > tStart + SCHEDULER_MAXIMUM_LOOPS_DURATION)
{
finished = true;
#if defined CPlayer_Debug_Time
::printf("Breaks because of loop time out\n");
#endif // CPlayer_Debug_Time
}
}
if ((m_status == EPlayerStatus::Forward && m_currentTimeToReach > m_scheduler.getCurrentTime() + schedulerStepDuration) || !hasTimeToReach)
{
m_currentTimeToReach = m_scheduler.getCurrentTime();
}
uint64_t lateness;
if (m_currentTimeToReach > m_scheduler.getCurrentTime()) { lateness = m_currentTimeToReach - m_scheduler.getCurrentTime(); }
else { lateness = 0; }
#if defined CPlayer_Debug_Time
::printf("Done -- New time to reach : %llx\n", m_currentTimeToReach);
#endif // CPlayer_Debug_Time
const uint64_t latenessSec = lateness >> 32;
const uint64_t prevlatenessSec = m_lateness >> 32;
std::stringstream ss;
ss << "<" << LogColor_PushStateBit << LogColor_ForegroundBlue << "Player" << LogColor_PopStateBit << "::" << LogColor_PushStateBit
<< LogColor_ForegroundBlue << "can not reach realtime" << LogColor_PopStateBit << "> " << latenessSec << " second(s) late...\n";
OV_WARNING_UNLESS_K(latenessSec == prevlatenessSec, ss.str());
return true;
}
uint64_t CPlayer::getCurrentSimulatedTime() const { return m_scheduler.getCurrentTime(); }
uint64_t CPlayer::getCurrentSimulatedLateness() const { return m_innerLateness; }
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,75 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkCScheduler.h"
#include "../ovkCKernelContext.h"
#include <system/ovCChrono.h>
#include <string>
namespace OpenViBE {
namespace Kernel {
class CScenarioSettingKeywordParserCallback;
class CPlayer final : public TKernelObject<IPlayer>
{
public:
explicit CPlayer(const IKernelContext& ctx);
~CPlayer() override;
bool setScenario(const CIdentifier& scenarioID, const CNameValuePairList* localConfigurationTokens) override;
IConfigurationManager& getRuntimeConfigurationManager() const override;
IScenarioManager& getRuntimeScenarioManager() const override;
CIdentifier getRuntimeScenarioIdentifier() const override;
bool isHoldingResources() const;
EPlayerReturnCodes initialize() override;
bool uninitialize() override;
bool stop() override;
bool pause() override;
bool step() override;
bool play() override;
bool forward() override;
EPlayerStatus getStatus() const override;
bool setFastForwardMaximumFactor(double fastForwardFactor) override;
double getFastForwardMaximumFactor() const override;
double getCPUUsage() const override;
bool loop(uint64_t elapsedTime, uint64_t maximumTimeToReach) override;
uint64_t getCurrentSimulatedTime() const override;
uint64_t getCurrentSimulatedLateness() const;
_IsDerivedFromClass_Final_(TKernelObject<IPlayer>, OVK_ClassId_Kernel_Player_Player)
protected:
CKernelContextBridge m_kernelCtxBridge;
IConfigurationManager* m_runtimeConfigManager = nullptr;
IScenarioManager* m_runtimeScenarioManager = nullptr;
CScenarioSettingKeywordParserCallback* m_scenarioSettingKeywordParserCB = nullptr;
CScheduler m_scheduler;
uint64_t m_currentTimeToReach = 0;
uint64_t m_lateness = 0;
uint64_t m_innerLateness = 0;
EPlayerStatus m_status = EPlayerStatus::Stop;
bool m_isInitializing = false;
double m_fastForwardMaximumFactor = 0;
std::string m_scenarioConfigFile;
std::string m_workspaceConfigFile;
// Stores the identifier of the scenario that is being played
CIdentifier m_scenarioID = CIdentifier::undefined();
private:
CIdentifier m_runtimeScenarioID = CIdentifier::undefined();
System::CChrono m_benchmarkChrono;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,28 @@
#include "ovkCPlayerContext.h"
#include "ovkCSimulatedBox.h"
#include "ovkCScheduler.h"
#include "ovkCPlayer.h"
namespace OpenViBE {
namespace Kernel {
CPlayerContext::CPlayerContext(const IKernelContext& ctx, CSimulatedBox* pSimulatedBox)
: TKernelObject<IPlayerContext>(ctx), m_simulatedBox(*pSimulatedBox), m_pluginManager(ctx.getPluginManager()),
m_algorithmManager(ctx.getAlgorithmManager()), m_configManager(ctx.getConfigurationManager()),
m_logManager(ctx.getLogManager()), m_errorManager(ctx.getErrorManager()), m_scenarioManager(ctx.getScenarioManager()),
m_typeManager(ctx.getTypeManager()), m_boxLogManager(*this, m_logManager, m_simulatedBox) {}
uint64_t CPlayerContext::getCurrentTime() const { return m_simulatedBox.getScheduler().getCurrentTime(); }
uint64_t CPlayerContext::getCurrentLateness() const { return m_simulatedBox.getScheduler().getCurrentLateness(); }
double CPlayerContext::getCurrentCPUUsage() const { return m_simulatedBox.getScheduler().getCPUUsage(); }
double CPlayerContext::getCurrentFastForwardMaximumFactor() const { return m_simulatedBox.getScheduler().getFastForwardMaximumFactor(); }
bool CPlayerContext::stop() { return m_simulatedBox.getScheduler().getPlayer().stop(); }
bool CPlayerContext::pause() { return m_simulatedBox.getScheduler().getPlayer().pause(); }
bool CPlayerContext::play() { return m_simulatedBox.getScheduler().getPlayer().play(); }
bool CPlayerContext::forward() { return m_simulatedBox.getScheduler().getPlayer().forward(); }
EPlayerStatus CPlayerContext::getStatus() const { return m_simulatedBox.getScheduler().getPlayer().getStatus(); }
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,53 @@
#pragma once
#include "ovkCBoxAlgorithmLogManager.h"
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CSimulatedBox;
class CPlayerContext final : public TKernelObject<IPlayerContext>
{
public:
CPlayerContext(const IKernelContext& ctx, CSimulatedBox* pSimulatedBox);
~CPlayerContext() override { }
uint64_t getCurrentTime() const override;
uint64_t getCurrentLateness() const override;
double getCurrentCPUUsage() const override;
double getCurrentFastForwardMaximumFactor() const override;
bool stop() override;
bool pause() override;
bool play() override;
bool forward() override;
EPlayerStatus getStatus() const override;
IAlgorithmManager& getAlgorithmManager() const override { return m_algorithmManager; }
IConfigurationManager& getConfigurationManager() const override { return m_configManager; }
ILogManager& getLogManager() const override { return m_boxLogManager; }
IErrorManager& getErrorManager() const override { return m_errorManager; }
IScenarioManager& getScenarioManager() const override { return m_scenarioManager; }
ITypeManager& getTypeManager() const override { return m_typeManager; }
bool canCreatePluginObject(const CIdentifier& pluginID) const override { return m_pluginManager.canCreatePluginObject(pluginID); }
Plugins::IPluginObject* createPluginObject(const CIdentifier& pluginID) const override { return m_pluginManager.createPluginObject(pluginID); }
bool releasePluginObject(Plugins::IPluginObject* pluginObject) const override { return m_pluginManager.releasePluginObject(pluginObject); }
_IsDerivedFromClass_Final_(TKernelObject<IPlayerContext>, OVK_ClassId_Kernel_Player_PlayerContext)
private:
CSimulatedBox& m_simulatedBox;
IPluginManager& m_pluginManager;
IAlgorithmManager& m_algorithmManager;
IConfigurationManager& m_configManager;
ILogManager& m_logManager;
IErrorManager& m_errorManager;
IScenarioManager& m_scenarioManager;
ITypeManager& m_typeManager;
mutable CBoxAlgorithmLogManager m_boxLogManager;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,57 @@
#include "ovkCPlayerManager.h"
#include "ovkCPlayer.h"
#include <system/ovCMath.h>
namespace OpenViBE {
namespace Kernel {
bool CPlayerManager::createPlayer(CIdentifier& playerID)
{
playerID = getUnusedIdentifier();
m_players[playerID] = new CPlayer(getKernelContext());
return true;
}
bool CPlayerManager::releasePlayer(const CIdentifier& playerID)
{
auto it = m_players.find(playerID);
OV_ERROR_UNLESS_KRF(it != m_players.end(), "Player release failed, identifier :" << playerID.str(), Kernel::ErrorType::ResourceNotFound);
delete it->second;
m_players.erase(it);
return true;
}
IPlayer& CPlayerManager::getPlayer(const CIdentifier& playerID)
{
const auto it = m_players.find(playerID);
// use fatal here because the signature does not allow
// proper checking
OV_FATAL_UNLESS_K(it != m_players.end(), "Trying to retrieve non existing player with id " << playerID.str(), Kernel::ErrorType::ResourceNotFound);
// use a fatal here because failing to meet this invariant
// means there is a bug in the manager implementation
OV_FATAL_UNLESS_K(it->second, "Null player found for id " << playerID.str(), Kernel::ErrorType::BadValue);
return *it->second;
}
CIdentifier CPlayerManager::getUnusedIdentifier() const
{
uint64_t id = CIdentifier::random().id();
CIdentifier res;
std::map<CIdentifier, CPlayer*>::const_iterator i;
do
{
id++;
res = CIdentifier(id);
i = m_players.find(res);
} while (i != m_players.end() || res == CIdentifier::undefined());
return res;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,30 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
namespace OpenViBE {
namespace Kernel {
class CPlayer;
class CPlayerManager final : public TKernelObject<IPlayerManager>
{
public:
explicit CPlayerManager(const IKernelContext& ctx) : TKernelObject<IPlayerManager>(ctx) {}
bool createPlayer(CIdentifier& playerID) override;
bool releasePlayer(const CIdentifier& playerID) override;
IPlayer& getPlayer(const CIdentifier& playerID) override;
CIdentifier getNextPlayerIdentifier(const CIdentifier& previousID) const override { return getNextIdentifier<CPlayer*>(m_players, previousID); }
_IsDerivedFromClass_Final_(TKernelObject<IPlayerManager>, OVK_ClassId_Kernel_Player_PlayerManager)
protected:
CIdentifier getUnusedIdentifier() const;
std::map<CIdentifier, CPlayer*> m_players;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,638 @@
#include <openvibe/ovExceptionHandler.h>
#include "ovkCScheduler.h"
#include "ovkCSimulatedBox.h"
#include "ovkCPlayer.h"
#include "../scenario/ovkCScenarioSettingKeywordParserCallback.h"
#include "ovkCBoxSettingModifierVisitor.h"
#include <fs/Files.h>
#include <string>
#include <cstdlib>
#include <set>
#if defined TARGET_OS_Windows
#define stricmp _stricmp
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <strings.h>
#define stricmp strcasecmp
#else
// TODO
#endif
#include <ovp_global_defines.h>
namespace OpenViBE {
namespace Kernel {
#define OVTK_Algorithm_ScenarioImporter_OutputParameterId_Scenario CIdentifier(0x29574C87, 0x7BA77780)
#define OVTK_Algorithm_ScenarioImporter_InputParameterId_MemoryBuffer CIdentifier(0x600463A3, 0x474B7F66)
#define OV_AttributeId_Box_Disabled CIdentifier(0x341D3912, 0x1478DE86)
CScheduler::CScheduler(const IKernelContext& ctx, CPlayer& player)
: TKernelObject<IKernelObject>(ctx), m_rPlayer(player), m_scenarioID(CIdentifier::undefined()) {}
CScheduler::~CScheduler() { this->uninitialize(); }
//___________________________________________________________________//
// //
bool CScheduler::setScenario(const CIdentifier& scenarioID)
{
this->getLogManager() << LogLevel_Trace << "Scheduler setScenario\n";
OV_ERROR_UNLESS_KRF(!this->isHoldingResources(), "Trying to configure a scheduler with non-empty resources", Kernel::ErrorType::BadCall);
m_scenarioID = scenarioID;
// We need to flatten the scenario here as the application using the scheduler needs time
// between the moment the visualisation tree is complete and the moment when boxes are initialized.
// The application needs to initialize necessary windows for the boxes to draw into.
m_scenario = &m_rPlayer.getRuntimeScenarioManager().getScenario(m_scenarioID);
if (!this->flattenScenario())
{
// error handling is performed within flattenScenario
return false;
}
m_scenario = nullptr;
return true;
}
bool CScheduler::setFrequency(const uint64_t frequency)
{
this->getLogManager() << LogLevel_Trace << "Scheduler setFrequency\n";
OV_ERROR_UNLESS_KRF(!this->isHoldingResources(), "Trying to configure a scheduler with non-empty resources", Kernel::ErrorType::BadCall);
m_frequency = frequency;
m_stepDuration = (1LL << 32) / frequency;
return true;
}
//___________________________________________________________________//
// //
bool CScheduler::isHoldingResources() const { return !m_simulatedBoxes.empty(); }
//___________________________________________________________________//
// //
bool CScheduler::flattenScenario()
{
OV_ERROR_UNLESS_KRF(m_scenario->applyLocalSettings(), "Failed to flatten scenario: applying local settings failed", Kernel::ErrorType::Internal);
// We are going to find all metaboxes in the scenario and then push their contents to this one
// As the scenario itself can contain more metaboxes, we are going to repeat this process
// util there are no unhandled metaboxes left
bool hasFinishedHandlingMetaboxes = false;
while (!hasFinishedHandlingMetaboxes)
{
// First find all the metaboxes in the scenario
std::vector<CIdentifier> scenarioMetaboxes;
{
CIdentifier* listID = nullptr;
size_t nbElems = 0;
m_scenario->getBoxIdentifierList(&listID, &nbElems);
for (size_t i = 0; i < nbElems; ++i)
{
const CIdentifier boxID = listID[i];
const IBox* box = m_scenario->getBoxDetails(boxID);
if (box->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
{
if (box->hasAttribute(OV_AttributeId_Box_Disabled)) // We only process this box if it is not disabled
{
m_scenario->removeBox(boxID);
}
else if (box->hasAttribute(OVP_AttributeId_Metabox_ID)) // We verify that the box actually has a backend scenario
{
CIdentifier metaboxId;
metaboxId.fromString(box->getAttributeValue(OVP_AttributeId_Metabox_ID));
CString metaboxScenarioPath(this->getKernelContext().getMetaboxManager().getMetaboxFilePath(metaboxId));
if (FS::Files::fileExists(metaboxScenarioPath.toASCIIString()))
{
// If the scenario exists we will handle this metabox
scenarioMetaboxes.push_back(boxID);
}
else
{
// Non-utilisable metaboxes can be easily removed
OV_WARNING_K("The scenario for metabox [" << metaboxId.str() << "] is missing.");
m_scenario->removeBox(boxID);
}
}
else
{
OV_WARNING_K("The metabox [" << boxID << "] is missing its identifier field.");
m_scenario->removeBox(boxID);
}
}
}
m_scenario->releaseIdentifierList(listID);
}
if (scenarioMetaboxes.empty()) { hasFinishedHandlingMetaboxes = true; }
// Now load each of the found metaboxes, load the scenario that represents it
// Assign the settings from the box to the scenario
// Calculate new settings for each box inside this scenario (by expanding $var settings)
// Merge the scenario into this one
// Re-connect all links for the scenario
for (const auto& boxID : scenarioMetaboxes)
{
IBox* box = m_scenario->getBoxDetails(boxID);
// The box has an attribute with the metabox ID and config manager has a path to each metabox scenario
CString str = box->getAttributeValue(OVP_AttributeId_Metabox_ID);
CIdentifier metaboxId;
metaboxId.fromString(str);
CString metaboxScenarioPath(this->getKernelContext().getMetaboxManager().getMetaboxFilePath(metaboxId));
OV_ERROR_UNLESS_KRF(str != CString(""), "Failed to find metabox with id " << str, Kernel::ErrorType::ResourceNotFound);
// We are going to copy the template scenario, flatten it and then copy all
// Note that copy constructor for IScenario does not exist
CIdentifier metaboxScenarioTemplateID;
OV_ERROR_UNLESS_KRF(
m_rPlayer.getRuntimeScenarioManager().importScenarioFromFile(metaboxScenarioTemplateID,
OV_ScenarioImportContext_SchedulerMetaboxImport, metaboxScenarioPath),
"Failed to import the scenario file", Kernel::ErrorType::Internal);
IScenario& metaboxScenarioInstance = m_rPlayer.getRuntimeScenarioManager().getScenario(metaboxScenarioTemplateID);
OV_WARNING_UNLESS_K(metaboxScenarioInstance.hasAttribute(OV_AttributeId_Scenario_MetaboxHash),
"Box " << box->getName() << " [" << metaboxScenarioPath << "] has no computed hash");
OV_WARNING_UNLESS_K(
box->getAttributeValue(OV_AttributeId_Box_InitialPrototypeHashValue) == metaboxScenarioInstance.getAttributeValue(
OV_AttributeId_Scenario_MetaboxHash),
"Box " << box->getName() << " [" << str << "] should be updated");
metaboxScenarioInstance.addAttribute(OV_AttributeId_ScenarioFilename, metaboxScenarioPath);
// Push down the settings from the box to the scenario
for (size_t idx = 0; idx < box->getSettingCount(); ++idx)
{
CString value;
CIdentifier id;
box->getSettingValue(idx, value);
box->getInterfacorIdentifier(Setting, idx, id);
if (id != CIdentifier::undefined()) { metaboxScenarioInstance.setSettingValue(id, value); }
else { metaboxScenarioInstance.setSettingValue(idx, value); }
}
// Create settings with the path to the Metabox,
// these settings will be accessible from within the Metabox on runtime
std::string metaboxFilename = metaboxScenarioPath.toASCIIString();
std::string metaboxDirectoryPath = ".";
metaboxScenarioInstance.addSetting("Player_MetaboxScenarioFilename", OV_TypeId_Filename, metaboxScenarioPath);
const size_t lastSlashPos = metaboxFilename.rfind('/');
if (lastSlashPos != std::string::npos) { metaboxDirectoryPath = metaboxFilename.substr(0, lastSlashPos); }
metaboxScenarioInstance.addSetting("Player_MetaboxScenarioDirectory", OV_TypeId_Foldername, metaboxDirectoryPath.c_str());
// apply the settings within the loaded scenario
metaboxScenarioInstance.applyLocalSettings();
std::map<CIdentifier, CIdentifier> correspondenceID;
class CScenarioMergeCallback : public IScenario::IScenarioMergeCallback
{
public:
explicit
CScenarioMergeCallback(std::map<CIdentifier, CIdentifier>& rIdentifierCorrespondence) : m_correspondenceID(rIdentifierCorrespondence) { }
void process(CIdentifier& originalID, CIdentifier& newId) override { m_correspondenceID[originalID] = newId; }
private:
std::map<CIdentifier, CIdentifier>& m_correspondenceID;
};
CScenarioMergeCallback scenarioMergeCB(correspondenceID);
// Copy the boxes and the links from the template metabox scenario to this one
m_scenario->merge(metaboxScenarioInstance, &scenarioMergeCB, false, false);
// Now reconnect all the pipes
// Connect metabox inputs
{
CIdentifier* listID = nullptr;
size_t nbElems = 0;
m_scenario->getLinkIdentifierToBoxList(box->getIdentifier(), &listID, &nbElems);
for (size_t i = 0; i < nbElems; ++i)
{
ILink* link = m_scenario->getLinkDetails(listID[i]);
// Find out the target inside the metabox scenario
CIdentifier dstBoxID;
size_t dstBoxInputIdx = 0;
CIdentifier metaBoxInputID = link->getTargetBoxInputIdentifier();
size_t metaBoxInputIdx = link->getTargetBoxInputIndex();
if (metaBoxInputID != CIdentifier::undefined()) { metaboxScenarioInstance.getInterfacorIndex(Input, metaBoxInputID, metaBoxInputIdx); }
OV_ERROR_UNLESS_KRF(metaBoxInputIdx != size_t(-1), "Failed to find metabox input with identifier " << metaBoxInputID.str(),
ErrorType::ResourceNotFound);
metaboxScenarioInstance.getScenarioInputLink(metaBoxInputIdx, dstBoxID, dstBoxInputIdx);
// Now redirect the link to the newly created copy of the box in the scenario
CIdentifier dstBoxInputID = CIdentifier::undefined();
if (dstBoxID != CIdentifier::undefined())
{
m_scenario->getBoxDetails(correspondenceID[dstBoxID])->getInterfacorIdentifier(Input, dstBoxInputIdx, dstBoxInputID);
link->setTarget(correspondenceID[dstBoxID], dstBoxInputIdx, dstBoxInputID);
}
}
m_scenario->releaseIdentifierList(listID);
}
// Connect metabox outputs
{
CIdentifier* listID = nullptr;
size_t nbElems = 0;
m_scenario->getLinkIdentifierFromBoxList(box->getIdentifier(), &listID, &nbElems);
for (size_t i = 0; i < nbElems; ++i)
{
ILink* link = m_scenario->getLinkDetails(listID[i]);
// Find out the Source inside the metabox scenario
CIdentifier srcBoxID;
size_t srcBoxOutputIdx = 0;
CIdentifier metaBoxOutputID = link->getSourceBoxOutputIdentifier();
size_t metaBoxOutputIdx = link->getSourceBoxOutputIndex();
if (metaBoxOutputID != CIdentifier::undefined()) { metaboxScenarioInstance.getInterfacorIndex(Output, metaBoxOutputID, metaBoxOutputIdx); }
OV_ERROR_UNLESS_KRF(metaBoxOutputIdx != size_t(-1),
"Failed to find metabox input with identifier " << metaBoxOutputID.str(), Kernel::ErrorType::ResourceNotFound);
metaboxScenarioInstance.getScenarioOutputLink(metaBoxOutputIdx, srcBoxID, srcBoxOutputIdx);
// Now redirect the link to the newly created copy of the box in the scenario
CIdentifier srcBoxOutputID = CIdentifier::undefined();
if (srcBoxID != CIdentifier::undefined())
{
m_scenario->getBoxDetails(correspondenceID[srcBoxID])->getInterfacorIdentifier(Output, srcBoxOutputIdx, srcBoxOutputID);
link->setSource(correspondenceID[srcBoxID], srcBoxOutputIdx, srcBoxOutputID);
}
}
m_scenario->releaseIdentifierList(listID);
}
}
// Remove processed metaboxes from the scenario
for (const CIdentifier& metaboxID : scenarioMetaboxes) { m_scenario->removeBox(metaboxID); }
}
return true;
}
ESchedulerInitialization CScheduler::initialize()
{
this->getLogManager() << LogLevel_Trace << "Scheduler initialize\n";
OV_ERROR_UNLESS_K(!this->isHoldingResources(), "Trying to configure a scheduler with non-empty resources", Kernel::ErrorType::BadCall,
ESchedulerInitialization::Failed);
m_scenario = &m_rPlayer.getRuntimeScenarioManager().getScenario(m_scenarioID);
OV_ERROR_UNLESS_K(m_scenario, "Failed to find scenario with id " << m_scenarioID.str(), Kernel::ErrorType::ResourceNotFound,
ESchedulerInitialization::Failed);
OV_ERROR_UNLESS_K(m_scenario->getNextBoxIdentifier(CIdentifier::undefined()) != CIdentifier::undefined(),
"Cannot initialize scheduler with an empty scenario", Kernel::ErrorType::BadCall, ESchedulerInitialization::Failed);
CBoxSettingModifierVisitor boxSettingModifierVisitor(&this->getKernelContext().getConfigurationManager());
OV_ERROR_UNLESS_K(m_scenario->acceptVisitor(boxSettingModifierVisitor), "Failed to set box settings visitor for scenario with id "
<< m_scenarioID.str(), Kernel::ErrorType::Internal, ESchedulerInitialization::Failed);
{
CIdentifier* listID = nullptr;
size_t nbElems = 0;
m_scenario->getBoxIdentifierList(&listID, &nbElems);
// Create sets of x and y positions, note that C++ sets are always ordered
// We work with indexes rather than just positions because the positions are not bounded and can overflow
std::set<int> xpositions;
std::set<int> ypositions;
for (size_t i = 0; i < nbElems; ++i)
{
const IBox* box = m_scenario->getBoxDetails(listID[i]);
if (box->hasAttribute(OV_AttributeId_Box_YCenterPosition))
{
try
{
int y = std::stoi(box->getAttributeValue(OV_AttributeId_Box_YCenterPosition).toASCIIString());
ypositions.insert(y);
}
catch (const std::exception&)
{
OV_WARNING_K(
"The Y position (" << box->getAttributeValue(OV_AttributeId_Box_YCenterPosition) << ") " << " in the Box " << listID[i] <<
" is corrupted");
}
}
if (box->hasAttribute(OV_AttributeId_Box_XCenterPosition))
{
try
{
int x = std::stoi(box->getAttributeValue(OV_AttributeId_Box_XCenterPosition).toASCIIString());
xpositions.insert(x);
}
catch (const std::exception&)
{
OV_WARNING_K(
"The X position (" << box->getAttributeValue(OV_AttributeId_Box_XCenterPosition) << ") " << " in the Box " << listID[i] <<
" is corrupted");
}
}
}
for (size_t i = 0; i < nbElems; ++i)
{
const CIdentifier boxID = listID[i];
const IBox* box = m_scenario->getBoxDetails(boxID);
OV_ERROR_UNLESS_K(
!m_scenario->hasOutdatedBox() || !this->getConfigurationManager().expandAsBoolean("${Kernel_AbortPlayerWhenBoxIsOutdated}", false),
"Box [" << box->getName() << "] with class identifier [" << boxID.str() << "] should be updated",
ErrorType::Internal, ESchedulerInitialization::Failed);
OV_ERROR_UNLESS_K(box->getAlgorithmClassIdentifier() != OVP_ClassId_BoxAlgorithm_Metabox,
"Not expanded metabox with id [" << box->getAttributeValue(OVP_AttributeId_Metabox_ID) << "] detected in the scenario",
ErrorType::Internal, ESchedulerInitialization::Failed);
const Plugins::IPluginObjectDesc* boxDesc = this->getPluginManager().getPluginObjectDescCreating(box->getAlgorithmClassIdentifier());
OV_ERROR_UNLESS_K(
!(box->hasAttribute(OV_AttributeId_Box_Disabled) && this->getConfigurationManager().expandAsBoolean("${Kernel_AbortPlayerWhenBoxIsDisabled}",
false)),
"Disabled box [" << box->getName() << "] with class identifier [" << boxID.str() << "] detected in the scenario",
ErrorType::Internal, ESchedulerInitialization::Failed);
if (!box->hasAttribute(OV_AttributeId_Box_Disabled))
{
OV_ERROR_UNLESS_K(boxDesc != nullptr,
"Failed to create runtime box [" << box->getName() << "] with class identifier [" << boxID.str() << "]",
ErrorType::BadResourceCreation, ESchedulerInitialization::Failed);
CSimulatedBox* simulatedBox = new CSimulatedBox(this->getKernelContext(), *this);
simulatedBox->setScenarioIdentifier(m_scenarioID);
simulatedBox->setBoxIdentifier(boxID);
// Set priority so boxes execute in this order
// - boxes with positive priority attribute set in descending order
// - boxes in scenario from left to right and from top to bottom
// - boxes with negative priority set in descending order
int priority = 0;
try
{
if (box->hasAttribute(OV_AttributeId_Box_Priority))
{
const int p = std::stoi(box->getAttributeValue(OV_AttributeId_Box_Priority).toASCIIString());
if (p < 0) { priority = -int((ypositions.size() << 15) + xpositions.size()) + p; }
else { priority = p; }
}
else if (box->hasAttribute(OV_AttributeId_Box_YCenterPosition) && box->hasAttribute(OV_AttributeId_Box_XCenterPosition))
{
int x = std::stoi(box->getAttributeValue(OV_AttributeId_Box_XCenterPosition).toASCIIString());
int y = std::stoi(box->getAttributeValue(OV_AttributeId_Box_YCenterPosition).toASCIIString());
const int xindex = int(std::distance(xpositions.begin(), xpositions.find(x)));
const int yindex = int(std::distance(ypositions.begin(), ypositions.find(y)));
priority = - ((yindex << 15) + xindex);
}
}
catch (const std::exception&) { priority = 0; }
m_simulatedBoxes[std::make_pair(-priority, boxID)] = simulatedBox;
m_simulatedBoxChronos[boxID].reset(m_frequency);
}
}
m_scenario->releaseIdentifierList(listID);
}
OV_ERROR_UNLESS_K(!m_simulatedBoxes.empty(), "Cannot initialize scheduler with an empty scenario", Kernel::ErrorType::BadCall,
ESchedulerInitialization::Failed);
bool boxInitialization = true;
for (auto it = m_simulatedBoxes.begin(); it != m_simulatedBoxes.end(); ++
it)
{
if (auto simulatedBox = it->second)
{
this->getLogManager() << LogLevel_Trace << "Scheduled box : id = " << it->first.second << " priority = " << -it->first.first
<< " name = " << simulatedBox->getName() << "\n";
if (!translateException([&]() { return simulatedBox->initialize(); },
std::bind(&CScheduler::handleException, this, simulatedBox, "Box initialization", std::placeholders::_1)))
{
boxInitialization = false;
// return as soon as possible
// no need to keep on initializing boxes if a box failed to initialize
break;
}
}
}
m_steps = 0;
m_currentTime = 0;
m_oBenchmarkChrono.reset(m_frequency);
return (boxInitialization ? ESchedulerInitialization::Success : ESchedulerInitialization::Failed);
}
bool CScheduler::uninitialize()
{
this->getLogManager() << LogLevel_Trace << "Scheduler uninitialize\n";
bool boxUninitialization = true;
for (auto it = m_simulatedBoxes.begin(); it != m_simulatedBoxes.end(); ++
it)
{
if (auto simulatedBox = it->second)
{
if (!translateException([&]() { return simulatedBox->uninitialize(); },
std::bind(&CScheduler::handleException, this, simulatedBox, "Box uninitialization", std::placeholders::_1)))
{
// do not break here because we want to try to
// at least uninitialize other resources properly
boxUninitialization = false;
}
}
}
for (auto it = m_simulatedBoxes.begin(); it != m_simulatedBoxes.end(); ++it) { delete it->second; }
m_simulatedBoxes.clear();
m_scenario = nullptr;
return boxUninitialization;
}
//___________________________________________________________________//
// //
bool CScheduler::loop()
{
OV_ERROR_UNLESS_KRF(this->isHoldingResources(), "Trying to use an uninitialized scheduler", Kernel::ErrorType::BadCall);
bool boxProcessing = true;
m_oBenchmarkChrono.stepIn();
for (auto it = m_simulatedBoxes.begin(); it != m_simulatedBoxes.end(); ++
it)
{
CSimulatedBox* simulatedBox = it->second;
System::CChrono& simulatedBoxChrono = m_simulatedBoxChronos[it->first.second];
IBox* box = m_scenario->getBoxDetails(it->first.second);
OV_ERROR_UNLESS_KRF(box, "Unable to get box details for box with id " << it->first.second.str(), Kernel::ErrorType::ResourceNotFound);
simulatedBoxChrono.stepIn();
if (!translateException([&]() { return this->processBox(simulatedBox, it->first.second); },
std::bind(&CScheduler::handleException, this, simulatedBox, "Box processing", std::placeholders::_1)))
{
boxProcessing = false;
// break here because we do not want to keep on processing if one
// box fails
break;
}
simulatedBoxChrono.stepOut();
if (simulatedBoxChrono.hasNewEstimation())
{
//IBox* box=m_scenario->getBoxDetails(it->first.second);
box->addAttribute(OV_AttributeId_Box_ComputationTimeLastSecond, "");
box->setAttributeValue(OV_AttributeId_Box_ComputationTimeLastSecond, CIdentifier(simulatedBoxChrono.getTotalStepInDuration()).toString());
}
}
m_oBenchmarkChrono.stepOut();
if ((m_steps % m_frequency) == 0)
{
this->getLogManager() << LogLevel_Debug
<< "<" << LogColor_PushStateBit << LogColor_ForegroundBlue << "Scheduler" << LogColor_PopStateBit
<< "::" << LogColor_PushStateBit << LogColor_ForegroundBlue << "elapsed time" << LogColor_PopStateBit << "> "
<< m_steps / m_frequency << "s\n";
}
if (m_oBenchmarkChrono.hasNewEstimation())
{
this->getLogManager() << LogLevel_Benchmark
<< "<" << LogColor_PushStateBit << LogColor_ForegroundBlue << "Scheduler" << LogColor_PopStateBit
<< "::" << LogColor_PushStateBit << LogColor_ForegroundBlue << "processor use" << LogColor_PopStateBit << "> "
<< m_oBenchmarkChrono.getStepInPercentage() << "%\n";
}
m_steps++;
m_currentTime = m_steps * CTime(m_frequency, 1LL).time();
return boxProcessing;
}
bool CScheduler::processBox(CSimulatedBox* simulatedBox, const CIdentifier& boxID)
{
if (simulatedBox)
{
OV_ERROR_UNLESS_KRF(simulatedBox->processClock(), "Process clock failed for box with id " << boxID.str(), Kernel::ErrorType::Internal);
if (simulatedBox->isReadyToProcess())
{
OV_ERROR_UNLESS_KRF(simulatedBox->process(), "Process failed for box with id " << boxID.str(), Kernel::ErrorType::Internal);
}
//if the box is muted we still have to erase chunks that arrives at the input
std::map<size_t, std::list<CChunk>>& simulatedBoxInput = m_simulatedBoxInputs[boxID];
for (auto it1 = simulatedBoxInput.begin(); it1 != simulatedBoxInput.end(); ++it1)
{
std::list<CChunk>& simulatedBoxInputChunkList = it1->second;
for (auto it2 = simulatedBoxInputChunkList.begin(); it2 != simulatedBoxInputChunkList.end(); ++it2)
{
OV_ERROR_UNLESS_KRF(simulatedBox->processInput(it1->first, *it2),
"Process failed for box with id " << boxID.str() << " on input " << it1->first,
ErrorType::Internal);
if (simulatedBox->isReadyToProcess())
{
OV_ERROR_UNLESS_KRF(simulatedBox->process(), "Process failed for box with id " << boxID.str(), Kernel::ErrorType::Internal);
}
}
simulatedBoxInputChunkList.clear();
}
}
return true;
}
//___________________________________________________________________//
// //
bool CScheduler::sendInput(const CChunk& chunk, const CIdentifier& boxId, const size_t index)
{
IBox* box = m_scenario->getBoxDetails(boxId);
if (box->hasAttribute(OV_AttributeId_Box_Disabled)) { return true; }
OV_ERROR_UNLESS_KRF(box, "Tried to send data chunk with invalid box identifier " << boxId.str(), Kernel::ErrorType::ResourceNotFound);
OV_ERROR_UNLESS_KRF(index < box->getInputCount(),
"Tried to send data chunk with invalid input index " << index << " for box identifier" << boxId.str(),
ErrorType::OutOfBound);
auto it = m_simulatedBoxes.begin();
while (it != m_simulatedBoxes.end() && it->first.second != boxId) { ++it; }
OV_ERROR_UNLESS_KRF(it != m_simulatedBoxes.end(),
"Tried to send data chunk with invalid simulated box identifier " << boxId.str(),
ErrorType::ResourceNotFound);
CSimulatedBox* simulatedBox = it->second;
// use a fatal here because failing to meet this invariant
// means there is a bug in the scheduler implementation
OV_FATAL_UNLESS_K(simulatedBox, "Null box found for id " << boxId.str(), Kernel::ErrorType::BadValue);
// TODO: check if index does not overflow
m_simulatedBoxInputs[boxId][index].push_back(chunk);
return true;
}
uint64_t CScheduler::getCurrentLateness() const { return m_rPlayer.getCurrentSimulatedLateness(); }
double CScheduler::getFastForwardMaximumFactor() const { return m_rPlayer.getFastForwardMaximumFactor(); }
void CScheduler::handleException(const CSimulatedBox* box, const char* errorHint, const std::exception& exception)
{
CIdentifier dstBoxID = CIdentifier::undefined();
box->getBoxIdentifier(dstBoxID);
box->getLogManager() << LogLevel_Error << "Exception caught in box\n";
box->getLogManager() << LogLevel_Error << " [name:" << box->getName() << "]\n";
box->getLogManager() << LogLevel_Error << " [class identifier:" << dstBoxID << "]\n";
box->getLogManager() << LogLevel_Error << " [hint: " << (errorHint ? errorHint : "no hint") << "]\n";
box->getLogManager() << LogLevel_Error << " [cause:" << exception.what() << "]\n";
OV_ERROR_KRV("Caught exception: " << exception.what(), Kernel::ErrorType::ExceptionCaught);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,66 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <system/ovCChrono.h>
#include <map>
#include <list>
namespace OpenViBE {
namespace Kernel {
enum class ESchedulerInitialization { Success, BoxInitializationFailed, Failed };
class CSimulatedBox;
class CChunk;
class CPlayer;
class CScheduler final : public TKernelObject<IKernelObject>
{
public:
CScheduler(const IKernelContext& ctx, CPlayer& player);
~CScheduler() override;
bool setScenario(const CIdentifier& scenarioID);
bool setFrequency(uint64_t frequency);
bool isHoldingResources() const;
ESchedulerInitialization initialize();
bool uninitialize();
bool loop();
bool sendInput(const CChunk& chunk, const CIdentifier& boxId, size_t index);
uint64_t getCurrentTime() const { return m_currentTime; }
uint64_t getCurrentLateness() const;
uint64_t getFrequency() const { return m_frequency; }
uint64_t getStepDuration() const { return m_stepDuration; }
double getCPUUsage() const { return (const_cast<System::CChrono&>(m_oBenchmarkChrono)).getStepInPercentage(); }
double getFastForwardMaximumFactor() const;
_IsDerivedFromClass_Final_(TKernelObject<IKernelObject>, OVK_ClassId_Kernel_Player_Scheduler)
CPlayer& getPlayer() const { return m_rPlayer; }
protected:
CPlayer& m_rPlayer;
CIdentifier m_scenarioID = CIdentifier::undefined();
IScenario* m_scenario = nullptr;
size_t m_steps = 0;
uint64_t m_frequency = 0;
uint64_t m_stepDuration = 0;
uint64_t m_currentTime = 0;
std::map<std::pair<int, CIdentifier>, CSimulatedBox*> m_simulatedBoxes;
std::map<CIdentifier, System::CChrono> m_simulatedBoxChronos;
std::map<CIdentifier, std::map<size_t, std::list<CChunk>>> m_simulatedBoxInputs;
private:
void handleException(const CSimulatedBox* box, const char* errorHint, const std::exception& exception);
bool processBox(CSimulatedBox* simulatedBox, const CIdentifier& boxID);
bool flattenScenario();
System::CChrono m_oBenchmarkChrono;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,428 @@
#include "ovkCSimulatedBox.h"
#include "ovkCPlayer.h"
#include "ovkCBoxAlgorithmContext.h"
#include <cstdlib>
#include <algorithm>
#include <cassert>
namespace OpenViBE {
namespace Kernel {
#define OV_IncorrectTime 0xffffffffffffffffULL
CSimulatedBox::CSimulatedBox(const IKernelContext& ctx, CScheduler& scheduler)
: TKernelObject<IBoxIO>(ctx), m_scheduler(scheduler), m_lastClockActivationDate(OV_IncorrectTime) {}
CSimulatedBox::~CSimulatedBox() {}
bool CSimulatedBox::setScenarioIdentifier(const CIdentifier& scenarioID)
{
OV_ERROR_UNLESS_KRF(m_scheduler.getPlayer().getRuntimeScenarioManager().isScenario(scenarioID),
"Scenario with identifier " << scenarioID.str() << " does not exist",
ErrorType::ResourceNotFound);
m_scenario = &m_scheduler.getPlayer().getRuntimeScenarioManager().getScenario(scenarioID);
return true;
}
bool CSimulatedBox::getBoxIdentifier(CIdentifier& boxId) const
{
OV_ERROR_UNLESS_KRF(m_box, "Simulated box not initialized", Kernel::ErrorType::BadCall);
boxId = m_box->getIdentifier();
return true;
}
bool CSimulatedBox::setBoxIdentifier(const CIdentifier& boxId)
{
OV_ERROR_UNLESS_KRF(m_scenario, "No scenario set", Kernel::ErrorType::BadCall);
m_box = m_scenario->getBoxDetails(boxId);
return m_box != nullptr;
}
bool CSimulatedBox::initialize()
{
OV_ERROR_UNLESS_KRF(m_box, "Simulated box not initialized", Kernel::ErrorType::BadCall);
OV_ERROR_UNLESS_KRF(m_scenario, "No scenario set", Kernel::ErrorType::BadCall);
m_chunkConsistencyChecking = this->getConfigurationManager().expandAsBoolean("${Kernel_CheckChunkConsistency}", true);
m_Inputs.resize(m_box->getInputCount());
m_Outputs.resize(m_box->getOutputCount());
m_CurrentOutputs.resize(m_box->getOutputCount());
m_LastOutputStartTimes.resize(m_box->getOutputCount(), 0);
m_LastOutputEndTimes.resize(m_box->getOutputCount(), 0);
m_lastClockActivationDate = OV_IncorrectTime;
m_clockFrequency = 0;
m_clockActivationStep = 0;
m_boxAlgorithm = getPluginManager().createBoxAlgorithm(m_box->getAlgorithmClassIdentifier(), nullptr);
OV_ERROR_UNLESS_KRF(m_boxAlgorithm, "Could not create box algorithm with class id " << m_box->getAlgorithmClassIdentifier().str(),
ErrorType::BadResourceCreation);
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
OV_ERROR_UNLESS_KRF(m_boxAlgorithm->initialize(context), "Box algorithm <" << m_box->getName() << "> initialization failed",
Kernel::ErrorType::Internal);
}
}
return true;
}
bool CSimulatedBox::uninitialize()
{
if (!m_boxAlgorithm) { return true; }
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
OV_ERROR_UNLESS_KRF(m_boxAlgorithm->uninitialize(context), "Box algorithm <" << m_box->getName() << "> uninitialization failed",
ErrorType::Internal);
}
}
getPluginManager().releasePluginObject(m_boxAlgorithm);
m_boxAlgorithm = nullptr;
return true;
}
bool CSimulatedBox::processClock()
{
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
const uint64_t newFreq = m_boxAlgorithm->getClockFrequency(context);
if (newFreq == 0)
{
m_clockActivationStep = OV_IncorrectTime;
m_lastClockActivationDate = OV_IncorrectTime;
}
else
{
OV_ERROR_UNLESS_KRF(newFreq <= m_scheduler.getFrequency()<<32,
"Box " << m_box->getName() << " requested higher clock frequency ("
<< newFreq << " == " << CTime(newFreq).toSeconds() << "hz) " << "than what the scheduler can handle ("
<< (m_scheduler.getFrequency()<<32) << " == " << CTime(m_scheduler.getFrequency()<<32).toSeconds() << "hz)",
ErrorType::BadConfig);
// note: 1LL should be left shifted 64 bits but this
// would result in an integer over shift (the one
// would exit). Thus the left shift of 63 bits
// and the left shift of 1 bit after the division
m_clockActivationStep = ((1ULL << 63) / newFreq) << 1;
}
m_clockFrequency = newFreq;
}
}
if ((m_clockFrequency != 0) && (m_lastClockActivationDate == OV_IncorrectTime || m_scheduler.getCurrentTime() - m_lastClockActivationDate >=
m_clockActivationStep))
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
if (m_lastClockActivationDate == OV_IncorrectTime) { m_lastClockActivationDate = m_scheduler.getCurrentTime(); }
else { m_lastClockActivationDate = m_lastClockActivationDate + m_clockActivationStep; }
CMessageClock message;
message.setTime(m_lastClockActivationDate);
OV_ERROR_UNLESS_KRF(m_boxAlgorithm->processClock(context, message),
"Box algorithm <" << m_box->getName() << "> processClock() function failed", Kernel::ErrorType::Internal);
m_readyToProcess |= context.isAlgorithmReadyToProcess();
}
}
return true;
}
bool CSimulatedBox::processInput(const size_t index, const CChunk& chunk)
{
m_Inputs[index].push_back(chunk);
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
OV_ERROR_UNLESS_KRF(m_boxAlgorithm->processInput(context, index),
"Box algorithm <" << m_box->getName() << "> processInput() function failed", Kernel::ErrorType::Internal);
}
m_readyToProcess |= context.isAlgorithmReadyToProcess();
}
return true;
}
bool CSimulatedBox::process()
{
if (!m_readyToProcess) { return true; }
{
CBoxAlgorithmCtx context(getKernelContext(), this, m_box);
{
OV_ERROR_UNLESS_KRF(m_boxAlgorithm->process(context), "Box algorithm <" << m_box->getName() << "> processInput function failed",
ErrorType::Internal);
}
}
// perform output sending
{
CIdentifier* listID = nullptr;
size_t nbElems = 0;
m_scenario->getLinkIdentifierFromBoxList(m_box->getIdentifier(), &listID, &nbElems);
for (size_t i = 0; i < nbElems; ++i)
{
const ILink* link = m_scenario->getLinkDetails(listID[i]);
if (link)
{
CIdentifier dstBoxID = link->getTargetBoxIdentifier();
const size_t dstBoxInputIdx = link->getTargetBoxInputIndex();
const size_t sourceOutputIdx = link->getSourceBoxOutputIndex();
for (auto& chunk : m_Outputs[sourceOutputIdx]) { m_scheduler.sendInput(chunk, dstBoxID, dstBoxInputIdx); }
}
}
m_scenario->releaseIdentifierList(listID);
}
// perform input cleaning
auto socketIterator = m_Inputs.begin();
while (socketIterator != m_Inputs.end())
{
auto inputChunkIterator = socketIterator->begin();
while (inputChunkIterator != socketIterator->end())
{
if (inputChunkIterator->isDeprecated()) { inputChunkIterator = socketIterator->erase(inputChunkIterator); }
else { ++inputChunkIterator; }
}
++socketIterator;
}
// flushes sent output chunks
for (auto& socket : m_Outputs) { socket.resize(0); }
// discards waiting output chunks
for (const auto& chunk : m_CurrentOutputs)
{
OV_FATAL_UNLESS_K(chunk.getBuffer().getSize() == 0, "Output buffer filled but not marked as ready to send. Possible loss of data.",
ErrorType::Internal);
}
m_readyToProcess = false;
return true;
}
bool CSimulatedBox::isReadyToProcess() const { return m_readyToProcess; }
// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
// - --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- -
// --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
CString CSimulatedBox::getName() const { return m_box->getName(); }
const IScenario& CSimulatedBox::getScenario() const { return *m_scenario; }
// ________________________________________________________________________________________________________________
//
size_t CSimulatedBox::getInputChunkCount(const size_t index) const
{
OV_ERROR_UNLESS_KRF(index < m_Inputs.size(),
"Input index = [" << index << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])",
ErrorType::OutOfBound);
return size_t(m_Inputs[index].size());
}
bool CSimulatedBox::getInputChunk(const size_t inputIdx, const size_t chunkIdx, uint64_t& startTime, uint64_t& endTime, size_t& size,
const uint8_t*& buffer) const
{
OV_ERROR_UNLESS_KRF(inputIdx < m_Inputs.size(),
"Input index = [" << inputIdx << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])", Kernel::ErrorType::OutOfBound);
OV_ERROR_UNLESS_KRF(chunkIdx < m_Inputs[inputIdx].size(),
"Input chunk index = [" << chunkIdx << "] is out of range (max index = [" << m_Inputs[inputIdx].size() - 1 << "])",
ErrorType::OutOfBound);
const CChunk& chunk = m_Inputs[inputIdx][chunkIdx];
startTime = chunk.getStartTime();
endTime = chunk.getEndTime();
size = chunk.getBuffer().getSize();
buffer = chunk.getBuffer().getDirectPointer();
return true;
}
const IMemoryBuffer* CSimulatedBox::getInputChunk(const size_t inputIdx, const size_t chunkIdx) const
{
OV_ERROR_UNLESS_KRN(inputIdx < m_Inputs.size(),
"Input index = [" << inputIdx << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])",
ErrorType::OutOfBound);
OV_ERROR_UNLESS_KRN(chunkIdx < m_Inputs[inputIdx].size(),
"Input chunk index = [" << chunkIdx << "] is out of range (max index = [" << m_Inputs[inputIdx].size() - 1 << "])",
ErrorType::OutOfBound);
return &(m_Inputs[inputIdx][chunkIdx]).getBuffer();
}
uint64_t CSimulatedBox::getInputChunkStartTime(const size_t inputIdx, const size_t chunkIdx) const
{
OV_ERROR_UNLESS_KRZ(inputIdx < m_Inputs.size(),
"Input index = [" << inputIdx << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])",
ErrorType::OutOfBound);
OV_ERROR_UNLESS_KRZ(chunkIdx < m_Inputs[inputIdx].size(),
"Input chunk index = [" << chunkIdx << "] is out of range (max index = [" << m_Inputs[inputIdx].size() - 1 << "])",
ErrorType::OutOfBound);
const CChunk& chunk = m_Inputs[inputIdx][chunkIdx];
return chunk.getStartTime();
}
uint64_t CSimulatedBox::getInputChunkEndTime(const size_t inputIdx, const size_t chunkIdx) const
{
OV_ERROR_UNLESS_KRZ(inputIdx < m_Inputs.size(),
"Input index = [" << inputIdx << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])",
ErrorType::OutOfBound);
OV_ERROR_UNLESS_KRZ(chunkIdx < m_Inputs[inputIdx].size(),
"Input chunk index = [" << chunkIdx << "] is out of range (max index = [" << m_Inputs[inputIdx].size() - 1 << "])",
ErrorType::OutOfBound);
const CChunk& chunk = m_Inputs[inputIdx][chunkIdx];
return chunk.getEndTime();
}
bool CSimulatedBox::markInputAsDeprecated(const size_t inputIdx, const size_t chunkIdx)
{
OV_ERROR_UNLESS_KRZ(inputIdx < m_Inputs.size(),
"Input index = [" << inputIdx << "] is out of range (max index = [" << m_Inputs.size() - 1 << "])",
ErrorType::OutOfBound);
OV_ERROR_UNLESS_KRZ(chunkIdx < m_Inputs[inputIdx].size(),
"Input chunk index = [" << chunkIdx << "] is out of range (max index = [" << m_Inputs[inputIdx].size() - 1 << "])",
ErrorType::OutOfBound);
m_Inputs[inputIdx][chunkIdx].markAsDeprecated(true);
return true;
}
// ________________________________________________________________________________________________________________
//
size_t CSimulatedBox::getOutputChunkSize(const size_t outputIdx) const
{
OV_ERROR_UNLESS_KRZ(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
return m_CurrentOutputs[outputIdx].getBuffer().getSize();
}
bool CSimulatedBox::setOutputChunkSize(const size_t outputIdx, const size_t size, const bool discard)
{
OV_ERROR_UNLESS_KRF(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
return m_CurrentOutputs[outputIdx].getBuffer().setSize(size, discard);
}
uint8_t* CSimulatedBox::getOutputChunkBuffer(const size_t outputIdx)
{
OV_ERROR_UNLESS_KRN(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
return m_CurrentOutputs[outputIdx].getBuffer().getDirectPointer();
}
bool CSimulatedBox::appendOutputChunkData(const size_t outputIdx, const uint8_t* buffer, const size_t size)
{
OV_ERROR_UNLESS_KRF(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
return m_CurrentOutputs[outputIdx].getBuffer().append(buffer, size);
}
IMemoryBuffer* CSimulatedBox::getOutputChunk(const size_t outputIdx)
{
OV_ERROR_UNLESS_KRN(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
return &m_CurrentOutputs[outputIdx].getBuffer();
}
bool CSimulatedBox::markOutputAsReadyToSend(const size_t outputIdx, const uint64_t startTime, const uint64_t endTime)
{
OV_ERROR_UNLESS_KRF(outputIdx < m_CurrentOutputs.size(),
"Output index = [" << outputIdx << "] is out of range (max index = [" << m_CurrentOutputs.size() - 1 << "])",
ErrorType::OutOfBound);
if (m_chunkConsistencyChecking)
{
bool isConsistent = true;
const char* specificMessage = nullptr;
// checks chunks consistency
CIdentifier type;
m_box->getOutputType(outputIdx, type);
if (type == OV_TypeId_Stimulations)
{
if (m_LastOutputEndTimes[outputIdx] != startTime)
{
isConsistent = false;
specificMessage = "'Stimulations' streams should have continuously dated chunks";
}
}
if (m_LastOutputEndTimes[outputIdx] > endTime)
{
isConsistent = false;
specificMessage = "Current 'end time' can not be earlier than previous 'end time'";
}
if (m_LastOutputStartTimes[outputIdx] > startTime)
{
isConsistent = false;
specificMessage = "Current 'start time' can not be earlier than previous 'start time'";
}
if (!isConsistent)
{
this->getLogManager() << m_chunkConsistencyCheckingLogLevel << "Box <" << m_box->getName() << "> sends inconsistent chunk dates on output [" <<
outputIdx << "] (current chunk dates are [" << startTime << "," << endTime << "] whereas previous chunk dates were [" <<
m_LastOutputStartTimes[outputIdx] << "," << m_LastOutputEndTimes[outputIdx] << "])\n";
if (specificMessage) { this->getLogManager() << m_chunkConsistencyCheckingLogLevel << specificMessage << "\n"; }
this->getLogManager() << m_chunkConsistencyCheckingLogLevel << "Please report to box author and attach your scenario\n";
this->getLogManager() << LogLevel_Trace << "Previous warning can be disabled setting Kernel_CheckChunkConsistency to false\n";
m_chunkConsistencyCheckingLogLevel = LogLevel_Trace;
}
// sets last times
m_LastOutputStartTimes[outputIdx] = startTime;
m_LastOutputEndTimes[outputIdx] = endTime;
}
// sets start and end time
m_CurrentOutputs[outputIdx].setStartTime(std::min(startTime, endTime));
m_CurrentOutputs[outputIdx].setEndTime(std::max(startTime, endTime));
// copies chunk
m_Outputs[outputIdx].push_back(m_CurrentOutputs[outputIdx]);
// resets chunk size
m_CurrentOutputs[outputIdx].getBuffer().setSize(0, true);
return true;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,126 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkCBuffer.h"
#include <system/ovCChrono.h>
#include <vector>
#include <string>
#include <deque>
namespace OpenViBE {
namespace Kernel {
class CScheduler;
class CChunk
{
public:
CChunk() { }
CChunk(const CChunk& chunk) : m_buffer(chunk.m_buffer), m_startTime(chunk.m_startTime), m_endTime(chunk.m_endTime) { }
const CBuffer& getBuffer() const { return m_buffer; }
uint64_t getStartTime() const { return m_startTime; }
uint64_t getEndTime() const { return m_endTime; }
bool isDeprecated() const { return m_isDeprecated; }
CBuffer& getBuffer() { return m_buffer; }
bool setStartTime(const uint64_t startTime)
{
m_startTime = startTime;
return true;
}
bool setEndTime(const uint64_t endTime)
{
m_endTime = endTime;
return true;
}
bool markAsDeprecated(const bool isDeprecated)
{
m_isDeprecated = isDeprecated;
return true;
}
protected:
CBuffer m_buffer;
uint64_t m_startTime = 0;
uint64_t m_endTime = 0;
bool m_isDeprecated = false;
};
class CSimulatedBox final : public TKernelObject<IBoxIO>
{
public:
CSimulatedBox(const IKernelContext& ctx, CScheduler& scheduler);
~CSimulatedBox() override;
bool setScenarioIdentifier(const CIdentifier& scenarioID);
bool getBoxIdentifier(CIdentifier& boxId) const;
bool setBoxIdentifier(const CIdentifier& boxId);
bool initialize();
bool uninitialize();
bool processClock();
bool processInput(const size_t index, const CChunk& chunk);
bool process();
bool isReadyToProcess() const;
CString getName() const;
const IScenario& getScenario() const;
/** \name IBoxIO inputs handling */
//@{
size_t getInputChunkCount(const size_t index) const override;
bool getInputChunk(const size_t inputIdx, const size_t chunkIdx, uint64_t& startTime, uint64_t& endTime, size_t& size,
const uint8_t*& buffer) const override;
const IMemoryBuffer* getInputChunk(const size_t inputIdx, const size_t chunkIdx) const override;
uint64_t getInputChunkStartTime(const size_t inputIdx, const size_t chunkIdx) const override;
uint64_t getInputChunkEndTime(const size_t inputIdx, const size_t chunkIdx) const override;
bool markInputAsDeprecated(const size_t inputIdx, const size_t chunkIdx) override;
//@}
/** \name IBoxIO outputs handling */
//@{
size_t getOutputChunkSize(const size_t outputIdx) const override;
bool setOutputChunkSize(const size_t outputIdx, const size_t size, const bool discard = true) override;
uint8_t* getOutputChunkBuffer(const size_t outputIdx) override;
bool appendOutputChunkData(const size_t outputIdx, const uint8_t* buffer, const size_t size) override;
IMemoryBuffer* getOutputChunk(const size_t outputIdx) override;
bool markOutputAsReadyToSend(const size_t outputIdx, const uint64_t startTime, const uint64_t endTime) override;
//@}
_IsDerivedFromClass_Final_(TKernelObject<IBoxIO>, OVK_ClassId_Kernel_Player_SimulatedBox)
CScheduler& getScheduler() const { return m_scheduler; }
protected:
bool m_readyToProcess = false;
bool m_chunkConsistencyChecking = false;
ELogLevel m_chunkConsistencyCheckingLogLevel = LogLevel_Warning;
Plugins::IBoxAlgorithm* m_boxAlgorithm = nullptr;
const IScenario* m_scenario = nullptr;
const IBox* m_box = nullptr;
CScheduler& m_scheduler;
uint64_t m_lastClockActivationDate = 0;
uint64_t m_clockFrequency = 0;
uint64_t m_clockActivationStep = 0;
public:
std::vector<std::deque<CChunk>> m_Inputs;
std::vector<std::deque<CChunk>> m_Outputs;
std::vector<CChunk> m_CurrentOutputs;
std::vector<uint64_t> m_LastOutputStartTimes;
std::vector<uint64_t> m_LastOutputEndTimes;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,411 @@
#include "ovkCPluginManager.h"
#include "ovkCPluginModule.h"
#include <fs/IEntryEnumerator.h>
#include <fs/Files.h>
#include <cinttypes>
#include <cstdio>
#include <iostream>
#include <map>
#include <algorithm>
#include "../../tools/ovkSBoxProto.h"
namespace OpenViBE {
namespace Kernel {
class CPluginManagerEntryEnumeratorCallBack final : public TKernelObject<IObject>, public FS::IEntryEnumeratorCallBack
{
public:
CPluginManagerEntryEnumeratorCallBack(const IKernelContext& ctx, std::vector<IPluginModule*>& pluginModule,
std::map<Plugins::IPluginObjectDesc*, IPluginModule*>& pluginObjectDesc, bool& haveAllPluginsLoadedCorrectly)
: TKernelObject<IObject>(ctx), m_rPluginManager(ctx.getPluginManager()), m_rPluginModule(pluginModule),
m_rPluginObjectDesc(pluginObjectDesc), m_HaveAllPluginsLoadedCorrectly(haveAllPluginsLoadedCorrectly) { }
bool callback(FS::IEntryEnumerator::IEntry& entry, FS::IEntryEnumerator::IAttributes& /*attributes*/) override
{
for (auto& pluginModule : m_rPluginModule)
{
CString name;
if (!pluginModule->getFileName(name)) { return true; }
if (FS::Files::equals(entry.getName(), name.toASCIIString()))
{
OV_WARNING_K(std::string("Module [") + entry.getName() + "] has already been loaded");
return true;
}
}
IPluginModule* module = new CPluginModule(this->getKernelContext());
CString loadError;
if (!module->load(entry.getName(), &loadError))
{
delete module;
OV_WARNING_K(std::string("File [") + entry.getName() + "] is not a plugin module (error:" + loadError.toASCIIString() + ")");
m_HaveAllPluginsLoadedCorrectly = false;
return true;
}
if (!module->initialize())
{
module->uninitialize();
module->unload();
delete module;
OV_WARNING_K(std::string("Module [") + entry.getName() + "] did not initialize correctly");
m_HaveAllPluginsLoadedCorrectly = false;
return true;
}
bool pluginObjectDescAdded = false;
size_t index = 0;
size_t n = 0;
Plugins::IPluginObjectDesc* desc = nullptr;
while (module->getPluginObjectDescription(index, desc))
{
bool found = false;
for (const auto& pluginObjectDesc : m_rPluginObjectDesc)
{
if (pluginObjectDesc.first->getClassIdentifier() == desc->getClassIdentifier())
{
OV_WARNING_K("Duplicate plugin object descriptor class identifier [" + pluginObjectDesc.first->getName()
+ "] and [" + desc->getName() + "]... second one is ignored");
found = true;
break;
}
}
if (!found)
{
if (!pluginObjectDescAdded)
{
m_rPluginModule.push_back(module);
pluginObjectDescAdded = true;
}
m_rPluginObjectDesc[desc] = module;
n++;
}
index++;
desc = nullptr;
}
OV_WARNING_UNLESS_K(pluginObjectDescAdded,
std::string("No 'plugin object descriptor' found from [") + entry.getName() + "] even if it looked like a plugin module\n");
this->getLogManager() << LogLevel_Info << "Added " << n << " plugin object descriptor(s) from [" << CString(entry.getName()) << "]\n";
return true;
}
_IsDerivedFromClass_Final_(TKernelObject<IObject>, CIdentifier::undefined())
protected:
IPluginManager& m_rPluginManager;
std::vector<IPluginModule*>& m_rPluginModule;
std::map<Plugins::IPluginObjectDesc*, IPluginModule*>& m_rPluginObjectDesc;
bool& m_HaveAllPluginsLoadedCorrectly;
};
CPluginManager::~CPluginManager()
{
std::unique_lock<std::mutex> lock(m_mutex);
for (auto& pluginObjectVector : m_pluginObjects)
{
for (auto& pluginObject : pluginObjectVector.second)
{
OV_WARNING_K(
"Trying to release plugin object with class id " + pluginObject->getClassIdentifier().str() + " and plugin object descriptor "
+ pluginObjectVector.first->getName().toASCIIString() + " at plugin manager destruction time");
pluginObject->release();
}
}
m_pluginObjects.clear();
for (auto& pluginObjectDesc : m_pluginObjectDescs) { pluginObjectDesc.first->release(); }
m_pluginObjectDescs.clear();
for (auto k = m_pluginModules.begin(); k != m_pluginModules.end(); ++k)
{
this->TKernelObject<IPluginManager>::getLogManager() << LogLevel_Trace << "Releasing plugin module with class id " << (*k)->getClassIdentifier() <<
"\n";
(*k)->uninitialize();
delete (*k);
}
m_pluginModules.clear();
}
bool CPluginManager::addPluginsFromFiles(const CString& rFileNameWildCard)
{
std::unique_lock<std::mutex> lock(m_mutex);
this->getLogManager() << LogLevel_Info << "Adding plugins from [" << rFileNameWildCard << "]\n";
bool res = true;
bool haveAllPluginsLoadedCorrectly = true;
CPluginManagerEntryEnumeratorCallBack cb(this->getKernelContext(), m_pluginModules, m_pluginObjectDescs, haveAllPluginsLoadedCorrectly);
FS::IEntryEnumerator* entryEnumerator = createEntryEnumerator(cb);
std::stringstream ss(rFileNameWildCard.toASCIIString());
std::string path;
while (getline(ss, path, ';'))
{
res &= entryEnumerator->enumerate(path.c_str());
if (!res) { break; }
}
entryEnumerator->release();
// Just return res. Error handling is performed within CPluginManagerEntryEnumeratorCallBack.
return res && haveAllPluginsLoadedCorrectly;
}
bool CPluginManager::registerPluginDesc(const Plugins::IPluginObjectDesc& rPluginObjectDesc)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_pluginObjectDescs[const_cast<Plugins::IPluginObjectDesc*>(&rPluginObjectDesc)] = nullptr;
return true;
}
CIdentifier CPluginManager::getNextPluginObjectDescIdentifier(const CIdentifier& previousID) const
{
std::unique_lock<std::mutex> lock(m_mutex);
bool foundPrevious = (previousID == CIdentifier::undefined());
for (const auto& elem : m_pluginObjectDescs)
{
if (!foundPrevious) { if (elem.first->getClassIdentifier() == previousID) { foundPrevious = true; } }
else { return elem.first->getClassIdentifier(); }
}
return CIdentifier::undefined();
}
CIdentifier CPluginManager::getNextPluginObjectDescIdentifier(const CIdentifier& previousID, const CIdentifier& baseClassID) const
{
std::unique_lock<std::mutex> lock(m_mutex);
bool foundPrevious = (previousID == CIdentifier::undefined());
for (const auto& elem : m_pluginObjectDescs)
{
if (!foundPrevious) { if (elem.first->getClassIdentifier() == previousID) { foundPrevious = true; } }
else { if (elem.first->isDerivedFromClass(baseClassID)) { return elem.first->getClassIdentifier(); } }
}
return CIdentifier::undefined();
}
bool CPluginManager::canCreatePluginObject(const CIdentifier& classID)
{
std::unique_lock<std::mutex> lock(m_mutex);
// this->getLogManager() << Kernel::LogLevel_Debug << "Searching if can build plugin object\n";
return std::any_of(m_pluginObjectDescs.begin(), m_pluginObjectDescs.end(), [classID](const std::pair<Plugins::IPluginObjectDesc*, IPluginModule*>& v)
{
return v.first->getCreatedClass() == classID;
});
}
const Plugins::IPluginObjectDesc* CPluginManager::getPluginObjectDesc(const CIdentifier& classID) const
{
std::unique_lock<std::mutex> lock(m_mutex);
// this->getLogManager() << Kernel::LogLevel_Debug << "Searching plugin object descriptor\n";
for (auto& pluginObject : m_pluginObjectDescs) { if (pluginObject.first->getClassIdentifier() == classID) { return pluginObject.first; } }
this->getLogManager() << LogLevel_Debug << "Plugin object descriptor class identifier " << classID << " not found\n";
return nullptr;
}
const Plugins::IPluginObjectDesc* CPluginManager::getPluginObjectDescCreating(const CIdentifier& classID) const
{
std::unique_lock<std::mutex> lock(m_mutex);
// this->getLogManager() << Kernel::LogLevel_Debug << "Searching plugin object descriptor\n";
const auto elem = std::find_if(m_pluginObjectDescs.begin(), m_pluginObjectDescs.end(),
[classID](const std::pair<Plugins::IPluginObjectDesc*, IPluginModule*>& v) { return v.first->getCreatedClass() == classID; });
if (elem != m_pluginObjectDescs.end()) { return elem->first; }
this->getLogManager() << LogLevel_Debug << "Plugin object descriptor class identifier " << classID << " not found\n";
return nullptr;
}
CIdentifier CPluginManager::getPluginObjectHashValue(const CIdentifier& classID) const
{
// std::unique_lock<std::mutex> lock(m_mutex);
const Plugins::IPluginObjectDesc* pluginObjectDesc = this->getPluginObjectDescCreating(classID);
const Plugins::IBoxAlgorithmDesc* boxAlgorithmDesc = dynamic_cast<const Plugins::IBoxAlgorithmDesc*>(pluginObjectDesc);
if (boxAlgorithmDesc)
{
SBoxProto prototype(getKernelContext().getTypeManager());
boxAlgorithmDesc->getBoxPrototype(prototype);
return prototype.m_hash;
}
return CIdentifier::undefined();
}
CIdentifier CPluginManager::getPluginObjectHashValue(const Plugins::IBoxAlgorithmDesc& boxAlgorithmDesc) const
{
std::unique_lock<std::mutex> lock(m_mutex);
SBoxProto prototype(getKernelContext().getTypeManager());
boxAlgorithmDesc.getBoxPrototype(prototype);
return prototype.m_hash;
}
bool CPluginManager::isPluginObjectFlaggedAsDeprecated(const CIdentifier& classID) const
{
// std::unique_lock<std::mutex> lock(m_mutex);
const Plugins::IPluginObjectDesc* pluginObjectDesc = this->getPluginObjectDescCreating(classID);
const Plugins::IBoxAlgorithmDesc* boxAlgorithmDesc = dynamic_cast<const Plugins::IBoxAlgorithmDesc*>(pluginObjectDesc);
if (boxAlgorithmDesc)
{
SBoxProto prototype(getKernelContext().getTypeManager());
boxAlgorithmDesc->getBoxPrototype(prototype);
return prototype.m_isDeprecated;
}
return false;
}
Plugins::IPluginObject* CPluginManager::createPluginObject(const CIdentifier& classID)
{
return createPluginObjectT<Plugins::IPluginObject, Plugins::IPluginObjectDesc>(classID, nullptr);
}
bool CPluginManager::releasePluginObject(Plugins::IPluginObject* pluginObject)
{
this->getLogManager() << LogLevel_Debug << "Releasing plugin object\n";
OV_ERROR_UNLESS_KRF(pluginObject, "Plugin object value is null", Kernel::ErrorType::BadProcessing);
{
std::unique_lock<std::mutex> lock(m_mutex);
for (auto& elem : m_pluginObjects)
{
auto pluginObjectIt = std::find(elem.second.begin(), elem.second.end(), pluginObject);
if (pluginObjectIt != elem.second.end())
{
elem.second.erase(pluginObjectIt);
pluginObject->release();
return true;
}
}
}
OV_ERROR_KRF("Plugin object has not been created by this plugin manager (class id was " << pluginObject->getClassIdentifier().str() << ")",
ErrorType::ResourceNotFound);
}
Plugins::IAlgorithm* CPluginManager::createAlgorithm(const CIdentifier& classID, const Plugins::IAlgorithmDesc** algorithmDesc)
{
return createPluginObjectT<Plugins::IAlgorithm, Plugins::IAlgorithmDesc>(classID, algorithmDesc);
}
Plugins::IAlgorithm* CPluginManager::createAlgorithm(const Plugins::IAlgorithmDesc& algorithmDesc)
{
Plugins::IAlgorithmDesc* desc = const_cast<Plugins::IAlgorithmDesc*>(&algorithmDesc);
Plugins::IPluginObject* pluginObject = desc->create();
OV_ERROR_UNLESS_KRN(pluginObject,
"Could not create plugin object from " << algorithmDesc.getName() << " plugin object descriptor",
ErrorType::BadResourceCreation);
Plugins::IAlgorithmDesc* pluginObjectDescT = dynamic_cast<Plugins::IAlgorithmDesc*>(desc);
Plugins::IAlgorithm* pluginObjectT = dynamic_cast<Plugins::IAlgorithm*>(pluginObject);
OV_ERROR_UNLESS_KRN(pluginObjectDescT && pluginObjectT,
"Could not downcast plugin object and/or plugin object descriptor for " << algorithmDesc.getName() << " plugin object descriptor",
ErrorType::BadResourceCreation);
{
std::unique_lock<std::mutex> lock(m_mutex);
m_pluginObjects[pluginObjectDescT].push_back(pluginObjectT);
}
return pluginObjectT;
}
Plugins::IBoxAlgorithm* CPluginManager::createBoxAlgorithm(const CIdentifier& classID, const Plugins::IBoxAlgorithmDesc** boxAlgorithmDesc)
{
return createPluginObjectT<Plugins::IBoxAlgorithm, Plugins::IBoxAlgorithmDesc>(classID, boxAlgorithmDesc);
}
template <class IPluginObjectT, class IPluginObjectDescT>
IPluginObjectT* CPluginManager::createPluginObjectT(const CIdentifier& classID, const IPluginObjectDescT** ppPluginObjectDescT)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (ppPluginObjectDescT) { *ppPluginObjectDescT = nullptr; }
CIdentifier substitutionTokenID;
char substitutionTokenName[1024];
const uint64_t srcClassID = classID.id();
uint64_t dstClassID = srcClassID;
sprintf(substitutionTokenName, "Kernel_PluginSubstitution_%0" PRIx64, srcClassID);
if ((substitutionTokenID = this->getConfigurationManager().lookUpConfigurationTokenIdentifier(substitutionTokenName)) !=
CIdentifier::undefined())
{
CString value = this->getConfigurationManager().getConfigurationTokenValue(substitutionTokenID);
value = this->getConfigurationManager().expand(value);
try { dstClassID = std::stoull(value.toASCIIString(), nullptr, 16); }
catch (const std::invalid_argument& exception)
{
OV_ERROR_KRN("Received exception while converting class identifier from string to number: " << exception.what(), Kernel::ErrorType::BadArgument);
}
catch (const std::out_of_range& exception)
{
OV_ERROR_KRN("Received exception while converting class identifier from string to number: " << exception.what(), Kernel::ErrorType::OutOfBound);
}
}
if (dstClassID != srcClassID)
{
this->getLogManager() << LogLevel_Trace << "Substituting plugin class identifier " << CIdentifier(srcClassID) <<
" with new class identifier " << CIdentifier(dstClassID) << "\n";
}
else
{
this->getLogManager() << LogLevel_Debug << "Not substitute plugin found for class identifier " << CIdentifier(srcClassID) <<
" (configuration token name was " << CString(substitutionTokenName) << ")\n";
}
Plugins::IPluginObjectDesc* pod = nullptr;
for (auto i = m_pluginObjectDescs.begin(); i != m_pluginObjectDescs.end(); ++i)
{
if (i->first->getCreatedClass() == CIdentifier(dstClassID)) { pod = i->first; }
}
OV_ERROR_UNLESS_KRN(pod,
"Did not find the plugin object descriptor with requested class identifier " << CIdentifier(srcClassID).str() <<
" in registered plugin object descriptors",
ErrorType::BadResourceCreation);
Plugins::IPluginObject* pluginObject = pod->create();
OV_ERROR_UNLESS_KRN(pluginObject,
"Could not create plugin object from " << pod->getName() << " plugin object descriptor",
ErrorType::BadResourceCreation);
IPluginObjectDescT* pluginObjectDescT = dynamic_cast<IPluginObjectDescT*>(pod);
IPluginObjectT* pluginObjectT = dynamic_cast<IPluginObjectT*>(pluginObject);
OV_ERROR_UNLESS_KRN(pluginObjectDescT && pluginObjectT,
"Could not downcast plugin object and/or plugin object descriptor for " << pod->getName() << " plugin object descriptor",
ErrorType::BadResourceCreation);
if (ppPluginObjectDescT) { *ppPluginObjectDescT = pluginObjectDescT; }
m_pluginObjects[pluginObjectDescT].push_back(pluginObjectT);
return pluginObjectT;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,47 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <vector>
#include <map>
#include <mutex>
namespace OpenViBE {
namespace Kernel {
class CPluginManager final : public TKernelObject<IPluginManager>
{
public:
explicit CPluginManager(const IKernelContext& ctx) : TKernelObject<IPluginManager>(ctx) {}
~CPluginManager() override;
bool addPluginsFromFiles(const CString& rFileNameWildCard) override;
bool registerPluginDesc(const Plugins::IPluginObjectDesc& rPluginObjectDesc) override;
CIdentifier getNextPluginObjectDescIdentifier(const CIdentifier& previousID) const override;
CIdentifier getNextPluginObjectDescIdentifier(const CIdentifier& previousID, const CIdentifier& baseClassID) const override;
bool canCreatePluginObject(const CIdentifier& classID) override;
const Plugins::IPluginObjectDesc* getPluginObjectDesc(const CIdentifier& classID) const override;
const Plugins::IPluginObjectDesc* getPluginObjectDescCreating(const CIdentifier& classID) const override;
CIdentifier getPluginObjectHashValue(const CIdentifier& classID) const override;
CIdentifier getPluginObjectHashValue(const Plugins::IBoxAlgorithmDesc& boxAlgorithmDesc) const override;
bool isPluginObjectFlaggedAsDeprecated(const CIdentifier& classID) const override;
Plugins::IPluginObject* createPluginObject(const CIdentifier& classID) override;
bool releasePluginObject(Plugins::IPluginObject* pluginObject) override;
Plugins::IAlgorithm* createAlgorithm(const CIdentifier& classID, const Plugins::IAlgorithmDesc** algorithmDesc) override;
Plugins::IAlgorithm* createAlgorithm(const Plugins::IAlgorithmDesc& algorithmDesc) override;
Plugins::IBoxAlgorithm* createBoxAlgorithm(const CIdentifier& classID, const Plugins::IBoxAlgorithmDesc** boxAlgorithmDesc) override;
_IsDerivedFromClass_Final_(TKernelObject<IPluginManager>, OVK_ClassId_Kernel_Plugins_PluginManager)
protected:
template <class TPluginObject, class TPluginObjectDesc>
TPluginObject* createPluginObjectT(const CIdentifier& classID, const TPluginObjectDesc** ppPluginObjectDescT);
std::vector<IPluginModule*> m_pluginModules;
std::map<Plugins::IPluginObjectDesc*, IPluginModule*> m_pluginObjectDescs;
std::map<Plugins::IPluginObjectDesc*, std::vector<Plugins::IPluginObject*>> m_pluginObjects;
mutable std::mutex m_mutex;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,336 @@
#include "ovkCPluginModule.h"
#include <map>
#include <vector>
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <dlfcn.h>
#elif defined TARGET_OS_Windows
#include <windows.h>
#else
#endif
namespace OpenViBE {
namespace Kernel {
class CPluginModuleBase : public TKernelObject<IPluginModule>
{
public:
explicit CPluginModuleBase(const IKernelContext& ctx)
: TKernelObject<IPluginModule>(ctx), m_onInitializeCB(nullptr), m_onGetPluginObjectDescCB(nullptr), m_onUninitializeCB(nullptr) {}
~CPluginModuleBase() override { }
bool initialize() override;
bool getPluginObjectDescription(size_t index, Plugins::IPluginObjectDesc*& pluginObjectDesc) override;
bool uninitialize() override;
bool getFileName(CString& fileName) const override;
_IsDerivedFromClass_Final_(TKernelObject<IPluginModule>, CIdentifier::undefined())
protected:
virtual bool isOpen() const = 0;
std::vector<Plugins::IPluginObjectDesc*> m_pluginObjectDescs;
CString m_filename;
bool m_gotDesc = false;
bool (*m_onInitializeCB)(const IPluginModuleContext&);
bool (*m_onGetPluginObjectDescCB)(const IPluginModuleContext&, size_t, Plugins::IPluginObjectDesc*&);
bool (*m_onUninitializeCB)(const IPluginModuleContext&);
};
namespace {
class CPluginModuleContext final : public TKernelObject<IPluginModuleContext>
{
public:
explicit CPluginModuleContext(const IKernelContext& ctx)
: TKernelObject<IPluginModuleContext>(ctx), m_logManager(ctx.getLogManager()), m_typeManager(ctx.getTypeManager()),
m_scenarioManager(ctx.getScenarioManager()) { }
ILogManager& getLogManager() const override { return m_logManager; }
ITypeManager& getTypeManager() const override { return m_typeManager; }
IScenarioManager& getScenarioManager() const override { return m_scenarioManager; }
_IsDerivedFromClass_Final_(TKernelObject<IPluginModuleContext>, OVK_ClassId_Kernel_Plugins_PluginModuleContext)
protected:
ILogManager& m_logManager;
ITypeManager& m_typeManager;
IScenarioManager& m_scenarioManager;
};
} // namespace
bool CPluginModuleBase::initialize()
{
if (!isOpen()) { return false; }
if (!m_onInitializeCB) { return true; }
return m_onInitializeCB(CPluginModuleContext(getKernelContext()));
}
bool CPluginModuleBase::getPluginObjectDescription(const size_t index, Plugins::IPluginObjectDesc*& pluginObjectDesc)
{
if (!m_gotDesc)
{
if (!isOpen() || !m_onGetPluginObjectDescCB) { return false; }
size_t idx = 0;
Plugins::IPluginObjectDesc* pod = nullptr;
while (m_onGetPluginObjectDescCB(CPluginModuleContext(getKernelContext()), idx, pod))
{
if (pod) { m_pluginObjectDescs.push_back(pod); }
idx++;
}
m_gotDesc = true;
}
if (index >= m_pluginObjectDescs.size())
{
pluginObjectDesc = nullptr;
return false;
}
pluginObjectDesc = m_pluginObjectDescs[index];
return true;
}
bool CPluginModuleBase::uninitialize()
{
if (!isOpen()) { return false; }
if (!m_onUninitializeCB) { return true; }
return m_onUninitializeCB(CPluginModuleContext(getKernelContext()));
}
bool CPluginModuleBase::getFileName(CString& fileName) const
{
if (!isOpen()) { return false; }
fileName = m_filename;
return true;
}
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
class CPluginModuleLinux : public CPluginModuleBase
{
public:
CPluginModuleLinux(const IKernelContext& ctx);
virtual bool load(const CString& filename, CString* pError);
virtual bool unload(CString* pError);
virtual bool isOpen() const;
protected:
void* m_fileHandle;
};
#elif defined TARGET_OS_Windows
class CPluginModuleWindows final : public CPluginModuleBase
{
public:
explicit CPluginModuleWindows(const IKernelContext& ctx);
bool load(const CString& filename, CString* pError) override;
bool unload(CString* pError) override;
protected:
bool isOpen() const override { return m_fileHandle != nullptr; }
HMODULE m_fileHandle;
private:
static CString getLastErrorMessageString();
};
#else
class CPluginModuleDummy : public CPluginModuleBase
{
public:
explicit CPluginModuleDummy(const IKernelContext& ctx);
virtual bool load(const CString& filename, CString* pError);
virtual bool unload(CString* pError);
protected:
virtual bool isOpen() const;
};
#endif
//
//___________________________________________________________________//
// //
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
CPluginModuleLinux::CPluginModuleLinux(const IKernelContext& ctx) :CPluginModuleBase(ctx),m_fileHandle(NULL) { }
bool CPluginModuleLinux::load(const CString& filename, CString* pError)
{
if(m_fileHandle)
{
if(pError) *pError="plugin module already loaded";
return false;
}
// m_fileHandle=dlopen(filename, RTLD_NOW|RTLD_LOCAL);
#if defined OV_LOCAL_SYMBOLS
m_fileHandle = dlopen(filename, RTLD_LAZY|RTLD_LOCAL);
#else
m_fileHandle = dlopen(filename, RTLD_LAZY|RTLD_GLOBAL);
#endif
if(!m_fileHandle)
{
if(pError) *pError=dlerror();
return false;
}
m_onInitializeCB=(bool (*)(const IPluginModuleContext&))dlsym(m_fileHandle, "onInitialize");
m_onUninitializeCB=(bool (*)(const IPluginModuleContext&))dlsym(m_fileHandle, "onUninitialize");
m_onGetPluginObjectDescCB=(bool (*)(const IPluginModuleContext&, size_t, Plugins::IPluginObjectDesc*&))dlsym(m_fileHandle, "onGetPluginObjectDescription");
if(!m_onGetPluginObjectDescCB)
{
if(pError) *pError=dlerror();
dlclose(m_fileHandle);
m_fileHandle=NULL;
m_onInitializeCB=NULL;
m_onUninitializeCB=NULL;
m_onGetPluginObjectDescCB=NULL;
return false;
}
m_filename=filename;
return true;
}
bool CPluginModuleLinux::unload(CString* error)
{
if(!m_fileHandle)
{
if (error) { *error = "no plugin module currently loaded"; }
return false;
}
dlclose(m_fileHandle);
m_fileHandle = NULL;
m_onInitializeCB = NULL;
m_onUninitializeCB = NULL;
m_onGetPluginObjectDescCB = NULL;
return true;
}
bool CPluginModuleLinux::isOpen() const { return m_fileHandle != nullptr; }
#elif defined TARGET_OS_Windows
CPluginModuleWindows::CPluginModuleWindows(const IKernelContext& ctx) : CPluginModuleBase(ctx), m_fileHandle(nullptr) {}
bool CPluginModuleWindows::load(const CString& filename, CString* pError)
{
if (m_fileHandle)
{
if (pError) { *pError = "plugin module already loaded"; }
return false;
}
m_fileHandle = LoadLibrary(filename);
if (!m_fileHandle)
{
if (pError) { *pError = this->getLastErrorMessageString(); }
return false;
}
m_onInitializeCB = reinterpret_cast<bool (*)(const IPluginModuleContext&)>(GetProcAddress(m_fileHandle, "onInitialize"));
m_onUninitializeCB = reinterpret_cast<bool (*)(const IPluginModuleContext&)>(GetProcAddress(m_fileHandle, "onUninitialize"));
m_onGetPluginObjectDescCB = reinterpret_cast<bool (*)(const IPluginModuleContext&, size_t, Plugins::IPluginObjectDesc*&)>(GetProcAddress(
m_fileHandle, "onGetPluginObjectDescription"));
if (!m_onGetPluginObjectDescCB)
{
if (pError) { *pError = this->getLastErrorMessageString(); }
FreeLibrary(m_fileHandle);
m_fileHandle = nullptr;
m_onInitializeCB = nullptr;
m_onGetPluginObjectDescCB = nullptr;
m_onUninitializeCB = nullptr;
return false;
}
m_filename = filename;
return true;
}
bool CPluginModuleWindows::unload(CString* pError)
{
if (!m_fileHandle)
{
if (pError) { *pError = "no plugin module currently loaded"; }
return false;
}
FreeLibrary(m_fileHandle);
m_fileHandle = nullptr;
m_onInitializeCB = nullptr;
m_onGetPluginObjectDescCB = nullptr;
m_onUninitializeCB = nullptr;
return true;
}
CString CPluginModuleWindows::getLastErrorMessageString()
{
CString res;
char* buffer = nullptr;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), 0,
reinterpret_cast<LPTSTR>(&buffer), 0, nullptr);
if (buffer)
{
const size_t length = strlen(buffer);
for (size_t i = 0; i < length; ++i) { if (buffer[i] == '\n' || buffer[i] == '\r') { buffer[i] = ' '; } }
res = buffer;
}
LocalFree(LPVOID(buffer));
return res;
}
#else
#endif
//___________________________________________________________________//
// //
CPluginModule::CPluginModule(const IKernelContext& ctx)
: TKernelObject<IPluginModule>(ctx)
{
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
m_impl=new CPluginModuleLinux(getKernelContext());
#elif defined TARGET_OS_Windows
m_impl = new CPluginModuleWindows(getKernelContext());
#else
#endif
}
CPluginModule::~CPluginModule() { delete m_impl; }
bool CPluginModule::load(const CString& filename, CString* error) { return !m_impl ? false : m_impl->load(filename, error); }
bool CPluginModule::unload(CString* error) { return !m_impl ? false : m_impl->unload(error); }
bool CPluginModule::initialize() { return !m_impl ? false : m_impl->initialize(); }
bool CPluginModule::getPluginObjectDescription(const size_t index, Plugins::IPluginObjectDesc*& desc)
{
return !m_impl ? false : m_impl->getPluginObjectDescription(index, desc);
}
bool CPluginModule::uninitialize() { return !m_impl ? false : m_impl->uninitialize(); }
bool CPluginModule::getFileName(CString& rFileName) const { return !m_impl ? false : m_impl->getFileName(rFileName); }
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,29 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Kernel {
class CPluginModule final : public TKernelObject<IPluginModule>
{
public:
explicit CPluginModule(const IKernelContext& ctx);
~CPluginModule() override;
bool load(const CString& filename, CString* error) override;
bool unload(CString* error) override;
bool getFileName(CString& rFileName) const override;
bool initialize() override;
bool getPluginObjectDescription(size_t index, Plugins::IPluginObjectDesc*& desc) override;
bool uninitialize() override;
_IsDerivedFromClass_Final_(TKernelObject<IPluginModule>, OVK_ClassId_Kernel_Plugins_PluginModule)
protected:
IPluginModule* m_impl = nullptr;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,40 @@
#pragma once
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CBoxListenerContext final : public TKernelObject<IBoxListenerContext>
{
public:
CBoxListenerContext(const IKernelContext& ctx, IBox& box, const size_t index)
: TKernelObject<IBoxListenerContext>(ctx), m_box(box), m_idx(index) { }
IAlgorithmManager& getAlgorithmManager() const override { return this->getKernelContext().getAlgorithmManager(); }
IPlayerManager& getPlayerManager() const override { return this->getKernelContext().getPlayerManager(); }
IPluginManager& getPluginManager() const override { return this->getKernelContext().getPluginManager(); }
IMetaboxManager& getMetaboxManager() const override { return this->getKernelContext().getMetaboxManager(); }
IScenarioManager& getScenarioManager() const override { return this->getKernelContext().getScenarioManager(); }
ITypeManager& getTypeManager() const override { return this->getKernelContext().getTypeManager(); }
ILogManager& getLogManager() const override { return this->getKernelContext().getLogManager(); }
IErrorManager& getErrorManager() const override { return this->getKernelContext().getErrorManager(); }
IConfigurationManager& getConfigurationManager() const override { return this->getKernelContext().getConfigurationManager(); }
IBox& getBox() const override { return m_box; }
IScenario& getScenario() const override
{
OV_FATAL("Getting scenario from box listener context is not yet implemented", Kernel::ErrorType::NotImplemented, this->getKernelContext().getLogManager());
}
size_t getIndex() const override { return m_idx; }
_IsDerivedFromClass_Final_(TKernelObject<IBoxListenerContext>, OVK_ClassId_Kernel_Scenario_BoxListenerContext)
private:
IBox& m_box;
size_t m_idx = 0;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,84 @@
#include "ovkCBoxProto.h"
namespace OpenViBE {
namespace Kernel {
bool CBoxProto::addInput(const CString& name, const CIdentifier& typeID, const CIdentifier& id, const bool notify)
{
if (!m_box.addInput(name, typeID, id, notify)) { return false; }
const std::string tmp = std::to_string(m_box.getInputCount());
const CString buffer(tmp.c_str());
if (m_box.hasAttribute(OV_AttributeId_Box_InitialInputCount)) { m_box.setAttributeValue(OV_AttributeId_Box_InitialInputCount, buffer); }
else { m_box.addAttribute(OV_AttributeId_Box_InitialInputCount, buffer); }
return true;
}
bool CBoxProto::addOutput(const CString& name, const CIdentifier& typeID, const CIdentifier& id, const bool notify)
{
if (!m_box.addOutput(name, typeID, id, notify)) { return false; }
const std::string tmp = std::to_string(m_box.getOutputCount());
const CString buffer(tmp.c_str());
if (m_box.hasAttribute(OV_AttributeId_Box_InitialOutputCount)) { m_box.setAttributeValue(OV_AttributeId_Box_InitialOutputCount, buffer); }
else { m_box.addAttribute(OV_AttributeId_Box_InitialOutputCount, buffer); }
return true;
}
bool CBoxProto::addSetting(const CString& name, const CIdentifier& typeID, const CString& value, const bool modifiable,
const CIdentifier& id, const bool notify)
{
if (!m_box.addSetting(name, typeID, value, size_t(-1), modifiable, id, notify)) { return false; }
const std::string tmp = std::to_string(m_box.getSettingCount());
const CString buffer(tmp.c_str());
if (m_box.hasAttribute(OV_AttributeId_Box_InitialSettingCount)) { m_box.setAttributeValue(OV_AttributeId_Box_InitialSettingCount, buffer); }
else { m_box.addAttribute(OV_AttributeId_Box_InitialSettingCount, buffer); }
return true;
}
/*
size_t CBoxProto::addSetting(const CString& name, const CIdentifier& typeID, const CString& sDefaultValue, const bool bModifiable)
{
addSetting(name, typeID, sDefaultValue);
size_t lastSetting = m_box.getSettingCount();
m_box.setSettingMod(lastSetting, bModifiable);
return true;
}
/*/
bool CBoxProto::addFlag(const EBoxFlag boxFlag)
{
switch (boxFlag)
{
case BoxFlag_CanAddInput: m_box.addAttribute(OV_AttributeId_Box_FlagCanAddInput, "");
break;
case BoxFlag_CanModifyInput: m_box.addAttribute(OV_AttributeId_Box_FlagCanModifyInput, "");
break;
case BoxFlag_CanAddOutput: m_box.addAttribute(OV_AttributeId_Box_FlagCanAddOutput, "");
break;
case BoxFlag_CanModifyOutput: m_box.addAttribute(OV_AttributeId_Box_FlagCanModifyOutput, "");
break;
case BoxFlag_CanAddSetting: m_box.addAttribute(OV_AttributeId_Box_FlagCanAddSetting, "");
break;
case BoxFlag_CanModifySetting: m_box.addAttribute(OV_AttributeId_Box_FlagCanModifySetting, "");
break;
case BoxFlag_ManualUpdate: m_box.addAttribute(OV_AttributeId_Box_FlagNeedsManualUpdate, "");
break;
case BoxFlag_IsDeprecated: break;
}
return true;
}
bool CBoxProto::addFlag(const CIdentifier& flagID)
{
const uint64_t value = getKernelContext().getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_BoxAlgorithmFlag, flagID.toString());
if (value == CIdentifier::undefined().id()) { return false; }
m_box.addAttribute(flagID, "");
return true;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,35 @@
#pragma once
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CBoxProto : public TKernelObject<IBoxProto>
{
public:
CBoxProto(const IKernelContext& ctx, IBox& box) : TKernelObject<IBoxProto>(ctx), m_box(box) {}
~CBoxProto() override = default;
bool addInput(const CString& name, const CIdentifier& typeID, const CIdentifier& id = CIdentifier::undefined(), const bool notify = true) override;
bool addOutput(const CString& name, const CIdentifier& typeID, const CIdentifier& id = CIdentifier::undefined(), const bool notify = true) override;
//virtual bool addSetting(const CString& name, const CIdentifier& typeID, const CString& value);
bool addSetting(const CString& name, const CIdentifier& typeID, const CString& value,
const bool modifiable = false, const CIdentifier& id = CIdentifier::undefined(), const bool notify = true) override;
bool addFlag(const EBoxFlag boxFlag) override;
bool addFlag(const CIdentifier& flagID) override;
bool addInputSupport(const CIdentifier& typeID) override { return m_box.addInputSupport(typeID); }
bool addOutputSupport(const CIdentifier& typeID) override { return m_box.addOutputSupport(typeID); }
_IsDerivedFromClass_Final_(TKernelObject<IBoxProto>, OVK_ClassId_Kernel_Scenario_BoxProto)
protected:
IBox& m_box;
private:
CBoxProto() = delete;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,296 @@
#include "../ovkTKernelObject.h"
#include "ovkCScenario.h"
#include "ovkCBoxUpdater.h"
#include "ovkTBox.hpp"
namespace OpenViBE {
namespace Kernel {
const std::array<CIdentifier, 10> CBoxUpdater::UPDATABLE_ATTRIBUTES = {
OV_AttributeId_Box_InitialPrototypeHashValue,
OV_AttributeId_Box_InitialInputCount,
OV_AttributeId_Box_InitialOutputCount,
OV_AttributeId_Box_InitialSettingCount,
OV_AttributeId_Box_FlagCanAddInput,
OV_AttributeId_Box_FlagCanModifyInput,
OV_AttributeId_Box_FlagCanAddOutput,
OV_AttributeId_Box_FlagCanModifyOutput,
OV_AttributeId_Box_FlagCanAddSetting,
OV_AttributeId_Box_FlagCanModifySetting
};
CBoxUpdater::CBoxUpdater(CScenario& scenario, IBox* box)
: TKernelObject<IKernelObject>(scenario.getKernelContext()), m_scenario(&scenario), m_sourceBox(box)
{
m_originalToUpdatedCorrespondence[Input] = std::map<size_t, size_t>();
m_originalToUpdatedCorrespondence[Output] = std::map<size_t, size_t>();
m_originalToUpdatedCorrespondence[Setting] = std::map<size_t, size_t>();
}
CBoxUpdater::~CBoxUpdater()
{
if (!m_kernelBox || !m_updatedBox) { return; }
if (m_kernelBox->getAlgorithmClassIdentifier() != OVP_ClassId_BoxAlgorithm_Metabox)
{
// do not manage destruction of metaboxes (done by metabox manager)
delete m_kernelBox;
}
delete m_updatedBox;
}
bool CBoxUpdater::initialize()
{
// initialize kernel box reference
if (m_sourceBox->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
{
const CString metaboxID = m_sourceBox->getAttributeValue(OVP_AttributeId_Metabox_ID);
OV_ERROR_UNLESS_KRF(metaboxID != CString(""), "Failed to find metabox with id " << metaboxID, Kernel::ErrorType::BadCall);
CIdentifier metaboxId;
metaboxId.fromString(metaboxID);
const CString path(this->getKernelContext().getMetaboxManager().getMetaboxFilePath(metaboxId));
OV_ERROR_UNLESS_KRF(path != CString(""), "Metabox scenario is not available for " << m_sourceBox->getName(), Kernel::ErrorType::BadCall);
// We are going to copy the template scenario, flatten it and then copy all
// Note that copy constructor for IScenario does not exist
CIdentifier templateID;
this->getKernelContext().getScenarioManager().importScenarioFromFile(templateID, OV_ScenarioImportContext_SchedulerMetaboxImport, path);
CScenario* instance = dynamic_cast<CScenario*>(&(this->getKernelContext().getScenarioManager().getScenario(templateID)));
instance->setAlgorithmClassIdentifier(OVP_ClassId_BoxAlgorithm_Metabox);
m_kernelBox = instance;
}
else
{
CBox* kernelBox = new CBox(m_scenario->getKernelContext());
kernelBox->initializeFromAlgorithmClassIdentifierNoInit(m_sourceBox->getAlgorithmClassIdentifier());
m_kernelBox = kernelBox;
}
// initialize updated box
m_updatedBox = new CBox(m_scenario->getKernelContext());
m_updatedBox->setIdentifier(m_sourceBox->getIdentifier());
m_updatedBox->setName(m_sourceBox->getName());
if (m_sourceBox->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
{
m_updatedBox->setAttributeValue(OV_AttributeId_Box_InitialPrototypeHashValue, m_kernelBox->getAttributeValue(OV_AttributeId_Scenario_MetaboxHash));
}
// initialize updated box attribute to kernel ones
CIdentifier attributeIdentifier;
while ((attributeIdentifier = m_kernelBox->getNextAttributeIdentifier(attributeIdentifier)) != CIdentifier::undefined())
{
CString attributeValue = m_kernelBox->getAttributeValue(attributeIdentifier);
m_updatedBox->addAttribute(attributeIdentifier, attributeValue);
}
// initialize supported types to kernel ones
if (m_sourceBox->getAlgorithmClassIdentifier() != OVP_ClassId_BoxAlgorithm_Metabox)
{
m_updatedBox->setSupportTypeFromAlgorithmIdentifier(m_kernelBox->getAlgorithmClassIdentifier());
}
// should not be done before adding IO elements so the box listener is never called
// updatedBox->setAlgorithmClassIdentifier(kernelBox->getAlgorithmClassIdentifier());
m_initialized = true;
m_isUpdateRequired = false;
const bool isHashDifferent = m_scenario->isBoxOutdated(m_sourceBox->getIdentifier());
if (this->flaggedForManualUpdate())
{
m_isUpdateRequired = isHashDifferent;
return true;
}
if (isHashDifferent)
{
m_isUpdateRequired |= this->updateInterfacors(Input);
m_isUpdateRequired |= this->updateInterfacors(Output);
m_isUpdateRequired |= this->updateInterfacors(Setting);
m_isUpdateRequired |= this->checkForSupportedTypesToBeUpdated();
m_isUpdateRequired |= this->checkForSupportedIOSAttributesToBeUpdated();
}
if (m_sourceBox->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox) { m_isUpdateRequired |= isHashDifferent; }
return true;
}
bool CBoxUpdater::checkForSupportedTypesToBeUpdated() const
{
//check for supported inputs diff
for (auto& type : m_sourceBox->getInputSupportTypes()) { if (!m_kernelBox->hasInputSupport(type)) { return true; } }
for (auto& type : m_kernelBox->getInputSupportTypes()) { if (!m_sourceBox->hasInputSupport(type)) { return true; } }
//check for supported outputs diff
for (auto& type : m_sourceBox->getOutputSupportTypes()) { if (!m_kernelBox->hasOutputSupport(type)) { return true; } }
for (auto& type : m_kernelBox->getOutputSupportTypes()) { if (!m_sourceBox->hasOutputSupport(type)) { return true; } }
return false;
}
bool CBoxUpdater::checkForSupportedIOSAttributesToBeUpdated() const
{
// check for attributes
for (auto& attr : UPDATABLE_ATTRIBUTES)
{
if ((m_sourceBox->hasAttribute(attr) && !m_kernelBox->hasAttribute(attr))
|| (!m_sourceBox->hasAttribute(attr) && m_kernelBox->hasAttribute(attr))) { return true; }
}
return false;
}
bool CBoxUpdater::updateInterfacors(const EBoxInterfacorType interfacorType)
{
std::vector<InterfacorRequest> interfacors;
bool updated = false;
// First add and optionally modify all requests from the Kernel prototype
size_t index = 0;
while (index < m_kernelBox->getInterfacorCount(interfacorType))
{
CIdentifier kTypeIdentifier;
CIdentifier kIdentifier;
CString kName;
m_kernelBox->getInterfacorType(interfacorType, index, kTypeIdentifier);
m_kernelBox->getInterfacorIdentifier(interfacorType, index, kIdentifier);
m_kernelBox->getInterfacorName(interfacorType, index, kName);
InterfacorRequest request;
request.index = index;
request.identifier = kIdentifier;
request.name = kName;
request.typeID = kTypeIdentifier;
request.toBeRemoved = false;
if (interfacorType == Setting)
{
CString defaultValue;
bool modifiable;
m_kernelBox->getSettingDefaultValue(index, defaultValue);
m_kernelBox->getSettingMod(index, modifiable);
request.defaultValue = defaultValue;
request.value = defaultValue;
request.modifiability = modifiable;
}
auto indexInBox = getInterfacorIndex(interfacorType, *m_sourceBox, kTypeIdentifier, kIdentifier, kName);
if (indexInBox != size_t(-1))
{
CIdentifier identifier;
CString name;
m_sourceBox->getInterfacorIdentifier(interfacorType, indexInBox, identifier);
m_sourceBox->getInterfacorName(interfacorType, indexInBox, name);
updated |= (identifier == kIdentifier && name != kName) || (identifier != kIdentifier && name == kName);
m_originalToUpdatedCorrespondence[interfacorType][indexInBox] = index;
// For settings, we need to give them the value from the original box
if (interfacorType == Setting)
{
CString valueInBox;
m_sourceBox->getSettingValue(indexInBox, valueInBox);
request.value = valueInBox;
}
}
else
{
// try to modify the type in the kernel proto to adjust to the inputs
updated = true;
}
interfacors.push_back(request);
++index;
}
// Now go throught the inputs we have not yet associated and decide what to do with them
// As we currently only support boxes with fixed amount of inputs/outputs this always means that the
// I/O is redundant
index = 0;
while (index < m_sourceBox->getInterfacorCount(interfacorType))
{
// Skip if the input was handled in the previous step
if (m_originalToUpdatedCorrespondence.at(interfacorType).find(index) != m_originalToUpdatedCorrespondence.at(interfacorType).end())
{
++index;
continue;
}
CIdentifier sTypeIdentifier;
CIdentifier sIdentifier;
CString name;
m_sourceBox->getInterfacorType(interfacorType, index, sTypeIdentifier);
m_sourceBox->getInterfacorIdentifier(interfacorType, index, sIdentifier);
m_sourceBox->getInterfacorName(interfacorType, index, name);
InterfacorRequest request;
request.index = index;
request.identifier = sIdentifier;
request.name = name;
request.typeID = sTypeIdentifier;
request.toBeRemoved = true;
if (interfacorType == Setting)
{
CString defaultValue;
CString value;
bool modifiable;
m_sourceBox->getSettingDefaultValue(index, defaultValue);
m_sourceBox->getSettingValue(index, value);
m_sourceBox->getSettingMod(index, modifiable);
request.defaultValue = defaultValue;
request.value = value;
request.modifiability = modifiable;
}
updated = true;
interfacors.push_back(request);
m_originalToUpdatedCorrespondence[interfacorType][index] = interfacors.size() - 1;
++index;
}
for (auto& i : interfacors)
{
m_updatedBox->addInterfacor(interfacorType, i.name, i.typeID, i.identifier);
if (interfacorType == Setting)
{
const auto idx = m_updatedBox->getInterfacorCountIncludingDeprecated(Setting) - 1;
m_updatedBox->setSettingDefaultValue(idx, i.defaultValue);
m_updatedBox->setSettingValue(idx, i.value);
m_updatedBox->setSettingMod(idx, i.modifiability);
}
if (i.toBeRemoved)
{
m_updatedBox->setInterfacorDeprecatedStatus(interfacorType, m_updatedBox->getInterfacorCountIncludingDeprecated(interfacorType) - 1, true);
}
}
return updated;
}
size_t CBoxUpdater::getInterfacorIndex(const EBoxInterfacorType type, const IBox& box, const CIdentifier& typeID, const CIdentifier& id, const CString& name)
{
size_t index = size_t(-1);
if (id != CIdentifier::undefined() && box.hasInterfacorWithIdentifier(type, id)) { box.getInterfacorIndex(type, id, index); }
else if (box.hasInterfacorWithNameAndType(type, name, typeID)) { box.getInterfacorIndex(type, name, index); }
return index;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,93 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkCScenario.h"
#include <map>
#include <array>
namespace OpenViBE {
namespace Kernel {
typedef struct _InterfacorRequest
{
size_t index = size_t(-1);
CIdentifier identifier = CIdentifier::undefined();
CIdentifier typeID = CIdentifier::undefined();
CString name;
bool toBeRemoved;
// only for settings
CString defaultValue;
bool modifiability = false;
CString value;
} InterfacorRequest;
class CBoxUpdater final : public TKernelObject<IKernelObject>
{
public:
CBoxUpdater(CScenario& scenario, IBox* box);
~CBoxUpdater() override;
bool initialize();
const std::map<size_t, size_t>& getOriginalToUpdatedInterfacorCorrespondence(const EBoxInterfacorType type) const
{
return m_originalToUpdatedCorrespondence.at(type);
}
IBox& getUpdatedBox() const { return *m_updatedBox; }
bool flaggedForManualUpdate() const
{
OV_FATAL_UNLESS_K(m_initialized, "Box Updater is not initialized", Kernel::ErrorType::BadCall);
return m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagNeedsManualUpdate)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanAddInput)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanModifyInput)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanAddOutput)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanAddSetting)
|| m_kernelBox->hasAttribute(OV_AttributeId_Box_FlagCanModifySetting);
}
bool isUpdateRequired() const { return m_isUpdateRequired; }
static const std::array<CIdentifier, 10> UPDATABLE_ATTRIBUTES;
_IsDerivedFromClass_Final_(TKernelObject<IKernelObject>, OV_ClassId_Kernel_Scenario_BoxUpdater)
private:
static size_t getInterfacorIndex(EBoxInterfacorType type, const IBox& box, const CIdentifier& typeID, const CIdentifier& id, const CString& name);
bool updateInterfacors(EBoxInterfacorType interfacorType);
/**
* \brief Check if supported type have to be updated between the box to be updated and the kernel
* \return true when at least input or output supported types have to be updated
*/
bool checkForSupportedTypesToBeUpdated() const;
/**
* \brief Check if supported inputs, outputs or settings attributes have to be updated between the box to be updated and the kernel
* \return true when at least input, output or settings attributes have to be updated
*/
bool checkForSupportedIOSAttributesToBeUpdated() const;
// pointer to the parent scenario
CScenario* m_scenario = nullptr;
// pointer to the original box to be updated
IBox* m_sourceBox = nullptr;
// pointer to the kernel box
const IBox* m_kernelBox = nullptr;
// pointer to the updated box. This box will be used to update the prototype of the original box
IBox* m_updatedBox = nullptr;
// true when updater has been initialized
bool m_initialized = false;
std::map<EBoxInterfacorType, std::map<size_t, size_t>> m_originalToUpdatedCorrespondence;
bool m_isUpdateRequired = false;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,55 @@
#include "ovkCComment.h"
#include "ovkCScenario.h"
#include "../ovkCObjectVisitorContext.h"
namespace OpenViBE {
namespace Kernel {
CComment::CComment(const IKernelContext& ctx, CScenario& rOwnerScenario)
: TAttributable<TKernelObject<IComment>>(ctx), m_rOwnerScenario(rOwnerScenario), m_text("") {}
//___________________________________________________________________//
// //
bool CComment::setIdentifier(const CIdentifier& id)
{
if (m_id != CIdentifier::undefined() || id == CIdentifier::undefined()) { return false; }
m_id = id;
return true;
}
bool CComment::setText(const CString& sText)
{
m_text = sText;
return true;
}
//___________________________________________________________________//
// //
bool CComment::initializeFromExistingComment(const IComment& rExisitingComment)
{
m_text = rExisitingComment.getText();
CIdentifier id = rExisitingComment.getNextAttributeIdentifier(CIdentifier::undefined());
while (id != CIdentifier::undefined())
{
addAttribute(id, rExisitingComment.getAttributeValue(id));
id = rExisitingComment.getNextAttributeIdentifier(id);
}
return true;
}
//___________________________________________________________________//
// //
bool CComment::acceptVisitor(IObjectVisitor& rObjectVisitor)
{
CObjectVisitorContext context(getKernelContext());
return rObjectVisitor.processBegin(context, *this) && rObjectVisitor.processEnd(context, *this);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,36 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkTAttributable.h"
#include <iostream>
namespace OpenViBE {
namespace Kernel {
class CScenario;
class CComment final : public TAttributable<TKernelObject<IComment>>
{
public:
CComment(const IKernelContext& ctx, CScenario& rOwnerScenario);
~CComment() override {}
CIdentifier getIdentifier() const override { return m_id; }
CString getText() const override { return m_text; }
bool setIdentifier(const CIdentifier& id) override;
bool setText(const CString& sText) override;
bool initializeFromExistingComment(const IComment& rExisitingComment) override;
bool acceptVisitor(IObjectVisitor& rObjectVisitor) override;
_IsDerivedFromClass_Final_(TAttributable<TKernelObject<IComment>>, OVK_ClassId_Kernel_Scenario_Comment)
protected:
CScenario& m_rOwnerScenario;
CIdentifier m_id = CIdentifier::undefined();
CString m_text;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,76 @@
#include "ovkCLink.h"
#include "ovkCScenario.h"
#include "../ovkCObjectVisitorContext.h"
namespace OpenViBE {
namespace Kernel {
bool CLink::initializeFromExistingLink(const ILink& link)
{
m_id = link.getIdentifier();
m_srcBoxID = link.getSourceBoxIdentifier();
m_dstBoxID = link.getTargetBoxIdentifier();
m_srcBoxOutputID = link.getSourceBoxOutputIdentifier();
m_dstBoxInputID = link.getTargetBoxInputIdentifier();
m_srcOutputIdx = link.getSourceBoxOutputIndex();
m_dstInputIdx = link.getTargetBoxInputIndex();
return true;
}
//___________________________________________________________________//
// //
bool CLink::setIdentifier(const CIdentifier& identifier)
{
m_id = identifier;
return true;
}
//___________________________________________________________________//
// //
bool CLink::setSource(const CIdentifier& boxId, const size_t boxOutputIdx, const CIdentifier boxOutputID)
{
m_srcBoxID = boxId;
m_srcOutputIdx = boxOutputIdx;
m_srcBoxOutputID = boxOutputID;
return true;
}
bool CLink::setTarget(const CIdentifier& boxId, const size_t boxInputIdx, const CIdentifier boxInputID)
{
m_dstBoxID = boxId;
m_dstInputIdx = boxInputIdx;
m_dstBoxInputID = boxInputID;
return true;
}
bool CLink::getSource(CIdentifier& boxId, size_t& boxOutputIdx, CIdentifier& boxOutputID) const
{
boxId = m_srcBoxID;
boxOutputIdx = m_srcOutputIdx;
boxOutputID = m_srcBoxOutputID;
return true;
}
bool CLink::getTarget(CIdentifier& dstBoxID, size_t& boxInputIndex, CIdentifier& dstBoxInputID) const
{
dstBoxID = m_dstBoxID;
boxInputIndex = m_dstInputIdx;
dstBoxInputID = m_dstBoxInputID;
return true;
}
//___________________________________________________________________//
// //
bool CLink::acceptVisitor(IObjectVisitor& visitor)
{
CObjectVisitorContext context(getKernelContext());
return visitor.processBegin(context, *this) && visitor.processEnd(context, *this);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,48 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkTAttributable.h"
namespace OpenViBE {
namespace Kernel {
class CScenario;
class CLink final : public TAttributable<TKernelObject<ILink>>
{
public:
CLink(const IKernelContext& ctx, CScenario& ownerScenario) : TAttributable<TKernelObject<ILink>>(ctx), m_ownerScenario(ownerScenario) {}
bool initializeFromExistingLink(const ILink& link) override;
bool setIdentifier(const CIdentifier& identifier) override;
CIdentifier getIdentifier() const override { return m_id; }
bool setSource(const CIdentifier& boxId, size_t boxOutputIdx, CIdentifier boxOutputID) override;
bool setTarget(const CIdentifier& boxId, size_t boxInputIdx, CIdentifier boxInputID) override;
bool getSource(CIdentifier& boxId, size_t& boxOutputIdx, CIdentifier& boxOutputID) const override;
CIdentifier getSourceBoxIdentifier() const override { return m_srcBoxID; }
size_t getSourceBoxOutputIndex() const override { return m_srcOutputIdx; }
CIdentifier getSourceBoxOutputIdentifier() const override { return m_srcBoxOutputID; }
bool getTarget(CIdentifier& dstBoxID, size_t& boxInputIndex, CIdentifier& dstBoxInputID) const override;
CIdentifier getTargetBoxIdentifier() const override { return m_dstBoxID; }
size_t getTargetBoxInputIndex() const override { return m_dstInputIdx; }
CIdentifier getTargetBoxInputIdentifier() const override { return m_dstBoxInputID; }
bool acceptVisitor(IObjectVisitor& visitor) override;
_IsDerivedFromClass_Final_(TAttributable<TKernelObject<ILink>>, OVK_ClassId_Kernel_Scenario_Link)
protected:
CScenario& m_ownerScenario;
CIdentifier m_id = CIdentifier::undefined();
CIdentifier m_srcBoxID = CIdentifier::undefined();
CIdentifier m_dstBoxID = CIdentifier::undefined();
size_t m_srcOutputIdx = 0;
CIdentifier m_srcBoxOutputID = CIdentifier::undefined();
size_t m_dstInputIdx = 0;
CIdentifier m_dstBoxInputID = CIdentifier::undefined();
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,61 @@
#include "../ovkTKernelObject.h"
#include "ovkCMetadata.h"
#include "ovkCScenario.h"
#include "../ovkCObjectVisitorContext.h"
namespace OpenViBE {
namespace Kernel {
bool CMetadata::setIdentifier(const CIdentifier& identifier)
{
OV_ERROR_UNLESS_KRF(m_id == CIdentifier::undefined(),
"Metadata [" << m_id.str() << "] in scenario [" << m_ownerScenario.getIdentifier() << "] already has an identifier.",
ErrorType::BadCall);
OV_ERROR_UNLESS_KRF(identifier != CIdentifier::undefined(),
"Attempted to assign undefined identifier to Metadata in scenario [" << m_ownerScenario.getIdentifier() << "].",
ErrorType::BadArgument);
m_id = identifier;
return true;
}
bool CMetadata::setType(const CIdentifier& typeID)
{
OV_ERROR_UNLESS_KRF(typeID != CIdentifier::undefined(),
"Attempted to assign undefined typeID to Metadata [" << m_id.str() << "] in scenario [" << m_ownerScenario.getIdentifier() << "].",
ErrorType::BadArgument);
m_type = typeID;
return true;
}
bool CMetadata::setData(const CString& data)
{
m_data = data;
return true;
}
//___________________________________________________________________//
// //
bool CMetadata::initializeFromExistingMetadata(const IMetadata& existingMetadata)
{
m_data = existingMetadata.getData();
m_type = existingMetadata.getType();
return true;
}
//___________________________________________________________________//
// //
bool CMetadata::acceptVisitor(IObjectVisitor& objectVisitor)
{
CObjectVisitorContext objectVisitorContext(this->getKernelContext());
return objectVisitor.processBegin(objectVisitorContext, *this) && objectVisitor.processEnd(objectVisitorContext, *this);
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,39 @@
#pragma once
#include <openvibe/kernel/scenario/ovIMetadata.h>
#include "../ovkTKernelObject.h"
namespace OpenViBE {
namespace Kernel {
class CScenario;
class CMetadata final : public TKernelObject<IMetadata>
{
public:
CMetadata(const IKernelContext& ctx, CScenario& ownerScenario) : TKernelObject<IMetadata>(ctx), m_ownerScenario(ownerScenario), m_data("") {}
~CMetadata() override {}
CIdentifier getIdentifier() const override { return m_id; }
CIdentifier getType() const override { return m_type; }
CString getData() const override { return m_data; }
bool setIdentifier(const CIdentifier& identifier) override;
bool setType(const CIdentifier& typeID) override;
bool setData(const CString& data) override;
bool initializeFromExistingMetadata(const IMetadata& existingMetadata) override;
bool acceptVisitor(IObjectVisitor& objectVisitor) override;
_IsDerivedFromClass_Final_(TKernelObject<IMetadata>, OVK_ClassId_Kernel_Scenario_Metadata)
private:
CScenario& m_ownerScenario;
CIdentifier m_id = CIdentifier::undefined();
CIdentifier m_type = CIdentifier::undefined();
CString m_data;
};
} // namespace Kernel
} // namespace OpenViBE
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,148 @@
#pragma once
#include "../ovkTKernelObject.h"
#include "ovkTBox.hpp"
#include <vector>
#include <map>
#include <memory>
namespace OpenViBE {
namespace Kernel {
typedef TBox<IBox> CBox;
class CComment;
class CMetadata;
class CLink;
class CScenario final : public TBox<IScenario>
{
public:
CScenario(const IKernelContext& ctx, const CIdentifier& identifier);
~CScenario() override { this->clear(); }
bool clear() override;
bool merge(const IScenario& scenario, IScenarioMergeCallback* scenarioMergeCallback, bool mergeSettings, bool preserveIDs) override;
CIdentifier getNextBoxIdentifier(const CIdentifier& previousID) const override;
bool isBox(const CIdentifier& boxID) const override;
const IBox* getBoxDetails(const CIdentifier& boxID) const override;
IBox* getBoxDetails(const CIdentifier& boxID) override;
bool addBox(CIdentifier& boxID, const CIdentifier& suggestedBoxID) override;
bool addBox(CIdentifier& boxID, const IBox& box, const CIdentifier& suggestedBoxID) override;
bool addBox(CIdentifier& boxID, const CIdentifier& boxAlgorithmID, const CIdentifier& suggestedBoxID) override;
bool addBox(CIdentifier& boxID, const Plugins::IBoxAlgorithmDesc& boxAlgorithmDesc, const CIdentifier& suggestedBoxID) override;
bool removeBox(const CIdentifier& boxID) override;
CIdentifier getNextCommentIdentifier(const CIdentifier& previousID) const override;
bool isComment(const CIdentifier& commentID) const override;
const IComment* getCommentDetails(const CIdentifier& commentID) const override;
IComment* getCommentDetails(const CIdentifier& commentID) override;
bool addComment(CIdentifier& commentID, const CIdentifier& suggestedCommentID) override;
bool addComment(CIdentifier& commentID, const IComment& comment, const CIdentifier& suggestedCommentID) override;
bool removeComment(const CIdentifier& commentID) override;
CIdentifier getNextMetadataIdentifier(const CIdentifier& previousID) const override;
bool isMetadata(const CIdentifier& metadataID) const override;
const IMetadata* getMetadataDetails(const CIdentifier& metadataID) const override;
IMetadata* getMetadataDetails(const CIdentifier& metadataID) override;
bool addMetadata(CIdentifier& metadataID, const CIdentifier& suggestedMetadataID) override;
bool removeMetadata(const CIdentifier& metadataID) override;
CIdentifier getNextLinkIdentifier(const CIdentifier& previousID) const override;
CIdentifier getNextLinkIdentifierFromBox(const CIdentifier& previousID, const CIdentifier& boxID) const override;
CIdentifier getNextLinkIdentifierFromBoxOutput(const CIdentifier& previousID, const CIdentifier& boxID, size_t index) const override;
CIdentifier getNextLinkIdentifierToBox(const CIdentifier& previousID, const CIdentifier& boxID) const override;
CIdentifier getNextLinkIdentifierToBoxInput(const CIdentifier& previousID, const CIdentifier& boxID, size_t index) const override;
bool isLink(const CIdentifier& boxID) const override;
bool setHasIO(bool hasIO) override;
bool hasIO() const override;
bool setScenarioInputLink(size_t scenarioInputIdx, const CIdentifier& boxID, size_t boxInputIdx) override;
bool setScenarioInputLink(size_t scenarioInputIdx, const CIdentifier& boxID, const CIdentifier& boxInputID) override;
bool setScenarioOutputLink(size_t scenarioOutputIdx, const CIdentifier& boxID, size_t boxOutputIdx) override;
bool setScenarioOutputLink(size_t scenarioOutputIdx, const CIdentifier& boxID, const CIdentifier& boxOutputID) override;
bool getScenarioInputLink(size_t scenarioInputIdx, CIdentifier& boxID, size_t& boxInputIdx) const override;
bool getScenarioInputLink(size_t scenarioInputIdx, CIdentifier& boxID, CIdentifier& boxOutputID) const override;
bool getScenarioOutputLink(size_t scenarioOutputIdx, CIdentifier& boxID, size_t& boxOutputIdx) const override;
bool getScenarioOutputLink(size_t scenarioOutputIdx, CIdentifier& boxID, CIdentifier& boxOutputID) const override;
bool removeScenarioInputLink(size_t scenarioInputIdx, const CIdentifier& boxID, size_t boxInputIdx) override;
bool removeScenarioOutputLink(size_t scenarioOutputIdx, const CIdentifier& boxID, size_t boxOutputIdx) override;
bool removeScenarioInput(size_t index) override;
bool removeScenarioOutput(size_t index) override;
const ILink* getLinkDetails(const CIdentifier& linkID) const override;
ILink* getLinkDetails(const CIdentifier& linkID) override;
bool connect(CIdentifier& linkID, const CIdentifier& srcBoxID, const size_t srcBoxOutputIdx, const CIdentifier& dstBoxID,
const size_t dstBoxInputIdx, const CIdentifier& suggestedLinkID) override;
bool connect(CIdentifier& linkID, const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID, const CIdentifier& dstBoxID,
const CIdentifier& dstBoxInputID, const CIdentifier& suggestedLinkID) override;
bool disconnect(const CIdentifier& srcBoxID, size_t srcBoxOutputIdx, const CIdentifier& dstBoxID, size_t dstBoxInputIdx) override;
bool disconnect(const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID, const CIdentifier& dstBoxID, const CIdentifier& dstBoxInputID) override;
bool disconnect(const CIdentifier& linkID) override;
bool getSourceBoxOutputIndex(const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID, size_t& srcBoxOutputIdx) override;
bool getTargetBoxInputIndex(const CIdentifier& dstBoxID, const CIdentifier& dstBoxInputID, size_t& dstBoxInputIdx) override;
bool getSourceBoxOutputIdentifier(const CIdentifier& srcBoxID, const size_t& srcBoxOutputIdx, CIdentifier& srcBoxOutputID) override;
bool getTargetBoxInputIdentifier(const CIdentifier& dstBoxID, const size_t& dstBoxInputIdx, CIdentifier& dstBoxInputID) override;
bool applyLocalSettings() override;
bool checkSettings(IConfigurationManager* configurationManager) override;
bool isBoxOutdated(const CIdentifier& boxId);
bool checkOutdatedBoxes() override;
bool hasOutdatedBox() override;
CIdentifier getNextOutdatedBoxIdentifier(const CIdentifier& previousID) const override;
bool isMetabox() override;
void getBoxIdentifierList(CIdentifier** listID, size_t* size) const override;
void getCommentIdentifierList(CIdentifier** listID, size_t* size) const override;
void getMetadataIdentifierList(CIdentifier** listID, size_t* size) const override;
void getLinkIdentifierList(CIdentifier** listID, size_t* size) const override;
void getLinkIdentifierFromBoxList(const CIdentifier& boxID, CIdentifier** listID, size_t* size) const override;
void getLinkIdentifierFromBoxOutputList(const CIdentifier& boxID, size_t index, CIdentifier** listID, size_t* size) const override;
void getLinkIdentifierToBoxList(const CIdentifier& boxID, CIdentifier** listID, size_t* size) const override;
void getLinkIdentifierToBoxInputList(const CIdentifier& boxID, size_t index, CIdentifier** listID, size_t* size) const override;
void getOutdatedBoxIdentifierList(CIdentifier** listID, size_t* size) const override;
void releaseIdentifierList(CIdentifier* listID) const override;
bool acceptVisitor(IObjectVisitor& objectVisitor) override;
bool updateBox(const CIdentifier& boxID) override;
bool containsBoxWithDeprecatedInterfacors() const override;
bool removeDeprecatedInterfacorsFromBox(const CIdentifier& boxID) override;
_IsDerivedFromClass_Final_(TBox<IScenario>, OVK_ClassId_Kernel_Scenario_Scenario)
private:
CIdentifier getUnusedIdentifier(const CIdentifier& suggestedID) const;
std::map<CIdentifier, CBox*> m_boxes;
std::map<CIdentifier, CComment*> m_comments;
std::map<CIdentifier, CMetadata*> m_metadatas;
std::map<CIdentifier, CLink*> m_links;
std::map<CIdentifier, std::shared_ptr<CBox>> m_outdatedBoxes;
std::map<EBoxInterfacorType, std::map<CIdentifier, std::map<size_t, size_t>>> m_updatedBoxIOCorrespondence;
bool m_hasIO = false;
mutable std::vector<std::pair<CIdentifier, size_t>> m_scenarioInputLinks;
mutable std::vector<std::pair<CIdentifier, size_t>> m_scenarioOutputLinks;
// Helper members. These are used for quick lookup of next identifiers for the purpose
// of the getNextMetadataIdentifier function.
std::map<CIdentifier, CIdentifier> m_nextMetadataID;
CIdentifier m_firstMetadataID = CIdentifier::undefined();
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,585 @@
#include "ovkCScenarioManager.h"
#include "ovkCScenario.h"
#include <cstdlib>
#include <fs/Files.h>
#include <cassert>
#include <openvibe/kernel/scenario/ovIAlgorithmScenarioImporter.h>
#include <openvibe/kernel/scenario/ovIAlgorithmScenarioExporter.h>
#include "../../tools/ovkSBoxProto.h"
namespace OpenViBE {
namespace Kernel {
CScenarioManager::CScenarioManager(const IKernelContext& ctx) : TKernelObject<IScenarioManager>(ctx) {}
CScenarioManager::~CScenarioManager() { for (auto i = m_scenarios.begin(); i != m_scenarios.end(); ++i) { delete i->second; } }
void CScenarioManager::cloneScenarioImportersAndExporters(const IScenarioManager& scenarioManager)
{
CIdentifier importContextId = CIdentifier::undefined();
// Copy the registered importers from the parent Scenario Manager
while ((importContextId = scenarioManager.getNextScenarioImportContext(importContextId)) != CIdentifier::undefined())
{
CString fileNameExtension = "";
while ((fileNameExtension = scenarioManager.getNextScenarioImporter(importContextId, fileNameExtension)) != CString(""))
{
CIdentifier algorithmId = scenarioManager.getScenarioImporterAlgorithmIdentifier(importContextId, fileNameExtension);
this->registerScenarioImporter(importContextId, fileNameExtension, algorithmId);
}
}
CIdentifier exportContextId = CIdentifier::undefined();
while ((exportContextId = scenarioManager.getNextScenarioExportContext(exportContextId)) != CIdentifier::undefined())
{
CString fileNameExtension = "";
while ((fileNameExtension = scenarioManager.getNextScenarioExporter(exportContextId, fileNameExtension)) != CString(""))
{
CIdentifier algorithmId = scenarioManager.getScenarioExporterAlgorithmIdentifier(exportContextId, fileNameExtension);
this->registerScenarioExporter(exportContextId, fileNameExtension, algorithmId);
}
}
}
CIdentifier CScenarioManager::getNextScenarioIdentifier(const CIdentifier& previousID) const
{
std::map<CIdentifier, CScenario*>::const_iterator itScenario;
if (previousID == CIdentifier::undefined()) { itScenario = m_scenarios.begin(); }
else
{
itScenario = m_scenarios.find(previousID);
if (itScenario == m_scenarios.end()) { return CIdentifier::undefined(); }
++itScenario;
}
return itScenario != m_scenarios.end() ? itScenario->first : CIdentifier::undefined();
}
bool CScenarioManager::isScenario(const CIdentifier& scenarioID) const { return m_scenarios.find(scenarioID) != m_scenarios.end(); }
bool CScenarioManager::createScenario(CIdentifier& scenarioID)
{
//create scenario object
scenarioID = getUnusedIdentifier();
CScenario* scenario = new CScenario(getKernelContext(), scenarioID);
m_scenarios[scenarioID] = scenario;
return true;
}
bool CScenarioManager::importScenario(CIdentifier& scenarioID, const IMemoryBuffer& iMemoryBuffer, const CIdentifier& scenarioImporterAlgorithmID)
{
scenarioID = CIdentifier::undefined();
OV_ERROR_UNLESS_KRF(this->createScenario(scenarioID), "Error creating new scenario", Kernel::ErrorType::BadResourceCreation);
const auto releaseScenario = [&]()
{
// use a fatal here because a release failure while creation succeeded
// means we are in an unexpected state
OV_FATAL_UNLESS_K(this->releaseScenario(scenarioID), "Releasing just created scenario failed for " << scenarioID.str(),
ErrorType::Internal);
scenarioID = CIdentifier::undefined();
};
IScenario& newScenarioInstance = this->getScenario(scenarioID);
if (!iMemoryBuffer.getSize())
{
releaseScenario();
OV_ERROR_KRF("Buffer containing scenario data is empty", Kernel::ErrorType::BadValue);
}
CIdentifier importerInstanceIdentifier = this->getKernelContext().getAlgorithmManager().createAlgorithm(scenarioImporterAlgorithmID);
if (importerInstanceIdentifier == CIdentifier::undefined())
{
releaseScenario();
OV_ERROR_KRF("Can not create the requested scenario importer", Kernel::ErrorType::BadResourceCreation);
}
IAlgorithmProxy* importer = &this->getKernelContext().getAlgorithmManager().getAlgorithm(importerInstanceIdentifier);
OV_FATAL_UNLESS_K(
importer,
"Importer with id " << importerInstanceIdentifier.str() << " not found although it has just been created",
ErrorType::ResourceNotFound);
const auto releaseAlgorithm = [&]()
{
// use a fatal here because a release failure while creation succeeded
// means we are in an unexpected state
OV_FATAL_UNLESS_K(
this->getKernelContext().getAlgorithmManager().releaseAlgorithm(*importer),
"Releasing just created algorithm failed for " << importerInstanceIdentifier.str(),
ErrorType::Internal);
};
if (!importer->initialize())
{
releaseScenario();
releaseAlgorithm();
OV_ERROR_KRF("Can not initialize the requested scenario importer", Kernel::ErrorType::Internal);
}
IParameter* memoryBufferParameter = importer->getInputParameter(OV_Algorithm_ScenarioImporter_InputParameterId_MemoryBuffer);
IParameter* scenarioParameter = importer->getOutputParameter(OV_Algorithm_ScenarioImporter_OutputParameterId_Scenario);
if (!(memoryBufferParameter && scenarioParameter))
{
releaseScenario();
releaseAlgorithm();
OV_ERROR_UNLESS_KRF(
memoryBufferParameter,
"The requested importer does not have a MemoryBuffer input parameter with scenarioID " <<
OV_Algorithm_ScenarioImporter_InputParameterId_MemoryBuffer,
ErrorType::BadInput);
OV_ERROR_UNLESS_KRF(
scenarioParameter,
"The requested importer does not have a Scenario output parameter with scenarioID " << OV_Algorithm_ScenarioImporter_OutputParameterId_Scenario,
ErrorType::BadOutput);
}
TParameterHandler<const IMemoryBuffer*> memoryBufferParameterHandler(memoryBufferParameter);
TParameterHandler<IScenario*> scenarioParameterHandler(scenarioParameter);
memoryBufferParameterHandler = &iMemoryBuffer;
scenarioParameterHandler = &newScenarioInstance;
if (!importer->process())
{
releaseScenario();
releaseAlgorithm();
OV_ERROR_KRF("Can not process data using the requested scenario importer", Kernel::ErrorType::Internal);
}
if (!importer->uninitialize())
{
releaseScenario();
releaseAlgorithm();
OV_ERROR_KRF("Can not uninitialize the requested scenario importer", Kernel::ErrorType::Internal);
}
releaseAlgorithm();
return true;
}
bool CScenarioManager::importScenarioFromFile(CIdentifier& scenarioID, const CString& fileName, const CIdentifier& scenarioImporterAlgorithmID)
{
scenarioID = CIdentifier::undefined();
CMemoryBuffer memoryBuffer;
FILE* inputFile = FS::Files::open(fileName, "rb");
OV_ERROR_UNLESS_KRF(
inputFile,
"Can not open scenario file '" << fileName << "'",
ErrorType::BadFileRead);
fseek(inputFile, 0, SEEK_END);
memoryBuffer.setSize(size_t(ftell(inputFile)), true);
fseek(inputFile, 0, SEEK_SET);
if (fread(reinterpret_cast<char*>(memoryBuffer.getDirectPointer()), size_t(memoryBuffer.getSize()), 1, inputFile) != 1)
{
fclose(inputFile);
OV_ERROR_KRF("Problem reading scenario file '" << fileName << "'", Kernel::ErrorType::BadFileRead);
}
fclose(inputFile);
return this->importScenario(scenarioID, memoryBuffer, scenarioImporterAlgorithmID);
}
bool CScenarioManager::importScenarioFromFile(CIdentifier& scenarioID, const CIdentifier& importContext, const CString& fileName)
{
OV_ERROR_UNLESS_KRF(m_importers.count(importContext),
"The import context " << importContext.str() << " has no associated importers",
ErrorType::Internal);
std::vector<char> fileNameExtension;
fileNameExtension.resize(fileName.length() + 1);
FS::Files::getFilenameExtension(fileName.toASCIIString(), &fileNameExtension[0]);
OV_ERROR_UNLESS_KRF(m_importers[importContext].count(&fileNameExtension[0]),
"The import context " << importContext.str() << " has no associated importers for extension [" << &fileNameExtension[0] << "]",
ErrorType::Internal);
return this->importScenarioFromFile(scenarioID, fileName, m_importers[importContext][&fileNameExtension[0]]);
}
bool CScenarioManager::registerScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension,
const CIdentifier& scenarioImporterAlgorithmIdentifier)
{
if (!m_importers.count(importContext)) { m_importers[importContext] = std::map<std::string, CIdentifier>(); }
OV_ERROR_UNLESS_KRF(!m_importers[importContext].count(fileNameExtension.toASCIIString()),
"The file name extension [" << fileNameExtension << "] already has an importer registered for context " << importContext.str(),
ErrorType::Internal);
m_importers[importContext][fileNameExtension.toASCIIString()] = scenarioImporterAlgorithmIdentifier;
return true;
}
bool CScenarioManager::unregisterScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension)
{
OV_ERROR_UNLESS_KRF(m_importers.count(importContext),
"The import context " << importContext.str() << " has no associated importers",
ErrorType::Internal);
OV_ERROR_UNLESS_KRF(m_importers[importContext].count(fileNameExtension.toASCIIString()),
"The import context " << importContext.str() << " has no associated importers for extension [" << fileNameExtension << "]",
ErrorType::Internal);
auto& contextImporters = m_importers[importContext];
for (auto it = contextImporters.begin(); it != contextImporters.end();)
{
if (it->first == fileNameExtension.toASCIIString()) { it = contextImporters.erase(it); }
else { ++it; }
}
for (auto it = m_importers.begin(); it != m_importers.end();)
{
if (it->second.empty()) { it = m_importers.erase(it); }
else { ++it; }
}
return true;
}
CIdentifier CScenarioManager::getNextScenarioImportContext(const CIdentifier& importContext) const
{
if (m_importers.empty()) { return CIdentifier::undefined(); }
if (importContext == CIdentifier::undefined()) { return m_importers.cbegin()->first; }
auto current = m_importers.find(importContext);
if (current == m_importers.end() || ++current == m_importers.end()) { return CIdentifier::undefined(); }
return current->first;
}
CString CScenarioManager::getNextScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension) const
{
if (m_importers.empty() || !m_importers.count(importContext)) { return ""; }
const auto& scenarioImportContextMap = m_importers.at(importContext);
if (fileNameExtension == CString("")) { return scenarioImportContextMap.cbegin()->first.c_str(); }
auto current = scenarioImportContextMap.find(fileNameExtension.toASCIIString());
if (current == scenarioImportContextMap.end() || ++current == scenarioImportContextMap.end()) { return ""; }
return current->first.c_str();
}
CIdentifier CScenarioManager::getScenarioImporterAlgorithmIdentifier(const CIdentifier& importContext, const CString& fileNameExtension) const
{
OV_ERROR_UNLESS_KRU(
!m_importers.empty() && m_importers.count(importContext) && m_importers.at(importContext).count(fileNameExtension.toASCIIString(
)),
"Scenario importer not found",
ErrorType::OutOfBound);
return m_importers.at(importContext).at(fileNameExtension.toASCIIString());
}
bool CScenarioManager::exportScenario(IMemoryBuffer& oMemoryBuffer, const CIdentifier& scenarioID,
const CIdentifier& scenarioExporterAlgorithmID) const
{
OV_ERROR_UNLESS_KRF(
m_scenarios.find(scenarioID) != m_scenarios.end(),
"Scenario with identifier " << scenarioID.str() << " does not exist.",
ErrorType::ResourceNotFound);
// If the scenario is a metabox, we will save its prototype hash into an attribute of the scenario
// that way the standalone scheduler can check whether metaboxes included inside need updating.
IScenario& scenario = this->getScenario(scenarioID);
if (scenario.isMetabox())
{
SBoxProto metaboxProto(getKernelContext().getTypeManager());
for (size_t scenarioInputIdx = 0; scenarioInputIdx < scenario.getInputCount(); ++scenarioInputIdx)
{
CIdentifier id;
CString name;
CIdentifier typeID;
scenario.getInterfacorIdentifier(Input, scenarioInputIdx, id);
scenario.getInputType(scenarioInputIdx, typeID);
scenario.getInputName(scenarioInputIdx, name);
metaboxProto.addInput(name, typeID, id, true);
}
for (size_t scenarioOutputIdx = 0; scenarioOutputIdx < scenario.getOutputCount(); ++scenarioOutputIdx)
{
CIdentifier id;
CString name;
CIdentifier typeID;
scenario.getInterfacorIdentifier(Output, scenarioOutputIdx, id);
scenario.getOutputType(scenarioOutputIdx, typeID);
scenario.getOutputName(scenarioOutputIdx, name);
metaboxProto.addOutput(name, typeID, id, true);
}
for (size_t scenarioSettingIdx = 0; scenarioSettingIdx < scenario.getSettingCount(); ++scenarioSettingIdx)
{
CString name;
CIdentifier typeID;
CString defaultValue;
CIdentifier id;
scenario.getSettingName(scenarioSettingIdx, name);
scenario.getSettingType(scenarioSettingIdx, typeID);
scenario.getSettingDefaultValue(scenarioSettingIdx, defaultValue);
scenario.getInterfacorIdentifier(Setting, scenarioSettingIdx, id);
metaboxProto.addSetting(name, typeID, defaultValue, false, id, true);
}
if (scenario.hasAttribute(OV_AttributeId_Scenario_MetaboxHash))
{
scenario.setAttributeValue(OV_AttributeId_Scenario_MetaboxHash, metaboxProto.m_hash.toString());
}
else { scenario.addAttribute(OV_AttributeId_Scenario_MetaboxHash, metaboxProto.m_hash.toString()); }
}
CIdentifier exporterInstanceIdentifier = this->getKernelContext().getAlgorithmManager().createAlgorithm(scenarioExporterAlgorithmID);
OV_ERROR_UNLESS_KRF(
exporterInstanceIdentifier != CIdentifier::undefined(),
"Can not create the requested scenario exporter",
ErrorType::BadResourceCreation);
IAlgorithmProxy* exporter = &this->getKernelContext().getAlgorithmManager().getAlgorithm(exporterInstanceIdentifier);
OV_FATAL_UNLESS_K(
exporter,
"Exporter with id " << exporterInstanceIdentifier.str() << " not found although it has just been created",
ErrorType::ResourceNotFound);
const auto releaseAlgorithm = [&]()
{
// use a fatal here because a release failure while creation succeeded
// means we are in an unexpected state
OV_FATAL_UNLESS_K(
this->getKernelContext().getAlgorithmManager().releaseAlgorithm(*exporter),
"Releasing just created algorithm failed for " << exporterInstanceIdentifier.str(),
ErrorType::Internal);
};
if (!exporter->initialize())
{
releaseAlgorithm();
OV_ERROR_KRF("Can not initialize the requested scenario exporter", Kernel::ErrorType::Internal);
}
IParameter* scenarioParameter = exporter->getInputParameter(OV_Algorithm_ScenarioExporter_InputParameterId_Scenario);
IParameter* memoryBufferParameter = exporter->getOutputParameter(OV_Algorithm_ScenarioExporter_OutputParameterId_MemoryBuffer);
if (!(memoryBufferParameter && scenarioParameter))
{
releaseAlgorithm();
OV_ERROR_UNLESS_KRF(
scenarioParameter,
"The requested exporter does not have a Scenario input parameter with identifier " << OV_Algorithm_ScenarioExporter_InputParameterId_Scenario,
ErrorType::BadInput);
OV_ERROR_UNLESS_KRF(
memoryBufferParameter,
"The requested exporter does not have a MemoryBuffer output parameter with identifier " <<
OV_Algorithm_ScenarioExporter_OutputParameterId_MemoryBuffer,
ErrorType::BadOutput);
}
TParameterHandler<IScenario*> scenarioParameterHandler(scenarioParameter);
TParameterHandler<const IMemoryBuffer*> memoryBufferParameterHandler(memoryBufferParameter);
scenarioParameterHandler = &scenario;
memoryBufferParameterHandler = &oMemoryBuffer;
if (!exporter->process())
{
releaseAlgorithm();
OV_ERROR_KRF("Can not process data using the requested scenario exporter", Kernel::ErrorType::Internal);
}
if (!exporter->uninitialize())
{
releaseAlgorithm();
OV_ERROR_KRF("Can not uninitialize the requested scenario exporter", Kernel::ErrorType::Internal);
}
releaseAlgorithm();
return true;
}
bool CScenarioManager::exportScenarioToFile(const CString& fileName, const CIdentifier& scenarioID,
const CIdentifier& scenarioExporterAlgorithmID) const
{
IScenario& scenario = this->getScenario(scenarioID);
if (scenario.containsBoxWithDeprecatedInterfacors())
{
OV_WARNING_K(
"Cannot export a scenario with pending deprecated I/O or Settings. Please remove them using the Designer."
);
return false;
}
CMemoryBuffer memoryBuffer;
this->exportScenario(memoryBuffer, scenarioID, scenarioExporterAlgorithmID);
std::ofstream outputFileStream;
FS::Files::openOFStream(outputFileStream, fileName, std::ios::binary);
OV_ERROR_UNLESS_KRF(
outputFileStream.good(),
"Failed to open file " << fileName,
ErrorType::BadFileRead);
outputFileStream.write(reinterpret_cast<const char*>(memoryBuffer.getDirectPointer()), long(memoryBuffer.getSize()));
outputFileStream.close();
return true;
}
bool CScenarioManager::exportScenarioToFile(const CIdentifier& exportContext, const CString& fileName, const CIdentifier& scenarioID)
{
OV_ERROR_UNLESS_KRF(m_exporters.count(exportContext),
"The export context " << exportContext.str() << " has no associated exporters",
ErrorType::Internal);
std::vector<char> fileNameExtension;
fileNameExtension.resize(fileName.length() + 1);
FS::Files::getFilenameExtension(fileName.toASCIIString(), &fileNameExtension[0]);
OV_ERROR_UNLESS_KRF(m_exporters[exportContext].count(&fileNameExtension[0]),
"The export context " << exportContext.str() << " has no associated exporters for extension [" << &fileNameExtension[0] << "]",
ErrorType::Internal);
return this->exportScenarioToFile(fileName, scenarioID, m_exporters[exportContext][&fileNameExtension[0]]);
}
bool CScenarioManager::registerScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension,
const CIdentifier& scenarioExporterAlgorithmIdentifier)
{
if (!m_exporters.count(exportContext)) { m_exporters[exportContext] = std::map<std::string, CIdentifier>(); }
OV_ERROR_UNLESS_KRF(!m_exporters[exportContext].count(fileNameExtension.toASCIIString()),
"The file name extension [" << fileNameExtension << "] already has an exporter registered for context " << exportContext.str(),
ErrorType::Internal);
m_exporters[exportContext][fileNameExtension.toASCIIString()] = scenarioExporterAlgorithmIdentifier;
return true;
}
bool CScenarioManager::unregisterScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension)
{
OV_ERROR_UNLESS_KRF(m_exporters.count(exportContext),
"The export context " << exportContext.str() << " has no associated exporters",
ErrorType::Internal);
OV_ERROR_UNLESS_KRF(m_exporters[exportContext].count(fileNameExtension.toASCIIString()),
"The export context " << exportContext.str() << " has no associated exporters for extension [" << fileNameExtension << "]",
ErrorType::Internal);
auto& contextExporters = m_exporters[exportContext];
for (auto it = contextExporters.begin(); it != contextExporters.end();)
{
if (it->first.c_str() == fileNameExtension.toASCIIString()) { it = contextExporters.erase(it); }
else { ++it; }
}
for (auto it = m_exporters.begin(); it != m_exporters.end();)
{
if (it->second.empty()) { it = m_exporters.erase(it); }
else { ++it; }
}
return true;
}
CIdentifier CScenarioManager::getNextScenarioExportContext(const CIdentifier& exportContext) const
{
if (m_exporters.empty()) { return CIdentifier::undefined(); }
if (exportContext == CIdentifier::undefined()) { return m_exporters.cbegin()->first; }
auto current = m_exporters.find(exportContext);
if (current == m_exporters.end() || ++current == m_exporters.end()) { return CIdentifier::undefined(); }
return current->first;
}
CString CScenarioManager::getNextScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension) const
{
if (m_exporters.empty() || !m_exporters.count(exportContext)) { return ""; }
const auto& scenarioExportContextMap = m_exporters.at(exportContext);
if (fileNameExtension == CString("")) { return scenarioExportContextMap.cbegin()->first.c_str(); }
auto current = scenarioExportContextMap.find(fileNameExtension.toASCIIString());
if (current == scenarioExportContextMap.end() || ++current == scenarioExportContextMap.end()) { return ""; }
return current->first.c_str();
}
CIdentifier CScenarioManager::getScenarioExporterAlgorithmIdentifier(const CIdentifier& exportContext, const CString& fileNameExtension) const
{
OV_ERROR_UNLESS_KRU(!m_exporters.empty() && m_exporters.count(exportContext) && m_exporters.at(exportContext).count(fileNameExtension.toASCIIString()),
"Scenario importer not found", Kernel::ErrorType::OutOfBound);
return m_exporters.at(exportContext).at(fileNameExtension.toASCIIString());
}
bool CScenarioManager::releaseScenario(const CIdentifier& scenarioID)
{
const auto it = m_scenarios.find(scenarioID);
if (it == m_scenarios.end())
{
// error is handled on a higher level
return false;
}
CScenario* scenario = it->second;
delete scenario;
m_scenarios.erase(it);
return true;
}
IScenario& CScenarioManager::getScenario(const CIdentifier& scenarioID)
{
const auto itScenario = m_scenarios.find(scenarioID);
// If the call is wrongly handled, and falls in this condition then next instruction causes a crash...
// At least, here the abortion is handled!
OV_FATAL_UNLESS_K(itScenario != m_scenarios.end(), "Scenario " << scenarioID.str() << " does not exist !", Kernel::ErrorType::ResourceNotFound);
return *itScenario->second;
}
IScenario& CScenarioManager::getScenario(const CIdentifier& scenarioID) const { return const_cast<CScenarioManager*>(this)->getScenario(scenarioID); }
CIdentifier CScenarioManager::getUnusedIdentifier() const
{
uint64_t id = (uint64_t(rand()) << 32) + uint64_t(rand());
CIdentifier res;
std::map<CIdentifier, CScenario*>::const_iterator i;
do
{
id++;
res = CIdentifier(id);
i = m_scenarios.find(res);
} while (i != m_scenarios.end() || res == CIdentifier::undefined());
return res;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,58 @@
#pragma once
#include "../ovkTKernelObject.h"
#include <map>
namespace OpenViBE {
namespace Kernel {
class CScenario;
class CScenarioManager final : public TKernelObject<IScenarioManager>
{
public:
explicit CScenarioManager(const IKernelContext& ctx);
~CScenarioManager() override;
void cloneScenarioImportersAndExporters(const IScenarioManager& scenarioManager) override;
CIdentifier getNextScenarioIdentifier(const CIdentifier& previousID) const override;
bool isScenario(const CIdentifier& scenarioID) const override;
bool createScenario(CIdentifier& scenarioID) override;
bool importScenario(CIdentifier& scenarioID, const IMemoryBuffer& iMemoryBuffer, const CIdentifier& scenarioImporterAlgorithmID) override;
bool importScenarioFromFile(CIdentifier& scenarioID, const CString& fileName, const CIdentifier& scenarioImporterAlgorithmID) override;
bool importScenarioFromFile(CIdentifier& scenarioID, const CIdentifier& importContext, const CString& fileName) override;
bool registerScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension,
const CIdentifier& scenarioImporterAlgorithmIdentifier) override;
bool unregisterScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension) override;
CIdentifier getNextScenarioImportContext(const CIdentifier& importContext) const override;
CString getNextScenarioImporter(const CIdentifier& importContext, const CString& fileNameExtension) const override;
CIdentifier getScenarioImporterAlgorithmIdentifier(const CIdentifier& importContext, const CString& fileNameExtension) const override;
bool exportScenario(IMemoryBuffer& oMemoryBuffer, const CIdentifier& scenarioID, const CIdentifier& scenarioExporterAlgorithmID) const override;
bool exportScenarioToFile(const CString& fileName, const CIdentifier& scenarioID, const CIdentifier& scenarioExporterAlgorithmID) const override;
bool exportScenarioToFile(const CIdentifier& exportContext, const CString& fileName, const CIdentifier& scenarioID) override;
bool registerScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension,
const CIdentifier& scenarioExporterAlgorithmIdentifier) override;
bool unregisterScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension) override;
CIdentifier getNextScenarioExportContext(const CIdentifier& exportContext) const override;
CString getNextScenarioExporter(const CIdentifier& exportContext, const CString& fileNameExtension) const override;
CIdentifier getScenarioExporterAlgorithmIdentifier(const CIdentifier& exportContext, const CString& fileNameExtension) const override;
bool releaseScenario(const CIdentifier& scenarioID) override;
IScenario& getScenario(const CIdentifier& scenarioID) override;
_IsDerivedFromClass_Final_(TKernelObject<IScenarioManager>, OVK_ClassId_Kernel_Scenario_ScenarioManager)
protected:
CIdentifier getUnusedIdentifier() const;
std::map<CIdentifier, CScenario*> m_scenarios;
private:
/// Scenario Import Context -> File Name Extension -> Scenario Importer Identifier
std::map<CIdentifier, std::map<std::string, CIdentifier>> m_importers;
/// Scenario Export Context -> File Name Extension -> Scenario Exporter Identifier
std::map<CIdentifier, std::map<std::string, CIdentifier>> m_exporters;
IScenario& getScenario(const CIdentifier& scenarioID) const;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,36 @@
#include "ovkCScenarioSettingKeywordParserCallback.h"
#include <openvibe/ov_all.h>
#include <iostream>
namespace OpenViBE {
namespace Kernel {
bool CScenarioSettingKeywordParserCallback::expand(const CString& rStringToExpand, CString& rExpandedString) const
{
// In the case there is no value present we return an empty string
rExpandedString = "";
// Expand the scenario directory
if (rStringToExpand == CString("ScenarioDirectory"))
{
if (m_rScenario.hasAttribute(OV_AttributeId_ScenarioFilename))
{
const std::string filename = m_rScenario.getAttributeValue(OV_AttributeId_ScenarioFilename).toASCIIString();
const size_t iDir = filename.rfind('/');
if (iDir != std::string::npos) { rExpandedString = CString(filename.substr(0, iDir).c_str()); }
}
return true;
}
// Expand settings from the scenario
if (m_rScenario.hasSettingWithName(rStringToExpand))
{
m_rScenario.getSettingValue(rStringToExpand, rExpandedString);
return true;
}
return false;
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,20 @@
#pragma once
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Kernel {
class CScenarioSettingKeywordParserCallback final : public IConfigurationKeywordExpandCallback
{
public:
explicit CScenarioSettingKeywordParserCallback(const IScenario& scenario)
: m_rScenario(scenario) {}
~CScenarioSettingKeywordParserCallback() override {}
bool expand(const CString& rStringToExpand, CString& rExpandedString) const override;
private:
const IScenario& m_rScenario;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,77 @@
#pragma once
#include "../../ovk_tools.h"
#include <openvibe/ov_all.h>
#include <map>
namespace OpenViBE {
namespace Kernel {
template <class T>
class TAttributable : public T
{
public:
explicit TAttributable(const IKernelContext& ctx) : T(ctx) { }
bool addAttribute(const CIdentifier& attributeID, const CString& sAttributeValue) override
{
const auto itAttribute = m_attributes.find(attributeID);
if (itAttribute != m_attributes.end()) { return false; }
m_attributes[attributeID] = sAttributeValue;
return true;
}
bool removeAttribute(const CIdentifier& attributeID) override
{
const auto itAttribute = m_attributes.find(attributeID);
if (itAttribute == m_attributes.end()) { return false; }
m_attributes.erase(itAttribute);
return true;
}
bool removeAllAttributes() override
{
m_attributes.clear();
return true;
}
CString getAttributeValue(const CIdentifier& attributeID) const override
{
const auto itAttribute = m_attributes.find(attributeID);
if (itAttribute == m_attributes.end()) { return CString(""); }
return itAttribute->second;
}
bool setAttributeValue(const CIdentifier& attributeID, const CString& value) override
{
auto itAttribute = m_attributes.find(attributeID);
if (itAttribute == m_attributes.end())
{
m_attributes[attributeID] = value;
return true;
}
itAttribute->second = value;
return true;
}
bool hasAttribute(const CIdentifier& attributeID) const override
{
const auto itAttribute = m_attributes.find(attributeID);
if (itAttribute == m_attributes.end()) { return false; }
return true;
}
bool hasAttributes() const override { return !m_attributes.empty(); }
CIdentifier getNextAttributeIdentifier(const CIdentifier& previousID) const override { return getNextIdentifier<CString>(m_attributes, previousID); }
_IsDerivedFromClass_(T, OVK_ClassId_Kernel_Scenario_AttributableT)
protected:
std::map<CIdentifier, CString> m_attributes;
};
} // namespace Kernel
} // namespace OpenViBE
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
#pragma once
#include "ovk_defines.h"
#include "ovk_tools.h"
+114
View File
@@ -0,0 +1,114 @@
#pragma once
// Global Class Identifiers
//---------------------------------------------------------------------------------------------------
#define OVK_ClassId_KernelDesc OpenViBE::CIdentifier(0x02F2D050, 0x25CD9EE5)
// Kernel Class Identifiers
//---------------------------------------------------------------------------------------------------
#define OVK_ClassId_Kernel_KernelObjectT OpenViBE::CIdentifier(0x672AE465, 0x60CB46D8)
#define OVK_ClassId_Kernel_KernelObjectFactory OpenViBE::CIdentifier(0x7D380DFA, 0x1B33AE2F)
#define OVK_ClassId_Kernel_KernelContext OpenViBE::CIdentifier(0x72D4050C, 0x543DDAD8)
#define OVK_ClassId_Kernel_TypeManager OpenViBE::CIdentifier(0x11D27788, 0x0602030A)
#define OVK_ClassId_Kernel_Algorithm_AlgorithmProxy OpenViBE::CIdentifier(0x7C741F2F, 0x24314CFF)
#define OVK_ClassId_Kernel_Algorithm_AlgorithmContext OpenViBE::CIdentifier(0xBE3E7F65, 0x2752D79E)
#define OVK_ClassId_Kernel_Algorithm_AlgorithmManager OpenViBE::CIdentifier(0xA75B6AA8, 0xEE0A25A4)
#define OVK_ClassId_Kernel_Algorithm_AlgorithmProto OpenViBE::CIdentifier(0x3C83B501, 0xD5992078)
#define OVK_ClassId_Kernel_Config_ConfigManager OpenViBE::CIdentifier(0xE483786B, 0xA464DD55)
#define OVK_ClassId_Kernel_Log_LogManager OpenViBE::CIdentifier(0x1FE5CF01, 0x0B2B3748)
#define OVK_ClassId_Kernel_Log_LogListenerConsole OpenViBE::CIdentifier(0x1EEB056C, 0x373C17C7)
#define OVK_ClassId_Kernel_Log_LogListenerFile OpenViBE::CIdentifier(0x6A7FCB0C, 0xC847976D)
#define OVK_ClassId_Kernel_Log_LogListenerDebug OpenViBE::CIdentifier(0x45C1A72F, 0x647DE336)
#define OVK_ClassId_Kernel_Log_LogListenerNull OpenViBE::CIdentifier(0x1341E7E6, 0x2232EB75)
#define OVK_ClassId_Kernel_Error_ErrorManager OpenViBE::CIdentifier(0xB4F8AA7C, 0x286FE5C6)
#define OVK_ClassId_Kernel_Error_Error OpenViBE::CIdentifier(0xA52EA1C9, 0xC14FC982)
#define OVK_ClassId_Kernel_Plugins_PluginManager OpenViBE::CIdentifier(0x07DB32AD, 0x76FAD392)
#define OVK_ClassId_Kernel_Plugins_PluginModule OpenViBE::CIdentifier(0x34883FDB, 0x78B6D1E6)
#define OVK_ClassId_Kernel_Plugins_PluginModuleContext OpenViBE::CIdentifier(0x78071145, 0x308261A2)
#define OVK_ClassId_Kernel_Plugins_PluginObject OpenViBE::CIdentifier(0x718F6C0B, 0x368B2E6C)
#define OVK_ClassId_Kernel_Plugins_PluginObjectDesc OpenViBE::CIdentifier(0x65853ECE, 0x6E203E71)
#define OVK_ClassId_Kernel_Metaboxes_MetaboxManager OpenViBE::CIdentifier(0x07DB34DA, 0xF67AD392)
#define OVK_ClassId_Kernel_Metaboxes_MetaboxObjectDesc OpenViBE::CIdentifier(0x100E6855, 0xFF905FEA)
#define OVK_ClassId_Kernel_Player_BoxAlgorithmContext OpenViBE::CIdentifier(0x48078FB2, 0x1897AD5A)
#define OVK_ClassId_Kernel_Player_DynamicBoxContext OpenViBE::CIdentifier(0x18FC58E0, 0x505E97CD)
#define OVK_ClassId_Kernel_Player_MessageT OpenViBE::CIdentifier(0x42A56E80, 0x289CBB09)
#define OVK_ClassId_Kernel_Player_MessageClock OpenViBE::CIdentifier(0x3AA34472, 0x2B1F67CB)
#define OVK_ClassId_Kernel_Player_Player OpenViBE::CIdentifier(0x7C8777FF, 0x52CE89C2)
#define OVK_ClassId_Kernel_Player_PlayerContext OpenViBE::CIdentifier(0x76A544A0, 0x01F508ED)
#define OVK_ClassId_Kernel_Player_PlayerManager OpenViBE::CIdentifier(0x4A46A96D, 0x6D4957C5)
#define OVK_ClassId_Kernel_Player_SimulatedBox OpenViBE::CIdentifier(0x74D37E85, 0x73952B1D)
#define OVK_ClassId_Kernel_Player_StaticBoxContext OpenViBE::CIdentifier(0x59B7AE07, 0x4A6FE704)
#define OVK_ClassId_Kernel_Player_Scheduler OpenViBE::CIdentifier(0x39A9093F, 0x363DDB65)
#define OVK_ClassId_Kernel_Scenario_AttributableT OpenViBE::CIdentifier(0x6F4071A4, 0x31183474)
#define OVK_ClassId_Kernel_Scenario_Box OpenViBE::CIdentifier(0x715A25DC, 0x5322E94C)
#define OVK_ClassId_Kernel_Scenario_BoxUpdater OpenViBE::CIdentifier(0x65E887AD, 0x25FA9EEF)
#define OVK_ClassId_Kernel_Scenario_Comment OpenViBE::CIdentifier(0xFD134BE1, 0x32BEC860)
#define OVK_ClassId_Kernel_Scenario_Metadata OpenViBE::CIdentifier(0xFC6AAE55, 0x7965FA25)
#define OVK_ClassId_Kernel_Scenario_BoxListenerContext OpenViBE::CIdentifier(0x6EBE7BA6, 0xF8C13ACA)
#define OVK_ClassId_Kernel_Scenario_BoxProto OpenViBE::CIdentifier(0x290EDACD, 0x6D08F87D)
#define OVK_ClassId_Kernel_Scenario_Link OpenViBE::CIdentifier(0x118EF337, 0x095F5A5E)
#define OVK_ClassId_Kernel_Scenario_Scenario OpenViBE::CIdentifier(0x4215A6BF, 0x6F4E5F96)
#define OVK_ClassId_Kernel_Scenario_ScenarioManager OpenViBE::CIdentifier(0x5BB96551, 0x133303F9)
#define OVK_ClassId_Kernel_Scenario_ScenarioExporterContext OpenViBE::CIdentifier(0x78606B83, 0x5EB11EC9)
#define OVK_ClassId_Kernel_Scenario_ScenarioImporterContext OpenViBE::CIdentifier(0x17DC0F51, 0x795A3A7E)
#define OVK_ClassId_Kernel_ObjectVisitorContext OpenViBE::CIdentifier(0x7F6010A3, 0x17F4F20A)
// Tools Class Identifiers
//---------------------------------------------------------------------------------------------------
#define OVK_ClassId_Tools_ScopeTester OpenViBE::CIdentifier(0x5978C732, 0x5903C7A2)
#define OVK_ClassId_Tools_KernelObjectFactoryHelper OpenViBE::CIdentifier(0x6059EA7C, 0x081FDAB5)
#define OVK_ClassId_Tools_BoxProxyT OpenViBE::CIdentifier(0x4DFDFCE4, 0x49EF078E)
#define OVK_ClassId_Tools_BoxIOProxyT OpenViBE::CIdentifier(0x25476613, 0x331633DA)
#define OVK_ClassId_Kernel_Configurable OpenViBE::CIdentifier(0x1C1D34A4, 0x2EAB9FA1)
#define OVK_ClassId_Kernel_ConfigurableT OpenViBE::CIdentifier(0x6500E672, 0x7520EB07)
#define OVK_ClassId_Kernel_ParameterT OpenViBE::CIdentifier(0x6DF62060, 0x65A40A62)
#define OVK_ClassId_Kernel_UIntegerParameter OpenViBE::CIdentifier(0x665A47DC, 0x650630EE)
#define OVK_ClassId_Kernel_IntegerParameter OpenViBE::CIdentifier(0x52DA0F06, 0x45155D4F)
#define OVK_ClassId_Kernel_EnumerationParameter OpenViBE::CIdentifier(0x1825F2CA, 0x5589B8E2)
#define OVK_ClassId_Kernel_BooleanParameter OpenViBE::CIdentifier(0x4E79D376, 0x151D2473)
#define OVK_ClassId_Kernel_FloatParameter OpenViBE::CIdentifier(0x37B24229, 0x6A053984)
#define OVK_ClassId_Kernel_StringParameter OpenViBE::CIdentifier(0x4AA96293, 0x2B76B89C)
#define OVK_ClassId_Kernel_IdentifierParameter OpenViBE::CIdentifier(0x1C4D8B4C, 0x67CFF22B)
#define OVK_ClassId_Kernel_MatrixParameter OpenViBE::CIdentifier(0xB1C67A8B, 0xED6BD64D)
#define OVK_ClassId_Kernel_StimulationSetParameter OpenViBE::CIdentifier(0x758D94E1, 0x40F42B99)
#define OVK_ClassId_Kernel_MemoryBufferParameter OpenViBE::CIdentifier(0x8E21C24F, 0xD77B7E6D)
#define OVK_ClassId_Kernel_ObjectParameter OpenViBE::CIdentifier(0x5A9CB52C, 0x7F02D159)
#define OVK_ClassId_Kernel_PointerParameter OpenViBE::CIdentifier(0x29BDDFF0, 0x85B669E2)
// API Definition
//---------------------------------------------------------------------------------------------------
// Taken from
// - http://people.redhat.com/drepper/dsohowto.pdf
// - http://www.nedprod.com/programs/gccvisibility.html
#if defined OVK_Shared
#if defined TARGET_OS_Windows
#define OVK_API_Export __declspec(dllexport)
#define OVK_API_Import __declspec(dllimport)
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#define OVK_API_Export __attribute__((visibility("default")))
#define OVK_API_Import __attribute__((visibility("default")))
#else
#define OVK_API_Export
#define OVK_API_Import
#endif
#else
#define OVK_API_Export
#define OVK_API_Import
#endif
#if defined OVK_Exports
#define OVK_API OVK_API_Export
#else
#define OVK_API OVK_API_Import
#endif
@@ -0,0 +1,46 @@
#include "kernel/ovkCKernelContext.h"
#include <openvibe/ov_all.h>
#include <system/ovCTime.h>
namespace OpenViBE {
namespace Kernel {
class CKernelDesc final : public IKernelDesc
{
public:
IKernelContext* createKernel(const CString& rApplicationName, const CString& rConfigurationFilename) override
{
return new CKernelContext(nullptr, rApplicationName, rConfigurationFilename);
}
IKernelContext* createKernel(const IKernelContext& masterKernelCtx, const CString& applicationName, const CString& configFilename)
override { return new CKernelContext(&masterKernelCtx, applicationName, configFilename); }
void releaseKernel(IKernelContext* pKernelContext) override { delete pKernelContext; }
CString getName() const override { return "OpenViBE Kernel Implementation"; }
CString getAuthorName() const override { return "Yann Renard"; }
CString getAuthorCompanyName() const override { return "INRIA/IRISA"; }
CString getShortDescription() const override { return "OpenViBE Kernel Implementation"; }
CString getDetailedDescription() const override { return "OpenViBE Kernel Implementation"; }
CString getVersion() const override { return "0.5"; }
_IsDerivedFromClass_Final_(IKernelDesc, OVK_ClassId_KernelDesc)
};
static CKernelDesc gKernelDesc;
extern "C" {
OVK_API bool onInitialize() { return true; }
OVK_API bool onUninitialize() { return true; }
OVK_API bool onGetKernelDesc(IKernelDesc*& rpKernelDesc)
{
rpKernelDesc = &gKernelDesc;
return true;
}
}
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,6 @@
#pragma once
#include "tools/ovkCKernelObjectFactoryHelper.h"
#include "tools/ovkCScopeTester.h"
#include "tools/ovk_identifier_map_iterator.h"
@@ -0,0 +1,37 @@
#pragma once
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Tools {
class CKernelObjectFactoryHelper final : public IObject
{
public:
explicit CKernelObjectFactoryHelper(Kernel::IKernelObjectFactory& rKernelObjectFactory)
: m_rKernelObjectFactory(rKernelObjectFactory) { }
template <class T>
T createObject(const CIdentifier& classID)
{
IObject* obj = m_rKernelObjectFactory.createObject(classID);
T res = dynamic_cast<T>(obj);
if (obj && !res) { m_rKernelObjectFactory.releaseObject(obj); }
return res;
}
template <class T>
bool releaseObject(T tObject) { return m_rKernelObjectFactory.releaseObject(tObject); }
_IsDerivedFromClass_Final_(IObject, OVK_ClassId_Tools_KernelObjectFactoryHelper)
protected:
Kernel::IKernelObjectFactory& m_rKernelObjectFactory;
private:
CKernelObjectFactoryHelper();
};
} // namespace Tools
} // namespace OpenViBE
@@ -0,0 +1,25 @@
#pragma once
#include "../ovk_base.h"
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Tools {
class CScopeTester final : public IObject
{
public:
CScopeTester(const Kernel::IKernelContext& ctx, const CString& prefix)
: m_prefix(prefix), m_kernelCtx(ctx) { m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "## CScopeTester [" << m_prefix << "] enter\n"; }
~CScopeTester() override { m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "## CScopeTester [" << m_prefix << "] leave\n"; }
_IsDerivedFromClass_Final_(IObject, OVK_ClassId_Tools_ScopeTester)
protected:
CString m_prefix;
const Kernel::IKernelContext& m_kernelCtx;
};
} // namespace Tools
} // namespace OpenViBE
@@ -0,0 +1,155 @@
#pragma once
#include <openvibe/kernel/scenario/ovIBoxProto.h>
#include <system/ovCMemory.h>
namespace OpenViBE {
namespace Kernel {
struct SBoxProto final : IBoxProto
{
SBoxProto(ITypeManager& typeManager) : m_TypeManager(typeManager) { }
bool addInput(const CString& /*name*/, const CIdentifier& typeID, const CIdentifier& id, const bool /*notify*/) override
{
uint64_t v = typeID.id();
swap_byte(v, m_inputCountHash);
swap_byte(m_inputCountHash, 0x7936A0F3BD12D936LL);
m_hash = m_hash.id() ^ v;
if (id != CIdentifier::undefined())
{
v = id.id();
swap_byte(v, 0x2BD1D158F340014D);
m_hash = m_hash.id() ^ v;
}
return true;
}
bool addOutput(const CString& /*name*/, const CIdentifier& typeID, const CIdentifier& id, const bool /*notify*/) override
{
uint64_t v = typeID.id();
swap_byte(v, m_outputCountHash);
swap_byte(m_outputCountHash, 0xCBB66A5B893AA4E9LL);
m_hash = m_hash.id() ^ v;
if (id != CIdentifier::undefined())
{
v = id.id();
swap_byte(v, 0x87CA0F5EFC4FAC68);
m_hash = m_hash.id() ^ v;
}
return true;
}
bool addSetting(const CString& /*name*/, const CIdentifier& typeID, const CString& /*defaultValue*/, const bool /*modifiable*/, const CIdentifier& id,
const bool /*notify*/) override
{
uint64_t v = typeID.id();
swap_byte(v, m_settingCountHash);
swap_byte(m_settingCountHash, 0x3C87F3AAE9F8303BLL);
m_hash = m_hash.id() ^ v;
if (id != CIdentifier::undefined())
{
v = id.id();
swap_byte(v, 0x17185F7CDA63A9FA);
m_hash = m_hash.id() ^ v;
}
return true;
}
bool addInputSupport(const CIdentifier& typeID) override
{
uint64_t v = typeID.id();
swap_byte(v, m_outputCountHash);
swap_byte(m_outputCountHash, 0xCBB66A5B893AA4E9LL);
m_hash = m_hash.id() ^ v;
return true;
}
bool addInputAndDerivedSupport(const CIdentifier& typeID)
{
uint64_t v = typeID.id();
swap_byte(v, m_outputCountHash);
swap_byte(m_outputCountHash, 0xCBB66A5B893AA4E9LL);
m_hash = m_hash.id() ^ v;
return true;
}
bool addOutputSupport(const CIdentifier& typeID) override
{
uint64_t v = typeID.id();
swap_byte(v, m_outputCountHash);
swap_byte(m_outputCountHash, 0xCBB66A5B893AA4E9LL);
m_hash = m_hash.id() ^ v;
return true;
}
bool addOutputAndDerivedSupport(const CIdentifier& typeID)
{
uint64_t v = typeID.id();
swap_byte(v, m_outputCountHash);
swap_byte(m_outputCountHash, 0xCBB66A5B893AA4E9LL);
m_hash = m_hash.id() ^ v;
return true;
}
bool addFlag(const EBoxFlag flag) override
{
switch (flag)
{
case BoxFlag_CanAddInput: m_hash = m_hash.id() ^ CIdentifier(0x07507AC8, 0xEB643ACE).id();
break;
case BoxFlag_CanModifyInput: m_hash = m_hash.id() ^ CIdentifier(0x5C985376, 0x8D74CDB8).id();
break;
case BoxFlag_CanAddOutput: m_hash = m_hash.id() ^ CIdentifier(0x58DEA69B, 0x12411365).id();
break;
case BoxFlag_CanModifyOutput: m_hash = m_hash.id() ^ CIdentifier(0x6E162C01, 0xAC979F22).id();
break;
case BoxFlag_CanAddSetting: m_hash = m_hash.id() ^ CIdentifier(0xFA7A50DC, 0x2140C013).id();
break;
case BoxFlag_CanModifySetting: m_hash = m_hash.id() ^ CIdentifier(0x624D7661, 0xD8DDEA0A).id();
break;
case BoxFlag_IsDeprecated: m_isDeprecated = true;
break;
default:
return false;
}
return true;
}
bool addFlag(const CIdentifier& flagId) override
{
const uint64_t value = m_TypeManager.getEnumerationEntryValueFromName(OV_TypeId_BoxAlgorithmFlag, flagId.toString());
if (value == CIdentifier::undefined().id()) { return false; }
// Flags do not modify internal hash
//m_hash=m_hash.id() ^ flagId.id();
return true;
}
void swap_byte(uint64_t& v, const uint64_t s)
{
uint8_t v2[sizeof(v)];
uint8_t s2[sizeof(s)];
System::Memory::hostToLittleEndian(v, v2);
System::Memory::hostToLittleEndian(s, s2);
for (size_t i = 0; i < sizeof(s); i += 2)
{
const size_t j = s2[i] % sizeof(v);
const size_t k = s2[i + 1] % sizeof(v);
const uint8_t t = v2[j];
v2[j] = v2[k];
v2[k] = t;
}
System::Memory::littleEndianToHost(v2, &v);
}
_IsDerivedFromClass_Final_(IBoxProto, CIdentifier::undefined())
CIdentifier m_hash = CIdentifier::undefined();
bool m_isDeprecated = false;
uint64_t m_inputCountHash = 0x64AC3CB54A35888CLL;
uint64_t m_outputCountHash = 0x21E0FAAFE5CAF1E1LL;
uint64_t m_settingCountHash = 0x6BDFB15B54B09F63LL;
ITypeManager& m_TypeManager;
};
} // namespace Kernel
} // namespace OpenViBE
@@ -0,0 +1,21 @@
#pragma once
#include <openvibe/ov_all.h>
#include <map>
template <class T>
OpenViBE::CIdentifier getNextIdentifier(const std::map<OpenViBE::CIdentifier, T>& map, const OpenViBE::CIdentifier& previousID)
{
typename std::map<OpenViBE::CIdentifier, T>::const_iterator it;
if (previousID == OpenViBE::CIdentifier::undefined()) { it = map.begin(); }
else
{
it = map.find(previousID);
if (it == map.end()) { return OpenViBE::CIdentifier::undefined(); }
++it;
}
return it != map.end() ? it->first : OpenViBE::CIdentifier::undefined();
}
@@ -0,0 +1,55 @@
#pragma once
#include "lepton/Lepton.h"
#include "../ovk_base.h"
#include <openvibe/kernel/ovITypeManager.h>
#include <algorithm>
#include <string>
#include <functional>
#include <cctype>
// because std::tolower has multiple signatures,
// it can not be easily used in std::transform
// this workaround is taken from http://www.gcek.net/ref/books/sw/cpp/ticppv2/
template <class TCharT>
TCharT ToLower(TCharT c) { return std::tolower(c); }
/**
* \brief Check the setting value (if the setting is numeric),
* test if it is a correct arithmetic expression, should be used after
* retrieving the value with "getSettingValue"
* \param value [in] : The value of setting to check
* \param typeID [in] : The type of setting to check
* \param typeManager [in] :
* \return \e true in case of success (numeric value is well formed).
* \return \e false in case of error. In such case,
* \c value remains unchanged.
*/
inline bool checkSettingValue(const OpenViBE::CString& value, const OpenViBE::CIdentifier& typeID, const OpenViBE::Kernel::ITypeManager& typeManager)
{
if (typeManager.isEnumeration(typeID))
{
const auto enumerationEntryValue = typeManager.getEnumerationEntryValueFromName(typeID, value);
const auto enumerationEntryReversedName = typeManager.getEnumerationEntryNameFromValue(typeID, enumerationEntryValue);
// We need to compare the reversed name of the enumerations because some enumeration values actually use max int
// which is the same value as the guard value for incorrect stimulations
if (enumerationEntryValue == OV_IncorrectStimulation && enumerationEntryReversedName != value) { return false; }
}
else if (typeID == OV_TypeId_Float || typeID == OV_TypeId_Integer)
{
// If the token is a numeric value, it may be an arithmetic operation
// parse and expression with no variables or functions
try { Lepton::Parser::parse(value.toASCIIString()).evaluate(); }
catch (...) { return false; }
}
else if (typeID == OV_TypeId_Boolean)
{
std::string val = value.toASCIIString();
std::transform(val.begin(), val.end(), val.begin(), ::ToLower<std::string::value_type>);
if (!(val == "true" || val == "on" || val == "1" || val == "false" || val == "off" || val == "0")) { return false; }
}
//TODO: else
return true;
}