]> SALOME platform Git repositories - modules/jobmanager.git/blob - src/genericgui/BL_JobsManager_QT.cxx
Salome HOME
Add possibility to specify extra parameters in the job submission file
[modules/jobmanager.git] / src / genericgui / BL_JobsManager_QT.cxx
1 // Copyright (C) 2009-2014  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "BL_JobsManager_QT.hxx"
21 #include "BL_GenericGui.hxx"
22 #include <vector>
23
24 using namespace std;
25
26 // To tokenize a string
27 static void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ");
28
29 BL::JobManagerEvent::JobManagerEvent(const std::string & action_i, 
30                                      const std::string & event_name_i, 
31                                      const std::string & job_name_i, 
32                                      const std::string & data_i) : QEvent(QEvent::User)
33 {
34   action = action_i;
35   event_name = event_name_i;
36   job_name = job_name_i;
37   data = data_i;
38 }
39
40 BL::JobManagerEvent::~JobManagerEvent() {}  
41
42 BL::JobsManager_QT::JobsManager_QT(QWidget * parent, BL::GenericGui * main_gui, BL::SALOMEServices * salome_services) : 
43   QDockWidget(parent), BL::JobsManager(salome_services)
44 {
45   DEBTRACE("Creating BL::JobsManager_QT");
46   _main_gui = main_gui;
47   setObserver(this);
48   _model_manager = NULL;
49
50   // Widget Part
51
52   QWidget * main_widget = new QWidget(this);
53
54   _load_jobs = new QPushButton("Load Jobs");
55   _save_jobs = new QPushButton("Save Jobs");
56   connect(_load_jobs, SIGNAL(clicked()), this, SLOT(load_jobs_button()));
57   connect(_save_jobs, SIGNAL(clicked()), this, SLOT(save_jobs_button()));
58
59   _auto_refresh_jobs = new QPushButton("Auto Refresh: no");
60   _timer = new QTimer(this);
61   _timer->stop();
62   connect(_timer, SIGNAL(timeout()), this, SLOT(RefreshJobs()));
63
64   // Menu for auto refresh
65   QMenu * refresh_menu = new QMenu(this);
66   refresh_menu->addAction("No", this, SLOT(no_auto_refresh()));
67   refresh_menu->addAction("10 seconds", this, SLOT(ten_seconds_refresh()));
68   refresh_menu->addAction("30 seconds", this, SLOT(thirty_seconds_refresh()));
69   refresh_menu->addAction("1 minute", this, SLOT(one_minute_refresh()));
70   refresh_menu->addAction("5 minutes", this, SLOT(five_minutes_refresh()));
71   refresh_menu->addAction("30 minutes", this, SLOT(thirty_minutes_refresh()));
72   refresh_menu->addAction("1 hour", this, SLOT(one_hour_refresh()));
73   _auto_refresh_jobs->setMenu(refresh_menu);
74
75   QHBoxLayout * button_layout = new QHBoxLayout();
76   button_layout->addWidget(_load_jobs);
77   button_layout->addWidget(_save_jobs);
78   button_layout->addWidget(_auto_refresh_jobs);
79
80   QGroupBox * message_box = new QGroupBox("Messages");
81   _log = new QTextEdit(this);
82   _log->setReadOnly(true);
83   QVBoxLayout * message_box_layout = new QVBoxLayout(message_box);
84   message_box_layout->addWidget(_log);
85   message_box->setLayout(message_box_layout);
86
87   QVBoxLayout * mainLayout = new QVBoxLayout();
88   mainLayout->addLayout(button_layout);
89   mainLayout->addWidget(message_box);
90   main_widget->setLayout(mainLayout);
91
92   QScrollArea * scroll_widget = new QScrollArea(this);
93   scroll_widget->setWidget(main_widget);
94   scroll_widget->setWidgetResizable(true);
95   setWidget(scroll_widget);
96   setWindowTitle("Job Manager");
97   setObjectName("jmJobManagerDock");
98 }
99
100 BL::JobsManager_QT::~JobsManager_QT()
101 {
102   DEBTRACE("Destroying BL::JobsManager_QT");
103 }
104
105 void
106 BL::JobsManager_QT::set_model_manager(BL::QModelManager * model_manager)
107 {
108   _model_manager = model_manager;
109 }
110
111 void
112 BL::JobsManager_QT::load_jobs_button()
113 {
114   DEBTRACE("load_jobs");
115   QString jobs_file = QFileDialog::getOpenFileName(this,
116                                                    tr("Choose an xml jobs file"), "",
117                                                    tr("xml (*.xml);;All Files (*)"));
118   if (jobs_file == "")
119   {
120     write_normal_text("Load jobs action cancelled\n");
121   }
122   else
123     load_jobs(jobs_file.toUtf8().constData());
124 }
125
126 void
127 BL::JobsManager_QT::save_jobs_button()
128 {
129   DEBTRACE("save_jobs");
130   QFileDialog dialog(this, "Save jobs file");
131   QStringList filters;
132   filters << "XML files (*.xml)"
133           << "Any files (*)";
134   dialog.setFileMode(QFileDialog::AnyFile);
135   dialog.setFilters(filters);
136   dialog.selectFilter("(*.xml)");
137   dialog.setDefaultSuffix("xml");
138   dialog.setConfirmOverwrite(true);
139   dialog.setAcceptMode(QFileDialog::AcceptSave);
140   QString jobs_file("");
141   QStringList fileNames;
142   fileNames.clear();
143   if (bool ret = dialog.exec())
144   {
145     DEBTRACE(ret << " " << dialog.confirmOverwrite());
146     fileNames = dialog.selectedFiles();
147     if (!fileNames.isEmpty())
148       jobs_file= fileNames.first();
149   }
150   if (jobs_file == "")
151   {
152     write_normal_text("Save jobs action cancelled\n");
153   }
154   else
155     save_jobs(jobs_file.toUtf8().constData());
156 }
157
158 void
159 BL::JobsManager_QT::RefreshJobs()
160 {
161   refresh_jobs(); 
162 }
163
164 void
165 BL::JobsManager_QT::no_auto_refresh()
166 {
167   _auto_refresh_jobs->setText("Auto Refresh: no");
168   _timer->stop();
169 }
170
171 void
172 BL::JobsManager_QT::ten_seconds_refresh()
173 {
174   _auto_refresh_jobs->setText("Auto Refresh: 10s");
175   _timer->stop();
176   _timer->start(10 * 1000);
177 }
178
179 void 
180 BL::JobsManager_QT::thirty_seconds_refresh()
181 {
182   _auto_refresh_jobs->setText("Auto Refresh: 30s");
183   _timer->stop();
184   _timer->start(30 * 1000);
185 }
186
187 void 
188 BL::JobsManager_QT::one_minute_refresh()
189 {
190   _auto_refresh_jobs->setText("Auto Refresh: 1min");
191   _timer->stop();
192   _timer->start(1 * 60 * 1000);
193 }
194
195 void 
196 BL::JobsManager_QT::five_minutes_refresh()
197 {
198   _auto_refresh_jobs->setText("Auto Refresh: 5min");
199   _timer->stop();
200   _timer->start(5 * 60 * 1000);
201 }
202
203 void 
204 BL::JobsManager_QT::thirty_minutes_refresh()
205 {
206   _auto_refresh_jobs->setText("Auto Refresh: 30min");
207   _timer->stop();
208   _timer->start(30 * 60 * 1000);
209 }
210
211 void 
212 BL::JobsManager_QT::one_hour_refresh()
213 {
214   _auto_refresh_jobs->setText("Auto Refresh: 1hour");
215   _timer->stop();
216   _timer->start(1 * 60 * 60 * 1000);
217 }
218
219 void 
220 BL::JobsManager_QT::restart_job(const std::string & name)
221 {
222   DEBTRACE("Restart job with name: " << name);
223   BL::CreateJobWizard wizard(this, _salome_services);
224   wizard.clone(name);
225   wizard.end(1);
226   wizard.job_name = name;
227   wizard.start_job = true;
228   _main_gui->delete_job_internal();
229   create_job_with_wizard(wizard);
230 }
231
232 void 
233 BL::JobsManager_QT::edit_clone_job(const std::string & name)
234 {
235   BL::CreateJobWizard wizard(this, _salome_services);
236   wizard.clone(name);
237   wizard.exec();
238
239   // Check if the job has the same name
240   if (name == wizard.job_name)
241   {
242     DEBTRACE("Job " << name << " has been edited");
243     _main_gui->delete_job_internal();
244   }
245
246   if (wizard.job_name != "")
247   {
248     create_job_with_wizard(wizard);
249   }
250   else
251   {
252     DEBTRACE("User cancel Create Job Wizard");
253   }
254 }
255
256 void
257 BL::JobsManager_QT::create_job()
258 {
259     BL::CreateJobWizard wizard(this, _salome_services);
260     wizard.exec();
261     if (wizard.job_name != "")
262     {
263       create_job_with_wizard(wizard);
264     }
265     else
266     {
267        DEBTRACE("User cancel Create Job Wizard");
268     }
269 }
270
271 void 
272 BL::JobsManager_QT::create_job_with_wizard(BL::CreateJobWizard & wizard)
273 {
274   BL::Job * new_job = createJob(wizard.job_name);
275   if (wizard.yacs_file != "")
276   {
277     // YACS schema job
278     new_job->setType(BL::Job::YACS_SCHEMA);
279     new_job->setJobFile(wizard.yacs_file);
280     new_job->setDumpYACSState(wizard.dump_yacs_state);
281   }
282   else if (wizard.command != "")
283   {
284     // Command Job
285     new_job->setType(BL::Job::COMMAND);
286     new_job->setJobFile(wizard.command);
287   }
288   else if (wizard.python_salome_file != "")
289   {
290     // Command Job
291     new_job->setType(BL::Job::PYTHON_SALOME);
292     new_job->setJobFile(wizard.python_salome_file);
293   }
294
295   // For all jobs
296   new_job->setEnvFile(wizard.env_file);
297   BL::Job::BatchParam param;
298
299   // For COORM
300   if (wizard.coorm_batch_directory != "")
301   {
302         param.batch_directory = wizard.coorm_batch_directory;
303   }
304   else if (wizard.batch_directory != "")
305   {
306         param.batch_directory = wizard.batch_directory;
307   }
308
309   param.maximum_duration = wizard.maximum_duration;
310   param.mem_limit = wizard.mem_limit;
311   param.mem_req_type = wizard.mem_req_type;
312   param.nb_proc = wizard.nb_proc;
313   param.exclusive = wizard.exclusive;
314
315   // Parameters for COORM
316   param.launcher_file = wizard.launcher_file;
317   param.launcher_args = wizard.launcher_args;
318
319   new_job->setBatchParameters(param);
320   BL::Job::FilesParam files_params;
321   files_params.result_directory = wizard.result_directory;
322   files_params.input_files_list = wizard.input_files_list;
323   files_params.output_files_list = wizard.output_files_list;
324   new_job->setFilesParameters(files_params);
325   new_job->setResource(wizard.resource_choosed);
326   new_job->setBatchQueue(wizard.batch_queue);
327   new_job->setLoadLevelerJobType(wizard.ll_jobtype);
328   new_job->setWCKey(wizard.wckey);
329   new_job->setExtraParams(wizard.extra_params);
330
331   // End
332   addJobToLauncher(wizard.job_name);
333   emit new_job_added(QString::fromUtf8(wizard.job_name.c_str()));
334   QStandardItemModel * model = _model_manager->getModel();
335   QList<QStandardItem *> item_list = model->findItems(QString::fromUtf8(wizard.job_name.c_str()));
336   QStandardItem * job_state_item = model->item(item_list.at(0)->row(), 2);
337   _main_gui->_jobs_table->selectRow(item_list.at(0)->row());
338   if (wizard.start_job)
339     start_job(wizard.job_name);
340 }
341
342 void
343 BL::JobsManager_QT::delete_job(QString job_name)
344 {
345   BL::JobsManager::removeJob(job_name.toUtf8().constData());
346   _model_manager->delete_job(job_name);
347   _main_gui->_job_tab->reset(job_name);
348 }
349
350 void 
351 BL::JobsManager_QT::sendEvent(const std::string & action, 
352                               const std::string & event_name, 
353                               const std::string & job_name, 
354                               const std::string & data)
355 {
356   DEBTRACE("sendEvent BL::JobsManager_QT");
357
358   // Sending a QEvent to go back to main thread
359   BL::JobManagerEvent * event = new JobManagerEvent(action, event_name, job_name, data);
360   QApplication::postEvent(this, event);
361 }
362
363 bool 
364 BL::JobsManager_QT::event(QEvent * e)
365 {
366   QDockWidget::event(e);
367   JobManagerEvent * event = dynamic_cast<JobManagerEvent*>(e);
368   if (!event) return false;
369
370   DEBTRACE("BL::JobsManager_QT Receiving event : " 
371            << event->action << " " 
372            << event->event_name << " "
373            << event->job_name << " "
374            << event->data);
375
376   QString job_name = QString::fromUtf8(event->job_name.c_str());
377   if (event->action == "create_job")
378   {
379     if (event->event_name == "Ok")
380     {
381       write_normal_text("Job " + job_name + " created\n");
382     }
383     else
384     {
385       write_error_text("Error in creating job: " + job_name + "\n");
386       write_error_text("*** ");
387       write_error_text((event->data).c_str());
388       write_error_text(" ***\n");
389     }
390   }
391   else if (event->action == "start_job")
392   {
393     if (event->event_name == "Ok")
394     {
395       write_normal_text("Job " + job_name + " queued\n");
396     }
397     else
398     {
399       write_error_text("Error in starting job: " + job_name + "\n");
400       write_error_text("*** ");
401       write_error_text((event->data).c_str());
402       write_error_text(" ***\n");
403     }
404     emit job_state_changed(job_name);
405   }
406   else if (event->action == "refresh_job")
407   {
408     if (event->event_name == "Ok")
409     {
410       QString state((event->data).c_str());
411       state = state.toLower();
412       write_normal_text("Job " + job_name + " new state is " + state + "\n");
413       emit job_state_changed(job_name);
414     }
415     else
416     {
417       write_error_text("Error in refreshing job: " + job_name + "\n");
418       write_error_text("*** ");
419       write_error_text((event->data).c_str());
420       write_error_text(" ***\n");
421     }
422   }
423   else if (event->action == "delete_job")
424   {
425     if (event->event_name == "Ok")
426     {
427       write_normal_text("Job " + job_name + " deleted\n");
428     }
429     else
430     {
431       write_error_text("Warning delete job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
432       write_error_text("*** ");
433       write_error_text((event->data).c_str());
434       write_error_text(" ***\n");
435     }
436   }
437   else if (event->action == "get_results_job")
438   {
439     if (event->event_name == "Ok")
440     {
441       write_normal_text("Results of Job " + job_name + " are get\n");
442     }
443     else
444     {
445       write_error_text("Warning for results of job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
446       write_error_text("*** ");
447       write_error_text((event->data).c_str());
448       write_error_text(" ***\n");
449     }
450   }
451   else if (event->action == "stop_job")
452   {
453     if (event->event_name == "Ok")
454     {
455       write_normal_text("Job " + job_name + " is stopped\n");
456     }
457     else
458     {
459       write_error_text("Error when trying to stop job: " + job_name + "\n");
460       write_error_text("*** ");
461       write_error_text((event->data).c_str());
462       write_error_text(" ***\n");
463     }
464   }
465   else if (event->action == "get_assigned_hostnames")
466   {
467     if (event->event_name == "Ok")
468     {
469           vector<string> hostnames;
470
471           Tokenize(event->data, hostnames, "+");
472
473           vector<string>::iterator it;
474
475       write_normal_text("Job " + job_name + " assigned hostnames are :\n");
476
477           for (it = hostnames.begin(); it < hostnames.end(); it++)
478           {
479                   vector<string> hostname;
480                   Tokenize(*it, hostname, ".");
481                   QString assigned_hostname(hostname[0].c_str());
482                   write_normal_text("+ " + assigned_hostname + "\n");
483           }
484     }
485     else
486     {
487                 // Do nothing in case the batch manager does not support this
488     }
489   }
490   else if (event->action == "save_jobs")
491   {
492     if (event->event_name == "Error")
493     {
494       write_error_text("Error in saving jobs: \n");
495       write_error_text("*** ");
496       write_error_text((event->data).c_str());
497       write_error_text(" ***\n");
498     }
499     else
500     {
501       QString str((event->data).c_str());
502       write_normal_text("Jobs saved in file " + str + "\n");
503     }
504   }
505   else if (event->action == "load_jobs")
506   {
507     if (event->event_name == "Error")
508     {
509       write_error_text("Error in loading jobs: \n");
510       write_error_text("*** ");
511       write_error_text((event->data).c_str());
512       write_error_text(" ***\n");
513     }
514     else
515     {
516       QString str((event->data).c_str());
517       write_normal_text("Jobs loaded from file " + str + "\n");
518     }
519   }
520   else if (event->action == "add_job")
521   {
522     if (event->event_name == "Ok")
523     {
524       write_normal_text("New job added " + job_name + "\n");
525       emit new_job_added(job_name);
526     }
527   }
528   else if (event->action == "to_remove_job")
529   {
530     if (event->event_name == "Ok")
531       _main_gui->delete_job_external(job_name);
532   }
533   else
534   {
535     QString str((event->action).c_str());
536     write_error_text("Unknown type of event received:" + str + "\n");
537   }
538   return true;
539 }
540
541 void 
542 BL::JobsManager_QT::write_normal_text(const QString & text)
543 {
544   _log->setReadOnly(false);
545   QTextCursor cursor = _log->textCursor();
546   QTextCharFormat text_format;
547   text_format.setForeground(Qt::darkBlue);
548   cursor.insertText(text, text_format);
549   _log->setTextCursor(cursor);
550   _log->setReadOnly(true);
551 }
552
553 void 
554 BL::JobsManager_QT::write_error_text(const QString & text)
555 {
556   _log->setReadOnly(false);
557   QTextCursor cursor = _log->textCursor();
558   QTextCharFormat text_format;
559   text_format.setForeground(Qt::red);
560   cursor.insertText(text, text_format);
561   _log->setTextCursor(cursor);
562   _log->setReadOnly(true);
563 }
564
565 // To tokenize a string
566 void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters)
567 {
568         // Skip delimiters at beginning.
569         string::size_type lastPos = str.find_first_not_of(delimiters, 0);
570         // Find first "non-delimiter".
571         string::size_type pos     = str.find_first_of(delimiters, lastPos);
572
573         while (string::npos != pos || string::npos != lastPos)
574         {
575                 // Found a token, add it to the vector.
576                 tokens.push_back(str.substr(lastPos, pos - lastPos));
577                 // Skip delimiters.  Note the "not_of"
578                 lastPos = str.find_first_not_of(delimiters, pos);
579                 // Find next "non-delimiter"
580                 pos = str.find_first_of(delimiters, lastPos);
581         }
582 }