First release

- Added build script and AMPL license file
- Fixed merge errors for the makefile
- Extended the makefile header
- Added initial AMQ message topics
- Tested remote build
- Removed AMPL license file
- Validated build script
- Accepting the metric definition message from the EMS
- Execution control status messages + solver type command line option
- Executing solver component

Change-Id: I3972b7e24a65eae8555aa60404d5f75cb4fc0705
This commit is contained in:
Geir Horn 2024-01-10 16:26:09 +01:00
parent 79cf79e7fb
commit ae588d8955
7 changed files with 144 additions and 28 deletions

View File

@ -10,7 +10,8 @@
"/home/GHo/Documents/Code/Theron++/Communication",
"/home/GHo/Documents/Code/Theron++/Communication/AMQ",
"/opt/AMPL/amplapi/include/ampl",
"${workspaceFolder}/**"
"${workspaceFolder}/**",
"/home/GHo/Documents/Code/Theron++"
],
"defines": [],
"cStandard": "c23",
@ -23,7 +24,6 @@
"-I/opt/AMPL/amplapi/include/ampl"
],
"cppStandard": "c++23",
"compilerPathInCppPropertiesJson": "/usr/bin/g++",
"mergeConfigurations": false,
"configurationProvider": "ms-vscode.makefile-tools",
"browse": {

View File

@ -15,6 +15,8 @@ License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
#include <stdexcept> // Standard exceptions
#include <system_error> // Error codes
#include "Utility/ConsolePrint.hpp"
#include "AMPLSolver.hpp"
namespace NebulOuS
@ -92,7 +94,12 @@ std::string AMPLSolver::SaveFile( const JSON & TheMessage,
void AMPLSolver::DefineProblem(const Solver::OptimisationProblem & TheProblem,
const Address TheOracle)
{
ProblemDefinition.read( SaveFile( TheProblem ) );
Theron::ConsoleOutput Output;
Output << "AMPL Solver received the AMPL problem: " << TheProblem.dump(2)
<< std::endl;
ProblemDefinition.read( SaveFile( TheProblem ) );
Output << "Problem loaded!" << std::endl;
}
// The data file(s) corresponding to the current optimisation problem will be
@ -215,7 +222,8 @@ void AMPLSolver::SolveProblem(
AMPLSolver::AMPLSolver( const std::string & TheActorName,
const ampl::Environment & InstallationDirectory,
const std::filesystem::path & ProblemPath )
const std::filesystem::path & ProblemPath,
const std::string TheSolverType )
: Actor( TheActorName ),
StandardFallbackHandler( Actor::GetAddress().AsString() ),
NetworkingActor( Actor::GetAddress().AsString() ),
@ -225,6 +233,8 @@ AMPLSolver::AMPLSolver( const std::string & TheActorName,
{
RegisterHandler( this, &AMPLSolver::DataFileUpdate );
ProblemDefinition.setOption( "solver", TheSolverType );
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
Theron::AMQ::TopicName( DataFileTopic )

View File

@ -223,23 +223,26 @@ public:
explicit AMPLSolver( const std::string & TheActorName,
const ampl::Environment & InstallationDirectory,
const std::filesystem::path & ProblemPath );
const std::filesystem::path & ProblemPath,
std::string TheSolverType );
// If the path to the problem directory is omitted, it will be initialised to
// a temporary directory.
explicit AMPLSolver( const std::string & TheActorName,
const ampl::Environment & InstallationDirectory )
const ampl::Environment & InstallationDirectory,
std::string TheSolverType )
: AMPLSolver( TheActorName, InstallationDirectory,
std::filesystem::temp_directory_path() )
std::filesystem::temp_directory_path(), TheSolverType )
{}
// If the AMPL installation environment is omitted, the installation directory
// will be taken form the environment variables.
explicit AMPLSolver( const std::string & TheActorName,
const std::filesystem::path & ProblemPath )
: AMPLSolver( TheActorName, ampl::Environment(), ProblemPath )
const std::filesystem::path & ProblemPath,
std::string TheSolverType )
: AMPLSolver( TheActorName, ampl::Environment(), ProblemPath, TheSolverType )
{}
// Finally, it is just the standard constructor taking only the name of the
@ -247,7 +250,7 @@ public:
AMPLSolver( const std::string & TheActorName )
: AMPLSolver( TheActorName, ampl::Environment(),
std::filesystem::temp_directory_path() )
std::filesystem::temp_directory_path(), "couenne" )
{}
// The solver will just close the open connections for listening to data file

View File

@ -11,6 +11,7 @@ License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
#include "Actor.hpp"
#include "Communication/NetworkEndpoint.hpp"
#include "Communication/AMQ/AMQEndpoint.hpp"
#include "ExecutionControl.hpp"
namespace NebulOuS
@ -56,6 +57,9 @@ void ExecutionControl::StopMessageHandler( const StopMessage & Command,
{
std::lock_guard< std::mutex > Lock( TerminationLock );
Send( StatusMessage( StatusMessage::State::Stopped ),
Address( std::string( StatusTopic ) ) );
Send( Theron::Network::ShutDown(),
Theron::Network::GetAddress( Theron::Network::Layer::Session ) );
@ -67,14 +71,24 @@ void ExecutionControl::StopMessageHandler( const StopMessage & Command,
// Constructor
// -----------------------------------------------------------------------------
//
// The only action taken by the constructor is to register the handler for the
// stop message.
// The constructor registers the stop message handler and sets up a publisher
// for the status topic, and then post a message that the solver is starting.
ExecutionControl::ExecutionControl( const std::string & TheActorName )
: Actor( TheActorName ),
StandardFallbackHandler( Actor::GetAddress().AsString() )
StandardFallbackHandler( Actor::GetAddress().AsString() ),
NetworkingActor( Actor::GetAddress().AsString() )
{
RegisterHandler( this, &ExecutionControl::StopMessageHandler );
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Publisher,
std::string( StatusTopic )
), GetSessionLayerAddress() );
Send( StatusMessage( StatusMessage::State::Starting ),
Address( std::string( StatusTopic ) ) );
}
} // namespace NebulOuS

View File

@ -16,6 +16,9 @@ to call the function to wait for termination.
The threads calling the function to wait for termination will block until the
required message is received.
The Agent is also involved with the general component status messages to be
sent to the Solver's status topic.
Author and Copyright: Geir Horn, University of Oslo
Contact: Geir.Horn@mn.uio.no
License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
@ -26,6 +29,10 @@ License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
// Standard headers
#include <string_view> // For constant strings
#include <map> // Standard maps
#include <sstream> // Stream conversion
#include <chrono> // For standard time points
#include <condition_variable> // Execution stop management
#include <mutex> // Lock the condtion variable
@ -34,6 +41,12 @@ License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
#include "Actor.hpp" // Actor base class
#include "Utility/StandardFallbackHandler.hpp" // Exception unhanded messages
// AMQ communication
#include "Communication/NetworkingActor.hpp" // The networking actor
#include "Communication/AMQ/AMQMessage.hpp"
#include "Communication/AMQ/AMQjson.hpp" // JSON messages to be sent
namespace NebulOuS
{
@ -45,7 +58,9 @@ namespace NebulOuS
class ExecutionControl
: virtual public Theron::Actor,
virtual public Theron::StandardFallbackHandler
virtual public Theron::StandardFallbackHandler,
virtual public Theron::NetworkingActor<
typename Theron::AMQ::Message::PayloadType >
{
// The mechanism used for blocking other threads will be to make them wait
// for a condition variable until the message handler for the exit message
@ -57,6 +72,57 @@ private:
static std::mutex TerminationLock;
static std::condition_variable ReadyToTerminate;
protected:
// There is a status message class that can be used to send the status to
// other components.
class StatusMessage
: virtual public Theron::AMQ::JSONMessage
{
public:
enum class State
{
Starting,
Started,
Stopping,
Stopped
};
private:
std::string ToString( State TheSituation )
{
static const std::map< State, std::string > StateString {
{State::Starting, "starting"}, {State::Started, "started"},
{State::Stopping, "stopping"}, {State::Stopped, "stopped"} };
return StateString.at( TheSituation );
}
std::string UTCNow( void )
{
std::ostringstream TimePoint;
TimePoint << std::chrono::system_clock::now();
return TimePoint.str();
}
public:
StatusMessage( State TheSituation,
std::string AdditionalInformation = std::string() )
: JSONMessage( std::string( StatusTopic ),
{ {"when", UTCNow() }, {"state", ToString( TheSituation ) },
{"message", AdditionalInformation } } )
{}
};
// The status of the solver is communicated on the dedicated status topic
static constexpr std::string_view StatusTopic
= "eu.nebulouscloud.solver.state";
public:
// The function used to wait for the termination message simply waits on the

View File

@ -17,6 +17,7 @@ The command line arguments that can be givne to the Solver Component are
-M ir --ModelDir <directory> for model and data files
-N or --name The AMQ identity of the solver (see below)
-P or --port <n> the port to use on the AMQ broker URL
-S or --Solver <label> The back-end solver used by AMPL
-U or --user <user> the user to authenticate for the AMQ broker
-Pw or --password <password> the AMQ broker password for the user
-? or --Help prints a help message for the options
@ -29,6 +30,7 @@ Default values:
-M <temporary directory created by the OS>
-N "NebulOuS::Solver"
-P 5672
-S couenne
-U admin
-Pw admin
@ -38,6 +40,20 @@ will be established as "name@endpoint" and so if there are several
solver components running, the endpoint is the only way for the AMQ solvers to
distinguish the different solver component subscriptions.
Notes on use:
The path to the AMPL API shared libray must be in the LIB path environment
variable. For instance, the installation of AMPL on the author's machine is in
/opt/AMPL and so the first thing to ensure is that the path to the API library
directory is added to the link library path, e.g.,
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/AMPL/amplapi/lib
The AMPL directory also needs to be in the path variable, and the path must
be extended with the AMPL execution file path, e.g.,
export PATH=$PATH:/opt/AMPL
Author and Copyright: Geir Horn, University of Oslo
Contact: Geir.Horn@mn.uio.no
License: MPL2.0 (https://www.mozilla.org/en-US/MPL/2.0/)
@ -100,20 +116,22 @@ int main( int NumberOfCLIOptions, char ** CLIOptionStrings )
CLIOptions.add_options()
("A,AMPLDir", "The AMPL installation path",
cxxopts::value<std::string>()->default_value("") )
("B,broker", "The URL of the AMQ broker",
("B,Broker", "The URL of the AMQ broker",
cxxopts::value<std::string>()->default_value("localhost") )
("E,endpoint", "The endpoint name", cxxopts::value<std::string>() )
("E,Endpoint", "The endpoint name", cxxopts::value<std::string>() )
("M,ModelDir", "Directory to store the model and its data",
cxxopts::value<std::string>()->default_value("") )
("N,name", "The name of the Solver Component",
("N,Name", "The name of the Solver Component",
cxxopts::value<std::string>()->default_value("NebulOuS::Solver") )
("P,port", "TCP port on AMQ Broker",
("P,Port", "TCP port on AMQ Broker",
cxxopts::value<unsigned int>()->default_value("5672") )
("U,user", "The user name used for the AMQ Broker connection",
("S,Solver", "Solver to use, devault Couenne",
cxxopts::value<std::string>()->default_value("couenne") )
("U,User", "The user name used for the AMQ Broker connection",
cxxopts::value<std::string>()->default_value("admin") )
("Pw,password", "The password for the AMQ Broker connection",
("Pw,Password", "The password for the AMQ Broker connection",
cxxopts::value<std::string>()->default_value("admin") )
("?,help", "Print help information");
("h,help", "Print help information");
CLIOptions.allow_unrecognised_options();
@ -170,17 +188,17 @@ int main( int NumberOfCLIOptions, char ** CLIOptionStrings )
proton::connection_options AMQOptions;
AMQOptions.user( CLIValues["user"].as< std::string >() );
AMQOptions.password( CLIValues["password"].as< std::string >() );
AMQOptions.user( CLIValues["User"].as< std::string >() );
AMQOptions.password( CLIValues["Password"].as< std::string >() );
// Then the network endpoint cna be constructed using the default names for
// the various network endpoint servers in order to pass the defined
// connection options.
Theron::AMQ::NetworkEndpoint AMQNetWork(
CLIValues["endpoint"].as< std::string >(),
CLIValues["broker"].as< std::string >(),
CLIValues["port"].as< unsigned int >(),
CLIValues["Endpoint"].as< std::string >(),
CLIValues["Broker"].as< std::string >(),
CLIValues["Port"].as< unsigned int >(),
Theron::AMQ::Network::NetworkLayerLabel,
Theron::AMQ::Network::SessionLayerLabel,
Theron::AMQ::Network::PresentationLayerLabel,
@ -203,11 +221,12 @@ int main( int NumberOfCLIOptions, char ** CLIOptionStrings )
// the root solver name.
NebulOuS::SolverManager< NebulOuS::AMPLSolver >
WorkloadMabager( "WorkloadManager",
WorkloadMabager( CLIValues["Name"].as<std::string>(),
std::string( NebulOuS::Solver::Solution::MessageIdentifier ),
std::string( NebulOuS::Solver::ApplicationExecutionContext::MessageIdentifier ),
1, "AMPLSolver",
ampl::Environment( TheAMPLDirectory.native() ), ModelDirectory );
ampl::Environment( TheAMPLDirectory.native() ), ModelDirectory,
CLIValues["Solver"].as<std::string>() );
NebulOuS::MetricUpdater
ContextMabager( "MetricUpdater", WorkloadMabager.GetAddress() );

View File

@ -299,6 +299,10 @@ public:
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
ContextPublisherTopic ), GetSessionLayerAddress() );
Send( ExecutionControl::StatusMessage(
ExecutionControl::StatusMessage::State::Started
), Address( std::string( ExecutionControl::StatusTopic ) ) );
}
else
{