1461 lines
52 KiB
C++
Executable File
1461 lines
52 KiB
C++
Executable File
#include "ovkCScenario.h"
|
|
#include "ovkCScenarioSettingKeywordParserCallback.h"
|
|
|
|
#include "ovkTBox.hpp"
|
|
#include "ovkCBoxUpdater.h"
|
|
#include "ovkCComment.h"
|
|
#include "ovkCMetadata.h"
|
|
#include "ovkCLink.h"
|
|
|
|
#include "../ovkCObjectVisitorContext.h"
|
|
#include "../../tools/ovk_setting_checker.h"
|
|
|
|
#include <iostream>
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
|
|
#define OV_AttributeId_Box_Disabled OpenViBE::CIdentifier(0x341D3912, 0x1478DE86)
|
|
#define OVD_AttributeId_SettingOverrideFilename OpenViBE::CIdentifier(0x8D21FF41, 0xDF6AFE7E)
|
|
|
|
// The following is a hack, can be removed once there is a copy constructor for scenarios, boxes, etc
|
|
#include <ovp_global_defines.h>
|
|
//___________________________________________________________________//
|
|
// //
|
|
namespace OpenViBE {
|
|
namespace Kernel {
|
|
|
|
namespace {
|
|
template <class T>
|
|
struct STestTrue
|
|
{
|
|
bool operator()(typename std::map<CIdentifier, T>::const_iterator /*it*/) const { return true; }
|
|
};
|
|
|
|
struct STestEqSourceBox
|
|
{
|
|
explicit STestEqSourceBox(const CIdentifier& boxId) : m_BoxId(boxId) { }
|
|
bool operator()(const std::map<CIdentifier, CLink*>::const_iterator& it) const { return it->second->getSourceBoxIdentifier() == m_BoxId; }
|
|
const CIdentifier& m_BoxId;
|
|
};
|
|
|
|
struct STestEqSourceBoxOutput
|
|
{
|
|
STestEqSourceBoxOutput(const CIdentifier& boxId, const size_t index) : m_BoxId(boxId), m_OutputIdx(index) { }
|
|
|
|
bool operator()(const std::map<CIdentifier, CLink*>::const_iterator& it) const
|
|
{
|
|
return it->second->getSourceBoxIdentifier() == m_BoxId && it->second->getSourceBoxOutputIndex() == m_OutputIdx;
|
|
}
|
|
|
|
const CIdentifier& m_BoxId;
|
|
size_t m_OutputIdx;
|
|
};
|
|
|
|
struct STestEqTargetBox
|
|
{
|
|
explicit STestEqTargetBox(const CIdentifier& boxId) : m_BoxId(boxId) { }
|
|
bool operator()(const std::map<CIdentifier, CLink*>::const_iterator& it) const { return it->second->getTargetBoxIdentifier() == m_BoxId; }
|
|
const CIdentifier& m_BoxId;
|
|
};
|
|
|
|
struct STestEqTargetBoxInput
|
|
{
|
|
STestEqTargetBoxInput(const CIdentifier& boxId, const size_t index) : m_BoxId(boxId), m_InputIdx(index) { }
|
|
|
|
bool operator()(const std::map<CIdentifier, CLink*>::const_iterator& it) const
|
|
{
|
|
return it->second->getTargetBoxIdentifier() == m_BoxId && it->second->getTargetBoxInputIndex() == m_InputIdx;
|
|
}
|
|
|
|
const CIdentifier& m_BoxId;
|
|
size_t m_InputIdx;
|
|
};
|
|
|
|
template <class T, class TTest>
|
|
CIdentifier getNextID(const std::map<CIdentifier, T>& elementMap, const CIdentifier& previousID, const TTest& testFunctor)
|
|
{
|
|
typename std::map<CIdentifier, T>::const_iterator it;
|
|
|
|
if (previousID == CIdentifier::undefined()) { it = elementMap.begin(); }
|
|
else
|
|
{
|
|
it = elementMap.find(previousID);
|
|
if (it == elementMap.end()) { return CIdentifier::undefined(); }
|
|
++it;
|
|
}
|
|
|
|
while (it != elementMap.end())
|
|
{
|
|
if (testFunctor(it)) { return it->first; }
|
|
++it;
|
|
}
|
|
|
|
return CIdentifier::undefined();
|
|
}
|
|
|
|
/*
|
|
template <class T, class TTest>
|
|
CIdentifier getNextID(const std::map<CIdentifier, T*>& elementMap, const CIdentifier& previousID, const TTest& testFunctor)
|
|
{
|
|
typename std::map<CIdentifier, T*>::const_iterator it;
|
|
|
|
if(previousID==CIdentifier::undefined())
|
|
{
|
|
it=elementMap.begin();
|
|
}
|
|
else
|
|
{
|
|
it=elementMap.find(previousID);
|
|
if(it==elementMap.end()) { return CIdentifier::undefined(); }
|
|
++it;
|
|
}
|
|
|
|
while(it!=elementMap.end())
|
|
{
|
|
if(testFunctor(it)) { return it->first; }
|
|
++it;
|
|
}
|
|
|
|
return CIdentifier::undefined();
|
|
}
|
|
*/
|
|
} // namespace
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
CScenario::CScenario(const IKernelContext& ctx, const CIdentifier& identifier) : TBox<IScenario>(ctx), m_firstMetadataID(CIdentifier::undefined())
|
|
{
|
|
// Some operations on boxes manipulate the owner scenario, for example removing inputs
|
|
// by default we set the scenario as owning itself to avoid segfaults
|
|
this->setOwnerScenario(this);
|
|
this->m_identifier = identifier;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
bool CScenario::clear()
|
|
{
|
|
this->getLogManager() << LogLevel_Debug << "Clearing scenario\n";
|
|
|
|
for (auto& box : m_boxes) { delete box.second; }
|
|
m_boxes.clear();
|
|
|
|
for (auto& comment : m_comments) { delete comment.second; }
|
|
m_comments.clear();
|
|
|
|
for (auto& metadata : m_metadatas) { delete metadata.second; }
|
|
m_metadatas.clear();
|
|
m_firstMetadataID = CIdentifier::undefined();
|
|
m_nextMetadataID.clear();
|
|
|
|
for (auto& link : m_links) { delete link.second; }
|
|
m_links.clear();
|
|
|
|
m_outdatedBoxes.clear();
|
|
|
|
while (this->getSettingCount()) { this->removeSetting(0); }
|
|
while (this->getInputCount()) { this->removeScenarioInput(0); }
|
|
while (this->getOutputCount()) { this->removeScenarioOutput(0); }
|
|
|
|
this->removeAllAttributes();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::removeScenarioInput(const size_t index)
|
|
{
|
|
OV_ERROR_UNLESS_KRF(index < this->getInputCount(), "Input index = [" << index << "] is out of range (max index = [" << (this->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
this->removeInput(index);
|
|
|
|
// Remove the link within the scenario to this input
|
|
if (index < m_scenarioInputLinks.size()) { m_scenarioInputLinks.erase(m_scenarioInputLinks.begin() + index); }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::removeScenarioOutput(const size_t index)
|
|
{
|
|
OV_ERROR_UNLESS_KRF(index < this->getOutputCount(),
|
|
"Output index = [" << index << "] is out of range (max index = [" << (this->getOutputCount() - 1) << "])",
|
|
Kernel::ErrorType::OutOfBound);
|
|
|
|
this->removeOutput(index);
|
|
|
|
// Remove the link within the scenario to this output
|
|
if (index < m_scenarioOutputLinks.size()) { m_scenarioOutputLinks.erase(m_scenarioOutputLinks.begin() + index); }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::merge(const IScenario& scenario, IScenarioMergeCallback* scenarioMergeCallback, const bool mergeSettings, const bool preserveIDs)
|
|
{
|
|
std::map<CIdentifier, CIdentifier> oldToNewIdMap;
|
|
|
|
// Copy boxes
|
|
{
|
|
CIdentifier* listID = nullptr;
|
|
size_t nbElems = 0;
|
|
scenario.getBoxIdentifierList(&listID, &nbElems);
|
|
for (size_t i = 0; i < nbElems; ++i)
|
|
{
|
|
CIdentifier boxID = listID[i];
|
|
const IBox* box = scenario.getBoxDetails(boxID);
|
|
CIdentifier newID;
|
|
CIdentifier suggestedNewID = preserveIDs ? box->getIdentifier() : CIdentifier::undefined();
|
|
this->addBox(newID, *box, suggestedNewID);
|
|
oldToNewIdMap[boxID] = newID;
|
|
|
|
if (scenarioMergeCallback) { scenarioMergeCallback->process(boxID, newID); }
|
|
}
|
|
scenario.releaseIdentifierList(listID);
|
|
}
|
|
|
|
// Copy links
|
|
{
|
|
CIdentifier* listID = nullptr;
|
|
size_t nbElems = 0;
|
|
scenario.getLinkIdentifierList(&listID, &nbElems);
|
|
for (size_t i = 0; i < nbElems; ++i)
|
|
{
|
|
CIdentifier linkID = listID[i];
|
|
const ILink* link = scenario.getLinkDetails(linkID);
|
|
CIdentifier newID;
|
|
this->connect(newID, oldToNewIdMap[link->getSourceBoxIdentifier()],
|
|
link->getSourceBoxOutputIndex(), oldToNewIdMap[link->getTargetBoxIdentifier()],
|
|
link->getTargetBoxInputIndex(), CIdentifier::undefined());
|
|
|
|
if (scenarioMergeCallback) { scenarioMergeCallback->process(linkID, newID); }
|
|
}
|
|
scenario.releaseIdentifierList(listID);
|
|
}
|
|
// Copy comments
|
|
|
|
// Copy metadata
|
|
{
|
|
CIdentifier* listID = nullptr;
|
|
size_t nbElems = 0;
|
|
scenario.getMetadataIdentifierList(&listID, &nbElems);
|
|
for (size_t i = 0; i < nbElems; ++i)
|
|
{
|
|
CIdentifier metadataID = listID[i];
|
|
const IMetadata* metadata = scenario.getMetadataDetails(metadataID);
|
|
CIdentifier newID;
|
|
CIdentifier suggestedNewID = preserveIDs ? metadataID : CIdentifier::undefined();
|
|
this->addMetadata(newID, suggestedNewID);
|
|
IMetadata* newMetadata = this->getMetadataDetails(newID);
|
|
newMetadata->initializeFromExistingMetadata(*metadata);
|
|
}
|
|
scenario.releaseIdentifierList(listID);
|
|
}
|
|
|
|
// Copy settings if requested
|
|
|
|
const size_t previousSettingCount = this->getSettingCount();
|
|
|
|
if (mergeSettings)
|
|
{
|
|
for (size_t i = 0; i < scenario.getSettingCount(); ++i)
|
|
{
|
|
CIdentifier typeID;
|
|
CString name;
|
|
CString defaultValue;
|
|
CString value;
|
|
bool isModifiable;
|
|
CIdentifier id;
|
|
scenario.getSettingType(i, typeID);
|
|
scenario.getSettingName(i, name);
|
|
scenario.getSettingDefaultValue(i, defaultValue);
|
|
scenario.getSettingValue(i, value);
|
|
scenario.getSettingMod(i, isModifiable);
|
|
scenario.getInterfacorIdentifier(Setting, i, id);
|
|
|
|
this->addSetting(name, typeID, defaultValue, size_t(-1), isModifiable, id, true);
|
|
this->setSettingValue(previousSettingCount + i, value);
|
|
}
|
|
|
|
// In this case we also merge the attributes
|
|
CIdentifier attributeIdentifier;
|
|
while ((attributeIdentifier = scenario.getNextAttributeIdentifier(attributeIdentifier)) != CIdentifier::undefined())
|
|
{
|
|
CString attributeValue = scenario.getAttributeValue(attributeIdentifier);
|
|
this->addAttribute(attributeIdentifier, attributeValue);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
CIdentifier CScenario::getNextBoxIdentifier(const CIdentifier& previousID) const
|
|
{
|
|
return getNextID<CBox*, STestTrue<CBox*>>(m_boxes, previousID, STestTrue<CBox*>());
|
|
}
|
|
|
|
const IBox* CScenario::getBoxDetails(const CIdentifier& boxID) const
|
|
{
|
|
const auto it = m_boxes.find(boxID);
|
|
OV_ERROR_UNLESS_KRN(it != m_boxes.end(), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::isBox(const CIdentifier& boxID) const { return m_boxes.count(boxID) == 1; }
|
|
|
|
IBox* CScenario::getBoxDetails(const CIdentifier& boxID)
|
|
{
|
|
// this->getLogManager() << Kernel::LogLevel_Debug << "Getting box details from scenario\n";
|
|
const auto it = m_boxes.find(boxID);
|
|
OV_ERROR_UNLESS_KRN(it != m_boxes.end(), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::addBox(CIdentifier& boxID, const CIdentifier& suggestedBoxID)
|
|
{
|
|
boxID = getUnusedIdentifier(suggestedBoxID);
|
|
CBox* box = new CBox(this->getKernelContext());
|
|
box->setOwnerScenario(this);
|
|
box->setIdentifier(boxID);
|
|
|
|
m_boxes[boxID] = box;
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::addBox(CIdentifier& boxID, const IBox& box, const CIdentifier& suggestedBoxID)
|
|
{
|
|
if (!addBox(boxID, suggestedBoxID))
|
|
{
|
|
// error is handled in addBox
|
|
return false;
|
|
}
|
|
|
|
IBox* newBox = getBoxDetails(boxID);
|
|
if (!newBox)
|
|
{
|
|
// error is handled in getBoxDetails
|
|
return false;
|
|
}
|
|
|
|
return newBox->initializeFromExistingBox(box);
|
|
}
|
|
|
|
bool CScenario::addBox(CIdentifier& boxID, const CIdentifier& boxAlgorithmID, const CIdentifier& suggestedBoxID)
|
|
{
|
|
if (!addBox(boxID, suggestedBoxID))
|
|
{
|
|
// error is handled in addBox
|
|
return false;
|
|
}
|
|
|
|
IBox* newBox = getBoxDetails(boxID);
|
|
if (!newBox)
|
|
{
|
|
// error is handled in getBoxDetails
|
|
return false;
|
|
}
|
|
|
|
return newBox->initializeFromAlgorithmClassIdentifier(boxAlgorithmID);
|
|
}
|
|
|
|
bool CScenario::addBox(CIdentifier& boxID, const Plugins::IBoxAlgorithmDesc& boxAlgorithmDesc, const CIdentifier& suggestedBoxID)
|
|
{
|
|
if (!addBox(boxID, suggestedBoxID))
|
|
{
|
|
// error is handled in addBox
|
|
return false;
|
|
}
|
|
|
|
IBox* newBox = getBoxDetails(boxID);
|
|
if (!newBox)
|
|
{
|
|
// error is handled in getBoxDetails
|
|
return false;
|
|
}
|
|
|
|
return dynamic_cast<CBox*>(newBox)->initializeFromBoxAlgorithmDesc(boxAlgorithmDesc);
|
|
}
|
|
|
|
bool CScenario::removeBox(const CIdentifier& boxID)
|
|
{
|
|
// Finds the box according to its identifier
|
|
auto itBox = m_boxes.find(boxID);
|
|
|
|
OV_ERROR_UNLESS_KRF(itBox != m_boxes.end(), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
// Find all the links that are used by this box
|
|
auto itLink = m_links.begin();
|
|
while (itLink != m_links.end())
|
|
{
|
|
auto itLinkCurrent = itLink;
|
|
++itLink;
|
|
|
|
if (itLinkCurrent->second->getSourceBoxIdentifier() == boxID || itLinkCurrent->second->getTargetBoxIdentifier() == boxID)
|
|
{
|
|
// Deletes this link
|
|
delete itLinkCurrent->second;
|
|
|
|
// Removes link from the link list
|
|
m_links.erase(itLinkCurrent);
|
|
}
|
|
}
|
|
|
|
// Deletes the box itself
|
|
delete itBox->second;
|
|
|
|
// Removes box from the box list
|
|
m_boxes.erase(itBox);
|
|
|
|
return true;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
|
|
CIdentifier CScenario::getNextCommentIdentifier(const CIdentifier& previousID) const
|
|
{
|
|
return getNextID<CComment*, STestTrue<CComment*>>(m_comments, previousID, STestTrue<CComment*>());
|
|
}
|
|
|
|
const IComment* CScenario::getCommentDetails(const CIdentifier& commentID) const
|
|
{
|
|
const auto it = m_comments.find(commentID);
|
|
OV_ERROR_UNLESS_KRN(it != m_comments.end(), "Comment [" << commentID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::isComment(const CIdentifier& commentID) const { return m_comments.find(commentID) != m_comments.end(); }
|
|
|
|
IComment* CScenario::getCommentDetails(const CIdentifier& commentID)
|
|
{
|
|
const auto it = m_comments.find(commentID);
|
|
OV_ERROR_UNLESS_KRN(it != m_comments.end(), "Comment [" << commentID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::addComment(CIdentifier& commentID, const CIdentifier& suggestedCommentID)
|
|
{
|
|
commentID = getUnusedIdentifier(suggestedCommentID);
|
|
CComment* newComment = new CComment(this->getKernelContext(), *this);
|
|
newComment->setIdentifier(commentID);
|
|
|
|
m_comments[commentID] = newComment;
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::addComment(CIdentifier& commentID, const IComment& comment, const CIdentifier& suggestedCommentID)
|
|
{
|
|
if (!addComment(commentID, suggestedCommentID))
|
|
{
|
|
// error is handled in addComment above
|
|
return false;
|
|
}
|
|
|
|
IComment* newComment = getCommentDetails(commentID);
|
|
if (!newComment)
|
|
{
|
|
// error is handled in getCommentDetails
|
|
return false;
|
|
}
|
|
|
|
return newComment->initializeFromExistingComment(comment);
|
|
}
|
|
|
|
bool CScenario::removeComment(const CIdentifier& commentID)
|
|
{
|
|
// Finds the comment according to its identifier
|
|
auto it = m_comments.find(commentID);
|
|
OV_ERROR_UNLESS_KRF(it != m_comments.end(), "Comment [" << commentID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
delete it->second; // Deletes the comment itself
|
|
m_comments.erase(it); // Removes comment from the comment list
|
|
return true;
|
|
}
|
|
|
|
CIdentifier CScenario::getNextMetadataIdentifier(const CIdentifier& previousID) const
|
|
{
|
|
if (previousID == CIdentifier::undefined()) { return m_firstMetadataID; }
|
|
if (m_metadatas.count(previousID) == 0) { return CIdentifier::undefined(); }
|
|
return m_nextMetadataID.at(previousID);
|
|
}
|
|
|
|
const IMetadata* CScenario::getMetadataDetails(const CIdentifier& metadataID) const
|
|
{
|
|
const auto it = m_metadatas.find(metadataID);
|
|
OV_ERROR_UNLESS_KRN(it != m_metadatas.end(), "Metadata [" << metadataID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
IMetadata* CScenario::getMetadataDetails(const CIdentifier& metadataID)
|
|
{
|
|
const auto it = m_metadatas.find(metadataID);
|
|
OV_ERROR_UNLESS_KRN(it != m_metadatas.end(), "Metadata [" << metadataID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::isMetadata(const CIdentifier& metadataID) const { return m_metadatas.count(metadataID) > 0; }
|
|
|
|
bool CScenario::addMetadata(CIdentifier& metadataID, const CIdentifier& suggestedMetadataID)
|
|
{
|
|
metadataID = getUnusedIdentifier(suggestedMetadataID);
|
|
CMetadata* metadata = new CMetadata(this->getKernelContext(), *this);
|
|
metadata->setIdentifier(metadataID);
|
|
|
|
m_nextMetadataID[metadataID] = m_firstMetadataID;
|
|
m_firstMetadataID = metadataID;
|
|
m_metadatas[metadataID] = metadata;
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::removeMetadata(const CIdentifier& metadataID)
|
|
{
|
|
// Finds the comment according to its identifier
|
|
auto itMetadata = m_metadatas.find(metadataID);
|
|
|
|
OV_ERROR_UNLESS_KRF(itMetadata != m_metadatas.end(), "Comment [" << metadataID.str() << "] is not part of the scenario",
|
|
Kernel::ErrorType::ResourceNotFound);
|
|
|
|
// Deletes the metadata and remove it from the cache
|
|
delete itMetadata->second;
|
|
|
|
m_metadatas.erase(itMetadata);
|
|
|
|
if (metadataID == m_firstMetadataID)
|
|
{
|
|
m_firstMetadataID = m_nextMetadataID[m_firstMetadataID];
|
|
m_nextMetadataID.erase(metadataID);
|
|
}
|
|
else
|
|
{
|
|
const auto previousID = std::find_if(m_nextMetadataID.begin(), m_nextMetadataID.end(), [metadataID](const std::pair<CIdentifier, CIdentifier>& v)
|
|
{
|
|
return v.second == metadataID;
|
|
});
|
|
|
|
OV_FATAL_UNLESS_K(previousID != m_nextMetadataID.end(), "Removing metadata [" << metadataID << "] which is not in the cache ",
|
|
Kernel::ErrorType::Internal);
|
|
|
|
m_nextMetadataID[previousID->first] = m_nextMetadataID[metadataID];
|
|
m_nextMetadataID.erase(metadataID);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Links
|
|
|
|
CIdentifier CScenario::getNextLinkIdentifier(const CIdentifier& previousID) const
|
|
{
|
|
return getNextID<CLink*, STestTrue<CLink*>>(m_links, previousID, STestTrue<CLink*>());
|
|
}
|
|
|
|
CIdentifier CScenario::getNextLinkIdentifierFromBox(const CIdentifier& previousID, const CIdentifier& boxID) const
|
|
{
|
|
return getNextID<CLink*, STestEqSourceBox>(m_links, previousID, STestEqSourceBox(boxID));
|
|
}
|
|
|
|
CIdentifier CScenario::getNextLinkIdentifierFromBoxOutput(const CIdentifier& previousID, const CIdentifier& boxID, const size_t index) const
|
|
{
|
|
return getNextID<CLink*, STestEqSourceBoxOutput>(m_links, previousID, STestEqSourceBoxOutput(boxID, index));
|
|
}
|
|
|
|
CIdentifier CScenario::getNextLinkIdentifierToBox(const CIdentifier& previousID, const CIdentifier& boxID) const
|
|
{
|
|
return getNextID<CLink*, STestEqTargetBox>(m_links, previousID, STestEqTargetBox(boxID));
|
|
}
|
|
|
|
CIdentifier CScenario::getNextLinkIdentifierToBoxInput(const CIdentifier& previousID, const CIdentifier& boxID, const size_t index) const
|
|
{
|
|
return getNextID<CLink*, STestEqTargetBoxInput>(m_links, previousID, STestEqTargetBoxInput(boxID, index));
|
|
}
|
|
|
|
bool CScenario::isLink(const CIdentifier& boxID) const
|
|
{
|
|
const auto itLink = m_links.find(boxID);
|
|
return itLink != m_links.end();
|
|
}
|
|
|
|
bool CScenario::setHasIO(const bool hasIO)
|
|
{
|
|
m_hasIO = hasIO;
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::hasIO() const { return m_hasIO; }
|
|
|
|
bool CScenario::setScenarioInputLink(const size_t scenarioInputIdx, const CIdentifier& boxID, const size_t boxInputIdx)
|
|
{
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(
|
|
scenarioInputIdx < this->getInputCount(),
|
|
"Scenario Input index = [" << scenarioInputIdx << "] is out of range (max index = [" << (this->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
OV_ERROR_UNLESS_KRF(
|
|
this->isBox(boxID),
|
|
"Box [" << boxID.str() << "] is not part of the scenario",
|
|
ErrorType::ResourceNotFound);
|
|
|
|
OV_ERROR_UNLESS_KRF(
|
|
boxInputIdx < this->getBoxDetails(boxID)->getInputCount(),
|
|
"Box Input index = [" << boxInputIdx << "] is out of range (max index = [" << (this->getBoxDetails(boxID)->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
}
|
|
|
|
if (scenarioInputIdx >= m_scenarioInputLinks.size()) { m_scenarioInputLinks.resize(this->getInputCount()); }
|
|
|
|
// Remove any existing inputs connected to the target
|
|
for (size_t i = 0; i < m_scenarioInputLinks.size(); ++i)
|
|
{
|
|
CIdentifier id;
|
|
size_t index;
|
|
this->getScenarioInputLink(i, id, index);
|
|
|
|
if (id == boxID && index == boxInputIdx) { this->removeScenarioInputLink(i, id, index); }
|
|
}
|
|
|
|
// Remove any existing link to this input
|
|
for (auto& kv : m_links)
|
|
{
|
|
CIdentifier linkID = kv.first;
|
|
const CLink* link = kv.second;
|
|
if (link->getTargetBoxIdentifier() == boxID && link->getTargetBoxInputIndex() == boxInputIdx) { this->disconnect(linkID); }
|
|
}
|
|
|
|
m_scenarioInputLinks[scenarioInputIdx] = std::make_pair(boxID, boxInputIdx);
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::setScenarioInputLink(const size_t scenarioInputIdx, const CIdentifier& boxID, const CIdentifier& boxInputID)
|
|
{
|
|
size_t boxInputIdx = size_t(-1);
|
|
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(this->isBox(boxID), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
this->getBoxDetails(boxID)->getInterfacorIndex(Input, boxInputID, boxInputIdx);
|
|
}
|
|
return this->setScenarioInputLink(scenarioInputIdx, boxID, boxInputIdx);
|
|
}
|
|
|
|
bool CScenario::setScenarioOutputLink(const size_t scenarioOutputIdx, const CIdentifier& boxID, const size_t boxOutputIdx)
|
|
{
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(scenarioOutputIdx < this->getOutputCount(),
|
|
"Scenario output index = [" << scenarioOutputIdx << "] is out of range (max index = [" << (this->getOutputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
OV_ERROR_UNLESS_KRF(this->isBox(boxID),
|
|
"Box [" << boxID.str() << "] is not part of the scenario",
|
|
ErrorType::ResourceNotFound);
|
|
|
|
OV_ERROR_UNLESS_KRF(boxOutputIdx < this->getBoxDetails(boxID)->getOutputCount(),
|
|
"Box output index = [" << boxOutputIdx << "] is out of range (max index = [" << (this->getBoxDetails(boxID)->getOutputCount() - 1)
|
|
<< "])",
|
|
ErrorType::OutOfBound);
|
|
}
|
|
|
|
if (scenarioOutputIdx >= m_scenarioOutputLinks.size()) { m_scenarioOutputLinks.resize(this->getOutputCount()); }
|
|
|
|
// Remove any existing outputs connected to the target
|
|
for (size_t i = 0; i < m_scenarioOutputLinks.size(); ++i)
|
|
{
|
|
CIdentifier id;
|
|
size_t index;
|
|
this->getScenarioOutputLink(i, id, index);
|
|
|
|
if (id == boxID && index == boxOutputIdx) { this->removeScenarioOutputLink(i, id, index); }
|
|
}
|
|
|
|
m_scenarioOutputLinks[scenarioOutputIdx] = std::make_pair(boxID, boxOutputIdx);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::setScenarioOutputLink(const size_t scenarioOutputIdx, const CIdentifier& boxID, const CIdentifier& boxOutputID)
|
|
{
|
|
size_t boxOutputIdx = size_t(-1);
|
|
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(this->isBox(boxID), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
this->getBoxDetails(boxID)->getInterfacorIndex(Output, boxOutputID, boxOutputIdx);
|
|
}
|
|
return this->setScenarioOutputLink(scenarioOutputIdx, boxID, boxOutputIdx);
|
|
}
|
|
|
|
bool CScenario::getScenarioInputLink(const size_t scenarioInputIdx, CIdentifier& boxID, size_t& boxInputIdx) const
|
|
{
|
|
OV_ERROR_UNLESS_KRF(scenarioInputIdx < this->getInputCount(),
|
|
"Scenario input index = [" << scenarioInputIdx << "] is out of range (max index = [" << (this->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
if (scenarioInputIdx >= m_scenarioInputLinks.size()) { m_scenarioInputLinks.resize(this->getInputCount()); }
|
|
|
|
boxID = m_scenarioInputLinks[scenarioInputIdx].first;
|
|
boxInputIdx = m_scenarioInputLinks[scenarioInputIdx].second;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getScenarioInputLink(const size_t scenarioInputIdx, CIdentifier& boxID, CIdentifier& boxOutputID) const
|
|
{
|
|
size_t index;
|
|
boxOutputID = CIdentifier::undefined();
|
|
|
|
this->getScenarioInputLink(scenarioInputIdx, boxID, index);
|
|
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
if (m_boxes.find(boxID) != m_boxes.end()) { this->getBoxDetails(boxID)->getInterfacorIdentifier(Input, index, boxOutputID); }
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getScenarioOutputLink(const size_t scenarioOutputIdx, CIdentifier& boxID, size_t& boxOutputIdx) const
|
|
{
|
|
OV_ERROR_UNLESS_KRF(scenarioOutputIdx < this->getOutputCount(),
|
|
"Scenario output index = [" << scenarioOutputIdx << "] is out of range (max index = [" << (this->getOutputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
if (scenarioOutputIdx >= m_scenarioOutputLinks.size()) { m_scenarioOutputLinks.resize(this->getOutputCount()); }
|
|
|
|
boxID = m_scenarioOutputLinks[scenarioOutputIdx].first;
|
|
boxOutputIdx = m_scenarioOutputLinks[scenarioOutputIdx].second;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getScenarioOutputLink(const size_t scenarioOutputIdx, CIdentifier& boxID, CIdentifier& boxOutputID) const
|
|
{
|
|
size_t index;
|
|
boxOutputID = CIdentifier::undefined();
|
|
|
|
this->getScenarioOutputLink(scenarioOutputIdx, boxID, index);
|
|
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
if (m_boxes.find(boxID) != m_boxes.end()) { this->getBoxDetails(boxID)->getInterfacorIdentifier(Output, index, boxOutputID); }
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Note: In current implementation only the scenarioInputIdx is necessary as it can only be connected to one input
|
|
// but to keep things simpler we give it all the info
|
|
bool CScenario::removeScenarioInputLink(const size_t scenarioInputIdx, const CIdentifier& boxID, const size_t boxInputIdx)
|
|
{
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(scenarioInputIdx < this->getInputCount(),
|
|
"Scenario Input index = [" << scenarioInputIdx << "] is out of range (max index = [" << (this->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
OV_ERROR_UNLESS_KRF(this->isBox(boxID), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
OV_ERROR_UNLESS_KRF(boxInputIdx < this->getBoxDetails(boxID)->getInputCount(),
|
|
"Box Input index = [" << boxInputIdx << "] is out of range (max index = [" << (this->getBoxDetails(boxID)->getInputCount() - 1) <<
|
|
"])",
|
|
ErrorType::OutOfBound);
|
|
}
|
|
|
|
// This should not happen either
|
|
if (scenarioInputIdx >= m_scenarioInputLinks.size()) { m_scenarioInputLinks.resize(this->getInputCount()); }
|
|
|
|
m_scenarioInputLinks[scenarioInputIdx] = std::make_pair(CIdentifier::undefined(), 0);
|
|
return true;
|
|
}
|
|
|
|
// Note: In current implementation only the scenarioOutputIdx is necessary as it can only be connected to one Output
|
|
// but to keep things simpler we give it all the info
|
|
bool CScenario::removeScenarioOutputLink(const size_t scenarioOutputIdx, const CIdentifier& boxID, const size_t boxOutputIdx)
|
|
{
|
|
if (boxID != CIdentifier::undefined())
|
|
{
|
|
OV_ERROR_UNLESS_KRF(scenarioOutputIdx < this->getOutputCount(),
|
|
"Scenario output index = [" << scenarioOutputIdx << "] is out of range (max index = [" << (this->getOutputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
OV_ERROR_UNLESS_KRF(this->isBox(boxID), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
OV_ERROR_UNLESS_KRF(boxOutputIdx < this->getBoxDetails(boxID)->getOutputCount(),
|
|
"Box output index = [" << boxOutputIdx << "] is out of range (max index = [" << (this->getBoxDetails(boxID)->getOutputCount() - 1)
|
|
<< "])",
|
|
ErrorType::OutOfBound);
|
|
}
|
|
|
|
// This should not happen either
|
|
if (scenarioOutputIdx >= m_scenarioOutputLinks.size()) { m_scenarioOutputLinks.resize(this->getOutputCount()); }
|
|
|
|
m_scenarioOutputLinks[scenarioOutputIdx] = std::make_pair(CIdentifier::undefined(), 0);
|
|
return true;
|
|
}
|
|
|
|
const ILink* CScenario::getLinkDetails(const CIdentifier& linkID) const
|
|
{
|
|
const auto it = m_links.find(linkID);
|
|
OV_ERROR_UNLESS_KRN(it != m_links.end(), "link [" << linkID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
ILink* CScenario::getLinkDetails(const CIdentifier& linkID)
|
|
{
|
|
const auto it = m_links.find(linkID);
|
|
OV_ERROR_UNLESS_KRN(it != m_links.end(), "Link [" << linkID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
return it->second;
|
|
}
|
|
|
|
bool CScenario::connect(CIdentifier& linkID, const CIdentifier& srcBoxID, const size_t srcBoxOutputIdx,
|
|
const CIdentifier& dstBoxID, const size_t dstBoxInputIdx, const CIdentifier& suggestedLinkID)
|
|
{
|
|
const auto itBox1 = m_boxes.find(srcBoxID);
|
|
const auto itBox2 = m_boxes.find(dstBoxID);
|
|
|
|
OV_ERROR_UNLESS_KRF(itBox1 != m_boxes.end(), "Source Box [" << srcBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
OV_ERROR_UNLESS_KRF(itBox2 != m_boxes.end(), "Target Box [" << dstBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
CBox* srcBox = itBox1->second;
|
|
CBox* dstBox = itBox2->second;
|
|
|
|
OV_ERROR_UNLESS_KRF(dstBoxInputIdx < dstBox->getInterfacorCountIncludingDeprecated(Input),
|
|
"Target box input index = [" << dstBoxInputIdx << "] is out of range (max index = [" << (dstBox->getInputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
OV_ERROR_UNLESS_KRF(srcBoxOutputIdx < srcBox->getInterfacorCountIncludingDeprecated(Output),
|
|
"Source box output index = [" << srcBoxOutputIdx << "] is out of range (max index = [" << (srcBox->getOutputCount() - 1) << "])",
|
|
ErrorType::OutOfBound);
|
|
|
|
// Looks for any connected link to this box input and removes it
|
|
auto itLink = m_links.begin();
|
|
while (itLink != m_links.end())
|
|
{
|
|
const auto itLinkCurrent = itLink;
|
|
++itLink;
|
|
|
|
CLink* link = itLinkCurrent->second;
|
|
if (link)
|
|
{
|
|
if (link->getTargetBoxIdentifier() == dstBoxID && link->getTargetBoxInputIndex() == dstBoxInputIdx)
|
|
{
|
|
delete link;
|
|
m_links.erase(itLinkCurrent);
|
|
}
|
|
}
|
|
}
|
|
|
|
linkID = getUnusedIdentifier(suggestedLinkID);
|
|
|
|
CLink* link = new CLink(this->getKernelContext(), *this);
|
|
CIdentifier srcBoxOutputID;
|
|
CIdentifier dstBoxInputID;
|
|
|
|
this->getSourceBoxOutputIdentifier(srcBoxID, srcBoxOutputIdx, srcBoxOutputID);
|
|
this->getTargetBoxInputIdentifier(dstBoxID, dstBoxInputIdx, dstBoxInputID);
|
|
|
|
link->setIdentifier(linkID);
|
|
link->setSource(srcBoxID, srcBoxOutputIdx, srcBoxOutputID);
|
|
link->setTarget(dstBoxID, dstBoxInputIdx, dstBoxInputID);
|
|
|
|
m_links[link->getIdentifier()] = link;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::connect(CIdentifier& linkID, const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID,
|
|
const CIdentifier& dstBoxID, const CIdentifier& dstBoxInputID, const CIdentifier& suggestedLinkID)
|
|
{
|
|
size_t srcBoxOutputIdx;
|
|
size_t dstBoxInputIdx;
|
|
|
|
this->getSourceBoxOutputIndex(srcBoxID, srcBoxOutputID, srcBoxOutputIdx);
|
|
this->getTargetBoxInputIndex(dstBoxID, dstBoxInputID, dstBoxInputIdx);
|
|
|
|
return this->connect(linkID, srcBoxID, srcBoxOutputIdx, dstBoxID, dstBoxInputIdx, suggestedLinkID);
|
|
}
|
|
|
|
|
|
bool CScenario::disconnect(const CIdentifier& srcBoxID, const size_t srcBoxOutputIdx, const CIdentifier& dstBoxID, const size_t dstBoxInputIdx)
|
|
{
|
|
// Looks for any link with the same signature
|
|
for (auto itLink = m_links.begin(); itLink != m_links.end(); ++itLink)
|
|
{
|
|
CLink* link = itLink->second;
|
|
if (link)
|
|
{
|
|
if (link->getTargetBoxIdentifier() == dstBoxID && link->getTargetBoxInputIndex() == dstBoxInputIdx)
|
|
{
|
|
if (link->getSourceBoxIdentifier() == srcBoxID && link->getSourceBoxOutputIndex() == srcBoxOutputIdx)
|
|
{
|
|
// Found a link, so removes it
|
|
delete link;
|
|
m_links.erase(itLink);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OV_ERROR_KRF("Link is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
}
|
|
|
|
bool CScenario::disconnect(const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID, const CIdentifier& dstBoxID, const CIdentifier& dstBoxInputID)
|
|
{
|
|
size_t srcBoxOutputIdx;
|
|
size_t dstBoxInputIdx;
|
|
|
|
this->getSourceBoxOutputIndex(srcBoxID, srcBoxOutputID, srcBoxOutputIdx);
|
|
this->getTargetBoxInputIndex(dstBoxID, dstBoxInputID, dstBoxInputIdx);
|
|
|
|
return this->disconnect(srcBoxID, srcBoxOutputIdx, dstBoxID, dstBoxInputIdx);
|
|
}
|
|
|
|
bool CScenario::disconnect(const CIdentifier& linkID)
|
|
{
|
|
// Finds the link according to its identifier
|
|
auto itLink = m_links.find(linkID);
|
|
|
|
OV_ERROR_UNLESS_KRF(itLink != m_links.end(), "Link [" << linkID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
// Deletes the link itself
|
|
delete itLink->second;
|
|
|
|
// Removes link from the link list
|
|
m_links.erase(itLink);
|
|
|
|
return true;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
bool CScenario::applyLocalSettings()
|
|
{
|
|
for (auto& box : m_boxes)
|
|
{
|
|
// Expand all the variables inside the newly created scenario by replacing only the $var variables
|
|
CScenarioSettingKeywordParserCallback scenarioSettingKeywordParserCallback(*this);
|
|
this->getConfigurationManager().registerKeywordParser("var", scenarioSettingKeywordParserCallback);
|
|
|
|
for (size_t settingIndex = 0; settingIndex < box.second->getSettingCount(); ++settingIndex)
|
|
{
|
|
CString settingName = "";
|
|
CString settingValue = "";
|
|
|
|
box.second->getSettingName(settingIndex, settingName);
|
|
box.second->getSettingValue(settingIndex, settingValue);
|
|
|
|
box.second->setSettingValue(settingIndex, this->getConfigurationManager().expandOnlyKeyword("var", settingValue, true));
|
|
box.second->getSettingValue(settingIndex, settingValue);
|
|
|
|
if (box.second->hasAttribute(OVD_AttributeId_SettingOverrideFilename))
|
|
{
|
|
settingValue = box.second->getAttributeValue(OVD_AttributeId_SettingOverrideFilename);
|
|
box.second->setAttributeValue(
|
|
OVD_AttributeId_SettingOverrideFilename, this->getConfigurationManager().expandOnlyKeyword("var", settingValue, true));
|
|
}
|
|
}
|
|
|
|
this->getConfigurationManager().unregisterKeywordParser("var");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::isMetabox()
|
|
{
|
|
// A scenario with inputs and/or outputs is a metabox
|
|
if (this->getInputCount() + this->getOutputCount() > 0) { return true; }
|
|
|
|
// TODO_JL: Find a way to check for other conditions as well
|
|
|
|
return false;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
bool CScenario::acceptVisitor(IObjectVisitor& objectVisitor)
|
|
{
|
|
CObjectVisitorContext objectVisitorContext(getKernelContext());
|
|
|
|
if (!objectVisitor.processBegin(objectVisitorContext, *this)) { return false; }
|
|
for (auto& box : m_boxes) { if (!box.second->acceptVisitor(objectVisitor)) { return false; } }
|
|
for (auto& comment : m_comments) { if (!comment.second->acceptVisitor(objectVisitor)) { return false; } }
|
|
for (auto& link : m_links) { if (!link.second->acceptVisitor(objectVisitor)) { return false; } }
|
|
if (!objectVisitor.processEnd(objectVisitorContext, *this)) { return false; }
|
|
|
|
return true;
|
|
}
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
CIdentifier CScenario::getUnusedIdentifier(const CIdentifier& suggestedID) const
|
|
{
|
|
uint64_t newID = (uint64_t(rand()) << 32) + uint64_t(rand());
|
|
if (suggestedID != CIdentifier::undefined()) { newID = suggestedID.id() - 1; }
|
|
|
|
CIdentifier result;
|
|
std::map<CIdentifier, CBox*>::const_iterator itBox;
|
|
std::map<CIdentifier, CComment*>::const_iterator itComment;
|
|
std::map<CIdentifier, CLink*>::const_iterator itLink;
|
|
do
|
|
{
|
|
newID++;
|
|
result = CIdentifier(newID);
|
|
itBox = m_boxes.find(result);
|
|
itComment = m_comments.find(result);
|
|
itLink = m_links.find(result);
|
|
} while (itBox != m_boxes.end() || itComment != m_comments.end() || itLink != m_links.end() || result == CIdentifier::undefined());
|
|
return result;
|
|
}
|
|
|
|
bool CScenario::checkSettings(IConfigurationManager* configurationManager)
|
|
{
|
|
for (auto& box : m_boxes)
|
|
{
|
|
if (!box.second->hasAttribute(OV_AttributeId_Box_Disabled))
|
|
{
|
|
this->applyLocalSettings();
|
|
// Expand all the variables inside the newly created scenario by replacing only the $var variables
|
|
CScenarioSettingKeywordParserCallback scenarioSettingKeywordParserCallback(*this);
|
|
this->getConfigurationManager().registerKeywordParser("var", scenarioSettingKeywordParserCallback);
|
|
|
|
for (size_t settingIndex = 0; settingIndex < box.second->getSettingCount(); ++settingIndex)
|
|
{
|
|
CString settingName = "";
|
|
CString rawSettingValue = "";
|
|
CIdentifier typeID;
|
|
|
|
if (box.second->hasAttribute(OVD_AttributeId_SettingOverrideFilename)) { return true; }
|
|
box.second->getSettingName(settingIndex, settingName);
|
|
box.second->getSettingValue(settingIndex, rawSettingValue);
|
|
box.second->getSettingType(settingIndex, typeID);
|
|
|
|
CString settingValue = rawSettingValue;
|
|
if (configurationManager) { settingValue = configurationManager->expand(settingValue); }
|
|
else { settingValue = this->getConfigurationManager().expandOnlyKeyword("var", settingValue); }
|
|
|
|
const auto settingTypeName = this->getTypeManager().getTypeName(typeID);
|
|
|
|
OV_ERROR_UNLESS_KRF(
|
|
::checkSettingValue(settingValue, typeID, this->getTypeManager()),
|
|
"<" << box.second->getName() << "> The following value: ["<< rawSettingValue <<"] expanded as ["<< settingValue <<
|
|
"] given as setting is not a valid [" << settingTypeName << "] value.",
|
|
ErrorType::BadValue);
|
|
}
|
|
|
|
this->getConfigurationManager().unregisterKeywordParser("var");
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//___________________________________________________________________//
|
|
// //
|
|
|
|
CIdentifier CScenario::getNextOutdatedBoxIdentifier(const CIdentifier& previousID) const
|
|
{
|
|
return getNextID<std::shared_ptr<CBox>, STestTrue<std::shared_ptr<CBox>>>(m_outdatedBoxes, previousID, STestTrue<std::shared_ptr<CBox>>());
|
|
}
|
|
|
|
bool CScenario::hasOutdatedBox()
|
|
{
|
|
for (auto& box : m_boxes) { if (box.second->hasAttribute(OV_AttributeId_Box_ToBeUpdated)) { return true; } }
|
|
return false;
|
|
}
|
|
|
|
bool CScenario::isBoxOutdated(const CIdentifier& boxId)
|
|
{
|
|
IBox* box = getBoxDetails(boxId);
|
|
if (!box) { return false; }
|
|
CIdentifier boxHashCode1;
|
|
CIdentifier boxHashCode2;
|
|
if (box->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
|
|
{
|
|
CIdentifier metaboxId;
|
|
metaboxId.fromString(box->getAttributeValue(OVP_AttributeId_Metabox_ID));
|
|
boxHashCode1 = getKernelContext().getMetaboxManager().getMetaboxHash(metaboxId);
|
|
}
|
|
else { boxHashCode1 = this->getKernelContext().getPluginManager().getPluginObjectHashValue(box->getAlgorithmClassIdentifier()); }
|
|
|
|
boxHashCode2.fromString(box->getAttributeValue(OV_AttributeId_Box_InitialPrototypeHashValue));
|
|
|
|
if (!(boxHashCode1 == CIdentifier::undefined() || boxHashCode1 == boxHashCode2)) { return true; }
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CScenario::checkOutdatedBoxes()
|
|
{
|
|
bool result = false;
|
|
|
|
m_outdatedBoxes.clear();
|
|
m_updatedBoxIOCorrespondence[Input] = std::map<CIdentifier, std::map<size_t, size_t>>();
|
|
m_updatedBoxIOCorrespondence[Output] = std::map<CIdentifier, std::map<size_t, size_t>>();
|
|
|
|
for (auto& box : m_boxes)
|
|
{
|
|
// Do not attempt to update boxes which do not have existing box algorithm identifiers
|
|
auto boxAlgorithmClassID = box.second->getAlgorithmClassIdentifier();
|
|
if (boxAlgorithmClassID != OVP_ClassId_BoxAlgorithm_Metabox
|
|
&& !dynamic_cast<const Plugins::IBoxAlgorithmDesc*>(this->getKernelContext().getPluginManager().getPluginObjectDescCreating(boxAlgorithmClassID)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Do not attempt to update metaboxes which do not have an associated scenario
|
|
if (boxAlgorithmClassID == OVP_ClassId_BoxAlgorithm_Metabox)
|
|
{
|
|
CString metaboxIdentifier = box.second->getAttributeValue(OVP_AttributeId_Metabox_ID);
|
|
if (metaboxIdentifier == CString("")) { continue; }
|
|
|
|
CIdentifier metaboxId;
|
|
metaboxId.fromString(metaboxIdentifier);
|
|
CString metaboxScenarioPath(this->getKernelContext().getMetaboxManager().getMetaboxFilePath(metaboxId));
|
|
|
|
if (metaboxScenarioPath == CString("")) { continue; }
|
|
}
|
|
|
|
// Box Updater instance which is in charge of create updated boxes and links
|
|
CBoxUpdater boxUpdater(*this, box.second);
|
|
|
|
if (!boxUpdater.initialize())
|
|
{
|
|
OV_WARNING_K("Could not check for update the box with id " << box.second->getIdentifier());
|
|
continue;
|
|
}
|
|
|
|
// exception for boxes that could not be automatically updated
|
|
if (boxUpdater.flaggedForManualUpdate())
|
|
{
|
|
if (this->isBoxOutdated(box.second->getIdentifier()))
|
|
{
|
|
auto toBeUpdatedBox = std::make_shared<CBox>(getKernelContext());
|
|
toBeUpdatedBox->initializeFromAlgorithmClassIdentifierNoInit(boxAlgorithmClassID);
|
|
m_outdatedBoxes[box.second->getIdentifier()] = toBeUpdatedBox;
|
|
m_boxes[box.first]->setAttributeValue(OV_AttributeId_Box_ToBeUpdated, "");
|
|
result = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// collect updated boxes
|
|
if (boxUpdater.isUpdateRequired())
|
|
{
|
|
m_updatedBoxIOCorrespondence[Input][box.second->getIdentifier()] = boxUpdater.getOriginalToUpdatedInterfacorCorrespondence(Input);
|
|
m_updatedBoxIOCorrespondence[Output][box.second->getIdentifier()] = boxUpdater.getOriginalToUpdatedInterfacorCorrespondence(Output);
|
|
// it is important to set box algorithm at
|
|
// last so the box listener is never called
|
|
boxUpdater.getUpdatedBox().setAlgorithmClassIdentifier(boxAlgorithmClassID);
|
|
// copy requested box into a new instance managed in scenario
|
|
auto newBox = std::make_shared<CBox>(this->getKernelContext());
|
|
newBox->initializeFromExistingBox(boxUpdater.getUpdatedBox());
|
|
m_outdatedBoxes[box.second->getIdentifier()] = newBox;
|
|
m_boxes[box.first]->setAttributeValue(OV_AttributeId_Box_ToBeUpdated, "");
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
template <class T, class TTest>
|
|
void getIdentifierList(const std::map<CIdentifier, T>& elementMap, const TTest& testFunctor, CIdentifier** listID, size_t* size)
|
|
{
|
|
*listID = new CIdentifier[elementMap.size()];
|
|
|
|
size_t index = 0;
|
|
for (auto it = elementMap.begin(); it != elementMap.end(); ++it) { if (testFunctor(it)) { (*listID)[index++] = it->first; } }
|
|
*size = index;
|
|
}
|
|
|
|
void CScenario::getBoxIdentifierList(CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CBox*, STestTrue<CBox*>>(m_boxes, STestTrue<CBox*>(), listID, size);
|
|
}
|
|
|
|
void CScenario::getCommentIdentifierList(CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CComment*, STestTrue<CComment*>>(m_comments, STestTrue<CComment*>(), listID, size);
|
|
}
|
|
|
|
void CScenario::getMetadataIdentifierList(CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CMetadata*, STestTrue<CMetadata*>>(m_metadatas, STestTrue<CMetadata*>(), listID, size);
|
|
}
|
|
|
|
void CScenario::getLinkIdentifierList(CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CLink*, STestTrue<CLink*>>(m_links, STestTrue<CLink*>(), listID, size);
|
|
}
|
|
|
|
void CScenario::getLinkIdentifierFromBoxList(const CIdentifier& boxID, CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CLink*, STestEqSourceBox>(m_links, STestEqSourceBox(boxID), listID, size);
|
|
}
|
|
|
|
void CScenario::getLinkIdentifierFromBoxOutputList(const CIdentifier& boxID, const size_t index, CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CLink*, STestEqSourceBoxOutput>(m_links, STestEqSourceBoxOutput(boxID, index), listID, size);
|
|
}
|
|
|
|
void CScenario::getLinkIdentifierToBoxList(const CIdentifier& boxID, CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CLink*, STestEqTargetBox>(m_links, STestEqTargetBox(boxID), listID, size);
|
|
}
|
|
|
|
void CScenario::getLinkIdentifierToBoxInputList(const CIdentifier& boxID, const size_t index, CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<CLink*, STestEqTargetBoxInput>(m_links, STestEqTargetBoxInput(boxID, index), listID, size);
|
|
}
|
|
|
|
void CScenario::getOutdatedBoxIdentifierList(CIdentifier** listID, size_t* size) const
|
|
{
|
|
getIdentifierList<std::shared_ptr<CBox>, STestTrue<std::shared_ptr<CBox>>>(m_outdatedBoxes, STestTrue<std::shared_ptr<CBox>>(), listID, size);
|
|
}
|
|
|
|
void CScenario::releaseIdentifierList(CIdentifier* listID) const { delete[] listID; }
|
|
|
|
bool CScenario::getSourceBoxOutputIndex(const CIdentifier& srcBoxID, const CIdentifier& srcBoxOutputID, size_t& srcBoxOutputIdx)
|
|
{
|
|
const auto itSourceBox = m_boxes.find(srcBoxID);
|
|
OV_ERROR_UNLESS_KRF(itSourceBox != m_boxes.end(), "Source Box [" << srcBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
m_boxes[srcBoxID]->getInterfacorIndex(Output, srcBoxOutputID, srcBoxOutputIdx);
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getTargetBoxInputIndex(const CIdentifier& dstBoxID, const CIdentifier& dstBoxInputID, size_t& dstBoxInputIdx)
|
|
{
|
|
const auto itTargetBox = m_boxes.find(dstBoxID);
|
|
OV_ERROR_UNLESS_KRF(itTargetBox != m_boxes.end(), "Target Box [" << dstBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
m_boxes[dstBoxID]->getInterfacorIndex(Input, dstBoxInputID, dstBoxInputIdx);
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getSourceBoxOutputIdentifier(const CIdentifier& srcBoxID, const size_t& srcBoxOutputIdx, CIdentifier& srcBoxOutputID)
|
|
{
|
|
const auto itSourceBox = m_boxes.find(srcBoxID);
|
|
OV_ERROR_UNLESS_KRF(itSourceBox != m_boxes.end(), "Source Box [" << srcBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
m_boxes[srcBoxID]->getInterfacorIdentifier(Output, srcBoxOutputIdx, srcBoxOutputID);
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::getTargetBoxInputIdentifier(const CIdentifier& dstBoxID, const size_t& dstBoxInputIdx, CIdentifier& dstBoxInputID)
|
|
{
|
|
const auto itTargetBox = m_boxes.find(dstBoxID);
|
|
OV_ERROR_UNLESS_KRF(itTargetBox != m_boxes.end(), "Target Box [" << dstBoxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
m_boxes[dstBoxID]->getInterfacorIdentifier(Input, dstBoxInputIdx, dstBoxInputID);
|
|
return true;
|
|
}
|
|
/**
|
|
* \brief Process to the update of the identified box.
|
|
* It consists in recreate the prototype of the box according to the updated reference box which is the box
|
|
* resulting of the add/pull requests to the kernel prototype.
|
|
* \param boxID the identifier of the box to be updated
|
|
* \return true when update has been done successfully
|
|
* \return false in case of failure
|
|
*/
|
|
bool CScenario::updateBox(const CIdentifier& boxID)
|
|
{
|
|
// Check if box must be updated
|
|
const auto itSourceBox = m_boxes.find(boxID);
|
|
OV_ERROR_UNLESS_KRF(itSourceBox != m_boxes.end(), "Box [" << boxID.str() << "] is not part of the scenario", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
auto itUpdateBox = m_outdatedBoxes.find(boxID);
|
|
|
|
if (itUpdateBox == m_outdatedBoxes.end())
|
|
{
|
|
this->checkOutdatedBoxes();
|
|
itUpdateBox = m_outdatedBoxes.find(boxID);
|
|
}
|
|
|
|
OV_ERROR_UNLESS_KRF(itUpdateBox != m_outdatedBoxes.end(), "Box [" << boxID.str() << "] misses an updated version", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
|
|
if (itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagNeedsManualUpdate)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanAddInput)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanAddOutput)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanAddSetting)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanModifyInput)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput)
|
|
|| itUpdateBox->second->hasAttribute(OV_AttributeId_Box_FlagCanModifySetting))
|
|
{
|
|
OV_ERROR_KRF(m_boxes[boxID]->getName() << " must be manually updated. Its prototype is too complex.", Kernel::ErrorType::NotImplemented);
|
|
}
|
|
OV_ERROR_UNLESS_KRF(itUpdateBox != m_outdatedBoxes.end(), "Box [" << boxID.str() << "] does not have to be updated", Kernel::ErrorType::ResourceNotFound);
|
|
|
|
// get all non-updatable attributes from the source box
|
|
std::map<CIdentifier, CString> nonUpdatableAttributes;
|
|
{
|
|
CIdentifier attributeId = CIdentifier::undefined();
|
|
while ((attributeId = itSourceBox->second->getNextAttributeIdentifier(attributeId)) != CIdentifier::undefined())
|
|
{
|
|
const auto& updatableAttrs = CBoxUpdater::UPDATABLE_ATTRIBUTES;
|
|
if (std::find(updatableAttrs.cbegin(), updatableAttrs.cend(), attributeId) == updatableAttrs.cend())
|
|
{
|
|
nonUpdatableAttributes[attributeId] = itSourceBox->second->getAttributeValue(attributeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// gather links coming to and from the box
|
|
std::map<EBoxInterfacorType, std::vector<std::shared_ptr<CLink>>> links;
|
|
for (auto interfacorType : { Input, Output })
|
|
{
|
|
links[interfacorType] = std::vector<std::shared_ptr<CLink>>();
|
|
for (size_t index = 0; index < itUpdateBox->second->getInterfacorCountIncludingDeprecated(interfacorType); ++index)
|
|
{
|
|
CIdentifier* linkIdentifierList = nullptr;
|
|
size_t linkCount = 0;
|
|
if (interfacorType == Input) { this->getLinkIdentifierToBoxInputList(boxID, index, &linkIdentifierList, &linkCount); }
|
|
else if (interfacorType == Output) { this->getLinkIdentifierFromBoxOutputList(boxID, index, &linkIdentifierList, &linkCount); }
|
|
|
|
for (size_t i = 0; i < linkCount; ++i)
|
|
{
|
|
auto link = std::make_shared<CLink>(this->getKernelContext(), *this);
|
|
link->initializeFromExistingLink(*this->getLinkDetails(linkIdentifierList[i]));
|
|
if (this->getLinkDetails(linkIdentifierList[i])->hasAttribute(OV_AttributeId_Link_Invalid))
|
|
{
|
|
link->addAttribute(OV_AttributeId_Link_Invalid, "");
|
|
}
|
|
links[interfacorType].emplace_back(link);
|
|
}
|
|
|
|
this->releaseIdentifierList(linkIdentifierList);
|
|
}
|
|
}
|
|
|
|
OV_FATAL_UNLESS_K(this->removeBox(boxID), "Failed to remove redundant box", Kernel::ErrorType::Internal);
|
|
|
|
CIdentifier updatedBoxIdentifier;
|
|
OV_FATAL_UNLESS_K(this->addBox(updatedBoxIdentifier, *(itUpdateBox->second.get()), boxID), "Failed to add box to the scenario",
|
|
Kernel::ErrorType::Internal);
|
|
OV_FATAL_UNLESS_K(updatedBoxIdentifier == boxID, "Updated box failed to initialize with same identifier", Kernel::ErrorType::Internal);
|
|
|
|
auto updatedBox = this->getBoxDetails(boxID);
|
|
|
|
for (const auto& attr : nonUpdatableAttributes)
|
|
{
|
|
if (attr.first == OV_AttributeId_Box_ToBeUpdated) { continue; }
|
|
if (updatedBox->hasAttribute(attr.first)) { updatedBox->setAttributeValue(attr.first, attr.second); }
|
|
else { updatedBox->addAttribute(attr.first, attr.second); }
|
|
}
|
|
|
|
// Reconnect links
|
|
std::map<EBoxInterfacorType, std::set<size_t>> isInterfacorConnected;
|
|
isInterfacorConnected[Input] = std::set<size_t>();
|
|
isInterfacorConnected[Output] = std::set<size_t>();
|
|
for (auto& link : links[Input])
|
|
{
|
|
CIdentifier newLinkIdentifier;
|
|
auto index = m_updatedBoxIOCorrespondence.at(Input).at(boxID).at(link->getTargetBoxInputIndex());
|
|
this->connect(newLinkIdentifier, link->getSourceBoxIdentifier(), link->getSourceBoxOutputIndex(), boxID, index, link->getIdentifier());
|
|
isInterfacorConnected[Input].insert(index);
|
|
if (link->hasAttribute(OV_AttributeId_Link_Invalid)) { this->getLinkDetails(newLinkIdentifier)->setAttributeValue(OV_AttributeId_Link_Invalid, ""); }
|
|
}
|
|
for (const auto& link : links[Output])
|
|
{
|
|
CIdentifier newLinkIdentifier;
|
|
auto index = m_updatedBoxIOCorrespondence.at(Output).at(boxID).at(link->getSourceBoxOutputIndex());
|
|
this->connect(newLinkIdentifier, boxID, index, link->getTargetBoxIdentifier(), link->getTargetBoxInputIndex(), link->getIdentifier());
|
|
isInterfacorConnected[Output].insert(index);
|
|
if (link->hasAttribute(OV_AttributeId_Link_Invalid)) { this->getLinkDetails(newLinkIdentifier)->setAttributeValue(OV_AttributeId_Link_Invalid, ""); }
|
|
}
|
|
|
|
// Cleanup the i/o that are redundant and disconnected
|
|
for (auto t : { Input, Output })
|
|
{
|
|
size_t i = updatedBox->getInterfacorCountIncludingDeprecated(t);
|
|
while (i > 0)
|
|
{
|
|
--i;
|
|
bool isDeprecated;
|
|
updatedBox->getInterfacorDeprecatedStatus(t, i, isDeprecated);
|
|
if (isDeprecated && isInterfacorConnected.at(t).find(i) == isInterfacorConnected.at(t).end()) { updatedBox->removeInterfacor(t, i, true); }
|
|
}
|
|
}
|
|
|
|
// Cleanup the settings that are redundant and have the same value as default
|
|
size_t settingIndex = updatedBox->getInterfacorCountIncludingDeprecated(Setting);
|
|
while (settingIndex > 0)
|
|
{
|
|
settingIndex--;
|
|
bool isDeprecated;
|
|
CString value;
|
|
CString defaultValue;
|
|
updatedBox->getInterfacorDeprecatedStatus(Setting, settingIndex, isDeprecated);
|
|
updatedBox->getSettingValue(settingIndex, value);
|
|
updatedBox->getSettingDefaultValue(settingIndex, defaultValue);
|
|
if (isDeprecated && value == defaultValue) { updatedBox->removeInterfacor(Setting, settingIndex, true); }
|
|
}
|
|
|
|
|
|
bool hasDeprecatedInterfacor = false;
|
|
for (auto t : { Setting, Input, Output })
|
|
{
|
|
hasDeprecatedInterfacor |= (updatedBox->getInterfacorCount(t) != updatedBox->getInterfacorCountIncludingDeprecated(t));
|
|
}
|
|
|
|
if (hasDeprecatedInterfacor)
|
|
{
|
|
OV_WARNING_K(m_boxes[boxID]->getName()
|
|
<< " box has not been fully updated. Deprecated Inputs, Outputs or Settings are pending.\n"
|
|
<< " Please remove them before exporting scenario\n");
|
|
this->getBoxDetails(boxID)->setAttributeValue(OV_AttributeId_Box_PendingDeprecatedInterfacors, "");
|
|
}
|
|
else { this->getLogManager() << LogLevel_Info << m_boxes[boxID]->getName() << " box has been updated successfully\n"; }
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::removeDeprecatedInterfacorsFromBox(const CIdentifier& boxID)
|
|
{
|
|
// Check if box must be updated
|
|
IBox* box = getBoxDetails(boxID);
|
|
if (!box) { return false; }
|
|
|
|
for (auto interfacorType : { Input, Output, Setting })
|
|
{
|
|
const auto nInterfacor = box->getInterfacorCountIncludingDeprecated(interfacorType);
|
|
if (nInterfacor == 0) { continue; }
|
|
auto index = nInterfacor;
|
|
do
|
|
{
|
|
index--;
|
|
bool isDeprecated = false;
|
|
box->getInterfacorDeprecatedStatus(interfacorType, index, isDeprecated);
|
|
if (isDeprecated) { box->removeInterfacor(interfacorType, index); }
|
|
} while (index != 0);
|
|
}
|
|
|
|
this->getLogManager() << LogLevel_Info << m_boxes[boxID]->getName()
|
|
<< " Deprecated I/O and settings have been removed successfully\n";
|
|
|
|
box->removeAttribute(OV_AttributeId_Box_PendingDeprecatedInterfacors);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScenario::containsBoxWithDeprecatedInterfacors() const
|
|
{
|
|
for (const auto& box : m_boxes)
|
|
{
|
|
for (const auto interfacorType : { Input, Output, Setting })
|
|
{
|
|
if (box.second->getInterfacorCountIncludingDeprecated(interfacorType) > box.second->getInterfacorCount(interfacorType)) { return true; }
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace Kernel
|
|
} // namespace OpenViBE
|