Salome HOME
836e69852a335f8105586639df450871c0ab2c48
[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("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.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
329   // End
330   addJobToLauncher(wizard.job_name);
331   emit new_job_added(QString::fromUtf8(wizard.job_name.c_str()));
332   QStandardItemModel * model = _model_manager->getModel();
333   QList<QStandardItem *> item_list = model->findItems(QString::fromUtf8(wizard.job_name.c_str()));
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.toUtf8().constData());
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   QString job_name = QString::fromUtf8(event->job_name.c_str());
375   if (event->action == "create_job")
376   {
377     if (event->event_name == "Ok")
378     {
379       write_normal_text("Job " + job_name + " created\n");
380     }
381     else
382     {
383       write_error_text("Error in creating job: " + job_name + "\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       write_normal_text("Job " + job_name + " queued\n");
394     }
395     else
396     {
397       write_error_text("Error in starting job: " + job_name + "\n");
398       write_error_text("*** ");
399       write_error_text((event->data).c_str());
400       write_error_text(" ***\n");
401     }
402     emit job_state_changed(job_name);
403   }
404   else if (event->action == "refresh_job")
405   {
406     if (event->event_name == "Ok")
407     {
408       QString state((event->data).c_str());
409       state = state.toLower();
410       write_normal_text("Job " + job_name + " new state is " + state + "\n");
411       emit job_state_changed(job_name);
412     }
413     else
414     {
415       write_error_text("Error in refreshing job: " + job_name + "\n");
416       write_error_text("*** ");
417       write_error_text((event->data).c_str());
418       write_error_text(" ***\n");
419     }
420   }
421   else if (event->action == "delete_job")
422   {
423     if (event->event_name == "Ok")
424     {
425       write_normal_text("Job " + job_name + " deleted\n");
426     }
427     else
428     {
429       write_error_text("Warning delete job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
430       write_error_text("*** ");
431       write_error_text((event->data).c_str());
432       write_error_text(" ***\n");
433     }
434   }
435   else if (event->action == "get_results_job")
436   {
437     if (event->event_name == "Ok")
438     {
439       write_normal_text("Results of Job " + job_name + " are get\n");
440     }
441     else
442     {
443       write_error_text("Warning for results of job: " + job_name + " maybe not complete, exception catch in SALOME Launcher service\n");
444       write_error_text("*** ");
445       write_error_text((event->data).c_str());
446       write_error_text(" ***\n");
447     }
448   }
449   else if (event->action == "stop_job")
450   {
451     if (event->event_name == "Ok")
452     {
453       write_normal_text("Job " + job_name + " is stopped\n");
454     }
455     else
456     {
457       write_error_text("Error when trying to stop job: " + job_name + "\n");
458       write_error_text("*** ");
459       write_error_text((event->data).c_str());
460       write_error_text(" ***\n");
461     }
462   }
463   else if (event->action == "get_assigned_hostnames")
464   {
465     if (event->event_name == "Ok")
466     {
467           vector<string> hostnames;
468
469           Tokenize(event->data, hostnames, "+");
470
471           vector<string>::iterator it;
472
473       write_normal_text("Job " + job_name + " assigned hostnames are :\n");
474
475           for (it = hostnames.begin(); it < hostnames.end(); it++)
476           {
477                   vector<string> hostname;
478                   Tokenize(*it, hostname, ".");
479                   QString assigned_hostname(hostname[0].c_str());
480                   write_normal_text("+ " + assigned_hostname + "\n");
481           }
482     }
483     else
484     {
485                 // Do nothing in case the batch manager does not support this
486     }
487   }
488   else if (event->action == "save_jobs")
489   {
490     if (event->event_name == "Error")
491     {
492       write_error_text("Error in saving jobs: \n");
493       write_error_text("*** ");
494       write_error_text((event->data).c_str());
495       write_error_text(" ***\n");
496     }
497     else
498     {
499       QString str((event->data).c_str());
500       write_normal_text("Jobs saved in file " + str + "\n");
501     }
502   }
503   else if (event->action == "load_jobs")
504   {
505     if (event->event_name == "Error")
506     {
507       write_error_text("Error in loading jobs: \n");
508       write_error_text("*** ");
509       write_error_text((event->data).c_str());
510       write_error_text(" ***\n");
511     }
512     else
513     {
514       QString str((event->data).c_str());
515       write_normal_text("Jobs loaded from file " + str + "\n");
516     }
517   }
518   else if (event->action == "add_job")
519   {
520     if (event->event_name == "Ok")
521     {
522       write_normal_text("New job added " + job_name + "\n");
523       emit new_job_added(job_name);
524     }
525   }
526   else if (event->action == "to_remove_job")
527   {
528     if (event->event_name == "Ok")
529       _main_gui->delete_job_external(job_name);
530   }
531   else
532   {
533     QString str((event->action).c_str());
534     write_error_text("Unknown type of event received:" + str + "\n");
535   }
536   return true;
537 }
538
539 void 
540 BL::JobsManager_QT::write_normal_text(const QString & text)
541 {
542   _log->setReadOnly(false);
543   QTextCursor cursor = _log->textCursor();
544   QTextCharFormat text_format;
545   text_format.setForeground(Qt::darkBlue);
546   cursor.insertText(text, text_format);
547   _log->setTextCursor(cursor);
548   _log->setReadOnly(true);
549 }
550
551 void 
552 BL::JobsManager_QT::write_error_text(const QString & text)
553 {
554   _log->setReadOnly(false);
555   QTextCursor cursor = _log->textCursor();
556   QTextCharFormat text_format;
557   text_format.setForeground(Qt::red);
558   cursor.insertText(text, text_format);
559   _log->setTextCursor(cursor);
560   _log->setReadOnly(true);
561 }
562
563 // To tokenize a string
564 void Tokenize(const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters)
565 {
566         // Skip delimiters at beginning.
567         string::size_type lastPos = str.find_first_not_of(delimiters, 0);
568         // Find first "non-delimiter".
569         string::size_type pos     = str.find_first_of(delimiters, lastPos);
570
571         while (string::npos != pos || string::npos != lastPos)
572         {
573                 // Found a token, add it to the vector.
574                 tokens.push_back(str.substr(lastPos, pos - lastPos));
575                 // Skip delimiters.  Note the "not_of"
576                 lastPos = str.find_first_not_of(delimiters, pos);
577                 // Find next "non-delimiter"
578                 pos = str.find_first_of(delimiters, lastPos);
579         }
580 }