Tested and validated (app ID filtering removed as it does not work in the EXE middleware yet)

Change-Id: If7ecb996fecba1ed1cbe78a633b38659a4b27c6f
This commit is contained in:
Geir Horn 2024-02-14 17:53:39 +01:00
parent 294eb0e62f
commit 4486393c02
6 changed files with 57 additions and 102 deletions

View File

@ -16,6 +16,7 @@
"compilerPath": "/usr/bin/g++",
"compilerArgs": [
"-std=c++23",
"-I/usr/include/",
"-I/home/GHo/Documents/Code/Theron++/",
"-I/home/GHo/Documents/Code/CxxOpts/include",
"-I/opt/AMPL/amplapi/include/"

View File

@ -43,7 +43,7 @@ std::string AMPLSolver::SaveFile( const JSON & TheMessage,
if( ProblemFile.is_open() )
{
ProblemFile << TheMessage.at( AMPLSolver::FileContent );
ProblemFile << TheMessage.at( AMPLSolver::FileContent ).get<std::string>();
ProblemFile.close();
return TheFileName;
}
@ -156,11 +156,6 @@ void AMPLSolver::SetAMPLParameter( const std::string & ParameterName,
void AMPLSolver::DefineProblem(const Solver::OptimisationProblem & TheProblem,
const Address TheOracle)
{
Theron::ConsoleOutput Output;
Output << "AMPL Solver received the AMPL problem: " << std::endl
<< TheProblem.dump(2)
<< std::endl;
// First storing the AMPL problem file from its definition in the message
// and read the file back to the AMPL interpreter.
@ -200,8 +195,6 @@ void AMPLSolver::DefineProblem(const Solver::OptimisationProblem & TheProblem,
SetAMPLParameter( ConstantName,
ConstantRecord.at( InitialConstantValue ) );
}
Output << "Problem loaded!" << std::endl;
}
// -----------------------------------------------------------------------------
@ -217,8 +210,6 @@ void AMPLSolver::DataFileUpdate( const DataFileMessage & TheDataFile,
const Address TheOracle )
{
ProblemDefinition.readData( SaveFile( TheDataFile ) );
//Theron::ConsoleOutput Output;
//Output << "Message recevied is: " << std::endl << TheDataFile.dump(2) << std::endl;
}
// -----------------------------------------------------------------------------

View File

@ -59,14 +59,14 @@ void MetricUpdater::AddMetricSubscription( const MetricTopic & TheMetrics,
// some of them may correspond to known metrics, some of them may
// correspond to metrics that are new.
std::set< std::string > MetricNames;
std::set< std::string > TheMetricNames;
for (auto & MetricRecord : TheMetrics.at( NebulOuS::MetricList ) )
{
auto [ MetricRecordPointer, MetricAdded ] = MetricValues.try_emplace(
MetricRecord.at( NebulOuS::MetricName ), JSON() );
MetricRecord.at( NebulOuS::MetricName ).get<std::string>(), JSON() );
MetricNames.insert( MetricRecordPointer->first );
TheMetricNames.insert( MetricRecordPointer->first );
if( MetricAdded )
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
@ -81,7 +81,7 @@ void MetricUpdater::AddMetricSubscription( const MetricTopic & TheMetrics,
// should be unsubcribed and their metric records removed.
for( const auto & TheMetric : std::views::keys( MetricValues ) )
if( !MetricName.contains( TheMetric ) )
if( !TheMetricNames.contains( TheMetric ) )
{
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
@ -136,17 +136,10 @@ void MetricUpdater::AddMetricSubscription( const MetricTopic & TheMetrics,
void MetricUpdater::UpdateMetricValue(
const MetricValueUpdate & TheMetricValue, const Address TheMetricTopic)
{
Theron::ConsoleOutput Output;
Output << "Metric topic: " << TheMetricTopic.AsString() << std::endl;
Theron::AMQ::TopicName TheTopic
= TheMetricTopic.AsString().erase( 0,
NebulOuS::MetricValueRootString.size() );
Output << "The metric: " << TheTopic << " has new value "
<< TheMetricValue[ NebulOuS::ValueLabel ] << std::endl;
if( MetricValues.contains( TheTopic ) )
{
MetricValues.at( TheTopic ) = TheMetricValue[ NebulOuS::ValueLabel ];

View File

@ -83,11 +83,6 @@ public:
// object. The attributes expected are defined as constant strings so that
// the actual textual representation can be changed without changing the code
//
// "Identifier" It can be anything corresponding to the need of the sender
// and returned to the sender with the found solution
static constexpr std::string_view ContextIdentifier = "Identifier";
// "Timestamp" : This is the field giving the implicit order of the
// different application execution execution contexts waiting for being
// solved when there are more requests than there are solvers available to

View File

@ -59,7 +59,7 @@ endpoint is set to some unique identifier of the application for which this
solver is used, e.g.,
./SolverComponent --AMPLDir /opt/AMPL \
--Endpoint f81ee-b42a8-a13d56-e28ec9-2f5578 --ModelDir AMPLTest/
--ModelDir AMPLTest/ --Endpoint f81ee-b42a8-a13d56-e28ec9-2f5578
Debugging after a coredump
@ -228,31 +228,31 @@ int main( int NumberOfCLIOptions, char ** CLIOptionStrings )
// example for an earlier Proton version (0.32.0) and the example at
// https://qpid.apache.org/releases/qpid-proton-0.32.0/proton/cpp/examples/selected_recv.cpp.html
virtual proton::receiver_options ReceiverOptions( void ) const override
{
proton::source::filter_map TheFilter;
proton::source_options TheSourceOptions;
proton::symbol FilterKey("selector");
proton::value FilterValue;
proton::codec::encoder EncodedFilter( FilterValue );
proton::receiver_options TheOptions(
Theron::AMQ::NetworkLayer::AMQProperties::ReceiverOptions() );
// virtual proton::receiver_options ReceiverOptions( void ) const override
// {
// proton::source::filter_map TheFilter;
// proton::source_options TheSourceOptions;
// proton::symbol FilterKey("selector");
// proton::value FilterValue;
// proton::codec::encoder EncodedFilter( FilterValue );
// proton::receiver_options TheOptions(
// Theron::AMQ::NetworkLayer::AMQProperties::ReceiverOptions() );
std::ostringstream SelectorString;
// std::ostringstream SelectorString;
SelectorString << "application = '" << ApplicationID << "'";
// SelectorString << "application = '" << ApplicationID << "'";
EncodedFilter << proton::codec::start::described()
<< proton::symbol("apache.org:selector-filter:string")
<< SelectorString.str()
<< proton::codec::finish();
// EncodedFilter << proton::codec::start::described()
// << proton::symbol("apache.org:selector-filter:string")
// << SelectorString.str()
// << proton::codec::finish();
TheFilter.put( FilterKey, FilterValue );
TheSourceOptions.filters( TheFilter );
TheOptions.source( TheSourceOptions );
// TheFilter.put( FilterKey, FilterValue );
// TheSourceOptions.filters( TheFilter );
// TheOptions.source( TheSourceOptions );
return TheOptions;
}
// return TheOptions;
// }
// The application identifier must also be provided in every message to
// allow other receivers to filter on this.

View File

@ -111,7 +111,7 @@ private:
// The solution manager dispatches the application execution contexts as
// requests for solutions to a pool of solvers.
std::list< SolverType > SolverPool;
std::list< SolverType > SolverPool;
std::unordered_set< Address > ActiveSolvers, PassiveSolvers;
// --------------------------------------------------------------------------
@ -120,94 +120,64 @@ private:
//
// The contexts are dispatched in time sorted order. However, the time
// to solve a problem depends on the complexity of the the context and the
// results may therefore become available out-of-order. Each application
// execution context should carry a unique identifier, and this is used as
// the index key for quickly finding the right execution context. There is
// a second view of the queue of application context where the identifiers
// are sorted based on their time stamp.
// results may therefore become available out-of-order.
std::unordered_map< Solver::ContextIdentifierType,
Solver:: ApplicationExecutionContext > Contexts;
std::multimap< Solver::TimePointType,
Solver::ApplicationExecutionContext > ContextQueue;
std::multimap< Solver::TimePointType, Solver::ContextIdentifierType >
ContextExecutionQueue;
// When the new applicaton execution context message arrives, it will be
// queued, and its time point recoreded. If there are passive solvers,
// the handler will immediately dispatch the contexts to each of these in
// time order. Essentially, it implements a 'riffle' for the passive solvers
// and the pending contexts.The issue is that there are likely different
// cardinalities of the two sets, and the solvers should be marked as
// active after the dispatch and the context identifiers should be
// removed from the queue after the dispatch.
// active after the dispatch and the contexts should be removed from the
// queue after the dispatch.
void DispatchToSolvers( void )
{
if( !PassiveSolvers.empty() && !ContextExecutionQueue.empty() )
if( !PassiveSolvers.empty() && !ContextQueue.empty() )
{
for( const auto & [ SolverAddress, ContextElement ] :
std::ranges::views::zip( PassiveSolvers, ContextExecutionQueue ) )
Send( Contexts.at( ContextElement.second ), SolverAddress );
std::ranges::views::zip( PassiveSolvers, ContextQueue ) )
Send( ContextElement.second, SolverAddress );
// The number of contexts dispatched must equal the minimum of the
// available solvers and the available contexts.
std::size_t DispatchedContexts
= std::min( PassiveSolvers.size(), ContextExecutionQueue.size() );
= std::min( PassiveSolvers.size(), ContextQueue.size() );
// Then move the passive solver addresses used to active solver addresses
std::ranges::move(
std::ranges::subrange( PassiveSolvers.begin(),
std::ranges::next( PassiveSolvers.begin(), DispatchedContexts,
PassiveSolvers.end() ) ),
std::ranges::next( PassiveSolvers.begin(),
DispatchedContexts,
PassiveSolvers.end() ) ),
std::inserter( ActiveSolvers, ActiveSolvers.end() ) );
// Then the dispatched context identifiers are removed from queue
ContextExecutionQueue.erase( ContextExecutionQueue.begin(),
std::ranges::next( ContextExecutionQueue.begin(), DispatchedContexts,
ContextExecutionQueue.end() ) );
ContextQueue.erase( ContextQueue.begin(),
std::ranges::next( ContextQueue.begin(),
DispatchedContexts,
ContextQueue.end() ) );
}
}
// The handler function simply enqueues the received context, records its
// timesamp and dispatch as many contexts as possible to the solvers. Note
// that the context identifiers must be unique and there is a logic error
// if there is already a context with the same identifier. Then an invalid
// arguemtn exception will be thrown. This strategy should be reconsidered
// if there will be multiple entities firing execution contexts.
// timesamp and dispatch as many contexts as possible to the solvers.
void HandleApplicationExecutionContext(
const Solver:: ApplicationExecutionContext & TheContext,
const Address TheRequester )
{
auto [_, Success] = Contexts.try_emplace(
TheContext[ Solver::ContextIdentifier.data() ], TheContext );
ContextQueue.emplace(
TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(),
TheContext );
if( Success )
{
ContextExecutionQueue.emplace(
TheContext[ Solver::TimeStamp.data() ],
TheContext[ Solver::ContextIdentifier.data() ] );
DispatchToSolvers();
}
else
{
std::source_location Location = std::source_location::current();
std::ostringstream ErrorMessage;
ErrorMessage << "[" << Location.file_name() << " at line "
<< Location.line()
<< "in function " << Location.function_name() <<"] "
<< "An Application Execution Context with identifier "
<< TheContext[ Solver::ContextIdentifier.data() ]
<< " was received while there is already one with the same "
<< "identifer. The identifiers must be unique!";
throw std::invalid_argument( ErrorMessage.str() );
}
DispatchToSolvers();
}
// --------------------------------------------------------------------------
@ -262,7 +232,7 @@ public:
SolutionReceiver( SolutionTopic ),
ContextTopic( ContextPublisherTopic ),
SolverPool(), ActiveSolvers(), PassiveSolvers(),
Contexts(), ContextExecutionQueue()
ContextQueue()
{
// The solvers are created by expanding the arguments for the solvers
// one by one creating new elements in the solver pool. The solvers
@ -318,10 +288,15 @@ public:
<< boost::core::demangle( typeid( SolverType ).name() )
<< " from the given constructor argument types: ";
(( ErrorMessage << boost::core::demangle( typeid( SolverArguments ).name() ) << " " ), ... );
(( ErrorMessage << boost::core::demangle( typeid( SolverArguments ).name() ) << " " ), ... );
throw std::invalid_argument( ErrorMessage.str() );
throw std::invalid_argument( ErrorMessage.str() );
}
// Finally, the handlers for the messages are registered
RegisterHandler(this, &SolverManager::HandleApplicationExecutionContext );
RegisterHandler(this, &SolverManager::PublishSolution );
}
// The destructor closes all the open topics if the network is still open