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