Salome HOME
Merge from V7_3_BR branch 18/12/2013
[modules/jobmanager.git] / src / genericgui / BL_JobsManager_QT.cxx
1 // Copyright (C) 2009-2013  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.
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("JobManagerDockWidget");
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.toStdString());
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.toStdString());
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
329   // End
330   addJobToLauncher(wizard.job_name);
331   emit new_job_added(QString::fromStdString(wizard.job_name));
332   QStandardItemModel * model = _model_manager->getModel();
333   QList<QStandardItem *> item_list = model->findItems(QString::fromStdString(wizard.job_name));
334   QStandardItem * job_state_item = model->item(item_list.at(0)->row(), 2);
335   _main_gui->_jobs_table->selectRow(item_list.at(0)->row());
336   if (wizard.start_job)
337     start_job(wizard.job_name);
338 }
339
340 void
341 BL::JobsManager_QT::delete_job(QString job_name)
342 {
343   BL::JobsManager::removeJob(job_name.toStdString());
344   _model_manager->delete_job(job_name);
345   _main_gui->_job_tab->reset(job_name);
346 }
347
348 void 
349 BL::JobsManager_QT::sendEvent(const std::string & action, 
350                               const std::string & event_name, 
351                               const std::string & job_name, 
352                               const std::string & data)
353 {
354   DEBTRACE("sendEvent BL::JobsManager_QT");
355
356   // Sending a QEvent to go back to main thread
357   BL::JobManagerEvent * event = new JobManagerEvent(action, event_name, job_name, data);
358   QApplication::postEvent(this, event);
359 }
360
361 bool 
362 BL::JobsManager_QT::event(QEvent * e)
363 {
364   QDockWidget::event(e);
365   JobManagerEvent * event = dynamic_cast<JobManagerEvent*>(e);
366   if (!event) return false;
367
368   DEBTRACE("BL::JobsManager_QT Receiving event : " 
369            << event->action << " " 
370            << event->event_name << " "
371            << event->job_name << " "
372            << event->data);
373
374   if (event->action == "create_job")
375   {
376     if (event->event_name == "Ok")
377     {
378       QString str((event->job_name).c_str());
379       write_normal_text("Job " + str + " created\n");
380     }
381     else
382     {
383       QString str((event->job_name).c_str());
384       write_error_text("Error in creating job: " + str + "\n");
385       write_error_text("*** ");
386       write_error_text((event->data).c_str());
387       write_error_text(" ***\n");
388     }
389   }
390   else if (event->action == "start_job")
391   {
392     if (event->event_name == "Ok")
393     {
394       QString str((event->job_name).c_str());
395       write_normal_text("Job " + str + " queued\n");
396     }
397     else
398     {
399       QString str((event->job_name).c_str());
400       write_error_text("Error in starting job: " + str + "\n");
401       write_error_text("*** ");
402       write_error_text((event->data).c_str());
403       write_error_text(" ***\n");
404     }
405     emit job_state_changed(QString((event->job_name).c_str()));
406   }
407   else if (event->action == "refresh_job")
408   {
409     if (event->event_name == "Ok")
410     {
411       QString name((event->job_name).c_str());
412       QString state((event->data).c_str());
413       state = state.toLower();
414       write_normal_text("Job " + name + " new state is " + state + "\n");
415       emit job_state_changed(QString((event->job_name).c_str()));
416     }
417     else
418     {
419       QString str((event->job_name).c_str());
420       write_error_text("Error in refreshing job: " + str + "\n");
421       write_error_text("*** ");
422       write_error_text((event->data).c_str());
423       write_error_text(" ***\n");
424     }
425   }
426   else if (event->action == "delete_job")
427   {
428     if (event->event_name == "Ok")
429     {
430       QString str((event->job_name).c_str());
431       write_normal_text("Job " + str + " deleted\n");
432     }
433     else
434     {
435       QString str((event->job_name).c_str());
436       write_error_text("Warning delete job: " + str + " maybe not complete, exception catch in SALOME Launcher service\n");
437       write_error_text("*** ");
438       write_error_text((event->data).c_str());
439       write_error_text(" ***\n");
440     }
441   }
442   else if (event->action == "get_results_job")
443   {
444     if (event->event_name == "Ok")
445     {
446       QString str((event->job_name).c_str());
447       write_normal_text("Results of Job " + str + " are get\n");
448     }
449     else
450     {
451       QString str((event->job_name).c_str());
452       write_error_text("Warning for results of job: " + str + " maybe not complete, exception catch in SALOME Launcher service\n");
453       write_error_text("*** ");
454       write_error_text((event->data).c_str());
455       write_error_text(" ***\n");
456     }
457   }
458   else if (event->action == "stop_job")
459   {
460     if (event->event_name == "Ok")
461     {
462       QString str((event->job_name).c_str());
463       write_normal_text("Job " + str + " is stopped\n");
464     }
465     else
466     {
467       QString str((event->job_name).c_str());
468       write_error_text("Error when trying to stop job: " + str + "\n");
469       write_error_text("*** ");
470       write_error_text((event->data).c_str());
471       write_error_text(" ***\n");
472     }
473   }
474   else if (event->action == "get_assigned_hostnames")
475   {
476     if (event->event_name == "Ok")
477     {
478       QString str((event->job_name).c_str());
479
480           vector<string> hostnames;
481
482           Tokenize(event->data, hostnames, "+");
483
484           vector<string>::iterator it;
485
486       write_normal_text("Job " + str + " assigned hostnames are :\n");
487
488           for (it = hostnames.begin(); it < hostnames.end(); it++)
489           {
490                   vector<string> hostname;
491                   Tokenize(*it, hostname, ".");
492                   QString assigned_hostname(hostname[0].c_str());
493                   write_normal_text("+ " + assigned_hostname + "\n");
494           }
495     }
496     else
497     {
498                 // Do nothing in case the batch manager does not support this
499     }
500   }
501   else if (event->action == "save_jobs")
502   {
503     if (event->event_name == "Error")
504     {
505       write_error_text("Error in saving jobs: \n");
506       write_error_text("*** ");
507       write_error_text((event->data).c_str());
508       write_error_text(" ***\n");
509     }
510     else
511     {
512       QString str((event->data).c_str());
513       write_normal_text("Jobs saved in file " + str + "\n");
514     }
515   }
516   else if (event->action == "load_jobs")
517   {
518     if (event->event_name == "Error")
519     {
520       write_error_text("Error in loading jobs: \n");
521       write_error_text("*** ");
522       write_error_text((event->data).c_str());
523       write_error_text(" ***\n");
524     }
525     else
526     {
527       QString str((event->data).c_str());
528       write_normal_text("Jobs loaded from file " + str + "\n");
529     }
530   }
531   else if (event->action == "add_job")
532   {
533     if (event->event_name == "Ok")
534     {
535       QString str((event->job_name).c_str());
536       write_normal_text("New job added " + str + "\n");
537       emit new_job_added(str);
538     }
539   }
540   else if (event->action == "to_remove_job")
541   {
542     if (event->event_name == "Ok")
543       _main_gui->delete_job_external((event->job_name).c_str());
544   }
545   else
546   {
547     QString str((event->action).c_str());
548     write_error_text("Unknown type of event received:" + str + "\n");
549   }
550   return true;
551 }
552
553 void 
554 BL::JobsManager_QT::write_normal_text(const QString & text)
555 {
556   _log->setReadOnly(false);
557   QTextCursor cursor = _log->textCursor();
558   QTextCharFormat text_format;
559   text_format.setForeground(Qt::darkBlue);
560   cursor.insertText(text, text_format);
561   _log->setTextCursor(cursor);
562   _log->setReadOnly(true);
563 }
564
565 void 
566 BL::JobsManager_QT::write_error_text(const QString & text)
567 {
568   _log->setReadOnly(false);
569   QTextCursor cursor = _log->textCursor();
570   QTextCharFormat text_format;
571   text_format.setForeground(Qt::red);
572   cursor.insertText(text, text_format);
573   _log->setTextCursor(cursor);
574   _log->setReadOnly(true);
575 }
576
577 // To tokenize a string
578 void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters)
579 {
580         // Skip delimiters at beginning.
581         string::size_type lastPos = str.find_first_not_of(delimiters, 0);
582         // Find first "non-delimiter".
583         string::size_type pos     = str.find_first_of(delimiters, lastPos);
584
585         while (string::npos != pos || string::npos != lastPos)
586         {
587                 // Found a token, add it to the vector.
588                 tokens.push_back(str.substr(lastPos, pos - lastPos));
589                 // Skip delimiters.  Note the "not_of"
590                 lastPos = str.find_first_not_of(delimiters, pos);
591                 // Find next "non-delimiter"
592                 pos = str.find_first_of(delimiters, lastPos);
593         }
594 }