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