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