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