-
Wuttke, Joachim authoredWuttke, Joachim authored
JobsSet.cpp 6.61 KiB
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file GUI/Model/Job/JobsSet.cpp
//! @brief Implements class JobsSet.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "GUI/Model/Job/JobsSet.h"
#include "GUI/Model/Device/DatafileItem.h"
#include "GUI/Model/Job/BatchInfo.h"
#include "GUI/Model/Job/JobItem.h"
#include "GUI/Model/Par/ParameterTreeItems.h"
#include "GUI/Support/Data/JobStatus.h"
namespace {
namespace Tag {
const QString Job("Job");
const QString CurrentIndex("CurrentIndex");
} // namespace Tag
} // namespace
JobsSet::JobsSet(QObject* parent)
: QObject(parent)
{
setObjectName("JobsSet");
}
JobsSet::~JobsSet() = default;
void JobsSet::writeTo(QXmlStreamWriter* w) const
{
XML::writeAttribute(w, XML::Attrib::version, uint(1));
// jobs
for (const auto* job : *this) {
w->writeStartElement(Tag::Job);
XML::writeAttribute(w, XML::Attrib::name, job->batchInfo()->jobName());
job->writeTo(w);
w->writeEndElement();
}
// selected index
w->writeStartElement(Tag::CurrentIndex);
XML::writeAttribute(w, XML::Attrib::value, currentIndex());
w->writeEndElement();
}
void JobsSet::readFrom(QXmlStreamReader* r)
{
ASSERT(empty());
const uint version = XML::readUIntAttribute(r, XML::Attrib::version);
Q_UNUSED(version)
while (r->readNextStartElement()) {
QString tag = r->name().toString();
// job
if (tag == Tag::Job) {
auto* job_item = createJobItem();
job_item->readFrom(r);
XML::gotoEndElementOfTag(r, tag);
// selected index
} else if (tag == Tag::CurrentIndex) {
size_t i;
XML::readAttribute(r, XML::Attrib::value, &i);
setCurrentIndex(i);
XML::gotoEndElementOfTag(r, tag);
} else
r->skipCurrentElement();
}
if (r->hasError())
throw std::runtime_error(r->errorString().toLatin1());
}
void JobsSet::saveAllDatafields(const QString& projectDir) const
{
for (const JobItem* job : *this)
job->saveDatafields(projectDir);
dataFilesCleaner.cleanOldFiles(projectDir, dataItems());
}
void JobsSet::loadAllDatafields(const QString& projectDir, MessageService* messageService)
{
for (JobItem* job : *this)
job->loadDatafields(projectDir, messageService);
dataFilesCleaner.recollectDataNames(dataItems());
}
JobItem* JobsSet::createJobItem()
{
auto* job_item = new JobItem;
push_back(job_item);
return job_item;
}
//! Main method to add a job
void JobsSet::addJobItem(JobItem* job_item)
{
job_item->batchInfo()->setJobName(generateJobName());
push_back(job_item);
emit jobAdded();
}
//! restore instrument and sample model from backup for given JobItem
void JobsSet::restoreBackupPars(JobItem* job_item, int index)
{
job_item->parameterContainerItem()->restoreBackupValues(index);
}
QVector<DataItem*> JobsSet::dataItems() const
{
QVector<DataItem*> result;
for (auto* job_item : *this) {
if (auto* dataItem = job_item->simulatedDataItem())
result.push_back(dataItem);
if (const auto* real_data = dynamic_cast<const DatafileItem*>(job_item->dfileItem()))
if (auto* data_item = real_data->dataItem())
result.push_back(data_item);
}
return result;
}
void JobsSet::cancelJob(JobItem* job_item)
{
job_item->haltWorker();
}
void JobsSet::removeJob(JobItem* job_item)
{
ASSERT(job_item);
job_item->haltWorker();
delete_element(job_item);
}
bool JobsSet::hasUnfinishedJobs() const
{
for (const JobItem* job_item : *this)
if (isFitting(job_item->batchInfo()->status()))
return true;
return false;
}
//! Submits job and run it in a thread.
void JobsSet::runJob(JobItem* job_item)
{
if (job_item->thread())
return;
connect(job_item, &JobItem::progressIncremented, this, &JobsSet::onProgressUpdate);
connect(job_item, &JobItem::jobFinished, this, &JobsSet::onFinishedJob);
try {
job_item->initWorker();
} catch (const std::exception& ex) {
QString message("JobsSet::runJob -> Error. "
"Attempt to create sample/instrument object from user description "
"has failed with following error message.\n\n");
message += QString::fromStdString(std::string(ex.what()));
job_item->batchInfo()->setComments(message);
job_item->batchInfo()->setProgress(100);
job_item->setFailed();
emit jobMeritsAttention(job_item);
return;
}
auto* thread = job_item->thread();
thread->start();
}
// ------------------------------------------------------------------------------------------------
// private slots
// ------------------------------------------------------------------------------------------------
//! Performs necessary actions when job is finished.
void JobsSet::onFinishedJob(JobItem* job_item)
{
onProgressUpdate();
emit jobMeritsAttention(job_item);
}
//! Estimates global progress from the progress of multiple running jobs and emits signal.
void JobsSet::onProgressUpdate()
{
int global_progress = 0;
int nRunningJobs = 0;
for (const JobItem* job_item : *this)
if (isRunning(job_item->batchInfo()->status())) {
global_progress += job_item->batchInfo()->progress();
nRunningJobs++;
}
if (nRunningJobs)
global_progress /= nRunningJobs;
else
global_progress = -1;
emit globalProgress(global_progress);
}
//! Cancels all running jobs.
void JobsSet::onCancelAllJobs()
{
for (auto* job_item : *this)
job_item->haltWorker();
}
// ------------------------------------------------------------------------------------------------
// private fcts
// ------------------------------------------------------------------------------------------------
//! generates numbered job name with new/unused number
QString JobsSet::generateJobName() const
{
int maxJobIndex = 0;
for (const JobItem* job_item : *this)
if (job_item->batchInfo()->jobName().startsWith("job"))
maxJobIndex = std::max(maxJobIndex, job_item->batchInfo()->jobName().mid(3).toInt());
return QString("job%1").arg(maxJobIndex + 1);
}