Commit 5ccebe31 authored by Pascal Palenda's avatar Pascal Palenda
Browse files

Add CMasterSimulationController class - WIP

parent 536d19b2
......@@ -3,7 +3,7 @@ set( RelativeSourceGroup "Header Files\\ITA\\SimulationScheduler" )
set( SubDirs
#audibility_filter
#network_interface
#room_acoustics
room_acoustics
)
set( DirFiles
......
set( RelativeDir "include/ITA/simulation_scheduler/room_acoustics" )
set( RelativeSourceGroup "Header Files\\ITA\\SimulationScheduler\\RoomAcoustics" )
set( SubDirs
#raven
)
set( DirFiles
master_simulation_controller.h
#simulation_result.h
#result_handler.h
#scheduler_interface.h
#scheduler.h
#_SourceFiles.cmake
)
set( DirFiles_SourceGroup "${RelativeSourceGroup}" )
set( LocalSourceGroupFiles )
foreach( File ${DirFiles} )
list( APPEND LocalSourceGroupFiles "${RelativeDir}/${File}" )
list( APPEND ProjectSources "${RelativeDir}/${File}" )
endforeach()
source_group( ${DirFiles_SourceGroup} FILES ${LocalSourceGroupFiles} )
set( SubDirFiles "" )
foreach( Dir ${SubDirs} )
list( APPEND SubDirFiles "${RelativeDir}/${Dir}/_SourceFiles.cmake" )
endforeach()
foreach( SubDirFile ${SubDirFiles} )
include( ${SubDirFile} )
endforeach()
#ifndef INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_MASTER_SIMULATION_CONTROLLER
#define INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_MASTER_SIMULATION_CONTROLLER
// std includes
#include <memory>
#include <list>
// API includes
#include <ITA/simulation_scheduler/definitions.h>
// simulation scheduler includes
#include <ITA/simulation_scheduler/update_message.h>
#include <ITA/simulation_scheduler/room_acoustics/scheduler_interface.h>
// VISTA includes
#include <VistaInterProcComm/Concurrency/VistaThreadLoop.h>
#include <VistaInterProcComm/Concurrency/VistaThreadEvent.h>
#include <VistaAspects/VistaPropertyList.h>
// ITA includes
#include <ITAAtomicPrimitives.h>
// Other includes
#include <tbb/concurrent_queue.h>
namespace ITA
{
namespace simulation_scheduler
{
namespace room_acoustics
{
class IResultHandler;
///
/// \brief Primary interaction class.
///
/// This class represents the interface with which external software should interact with the simulation scheduler.
/// It allows for easy interaction and configuration of the system.
/// - Updates can be issued via postUpdate().
/// - A IResultHandler can be registered via attachResultHandler.
/// - A simple replacement filter can be enabled for the CMasterSimulationController.
///
/// The actual computation takes place in its own thread (loop), see LoopBody().
///
class ITA_SIMULATION_SCHEDULER_API CMasterSimulationController : public VistaThreadLoop
{
public:
///
/// \brief Possible scheduler types for getDefaultMasterSimulationControllerConfig().
///
enum class SchedulerType
{
local, ///< local scheduler.
remote ///< remote scheduler.
};
///
/// \brief Returns the default Configuration for the CMasterSimulationController.
///
/// As the three schedulers can be either local or remote, this function gets a defining parameter for each scheduler,
/// \param DSScheduler type of scheduler for DS.
/// \param ERScheduler type of scheduler for ER.
/// \param DDScheduler type of scheduler for DS.
/// \return the default config for the CMasterSimulationController.
///
static VistaPropertyList getDefaultMasterSimulationControllerConfig ( SchedulerType DSScheduler,
SchedulerType ERScheduler,
SchedulerType DDScheduler )
{
auto config = VistaPropertyList ( );
auto subConfig = VistaPropertyList ( );
subConfig.SetValue ( "FilterUpdates", true );
config.SetPropertyListValue ( "MasterSimulationScheduler", subConfig );
if ( DSScheduler == SchedulerType::local )
{
config.SetPropertyListValue ( "MasterSimulationScheduler/DSScheduler", ISchedulerInterface::getDefaultLocalSchedulerConfig ( ) );
}
else
{
config.SetPropertyListValue ( "MasterSimulationScheduler/DSScheduler", ISchedulerInterface::getDefaultRemoteSchedulerConfig ( ) );
}
if ( ERScheduler == SchedulerType::local )
{
config.SetPropertyListValue ( "MasterSimulationScheduler/ERScheduler", ISchedulerInterface::getDefaultLocalSchedulerConfig ( ) );
}
else
{
config.SetPropertyListValue ( "MasterSimulationScheduler/ERScheduler", ISchedulerInterface::getDefaultRemoteSchedulerConfig ( ) );
}
if ( DDScheduler == SchedulerType::local )
{
config.SetPropertyListValue ( "MasterSimulationScheduler/DDScheduler", ISchedulerInterface::getDefaultLocalSchedulerConfig ( ) );
}
else
{
config.SetPropertyListValue ( "MasterSimulationScheduler/DDScheduler", ISchedulerInterface::getDefaultRemoteSchedulerConfig ( ) );
}
return config;
}
///
/// \brief Constructor for the CMasterSimulationController.
///
/// This constructor configures itself via the given configuration.
/// After a resultHandler is attached the controller is functional.
/// Note, that external schedulers have to be running for the master controller to work correctly.
/// \throw ITAException When creation or connection to schedulers failed.
/// \param pConfig the configuration from which to construct the CMasterSimulationController.
///
CMasterSimulationController ( const VistaPropertyList& pConfig );
///
/// \brief Destructor for CMasterSimulationController
///
~CMasterSimulationController();
///
/// \brief Post a new update to the CMasterSimulationController.
///
/// \note This method takes ownership of the update.
/// \param pUpdateMessage the new update for the CMasterSimulationController.
///
void postUpdate ( std::unique_ptr<IUpdateMessage> pUpdateMessage );
///
/// \brief Attach a IResultHandler to the controller.
/// \remark Internally, the IResultHandler is attached to the simulators themselves.
/// \param pResultHandler the new IResultHandler.
/// \todo Maybe use shared_ptr ... resultHandler have to support make shared form this ... and consequently have to be shared_ptr.
///
void attachResultHandler ( IResultHandler* pResultHandler ) const;
///
/// \brief Detach a IResultHandler from the controller.
/// \param pResultHandler the IResultHandler to detach.
///
void detachResultHandler ( IResultHandler* pResultHandler ) const;
protected:
///
/// \brief Loop body for the CMasterSimulationController.
///
/// The following steps are done in the loop:
/// - Wait for a new update in m_qUpdateQueue.
/// - move the updates from the concurrent queue to a list.
/// - If the updates should be filtered, filterReplace() is called.
/// - The updates get copied, and send to the three schedulers.
/// - The m_lUpdateList gets cleared for the next loop iteration.
/// \return true if VistaThreadLoop::ThreadBody() shall call VistaThread::YieldThread() before the next loop, false else
///
bool LoopBody ( ) override;
///
/// \brief Removes duplicate updates for source-receiver-pairs in its m_qUpdateList.
///
/// If the m_qUpdateQueue has multiple updates for the same source-receiver-pair, this method removes all but the newest update.
///
void filterReplace ( );
///
/// \brief Setter for the scheduler.
/// \note This function is primarily for testing.
/// However, it could also be useful in the long run.
/// \param DSScheduler the DS scheduler.
/// \param ERScheduler the ER scheduler.
/// \param DDScheduler the DD scheduler.
///
void setScheduler ( std::unique_ptr<ISchedulerInterface> DSScheduler,
std::unique_ptr<ISchedulerInterface> ERScheduler,
std::unique_ptr<ISchedulerInterface> DDScheduler );
private:
///
/// \brief Update queue for the controller.
///
/// postUpdate() adds new updates to this queue before they get passed to the schedulers in LoopBody().
///
tbb::concurrent_queue<std::unique_ptr<IUpdateMessage>> m_qUpdateQueue;
std::list<std::unique_ptr<IUpdateMessage>> m_lUpdateList;
///
/// \brief True if the controller should filter its received updates using filterReplace().
///
bool m_bFilterUpdates;
///
/// \brief The scheduler for the direct sound of the RIR.
///
std::unique_ptr<ISchedulerInterface> m_pDSScheduler;
///
/// \brief The scheduler for the early reflections of the RIR.
///
std::unique_ptr<ISchedulerInterface> m_pERScheduler;
///
/// \brief The scheduler for the diffuse decay of the RIR.
///
std::unique_ptr<ISchedulerInterface> m_pDDScheduler;
///
/// \brief Trigger for starting a thread loop.
///
VistaThreadEvent m_evTriggerLoop;
ITAAtomicBool m_bStopIndicated;
ITAAtomicBool m_bStopACK;
};
} // namespace room_acoustics
} // namespace simulation_scheduler
} // namespace ITA
#endif // INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_MASTER_SIMULATION_CONTROLLER
set( RelativeDir "src/ITA/simulation_scheduler/room_acoustics" )
set( RelativeSourceGroup "Source Files\\ITA\\SimulationScheduler\\RoomAcoustics" )
set( SubDirs
#raven
)
set( DirFiles
master_simulation_controller.cpp
#scheduler.cpp
replacement_filter.h
replacement_filter.cpp
#_SourceFiles.cmake
)
set( DirFiles_SourceGroup "${RelativeSourceGroup}" )
set( LocalSourceGroupFiles )
foreach( File ${DirFiles} )
list( APPEND LocalSourceGroupFiles "${RelativeDir}/${File}" )
list( APPEND ProjectSources "${RelativeDir}/${File}" )
endforeach()
source_group( ${DirFiles_SourceGroup} FILES ${LocalSourceGroupFiles} )
set( SubDirFiles "" )
foreach( Dir ${SubDirs} )
list( APPEND SubDirFiles "${RelativeDir}/${Dir}/_SourceFiles.cmake" )
endforeach()
foreach( SubDirFile ${SubDirFiles} )
include( ${SubDirFile} )
endforeach()
// Header include
#include <ITA/simulation_scheduler/room_acoustics/master_simulation_controller.h>
// simulation scheduler includes
#include <ITA/simulation_scheduler/update_scene.h>
#include <ITA/simulation_scheduler/update_config.h>
#include <ITA/simulation_scheduler/room_acoustics/scheduler.h>
#include <ITA/simulation_scheduler/room_acoustics/result_handler.h>
#include "../src/ITA/simulation_scheduler/room_acoustics/replacement_filter.h"
// Vista includes
#include <VistaBase/VistaTimeUtils.h>
// ITA includes
#include <ITAException.h>
namespace ITA
{
namespace simulation_scheduler
{
namespace room_acoustics
{
CMasterSimulationController::CMasterSimulationController ( const VistaPropertyList& pConfig ) :
m_evTriggerLoop ( VistaThreadEvent::NON_WAITABLE_EVENT )
{
if ( pConfig.HasSubList ( "MasterSimulationScheduler" ) )
{
m_bFilterUpdates = pConfig.GetValueInSubListOrDefault ( "FilterUpdates", "MasterSimulationScheduler", true );
if ( pConfig.HasSubList ( "MasterSimulationScheduler/DSScheduler" ) )
{
auto subConfig = pConfig.GetSubListConstRef ( "MasterSimulationScheduler/DSScheduler" );
if ( subConfig.HasProperty ( "IPAddress" ) )
{
// todo Remote Scheduler.
}
else // local scheduler
{
//m_pDSScheduler = std::make_unique<CScheduler> ( subConfig );
}
}
else
{
ITA_EXCEPT_INVALID_PARAMETER ( "Config does not have correct section \"MasterSimulationScheduler/DSScheduler\"." )
}
if ( pConfig.HasSubList ( "MasterSimulationScheduler/ERScheduler" ) )
{
auto subConfig = pConfig.GetSubListConstRef ( "MasterSimulationScheduler/ERScheduler" );
if ( subConfig.HasProperty ( "IPAddress" ) )
{
// todo Remote Scheduler.
}
else // local scheduler
{
//m_pERScheduler = std::make_unique<CScheduler> ( subConfig );
}
}
else
{
ITA_EXCEPT_INVALID_PARAMETER ( "Config does not have correct section \"MasterSimulationScheduler/ERScheduler\"." )
}
if ( pConfig.HasSubList ( "MasterSimulationScheduler/DDScheduler" ) )
{
auto subConfig = pConfig.GetSubListConstRef ( "MasterSimulationScheduler/DDScheduler" );
if ( subConfig.HasProperty ( "IPAddress" ) )
{
// todo Remote Scheduler.
}
else // local scheduler
{
//m_pDDScheduler = std::make_unique<CScheduler> ( subConfig );
}
}
else
{
ITA_EXCEPT_INVALID_PARAMETER ( "Config does not have correct section \"MasterSimulationScheduler/DDScheduler\"." )
}
}
else
{
ITA_EXCEPT_INVALID_PARAMETER ( "Config does not have correct section \"MasterSimulationScheduler\"." )
}
Run ( );
}
CMasterSimulationController::~CMasterSimulationController()
{
m_bStopIndicated = true;
m_evTriggerLoop.SignalEvent ( );
while ( m_bStopACK != true )
VistaTimeUtils::Sleep ( 250 );
m_evTriggerLoop.SignalEvent ( );
StopGently ( true );
}
void CMasterSimulationController::postUpdate ( std::unique_ptr<IUpdateMessage> pUpdateMessage )
{
m_qUpdateQueue.push ( std::move ( pUpdateMessage ) );
m_evTriggerLoop.SignalEvent ( );
}
void CMasterSimulationController::attachResultHandler ( IResultHandler* pResultHandler ) const
{
m_pDSScheduler->attachResultHandler ( pResultHandler );
m_pERScheduler->attachResultHandler ( pResultHandler );
m_pDDScheduler->attachResultHandler ( pResultHandler );
}
void CMasterSimulationController::detachResultHandler ( IResultHandler* pResultHandler ) const
{
m_pDSScheduler->detachResultHandler ( pResultHandler );
m_pERScheduler->detachResultHandler ( pResultHandler );
m_pDDScheduler->detachResultHandler ( pResultHandler );
}
bool CMasterSimulationController::LoopBody ( )
{
// Wait for trigger
m_evTriggerLoop.WaitForEvent ( true );
m_evTriggerLoop.ResetThisEvent ( );
// Handle the stop of the loop
if ( m_bStopIndicated )
{
IndicateLoopEnd ( );
m_bStopACK = true;
return false;
}
// get the updates from the queue to a list.
std::unique_ptr<IUpdateMessage> updateMessage;
while ( m_qUpdateQueue.try_pop ( updateMessage ) )
{
m_lUpdateList.push_back ( std::move ( updateMessage ) );
}
if ( m_bFilterUpdates )
filterReplace ( );
for ( auto& update : m_lUpdateList )
{
const auto rawUpdate = update.release ( );
if ( const auto rawUpdateScene = dynamic_cast< CUpdateScene* >( rawUpdate ) )
{
// We make three copies of the update message, one for each scheduler.
m_pDSScheduler->postUpdate ( std::make_unique<CUpdateScene> ( *rawUpdateScene ) );
m_pDDScheduler->postUpdate ( std::make_unique<CUpdateScene> ( *rawUpdateScene ) );
m_pERScheduler->postUpdate ( std::make_unique<CUpdateScene> ( *rawUpdateScene ) );
}
else
{
const auto rawUpdateConfig = dynamic_cast< CUpdateConfig* >( rawUpdate );
// We make three copies of the update message, one for each scheduler.
m_pDSScheduler->postUpdate ( std::make_unique<CUpdateConfig> ( *rawUpdateConfig ) );
m_pDDScheduler->postUpdate ( std::make_unique<CUpdateConfig> ( *rawUpdateConfig ) );
m_pERScheduler->postUpdate ( std::make_unique<CUpdateConfig> ( *rawUpdateConfig ) );
}
}
// Now, the list only contains non valid unique_ptr, we can clear it.
m_lUpdateList.clear ( );
return true;
}
void CMasterSimulationController::filterReplace ( )
{
CReplacementFilter::filterReplace ( m_lUpdateList );
}
void CMasterSimulationController::setScheduler ( std::unique_ptr<ISchedulerInterface> DSScheduler,
std::unique_ptr<ISchedulerInterface> ERScheduler,
std::unique_ptr<ISchedulerInterface> DDScheduler )
{
m_pDSScheduler = std::move ( DSScheduler );
m_pERScheduler = std::move ( ERScheduler );
m_pDDScheduler = std::move ( DDScheduler );
}
} // namespace room_acoustics
} // namespace simulation_scheduler
} // namespace ITA
\ No newline at end of file
#include "replacement_filter.h"
void ITA::simulation_scheduler::room_acoustics::CReplacementFilter::filterReplace ( std::list<std::unique_ptr<IUpdateMessage>>& updateList )
{
// Revers traversal of the updates, as the oldest updates are at the front,
// if a newer update removed them, the iterators are still valid.
for ( auto updateIter = updateList.rbegin ( ); updateIter != updateList.rend ( ); ++updateIter )
{
if ( auto updateScene = dynamic_cast< CUpdateScene* >( updateIter->get ( ) ) )
{
updateList.remove_if ( [&] ( const std::unique_ptr<IUpdateMessage>& update )->bool
{
if ( const auto updateSceneLambda = dynamic_cast< CUpdateScene* >( update.get ( ) ) )
{
return updateScene->getReferenceID ( ) == updateSceneLambda->getReferenceID ( ) &&
updateScene->getTimeStamp ( ) > updateSceneLambda->getTimeStamp ( );
}
return false;
} );
}
}
}
#ifndef INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_REPLACEMENT_FILTER
#define INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_REPLACEMENT_FILTER
// std includes
#include <memory>
#include <list>
// API includes
#include <ITA/simulation_scheduler/definitions.h>
// simulation scheduler includes
#include <ITA/simulation_scheduler/update_scene.h>
namespace ITA
{
namespace simulation_scheduler
{
namespace room_acoustics
{
struct ITA_SIMULATION_SCHEDULER_API CReplacementFilter
{
///
/// \brief Remove outdated CUpdateScene for a update list.
/// \param updateList update list from which outdated CSceneUpdates will be removed.
///
static inline void filterReplace ( std::list<std::unique_ptr<IUpdateMessage>>& updateList );
};
} // namespace room_acoustics
} // namespace simulation_scheduler
} // namespace ITA
#endif // INCLUDE_WATCHER_ITA_SIMULATION_SCHEDULER_ROOM_ACOUSTICS_REPLACEMENT_FILTER
......@@ -13,6 +13,7 @@ set( ProjectSources "all_tests.cpp"
"update_config_test.cpp"
"update_scene_test.cpp"
"enum_to_int.cpp"
"master_simulation_controller_test.cpp"
)
add_executable( all_tests ${ProjectSources} )
......
#include <iostream>
#include <string>
#include <memory>
// simulation scheduler includes
#include <ITA/simulation_scheduler/room_acoustics/master_simulation_controller.h>
#include <ITA/simulation_scheduler/room_acoustics/result_handler.h>
#include <ITA/simulation_scheduler/update_message.h>
#include <ITA/simulation_scheduler/update_scene.h>
#include <ITA/simulation_scheduler/update_config.h>
#include "../src/ITA/simulation_scheduler/room_acoustics/replacement_filter.h"
#include <ITA/simulation_scheduler/room_acoustics/simulation_result.h>
// Vista includes
#include <VistaInterProcComm/Connections/VistaByteBufferSerializer.h>
#include <VistaInterProcComm/Connections/VistaByteBufferDeSerializer.h>
#include <VistaBase/VistaTimeUtils.h>
// GTest
#include "gtest/gtest.h"
#include "enum_to_int.cpp"
using namespace ITA::simulation_scheduler;
using namespace ITA::simulation_scheduler