diff --git a/AMPLSolver.cpp b/AMPLSolver.cpp index 3e24d31..3865871 100644 --- a/AMPLSolver.cpp +++ b/AMPLSolver.cpp @@ -174,7 +174,11 @@ void AMPLSolver::DefineProblem(const Solver::OptimisationProblem & TheProblem, // The default values for the data will be loaded from the data file. This // operation is the same as the one done for data messages, and to avoid // code duplication the handler is just invoked using the address of this - // solver Actor as the the sender is not important for this update. + // solver Actor as the the sender is not important for this update. However, + // if the information is missing from the message, no data file should be + // loaded. It is necessary to convert the content to a string since the + // JSON library only sees the string and not its length before it has been + // unwrapped. if( TheProblem.contains( DataFileMessage::Keys::DataFile ) && TheProblem.contains( DataFileMessage::Keys::NewData ) ) @@ -255,7 +259,8 @@ void AMPLSolver::SolveProblem( // supported as values. for( const auto & [ TheName, MetricValue ] : - Solver::MetricValueType( TheContext.at( Solver::ExecutionContext ) ) ) + Solver::MetricValueType( TheContext.at( + Solver::ApplicationExecutionContext::Keys::ExecutionContext ) ) ) SetAMPLParameter( TheName, MetricValue ); // Setting the given objective as the active objective and all other @@ -264,8 +269,10 @@ void AMPLSolver::SolveProblem( std::string OptimisationGoal; - if( TheContext.contains( Solver::ObjectiveFunctionLabel ) ) - OptimisationGoal = TheContext.at( Solver::ObjectiveFunctionLabel ); + if( TheContext.contains( + Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel ) ) + OptimisationGoal = TheContext.at( + Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel ); else if( !DefaultObjectiveFunction.empty() ) OptimisationGoal = DefaultObjectiveFunction; else @@ -337,7 +344,8 @@ void AMPLSolver::SolveProblem( // application execution context has the deployment flag set. Solver::Solution::VariableValuesType VariableValues; - bool DeploymentFlagSet = TheContext.at( DeploymentFlag ).get(); + bool DeploymentFlagSet + = TheContext.at( Solver::Solution::Keys::DeploymentFlag ).get(); for( auto Variable : ProblemDefinition.getVariables() ) { @@ -350,8 +358,9 @@ void AMPLSolver::SolveProblem( // The found solution can then be returned to the requesting actor or topic - Send( Solver::Solution( - TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(), + Send( Solver::Solution( + TheContext.at( + Solver::Solution::Keys::TimeStamp ).get< Solver::TimePointType >(), OptimisationGoal, ObjectiveValues, VariableValues, DeploymentFlagSet ), TheRequester ); diff --git a/MetricUpdater.cpp b/MetricUpdater.cpp index 1607081..5b7c16b 100644 --- a/MetricUpdater.cpp +++ b/MetricUpdater.cpp @@ -40,7 +40,7 @@ namespace NebulOuS void MetricUpdater::AddMetricSubscription( const MetricTopic & MetricDefinitions, const Address OptimiserController ) { - JSON TheMetrics = MetricDefinitions.at( MetricList ); + JSON TheMetrics = MetricDefinitions.at( MetricTopic::Keys::MetricList ); if( TheMetrics.is_array() ) { @@ -57,18 +57,14 @@ void MetricUpdater::AddMetricSubscription( auto [ MetricRecordPointer, MetricAdded ] = MetricValues.try_emplace( MetricRecord.get(), JSON() ); - TheMetricNames.insert( MetricRecordPointer->first ); - - // If a new metric was added, a subscription will be set up for this - // new metric, and the flag indicating that values have been received - // for all metrics will be reset since this new metric has yet to receive - // its first value + TheMetricNames.insert( MetricRecordPointer->first ); if( MetricAdded ) { Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - std::string( MetricValueRootString ) + MetricRecordPointer->first ), + std::string( MetricValueUpdate::MetricValueRootString ) + + MetricRecordPointer->first ), GetSessionLayerAddress() ); AllMetricValuesSet = false; @@ -85,7 +81,7 @@ void MetricUpdater::AddMetricSubscription( { Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - std::string( MetricValueRootString ) + TheMetric ), + std::string( MetricValueUpdate::MetricValueRootString ) + TheMetric ), GetSessionLayerAddress() ); MetricValues.erase( TheMetric ); @@ -138,14 +134,16 @@ void MetricUpdater::UpdateMetricValue( { Theron::AMQ::TopicName TheTopic = TheMetricTopic.AsString().erase( 0, - NebulOuS::MetricValueRootString.size() ); + MetricValueUpdate::MetricValueRootString.size() ); if( MetricValues.contains( TheTopic ) ) { - MetricValues.at( TheTopic ) = TheMetricValue.at( NebulOuS::ValueLabel ); + MetricValues.at( TheTopic ) + = TheMetricValue.at( MetricValueUpdate::Keys::ValueLabel ); ValidityTime = std::max( ValidityTime, - TheMetricValue.at( NebulOuS::TimePoint ).get< Solver::TimePointType >() ); + TheMetricValue.at( + MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >() ); } } @@ -183,7 +181,8 @@ void MetricUpdater::SLOViolationHandler( [](const auto & MetricValue){ return MetricValue.is_null(); } ))) ) { Send( Solver::ApplicationExecutionContext( - SeverityMessage.at( NebulOuS::TimePoint ).get< Solver::TimePointType >(), + SeverityMessage.at( + MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >(), MetricValues, true ), TheSolverManager ); @@ -250,17 +249,17 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName, Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - NebulOuS::MetricSubscriptions ), + MetricTopic::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - NebulOuS::ReconfigurationTopic ), + ReconfigurationMessage::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - NebulOuS::SLOViolationTopic ), + SLOViolation::AMQTopic ), GetSessionLayerAddress() ); } @@ -275,24 +274,25 @@ MetricUpdater::~MetricUpdater() { Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - NebulOuS::MetricSubscriptions ), + MetricTopic::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - NebulOuS::ReconfigurationTopic ), + ReconfigurationMessage::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - NebulOuS::SLOViolationTopic ), + SLOViolation::AMQTopic ), GetSessionLayerAddress() ); std::ranges::for_each( std::views::keys( MetricValues ), [this]( const Theron::AMQ::TopicName & TheMetricTopic ){ Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - std::string( MetricValueRootString ) + TheMetricTopic ), + std::string( MetricValueUpdate::MetricValueRootString ) + + TheMetricTopic ), GetSessionLayerAddress() ); }); } diff --git a/MetricUpdater.hpp b/MetricUpdater.hpp index 51257f6..281500b 100644 --- a/MetricUpdater.hpp +++ b/MetricUpdater.hpp @@ -68,74 +68,6 @@ using JSON = nlohmann::json; // Short form name space namespace NebulOuS { -/*============================================================================== - - Basic interface definitions - -==============================================================================*/ -// -// Definitions for the terminology to facilitate changing the lables of the -// various message labels without changing the code. The definitions are -// compile time constants and as such should not lead to any run-time overhead. -// The JSON attribute names may be found under the "Predicted monitoring -// metrics" section on the Wiki page [1]. - -constexpr std::string_view ValueLabel = "metricValue"; -constexpr std::string_view TimePoint = "predictionTime"; - -// The topic used for receiving the message(s) defining the metrics of the -// application execution context as published by the Optimiser Controller is -// defined next. - -constexpr std::string_view MetricSubscriptions - = "eu.nebulouscloud.optimiser.controller.metric_list"; - -// The JSON message attribute for the list of metrics is another JSON object -// stored under the following key, see the Event type III defined in -// https://158.39.75.54/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector -// where the name of the metric is defined under as sub-key. - -constexpr std::string_view MetricList = "metrics"; - -// The metric value messages will be published on different topics and to -// check if an inbound message is from a metric value topic, it is necessary -// to test against the base string for the metric value topics according to -// the Wiki-page [1] - -constexpr std::string_view MetricValueRootString - = "eu.nebulouscloud.monitoring.predicted."; - -// The SLO violation detector will publish a message when a reconfiguration is -// deamed necessary for a future time point called "Event type V" on the wiki -// page [3]. The event contains a probability for at least one of the SLOs -// being violated at the predicted time point. It is not clear if the assessment -// is being made by the SLO violation detector at every new metric prediction, -// or if this event is only signalled when the probability is above some -// internal threshold of the SLO violation detector. -// -// The current implementation assumes that the latter is the case, and hence -// just receiving the message indicates that a new application configuration -// should be found given the application execution context as predicted by the -// metric values recorded by the Metric Updater. Should this assumption be -// wrong, the probability must be compared with some user set threshold for -// each message, and to cater for this the probability field will always be -// compared to a threshold, currently set to zero to ensure that every event -// message will trigger a reconfiguration. -// -// The messages from the Optimizer Controller will be sent on a topic that -// should follow some standard topic convention. - -constexpr std::string_view SLOViolationTopic - = "eu.nebulouscloud.monitoring.slo.severity_value"; - -// When a reconfiguration has been enacted by the Optimiser Controller and -// a new configuration is confirmed to be running on the new platofrm, it will -// send a message to inform all other components that the reconfiguration -// has happened on the following topic. - -constexpr std::string_view ReconfigurationTopic - = "eu.nebulouscloud.optimiser.adaptations"; - /*============================================================================== Metric Updater @@ -178,14 +110,12 @@ private: Solver::TimePointType ValidityTime; - // There is also a flag to indicate when all metric values have received - // values since optimising for a application execution context defiend all - // metrics requires that at least one value is received for each metric. This - // condition could be tested before sending the request to find a new - // solution, but this means testing all metrics in a linear scan for a - // condition that will only happen initially until all metrics have been seen - // and so it is better for the performance if there is a flag to check for - // this condition. + // When an SLO violation message is received the current vector of metric + // values should be sent as an application execution context (message) to the + // Solution Manager actor that will invoke a solver to find the optimal + // configuration for this configuration. The Metric Updater must therefore + // know the address of the Soler Manager, and this must be passed to + // the constructor. bool AllMetricValuesSet; @@ -204,18 +134,35 @@ private: { public: + // The topic used for receiving the message(s) defining the metrics of the + // application execution context as published by the Optimiser Controller + // is the following + + static constexpr std::string_view AMQTopic + = "eu.nebulouscloud.optimiser.controller.metric_list"; + + // The EXN middleware sending the AMQP messages for the other component + // of the NebulOuS project only sends JSON objects, meaning that the list + // of metric names to subscribe is sent as a JSON array, but it must be + // embedded in a map with a single key, see the message format described + // in the Wiki page at + // https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/1-optimiser-controller#controller-to-metric-updater-and-ems-metric-list + + struct Keys + { + static constexpr std::string_view MetricList = "metrics"; + }; + + // Constructors + MetricTopic( void ) - : JSONTopicMessage( std::string( MetricSubscriptions ) ) + : JSONTopicMessage( AMQTopic ) {} MetricTopic( const MetricTopic & Other ) : JSONTopicMessage( Other ) {} - // MetricTopic( const JSONTopicMessage & Other ) - // : JSONTopicMessage( Other ) - // {} - virtual ~MetricTopic() = default; }; @@ -241,6 +188,25 @@ private: { public: + // The metric value messages will be published on different topics and to + // check if an inbound message is from a metric value topic, it is + // necessary to test against the base string for the metric value topics + // according to the Wiki-page describing the Type II message: + // https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/monitoringdata-interface#type-ii-messages-predicted-monitoring-metrics + + static constexpr std::string_view MetricValueRootString + = "eu.nebulouscloud.monitoring.predicted."; + + // Only two of the fields in this message will be looked up and stored + // in the current application context map. + + struct Keys + { + static constexpr std::string_view + ValueLabel = "metricValue", + TimePoint = "predictionTime"; + }; + MetricValueUpdate( void ) : JSONWildcardMessage( std::string( MetricValueRootString ) ) {} @@ -263,21 +229,48 @@ private: // SLO violations // -------------------------------------------------------------------------- // - // The SLO Violation detector publishes an event to indicate that at least - // one of the constraints for the application deployment will be violated in - // the predicted future, and that the search for a new solution should start. - // This will trigger the the publication of the Solver's Application Execution - // context message. The context message will contain the current status of the - // metric values, and trigger a solver to find a new, optimal variable - // assignment to be deployed to resolve the identified problem. - + // The SLO violation detector will publish a message when a reconfiguration is + // deamed necessary for a future time point called "Event type VI" on the wiki + // page: https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector#output-event-type-vi + // The event contains a probability for at least one of the SLOs being + // violated at the predicted time point. It is not clear if the assessment + // is being made by the SLO violation detector at every new metric prediction, + // or if this event is only signalled when the probability is above some + // internal threshold of the SLO violation detector. + // + // The current implementation assumes that the latter is the case, and hence + // just receiving the message indicates that a new application configuration + // should be found given the application execution context as predicted by the + // metric values recorded by the Metric Updater. Should this assumption be + // wrong, the probability must be compared with some user set threshold for + // each message, and to cater for this the probability field will always be + // compared to a threshold, currently set to zero to ensure that every event + // message will trigger a reconfiguration. + class SLOViolation : public Theron::AMQ::JSONTopicMessage { public: + // The messages from the SLO Violation Detector will be sent on a topic that + // should follow some standard topic convention. + + static constexpr std::string_view AMQTopic + = "eu.nebulouscloud.monitoring.slo.severity_value"; + + // The only information taken from this detction message is the prediction + // time which will be used as the time for the application's execution + // context when this is forwarded to the solvers for processing. + + struct Keys + { + static constexpr std::string_view TimePoint = "predictionTime"; + }; + + // Constructors + SLOViolation( void ) - : JSONTopicMessage( std::string( SLOViolationTopic ) ) + : JSONTopicMessage( AMQTopic ) {} SLOViolation( const SLOViolation & Other ) @@ -311,18 +304,28 @@ private: bool ReconfigurationInProgress; - // When the reconfiguration has been done and the Optimizer Controller - // confirms that the application is running in a new configuration, it will - // send a reconfiguration completed message. This message will just be a - // JSON message. + // When a reconfiguration has been enacted by the Optimiser Controller and + // a new configuration is confirmed to be running on the new platofrm, it + // will send a message to inform all other components that the + // reconfiguration has happened. The event is just the reception of the + // message and its content will not be processed, so there are no keys for + // the JSON map received. class ReconfigurationMessage : public Theron::AMQ::JSONTopicMessage { public: + // The topic for the reconfiguration finished messages is defined by the + // optimiser as the sender. + + static constexpr std::string_view AMQTopic + = "eu.nebulouscloud.optimiser.controller.reconfiguration"; + + // Constructors + ReconfigurationMessage( void ) - : JSONTopicMessage( std::string( ReconfigurationTopic ) ) + : JSONTopicMessage( AMQTopic ) {} ReconfigurationMessage( const ReconfigurationMessage & Other ) diff --git a/Solver.hpp b/Solver.hpp index c9228a3..befd603 100644 --- a/Solver.hpp +++ b/Solver.hpp @@ -79,52 +79,6 @@ public: // Application Execution Context // -------------------------------------------------------------------------- // - // The message is defined as a JSON message representing an attribute-value - // object. The attributes expected are defined as constant strings so that - // the actual textual representation can be changed without changing the code - // - // "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 - // work on the different problems. - - static constexpr std::string_view TimeStamp = "Timestamp"; - - // There is also a definition for the objective function label since a multi- - // objective optimisation problem can have multiple objective functions and - // the solution is found for only one of these functions at the time even - // though all objective function values will be returned with the solution, - // the solution will maximise only the objective function whose label is - // given in the application execution context request message. - // - // The Application Execution Cntext message may contain the name of the - // objective function to maximise. If so, this should be stored under the - // key name indicated here. However, if the objective function name is not - // given, the default objective function is used. The default objective - // function will be named when defining the optimisation problem. - - static constexpr std::string_view - ObjectiveFunctionLabel = "ObjectiveFunction"; - - // Finally, there is another JSON object that defines all the metric name and - // value pairs that define the actual execution context. Note that there must - // be at least one metric-value pair for the request to be valid. - - static constexpr std::string_view ExecutionContext = "ExecutionContext"; - - // Finally, the execution context can come from the Metric Collector actor - // as a consequence of an SLO Violation being detected. In this case the - // optimised solution found by the solver should trigger a reconfiguration. - // However, various application execution context can also be tried for - // simulating future events and to investigate which configuration would be - // the best for these situations. In this case the optimised solution should - // not reconfigure the running application. For this reason there is a flag - // in the message indicating whether the solution should be deployed, and - // its default value is 'false' to prevent solutions form accidentially being - // deployed. - - static constexpr std::string_view DeploymentFlag = "DeploySolution"; - // To ensure that the execution context is correctly provided by the senders // The expected metric value structure is defined as a type based on the // standard unsorted map based on a JSON value object since this can hold @@ -161,6 +115,48 @@ public: static constexpr std::string_view AMQTopic = "eu.nebulouscloud.optimiser.solver.context"; + // The keys used in the JSON message to send are defined first: + // + // "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 work on the different problems. + // "ObjectFunction" : There is also a definition for the objective function + // label since a multi-objective optimisation problem can have multiple + // objective functions and the solution is found for only one of these + // functions at the time even though all objective function values will + // be returned with the solution, the solution will maximise only the + // objective function whose label is given in the application execution + // context request message. The Application Execution Cntext message may + // contain the name of the objective function to maximise. If so, this + // should be stored under the key name indicated here. However, if the + // objective function name is not given, the default objective function + // is used. The default objective function will be named when defining + // the optimisation problem. + // "ExecutionContext" : Defines all the metric name and value pairs that + // define the actual execution context. Note that there must be at least + // one metric-value pair for the request to be valid. + // "DeploySolution" : The execution context can come from the Metric + // Collector actor as a consequence of an SLO Violation being detected. + // In this case the optimised solution found by the solver should trigger + // a reconfiguration. However, various application execution context can + // also be tried for simulating future events and to investigate which + // configuration would be the best for these situations. In this case the + // optimised solution should not reconfigure the running application. For + // this reason there is a flag in the message indicating whether the + // solution should be deployed, and its default value is 'false' to + // prevent solutions form accidentially being deployed. + + + struct Keys + { + static constexpr std::string_view + TimeStamp = "Timestamp", + ObjectiveFunctionLabel = "ObjectiveFunction", + ExecutionContext = "ExecutionContext", + DeploymentFlag = "DeploySolution"; + }; + // The full constructor takes the time point, the objective function to // solve for, and the application's execution context as the metric map @@ -169,10 +165,10 @@ public: const MetricValueType & TheContext, bool DeploySolution = false ) : JSONTopicMessage( std::string( AMQTopic ), - { { std::string( TimeStamp ), MicroSecondTimePoint }, - { std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID }, - { std::string( ExecutionContext ), TheContext }, - { std::string( DeploymentFlag ), DeploySolution } + { { Keys::TimeStamp, MicroSecondTimePoint }, + { Keys::ObjectiveFunctionLabel, ObjectiveFunctionID }, + { Keys::ExecutionContext, TheContext }, + { Keys::DeploymentFlag, DeploySolution } }) {} // The constructor omitting the objective function identifier is similar @@ -183,9 +179,9 @@ public: const MetricValueType & TheContext, bool DeploySolution = false ) : JSONTopicMessage( std::string( AMQTopic ), - { { std::string( TimeStamp ), MicroSecondTimePoint }, - { std::string( ExecutionContext ), TheContext }, - { std::string( DeploymentFlag ), DeploySolution } + { { Keys::TimeStamp, MicroSecondTimePoint }, + { Keys::ExecutionContext, TheContext }, + { Keys::DeploymentFlag, DeploySolution } }) {} // The copy constructor simply passes the job on to the JSON Topic @@ -206,9 +202,9 @@ public: virtual ~ApplicationExecutionContext() = default; }; - // The handler for this message is virtual as it where the real action will - // happen and the search for the optimal solution will hopefully lead to a - // feasible soltuion that can be returned to the sender of the applicaton + // The handler for this message is virtual as it is where the real action + // will happen and the search for the optimal solution will hopefully lead + // to a feasible soltuion that can be returned to the sender of the applicaton // context. protected: @@ -241,26 +237,47 @@ public: { public: + // There are some aliases that can be used in other Actors processing this + // message in order to ensure portability + using ObjectiveValuesType = MetricValueType; using VariableValuesType = MetricValueType; - static constexpr std::string_view ObjectiveValues = "ObjectiveValues"; - static constexpr std::string_view VariableValues = "VariableValues"; - + // The topic for which the message is published is defined first + static constexpr std::string_view AMQTopic = "eu.nebulouscloud.optimiser.solver.solution"; + // Most of the message keys are the same as for the application execution + // context, but there are two new: + // + // "ObjectiveValues" : This holds a map of objective function names and + // their values under the currently found solution which is optimised + // for the given objective function or the default objective function. + // The other objective values is useful if one is searching for the + // Pareto front of the problem. + // "VariableValues" : This key is a map holding the variable names and + // their values found by the solver for the optimal solution. This is + // used to reconfigure the application. + + struct Keys : public ApplicationExecutionContext::Keys + { + static constexpr std::string_view + ObjectiveValues = "ObjectiveValues", + VariableValues = "VariableValues"; + }; + Solution( const TimePointType MicroSecondTimePoint, const std::string ObjectiveFunctionID, const ObjectiveValuesType & TheObjectiveValues, const VariableValuesType & TheVariables, bool DeploySolution ) : JSONTopicMessage( std::string( AMQTopic ) , - { { std::string( TimeStamp ), MicroSecondTimePoint }, - { std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID }, - { std::string( ObjectiveValues ) , TheObjectiveValues }, - { std::string( VariableValues ), TheVariables }, - { std::string( DeploymentFlag ), DeploySolution } + { { Keys::TimeStamp, MicroSecondTimePoint }, + { Keys::ObjectiveFunctionLabel, ObjectiveFunctionID }, + { Keys::ObjectiveValues, TheObjectiveValues }, + { Keys::VariableValues, TheVariables }, + { Keys::DeploymentFlag, DeploySolution } } ) {} @@ -335,6 +352,11 @@ public: Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, OptimisationProblem::AMQTopic ), GetSessionLayerAddress() ); + + Send( Theron::AMQ::NetworkLayer::TopicSubscription( + Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, + ApplicationExecutionContext::AMQTopic + ), GetSessionLayerAddress() ); } Solver() = delete; @@ -342,10 +364,18 @@ public: virtual ~Solver() { if( HasNetwork() ) + { Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, OptimisationProblem::AMQTopic ), GetSessionLayerAddress() ); + + Send( Theron::AMQ::NetworkLayer::TopicSubscription( + Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, + ApplicationExecutionContext::AMQTopic + ), GetSessionLayerAddress() ); + + } } }; diff --git a/SolverManager.hpp b/SolverManager.hpp index 7e4c423..bb9570d 100644 --- a/SolverManager.hpp +++ b/SolverManager.hpp @@ -174,7 +174,8 @@ private: const Address TheRequester ) { ContextQueue.emplace( - TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(), + TheContext.at( Solver::ApplicationExecutionContext::Keys::TimeStamp + ).get< Solver::TimePointType >(), TheContext ); DispatchToSolvers();