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