1 // Copyright (C) 2009-2022 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "BL_JobsManager_QT.hxx"
21 #include "BL_GenericGui.hxx"
23 #include <QApplication>
24 #include <QFileDialog>
26 #include <QHBoxLayout>
28 #include <QPushButton>
29 #include <QScrollArea>
30 #include <QStandardItemModel>
33 #include <QVBoxLayout>
38 // To tokenize a string
39 static void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ");
41 BL::JobManagerEvent::JobManagerEvent(const std::string & action_i,
42 const std::string & event_name_i,
43 const std::string & job_name_i,
44 const std::string & data_i) : QEvent(QEvent::User)
47 event_name = event_name_i;
48 job_name = job_name_i;
52 BL::JobManagerEvent::~JobManagerEvent() {}
54 BL::JobsManager_QT::JobsManager_QT(QWidget * parent, BL::GenericGui * main_gui, BL::SALOMEServices * salome_services) :
55 QDockWidget(parent), BL::JobsManager(salome_services)
57 DEBTRACE("Creating BL::JobsManager_QT");
60 _model_manager = NULL;
64 QWidget * main_widget = new QWidget(this);
66 _load_jobs = new QPushButton("Load Jobs");
67 _save_jobs = new QPushButton("Save Jobs");
68 connect(_load_jobs, SIGNAL(clicked()), this, SLOT(load_jobs_button()));
69 connect(_save_jobs, SIGNAL(clicked()), this, SLOT(save_jobs_button()));
71 _auto_refresh_jobs = new QPushButton("Auto Refresh: 30s");
72 _timer = new QTimer(this);
74 _timer->start(30 * 1000);
75 connect(_timer, SIGNAL(timeout()), this, SLOT(RefreshJobs()));
77 // Menu for auto refresh
78 QMenu * refresh_menu = new QMenu(this);
79 refresh_menu->addAction("No", this, SLOT(no_auto_refresh()));
80 refresh_menu->addAction("10 seconds", this, SLOT(ten_seconds_refresh()));
81 refresh_menu->addAction("30 seconds", this, SLOT(thirty_seconds_refresh()));
82 refresh_menu->addAction("1 minute", this, SLOT(one_minute_refresh()));
83 refresh_menu->addAction("5 minutes", this, SLOT(five_minutes_refresh()));
84 refresh_menu->addAction("30 minutes", this, SLOT(thirty_minutes_refresh()));
85 refresh_menu->addAction("1 hour", this, SLOT(one_hour_refresh()));
86 _auto_refresh_jobs->setMenu(refresh_menu);
88 QHBoxLayout * button_layout = new QHBoxLayout();
89 button_layout->addWidget(_load_jobs);
90 button_layout->addWidget(_save_jobs);
91 button_layout->addWidget(_auto_refresh_jobs);
93 QGroupBox * message_box = new QGroupBox("Messages");
94 _log = new QTextEdit(this);
95 _log->setReadOnly(true);
96 QVBoxLayout * message_box_layout = new QVBoxLayout(message_box);
97 message_box_layout->addWidget(_log);
98 message_box->setLayout(message_box_layout);
100 QVBoxLayout * mainLayout = new QVBoxLayout();
101 mainLayout->addLayout(button_layout);
102 mainLayout->addWidget(message_box);
103 main_widget->setLayout(mainLayout);
105 QScrollArea * scroll_widget = new QScrollArea(this);
106 scroll_widget->setWidget(main_widget);
107 scroll_widget->setWidgetResizable(true);
108 setWidget(scroll_widget);
109 setWindowTitle("Job Manager");
110 setObjectName("jmJobManagerDock");
113 BL::JobsManager_QT::~JobsManager_QT()
115 DEBTRACE("Destroying BL::JobsManager_QT");
119 BL::JobsManager_QT::set_model_manager(BL::QModelManager * model_manager)
121 _model_manager = model_manager;
125 BL::JobsManager_QT::load_jobs_button()
127 DEBTRACE("load_jobs");
128 QString jobs_file = QFileDialog::getOpenFileName(this,
129 tr("Choose an xml jobs file"), "",
130 tr("xml (*.xml);;All Files (*)"));
133 write_normal_text("Load jobs action cancelled\n");
136 load_jobs(jobs_file.toUtf8().constData());
140 BL::JobsManager_QT::save_jobs_button()
142 DEBTRACE("save_jobs");
143 QFileDialog dialog(this, "Save jobs file");
145 filters << "XML files (*.xml)"
147 dialog.setFileMode(QFileDialog::AnyFile);
148 dialog.setNameFilters(filters);
149 dialog.selectNameFilter("(*.xml)");
150 dialog.setDefaultSuffix("xml");
151 dialog.setConfirmOverwrite(true);
152 dialog.setAcceptMode(QFileDialog::AcceptSave);
153 QString jobs_file("");
154 QStringList fileNames;
156 if (bool ret = dialog.exec())
158 DEBTRACE(ret << " " << dialog.confirmOverwrite());
159 fileNames = dialog.selectedFiles();
160 if (!fileNames.isEmpty())
161 jobs_file= fileNames.first();
165 write_normal_text("Save jobs action cancelled\n");
168 save_jobs(jobs_file.toUtf8().constData());
172 BL::JobsManager_QT::RefreshJobs()
178 BL::JobsManager_QT::no_auto_refresh()
180 _auto_refresh_jobs->setText("Auto Refresh: no");
185 BL::JobsManager_QT::ten_seconds_refresh()
187 _auto_refresh_jobs->setText("Auto Refresh: 10s");
189 _timer->start(10 * 1000);
193 BL::JobsManager_QT::thirty_seconds_refresh()
195 _auto_refresh_jobs->setText("Auto Refresh: 30s");
197 _timer->start(30 * 1000);
201 BL::JobsManager_QT::one_minute_refresh()
203 _auto_refresh_jobs->setText("Auto Refresh: 1min");
205 _timer->start(1 * 60 * 1000);
209 BL::JobsManager_QT::five_minutes_refresh()
211 _auto_refresh_jobs->setText("Auto Refresh: 5min");
213 _timer->start(5 * 60 * 1000);
217 BL::JobsManager_QT::thirty_minutes_refresh()
219 _auto_refresh_jobs->setText("Auto Refresh: 30min");
221 _timer->start(30 * 60 * 1000);
225 BL::JobsManager_QT::one_hour_refresh()
227 _auto_refresh_jobs->setText("Auto Refresh: 1hour");
229 _timer->start(1 * 60 * 60 * 1000);
233 BL::JobsManager_QT::restart_job(const std::string & name)
235 DEBTRACE("Restart job with name: " << name);
236 BL::CreateJobWizard wizard(this, _salome_services);
239 wizard.job_name = name;
240 wizard.start_job = true;
241 _main_gui->delete_job_internal();
242 create_job_with_wizard(wizard);
246 BL::JobsManager_QT::edit_clone_job(const std::string & name)
248 BL::CreateJobWizard wizard(this, _salome_services);
252 // Check if the job has the same name
253 if (name == wizard.job_name)
255 DEBTRACE("Job " << name << " has been edited");
256 _main_gui->delete_job_internal();
259 if (wizard.job_name != "")
261 create_job_with_wizard(wizard);
265 DEBTRACE("User cancel Create Job Wizard");
270 BL::JobsManager_QT::create_job()
272 BL::CreateJobWizard wizard(this, _salome_services);
274 if (wizard.job_name != "")
276 create_job_with_wizard(wizard);
280 DEBTRACE("User cancel Create Job Wizard");
285 BL::JobsManager_QT::create_job_with_wizard(BL::CreateJobWizard & wizard)
287 BL::Job * new_job = createJob(wizard.job_name);
288 switch (wizard.job_type)
290 case BL::CreateJobWizard::YACS:
292 new_job->setType(BL::Job::YACS_SCHEMA);
293 new_job->setJobFile(wizard.yacs_file);
294 new_job->setDumpYACSState(wizard.dump_yacs_state);
295 new_job->setYacsDriverOptions(wizard.yacs_driver_options);
297 case BL::CreateJobWizard::COMMAND:
299 new_job->setType(BL::Job::COMMAND);
300 new_job->setJobFile(wizard.command);
302 case BL::CreateJobWizard::COMMAND_SALOME:
304 new_job->setType(BL::Job::COMMAND_SALOME);
305 new_job->setJobFile(wizard.command);
307 case BL::CreateJobWizard::PYTHON_SALOME:
309 new_job->setType(BL::Job::PYTHON_SALOME);
310 new_job->setJobFile(wizard.python_salome_file);
313 throw BL::Exception("Unknown job type");
317 new_job->setEnvFile(wizard.env_file);
318 new_job->setPreCommand(wizard.pre_command);
319 BL::Job::BatchParam param;
322 if (wizard.coorm_batch_directory != "")
324 param.batch_directory = wizard.coorm_batch_directory;
326 else if (wizard.batch_directory != "")
328 param.batch_directory = wizard.batch_directory;
331 param.maximum_duration = wizard.maximum_duration;
332 param.mem_limit = wizard.mem_limit;
333 param.mem_req_type = wizard.mem_req_type;
334 param.nb_proc = wizard.nb_proc;
335 param.nb_node = wizard.nb_node;
336 param.exclusive = wizard.exclusive;
338 // Parameters for COORM
339 param.launcher_file = wizard.launcher_file;
340 param.launcher_args = wizard.launcher_args;
342 new_job->setBatchParameters(param);
343 BL::Job::FilesParam files_params;
344 files_params.result_directory = wizard.result_directory;
345 files_params.input_files_list = wizard.input_files_list;
346 files_params.output_files_list = wizard.output_files_list;
347 new_job->setFilesParameters(files_params);
348 new_job->setResource(wizard.resource_choosed);
349 new_job->setBatchQueue(wizard.batch_queue);
350 new_job->setBatchPartition(wizard.batch_partition);
351 new_job->setLoadLevelerJobType(wizard.ll_jobtype);
352 new_job->setWCKey(wizard.wckey);
353 new_job->setExtraParams(wizard.extra_params);
356 addJobToLauncher(wizard.job_name);
357 emit new_job_added(QString::fromUtf8(wizard.job_name.c_str()));
358 QStandardItemModel * model = _model_manager->getModel();
359 QList<QStandardItem *> item_list = model->findItems(QString::fromUtf8(wizard.job_name.c_str()));
360 QStandardItem * job_state_item = model->item(item_list.at(0)->row(), 2);
361 _main_gui->_jobs_table->selectRow(item_list.at(0)->row());
362 if (wizard.start_job)
363 start_job(wizard.job_name);
367 BL::JobsManager_QT::delete_job(QString job_name)
369 BL::JobsManager::removeJob(job_name.toUtf8().constData());
370 _model_manager->delete_job(job_name);
371 _main_gui->_job_tab->reset(job_name);
375 BL::JobsManager_QT::sendEvent(const std::string & action,
376 const std::string & event_name,
377 const std::string & job_name,
378 const std::string & data)
380 DEBTRACE("sendEvent BL::JobsManager_QT");
382 // Sending a QEvent to go back to main thread
383 BL::JobManagerEvent * event = new JobManagerEvent(action, event_name, job_name, data);
384 QApplication::postEvent(this, event);
388 BL::JobsManager_QT::event(QEvent * e)
390 QDockWidget::event(e);
391 JobManagerEvent * event = dynamic_cast<JobManagerEvent*>(e);
392 if (!event) return false;
394 DEBTRACE("BL::JobsManager_QT Receiving event : "
395 << event->action << " "
396 << event->event_name << " "
397 << event->job_name << " "
400 QString job_name = QString::fromUtf8(event->job_name.c_str());
401 if (event->action == "create_job")
403 if (event->event_name == "Ok")
405 write_normal_text("Job " + job_name + " created\n");
409 write_error_text("Error in creating job: " + job_name + "\n");
410 write_error_text("*** ");
411 write_error_text((event->data).c_str());
412 write_error_text(" ***\n");
415 else if (event->action == "start_job")
417 if (event->event_name == "Ok")
419 write_normal_text("Job " + job_name + " queued\n");
423 write_error_text("Error in starting job: " + job_name + "\n");
424 write_error_text("*** ");
425 write_error_text((event->data).c_str());
426 write_error_text(" ***\n");
428 emit job_state_changed(job_name);
430 else if (event->action == "refresh_job")
432 if (event->event_name == "Ok")
434 QString state((event->data).c_str());
435 state = state.toLower();
436 write_normal_text("Job " + job_name + " new state is " + state + "\n");
437 emit job_state_changed(job_name);
441 write_error_text("Error in refreshing job: " + job_name + "\n");
442 write_error_text("*** ");
443 write_error_text((event->data).c_str());
444 write_error_text(" ***\n");
447 else if (event->action == "delete_job")
449 if (event->event_name == "Ok")
451 write_normal_text("Job " + job_name + " deleted\n");
455 write_error_text("Warning delete job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
456 write_error_text("*** ");
457 write_error_text((event->data).c_str());
458 write_error_text(" ***\n");
461 else if (event->action == "get_results_job")
463 if (event->event_name == "Ok")
465 write_normal_text("Results of Job " + job_name + " have been got.\n");
469 write_error_text("Warning for results of job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
470 write_error_text("*** ");
471 write_error_text((event->data).c_str());
472 write_error_text(" ***\n");
475 else if (event->action == "stop_job")
477 if (event->event_name == "Ok")
479 write_normal_text("Job " + job_name + " is stopped\n");
483 write_error_text("Error when trying to stop job: " + job_name + "\n");
484 write_error_text("*** ");
485 write_error_text((event->data).c_str());
486 write_error_text(" ***\n");
489 else if (event->action == "get_assigned_hostnames")
491 if (event->event_name == "Ok")
493 vector<string> hostnames;
495 Tokenize(event->data, hostnames, "+");
497 vector<string>::iterator it;
499 write_normal_text("Job " + job_name + " assigned hostnames are :\n");
501 for (it = hostnames.begin(); it < hostnames.end(); it++)
503 vector<string> hostname;
504 Tokenize(*it, hostname, ".");
505 QString assigned_hostname(hostname[0].c_str());
506 write_normal_text("+ " + assigned_hostname + "\n");
511 // Do nothing in case the batch manager does not support this
514 else if (event->action == "save_jobs")
516 if (event->event_name == "Error")
518 write_error_text("Error in saving jobs: \n");
519 write_error_text("*** ");
520 write_error_text((event->data).c_str());
521 write_error_text(" ***\n");
525 QString str((event->data).c_str());
526 write_normal_text("Jobs saved in file " + str + "\n");
529 else if (event->action == "load_jobs")
531 if (event->event_name == "Error")
533 write_error_text("Error in loading jobs: \n");
534 write_error_text("*** ");
535 write_error_text((event->data).c_str());
536 write_error_text(" ***\n");
540 QString str((event->data).c_str());
541 write_normal_text("Jobs loaded from file " + str + "\n");
544 else if (event->action == "add_job")
546 if (event->event_name == "Ok")
548 write_normal_text("New job added " + job_name + "\n");
549 emit new_job_added(job_name);
552 else if (event->action == "to_remove_job")
554 if (event->event_name == "Ok")
555 _main_gui->delete_job_external(job_name);
559 QString str((event->action).c_str());
560 write_error_text("Unknown type of event received:" + str + "\n");
566 BL::JobsManager_QT::write_normal_text(const QString & text)
568 _log->setReadOnly(false);
569 QTextCursor cursor = _log->textCursor();
570 QTextCharFormat text_format;
571 text_format.setForeground(Qt::darkBlue);
572 cursor.insertText(text, text_format);
573 _log->setTextCursor(cursor);
574 _log->setReadOnly(true);
578 BL::JobsManager_QT::write_error_text(const QString & text)
580 _log->setReadOnly(false);
581 QTextCursor cursor = _log->textCursor();
582 QTextCharFormat text_format;
583 text_format.setForeground(Qt::red);
584 cursor.insertText(text, text_format);
585 _log->setTextCursor(cursor);
586 _log->setReadOnly(true);
589 // To tokenize a string
590 void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters)
592 // Skip delimiters at beginning.
593 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
594 // Find first "non-delimiter".
595 string::size_type pos = str.find_first_of(delimiters, lastPos);
597 while (string::npos != pos || string::npos != lastPos)
599 // Found a token, add it to the vector.
600 tokens.push_back(str.substr(lastPos, pos - lastPos));
601 // Skip delimiters. Note the "not_of"
602 lastPos = str.find_first_not_of(delimiters, pos);
603 // Find next "non-delimiter"
604 pos = str.find_first_of(delimiters, lastPos);