Mantid
Loading...
Searching...
No Matches
BatchAlgorithmRunner.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7
12
15#include "MantidKernel/Logger.h"
16
17#include <memory>
18#include <utility>
19
20using namespace Mantid::API;
21
22namespace {
23Mantid::Kernel::Logger g_log("BatchAlgorithmRunner");
24
25// Throw if any of the given properties do not exist in the algorithm's declared property names
26void throwIfAnyPropertiesInvalid(IAlgorithm_sptr alg, MantidQt::API::IAlgorithmRuntimeProps const &props) {
27 auto allowedPropNames = alg->getDeclaredPropertyNames();
28 auto propNamesToUpdate = props.getDeclaredPropertyNames();
29
30 // Note that for std::set_difference the lists need to be sorted
31 std::sort(allowedPropNames.begin(), allowedPropNames.end());
32 std::sort(propNamesToUpdate.begin(), propNamesToUpdate.end());
33
34 std::vector<std::string> invalidProps;
35 std::set_difference(propNamesToUpdate.cbegin(), propNamesToUpdate.cend(), allowedPropNames.cbegin(),
36 allowedPropNames.cend(), std::back_inserter(invalidProps));
37
38 if (invalidProps.size() > 0) {
39 const auto invalidPropsStr = std::accumulate(std::next(invalidProps.cbegin()), invalidProps.cend(), invalidProps[0],
40 [](const auto &a, const auto &b) { return a + "," + b; });
41 throw Mantid::Kernel::Exception::NotFoundError("Invalid Properties given: ", invalidPropsStr);
42 }
43}
44} // namespace
45
46namespace MantidQt::API {
47
49 : QObject(parent), m_stopOnFailure(true), m_cancelRequested(false), m_notificationCenter(),
50 m_batchCompleteObserver(*this, &BatchAlgorithmRunner::handleBatchComplete),
51 m_batchCancelledObserver(*this, &BatchAlgorithmRunner::handleBatchCancelled),
52 m_algorithmStartedObserver(*this, &BatchAlgorithmRunner::handleAlgorithmStarted),
53 m_algorithmCompleteObserver(*this, &BatchAlgorithmRunner::handleAlgorithmComplete),
54 m_algorithmErrorObserver(*this, &BatchAlgorithmRunner::handleAlgorithmError),
55 m_executeAsync(this, &BatchAlgorithmRunner::executeBatchAsyncImpl) {}
56
58
65}
66
73}
74
83
90 this->addAlgorithm(algo, std::make_unique<AlgorithmRuntimeProps>());
91}
92
100void BatchAlgorithmRunner::addAlgorithm(const IAlgorithm_sptr &algo, std::unique_ptr<IAlgorithmRuntimeProps> props) {
101 m_algorithms.emplace_back(std::make_unique<ConfiguredAlgorithm>(algo, std::move(props)));
102
103 g_log.debug() << "Added algorithm \"" << m_algorithms.back()->algorithm()->name() << "\" to batch queue\n";
104}
105
111void BatchAlgorithmRunner::setQueue(std::deque<IConfiguredAlgorithm_sptr> algorithms) {
112 g_log.debug() << "Set batch queue to algorithm list:\n";
113 for (auto &algorithm : algorithms)
114 g_log.debug() << algorithm->algorithm()->name() << "\n";
115
116 m_algorithms = std::move(algorithms);
117}
118
123
128
136 Poco::ActiveResult<bool> result = m_executeAsync(Poco::Void());
137 result.wait();
139 return result.data();
140}
141
147 m_executeAsync(Poco::Void());
148}
149
154 std::lock_guard<std::mutex> lock(m_mutex);
155 // If the queue is empty, notify straight away that the batch has been
156 // cancelled. Otherwise, set a flag so that it will be cancelled after the
157 // current algorithm finishes processing
158 if (queueLength() < 1) {
160 m_notificationCenter.postNotification(new BatchCancelledNotification());
162 } else {
163 m_cancelRequested = true;
164 }
165}
166
171 std::lock_guard<std::mutex> lock(m_mutex);
173 clearQueue();
174 m_cancelRequested = false;
175}
176
178 std::lock_guard<std::mutex> lock(m_mutex);
179 return m_cancelRequested;
180}
181
185bool BatchAlgorithmRunner::executeBatchAsyncImpl(const Poco::Void & /*unused*/) {
186 bool errorFlag = false;
187
188 for (auto &it : m_algorithms) {
189 if (cancelRequested()) {
190 g_log.information("Stopping batch algorithm execution: cancelled");
191 break;
192 }
193
194 // Try to execute the algorithm
195 if (!executeAlgo(it)) {
196 g_log.warning() << "Got error from algorithm \"" << m_currentAlgorithm->name() << "\"\n";
197
198 // Stop executing the entire batch if appropriate
199 if (m_stopOnFailure) {
200 g_log.warning("Stopping batch algorithm because of execution error");
201 errorFlag = true;
202 break;
203 }
204 } else {
205 g_log.information() << "Algorithm \"" << m_currentAlgorithm->name() << "\" finished\n";
206 }
207 }
208
209 // Notify observers
210 if (cancelRequested())
211 m_notificationCenter.postNotification(new BatchCancelledNotification());
212 else
213 m_notificationCenter.postNotification(new BatchCompleteNotification(false, errorFlag));
214
215 resetState();
216
217 return !errorFlag;
218}
219
227 try {
228 m_currentAlgorithm = algorithm->algorithm();
229 auto const &props = algorithm->getAlgorithmRuntimeProps();
230 throwIfAnyPropertiesInvalid(m_currentAlgorithm, props);
231
232 // Assign the properties to be set at runtime
233 m_currentAlgorithm->updatePropertyValues(props);
234
235 g_log.information() << "Starting next algorithm in queue: " << m_currentAlgorithm->name() << "\n";
236
237 // Start algorithm running
238 m_notificationCenter.postNotification(new AlgorithmStartedNotification(algorithm));
239 auto result = m_currentAlgorithm->execute();
240
241 if (!result) {
242 auto message = std::string("Algorithm") + algorithm->algorithm()->name() + std::string(" execution failed");
243 m_notificationCenter.postNotification(new AlgorithmErrorNotification(algorithm, message));
244 } else {
245 m_notificationCenter.postNotification(new AlgorithmCompleteNotification(algorithm));
246 }
247
248 return result;
249 }
250 // If a property name was given that does not match a property
251 catch (Mantid::Kernel::Exception::NotFoundError &notFoundEx) {
252 UNUSED_ARG(notFoundEx);
253 g_log.warning("Algorithm property does not exist.\nStopping queue execution.");
254 m_notificationCenter.postNotification(new AlgorithmErrorNotification(algorithm, notFoundEx.what()));
255 return false;
256 }
257 // If a property was assigned a value of the wrong type
258 catch (std::invalid_argument &invalidArgEx) {
259 UNUSED_ARG(invalidArgEx);
260 g_log.warning("Algorithm property given value of incorrect type.\nStopping "
261 "queue execution.");
262 m_notificationCenter.postNotification(new AlgorithmErrorNotification(algorithm, invalidArgEx.what()));
263 return false;
264 }
265 // For anything else that could go wrong
266 catch (std::exception &ex) {
267 g_log.warning("Error starting batch algorithm");
268 m_notificationCenter.postNotification(new AlgorithmErrorNotification(algorithm, ex.what()));
269 return false;
270 } catch (...) {
271 g_log.warning("Unknown error starting next batch algorithm");
272 m_notificationCenter.postNotification(
273 new AlgorithmErrorNotification(algorithm, "Unknown error starting algorithm"));
274 return false;
275 }
276}
277
283void BatchAlgorithmRunner::handleBatchComplete(const Poco::AutoPtr<BatchCompleteNotification> &pNf) {
284 bool inProgress = pNf->isInProgress();
285 if (!inProgress) {
286 // Notify UI elements
287 emit batchComplete(pNf->hasError());
288 }
289}
290
291void BatchAlgorithmRunner::handleBatchCancelled(const Poco::AutoPtr<BatchCancelledNotification> &pNf) {
292 UNUSED_ARG(pNf);
293 // Notify UI elements
294 emit batchCancelled();
295}
296
297void BatchAlgorithmRunner::handleAlgorithmStarted(const Poco::AutoPtr<AlgorithmStartedNotification> &pNf) {
298 // Notify UI elements
299 emit algorithmStarted(pNf->algorithm());
300}
301
302void BatchAlgorithmRunner::handleAlgorithmComplete(const Poco::AutoPtr<AlgorithmCompleteNotification> &pNf) {
303 // Notify UI elements
304 emit algorithmComplete(pNf->algorithm());
305}
306
307void BatchAlgorithmRunner::handleAlgorithmError(const Poco::AutoPtr<AlgorithmErrorNotification> &pNf) {
308 auto errorMessage = pNf->errorMessage();
309 // Notify UI elements
310 emit algorithmError(pNf->algorithm(), errorMessage);
311}
312} // namespace MantidQt::API
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition: System.h:64
Algorithm runner for execution of a queue of algorithms.
void handleAlgorithmComplete(const Poco::AutoPtr< AlgorithmCompleteNotification > &pNf)
bool m_cancelRequested
User has requested to cancel processing.
void handleBatchCancelled(const Poco::AutoPtr< BatchCancelledNotification > &pNf)
void algorithmStarted(MantidQt::API::IConfiguredAlgorithm_sptr algorithm)
Mantid::API::IAlgorithm_sptr m_currentAlgorithm
The current algorithm being executed.
void setQueue(std::deque< IConfiguredAlgorithm_sptr > algorithm)
Set the queue of algorithms.
void handleBatchComplete(const Poco::AutoPtr< BatchCompleteNotification > &pNf)
Handlers for notifications.
std::deque< IConfiguredAlgorithm_sptr > m_algorithms
The queue of algorithms to be executed.
BatchAlgorithmRunner(QObject *parent=nullptr)
bool m_stopOnFailure
If execution should be stopped on algorithm failure.
void resetState()
Reset state ready for executing a new batch.
void stopOnFailure(bool stopOnFailure)
Sets if the execuion should be stopped if an error is detected.
void handleAlgorithmError(const Poco::AutoPtr< AlgorithmErrorNotification > &pNf)
void cancelBatch()
Request to cancel processing the batch.
bool executeBatch()
Executes the batch, waits for the result and returns it.
Poco::NObserver< BatchAlgorithmRunner, AlgorithmStartedNotification > m_algorithmStartedObserver
Poco::NObserver< BatchAlgorithmRunner, BatchCompleteNotification > m_batchCompleteObserver
Observer for notifications.
void executeBatchAsync()
Starts the batch executing and returns immediately.
Poco::NotificationCenter m_notificationCenter
Notification center used to handle notifications from active method.
void handleAlgorithmStarted(const Poco::AutoPtr< AlgorithmStartedNotification > &pNf)
bool executeAlgo(const IConfiguredAlgorithm_sptr &algorithm)
Sets up and executes an algorithm.
Poco::ActiveMethod< bool, Poco::Void, BatchAlgorithmRunner, Poco::ActiveStarter< BatchAlgorithmRunner > > m_executeAsync
Active method to run batch runner on separate thread.
Poco::NObserver< BatchAlgorithmRunner, AlgorithmErrorNotification > m_algorithmErrorObserver
void algorithmError(MantidQt::API::IConfiguredAlgorithm_sptr algorithm, std::string errorMessage)
void algorithmComplete(MantidQt::API::IConfiguredAlgorithm_sptr algorithm)
Poco::NObserver< BatchAlgorithmRunner, AlgorithmCompleteNotification > m_algorithmCompleteObserver
void batchComplete(bool error)
Emitted when a batch has finished executing.
void clearQueue()
Clears all algorithms from queue.
size_t queueLength()
Gets size of queue.
bool executeBatchAsyncImpl(const Poco::Void &)
Implementation of algorithm runner.
Poco::NObserver< BatchAlgorithmRunner, BatchCancelledNotification > m_batchCancelledObserver
void addAlgorithm(const Mantid::API::IAlgorithm_sptr &algo)
Adds an algorithm to the execution queue.
Exception for when an item is not found in a collection.
Definition: Exception.h:145
const char * what() const noexcept override
Writes out the range and limits.
Definition: Exception.cpp:105
virtual std::vector< std::string > getDeclaredPropertyNames() const noexcept=0
Get the list of managed property names.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition: Logger.h:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
std::shared_ptr< IConfiguredAlgorithm > IConfiguredAlgorithm_sptr
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
Kernel::Logger g_log("ExperimentInfo")
static logger object