1 /***************************************************************************
2 timetablegeneratemultipleform.cpp - description
3 -------------------
4 begin : Aug 20, 2007
5 copyright : (C) 2007 by Lalescu Liviu
6 email : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software: you can redistribute it and/or modify *
12 * it under the terms of the GNU Affero General Public License as *
13 * published by the Free Software Foundation, either version 3 of the *
14 * License, or (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "longtextmessagebox.h"
19
20 #include "generate.h"
21
22 #include "timetablegeneratemultipleform.h"
23 #include "timetable_defs.h"
24 #include "timetable.h"
25 #include "fet.h"
26 #include "timetableexport.h"
27
28 #include "rules.h"
29
30 #include <ctime>
31
32 #include <QMessageBox>
33
34 //#include <QMutex>
35
36 #include <QScrollBar>
37
38 #include <QVBoxLayout>
39
40 #include <QDir>
41
42 #include <QApplication>
43 #include <QtGlobal>
44
45 #include <QProcess>
46 #include <QTimer>
47
48 #include <QDate>
49 #include <QTime>
50 #include <QLocale>
51 #include <QString>
52
53 #include <QSemaphore>
54
55 #include <QSettings>
56
57 #include <QThread> //only for QThread::idealThreadCount()
58
59 #include <mutex>
60 //#include <condition_variable>
61
62 //extern QMutex myMutex;
63
64 //Old comment below:
65 //The 'static' qualifiers were commented out below (in 8 places) to address possible crashes on some platforms.
66 //static QMutex fitnessMutexInThreads;
67 static std::mutex fitnessMutexInThreads;
68
69 extern const QString COMPANY;
70 extern const QString PROGRAM;
71
72 extern Generate gen;
73
74 //static Matrix1D<Worker> generateMultipleWorker;
75 //static Matrix1D<QThread> generateMultipleThread;
76 static int allNThreads;
77 static Matrix1D<QSemaphore> semaphoreTimetableStarted;
78 static Matrix1D<QSemaphore> semaphoreTimetableFinished;
79 /*static Matrix1D<std::binary_semaphore> semaphoreTimetableStarted;
80 static Matrix1D<std::binary_semaphore> semaphoreTimetableFinished;*/
81 /*static Matrix1D<std::condition_variable> cvTimetableStarted;
82 static Matrix1D<std::condition_variable> cvTimetableFinished;*/
83
84 //static QSemaphore semaphoreTimetableFinished;
85
86 //static QSemaphore semaphoreTimetableStarted;
87
88 //Represents the current status of the simulation - running or stopped.
89 extern bool simulation_running_multi;
90
91 //extern QSemaphore semaphorePlacedActivity;
92
93 static Matrix1D<Generate> genMultiMatrix;
94 //Generate genMulti;
95
96 static int nTimetables;
97 static int timeLimit;
98
99 extern Solution best_solution;
100
101 extern QString conflictsStringTitle;
102 extern QString conflictsString;
103
104 static Matrix1D<time_t> process_start_time;
105
106 static Matrix1D<TimetablingThread> timetablingThreads;
107
startGenerating()108 void TimetablingThread::startGenerating()
109 {
110 //time_t start_time;
111
112 genMultiMatrix[_nThread].abortOptimization=false;
113
114 //time(&start_time);
115 time(&process_start_time[_nThread]);
116
117 bool impossible;
118 bool timeExceeded;
119
120 //emit(timetableStarted(_nThread/*, nOverallTimetable+1*/));
121 //semaphoreTimetableStarted[_nThread].acquire();
122
123 genMultiMatrix[_nThread].generate(timeLimit, impossible, timeExceeded, true); //true means threaded
124 QString s;
125
126 bool ok;
127
128 //genMultiMatrix[_nThread].myMutex.lock();
129 if(genMultiMatrix[_nThread].abortOptimization){
130 //genMultiMatrix[_nThread].myMutex.unlock();
131 //cout<<"returning, because abortOptimization of _nThread="<<_nThread<<endl;
132 return;
133 }
134
135 if(allNThreads>=2)
136 s=tr("(Thread %1)").arg(_nThread+1)+QString(" ");
137 else
138 s=QString("");
139
140 if(impossible){
141 s+=tr("Timetable impossible to generate");
142 s+=QString(".");
143 ok=false;
144 }
145 else if(timeExceeded){
146 s+=tr("Time exceeded for current timetable");
147
148 ////////2011-05-26
149 int mact=genMultiMatrix[_nThread].maxActivitiesPlaced;
150 int mseconds=genMultiMatrix[_nThread].timeToHighestStage;
151
152 bool zero=false;
153 if(mseconds==0)
154 zero=true;
155 int hh=mseconds/3600;
156 mseconds%=3600;
157 int mm=mseconds/60;
158 mseconds%=60;
159 int ss=mseconds;
160 QString tim;
161 if(hh>0){
162 tim+=" ";
163 tim+=tr("%1 h", "hours").arg(hh);
164 }
165 if(mm>0){
166 tim+=" ";
167 tim+=tr("%1 m", "minutes").arg(mm);
168 }
169 if(ss>0 || zero){
170 tim+=" ";
171 tim+=tr("%1 s", "seconds").arg(ss);
172 }
173 tim.remove(0, 1);
174 s+=QString(". ");
175 s+=tr("Max placed activities: %1 (at %2)", "%1 represents the maximum number of activities placed, %2 is a time interval").arg(mact).arg(tim);
176 ///////
177
178 s+=QString(".");
179
180 ok=false;
181 }
182 else{
183 ok=true;
184
185 time_t finish_time;
186 time(&finish_time);
187 int seconds=int(difftime(finish_time, process_start_time[_nThread]));
188 int hours=seconds/3600;
189 seconds%=3600;
190 int minutes=seconds/60;
191 seconds%=60;
192
193 FakeString tmp;
194 fitnessMutexInThreads.lock();
195 genMultiMatrix[_nThread].c.fitness(gt.rules, &tmp);
196 fitnessMutexInThreads.unlock();
197
198 s+=tr("Timetable breaks %1 soft constraints, has %2 soft conflicts total, and was generated in %3 hours, %4 minutes and %5 seconds.")
199 .arg(genMultiMatrix[_nThread].c.conflictsWeightList.count())
200 .arg(CustomFETString::numberPlusTwoDigitsPrecision(genMultiMatrix[_nThread].c.conflictsTotal))
201 .arg(hours)
202 .arg(minutes)
203 .arg(seconds);
204 }
205 //genMultiMatrix[_nThread].myMutex.unlock();
206 //emit(resultReady(_nThread, s, ok));
207
208 emit(timetableGenerated(_nThread, nOverallTimetable+1, s, ok));
209 semaphoreTimetableFinished[_nThread].acquire();
210 /*std::mutex mtx;
211 std::unique_lock<std::mutex> lck(mtx);
212 cvTimetableFinished[_nThread].wait(lck);*/
213 }
214
215 /*Controller::Controller()
216 {
217 Worker* worker=new Worker;
218 worker->moveToThread(&workerThread);
219 connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
220 connect(this, SIGNAL(operate(int)), worker, SLOT(doWork(int)));
221 connect(worker, SIGNAL(resultReady(int, const QString&, bool)), this, SLOT(handleResults(int, const QString&, bool)));
222 workerThread.start();
223 }
224
225 Controller::~Controller()
226 {
227 workerThread.quit();
228 workerThread.wait();
229 }
230
231 void Controller::handleResults(int nThread, const QString& s, bool ok)
232 {
233 emit(timetableGenerated(nThread, nOverallTimetable+1, s, ok));
234 semaphoreTimetableFinished[_nThread].acquire();
235 }
236
237 void Controller::startOperate(int nThread)
238 {
239 emit(operate(nThread));
240 }*/
241
TimetableGenerateMultipleForm(QWidget * parent)242 TimetableGenerateMultipleForm::TimetableGenerateMultipleForm(QWidget* parent): QDialog(parent)
243 {
244 setupUi(this);
245
246 timetablesTabWidget->setUsesScrollButtons(true);
247
248 currentResultsTextEdit->setReadOnly(true);
249
250 startPushButton->setDefault(true);
251
252 connect(startPushButton, SIGNAL(clicked()), this, SLOT(start()));
253 connect(stopPushButton, SIGNAL(clicked()), this, SLOT(stop()));
254 connect(closePushButton, SIGNAL(clicked()), this, SLOT(closePressed()));
255 connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
256
257 centerWidgetOnScreen(this);
258 restoreFETDialogGeometry(this);
259
260 simulation_running_multi=false;
261
262 startPushButton->setEnabled(true);
263 stopPushButton->setDisabled(true);
264 closePushButton->setEnabled(true);
265 minutesGroupBox->setEnabled(true);
266 timetablesGroupBox->setEnabled(true);
267 threadsGroupBox->setEnabled(true);
268
269 labels.append(textLabel);
270
271 nThreadsSpinBox->setMinimum(1);
272 nThreadsSpinBox->setMaximum(QThread::idealThreadCount());
273 nThreadsSpinBox->setValue(1); //this is necessary, before the connection to nThreadsChanged(int)
274
275 connect(nThreadsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(nThreadsChanged(int)));
276 QSettings settings(COMPANY, PROGRAM);
277 nThreadsSpinBox->setValue(settings.value(this->metaObject()->className()+QString("/number-of-threads"), "1").toInt()); //this is necessary, after the connection to nThreadsChanged(int)
278 timetablesTabWidget->setCurrentIndex(0);
279
280 minutesSpinBox->setValue(settings.value(this->metaObject()->className()+QString("/time-limit"), "600000").toInt());
281 timetablesSpinBox->setValue(settings.value(this->metaObject()->className()+QString("/number-of-timetables"), "10").toInt());
282 }
283
~TimetableGenerateMultipleForm()284 TimetableGenerateMultipleForm::~TimetableGenerateMultipleForm()
285 {
286 QSettings settings(COMPANY, PROGRAM);
287 saveFETDialogGeometry(this);
288 settings.setValue(this->metaObject()->className()+QString("/number-of-threads"), nThreadsSpinBox->value());
289
290 settings.setValue(this->metaObject()->className()+QString("/time-limit"), minutesSpinBox->value());
291 settings.setValue(this->metaObject()->className()+QString("/number-of-timetables"), timetablesSpinBox->value());
292
293 if(simulation_running_multi)
294 this->stop();
295
296 for(Solution* sol : qAsConst(highestStageSolutions))
297 delete sol;
298 highestStageSolutions.clear();
299 nTimetableForHighestStageSolutions.clear();
300 nThreadForHighest.clear();
301 simulationTimedOutForHighest.clear();
302
303 //assert(controllersList.count()==0);
304 }
305
nThreadsChanged(int nt)306 void TimetableGenerateMultipleForm::nThreadsChanged(int nt)
307 {
308 int oldIndex=timetablesTabWidget->currentIndex();
309
310 int oldN=timetablesTabWidget->count();
311 if(oldN>nt){
312 for(int i=oldN-1; i>=nt; i--){
313 QWidget* wd=timetablesTabWidget->widget(i);
314 timetablesTabWidget->removeTab(i);
315 assert(labels.count()>0);
316 labels.removeLast();
317 delete wd;
318 }
319
320 if(oldIndex<timetablesTabWidget->count())
321 timetablesTabWidget->setCurrentIndex(oldIndex);
322 else
323 timetablesTabWidget->setCurrentIndex(timetablesTabWidget->count()-1);
324 }
325 else if(oldN<nt){
326 for(int i=oldN; i<nt; i++){
327 QWidget* wd=new QWidget(timetablesTabWidget);
328
329 QLabel* lb=new QLabel(tr("Current timetable: 0 out of 0 activities placed, 0h 0m 0s\nMax placed activities: 0 (at 0 s)"), wd);
330 QVBoxLayout* bl=new QVBoxLayout(wd);
331 bl->addWidget(lb);
332
333 timetablesTabWidget->addTab(wd, QString::number(i+1));
334
335 labels.append(lb);
336 }
337
338 timetablesTabWidget->setCurrentIndex(timetablesTabWidget->count()-1);
339 }
340 }
341
help()342 void TimetableGenerateMultipleForm::help()
343 {
344 QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
345
346 if(s2.right(4)==".fet")
347 s2=s2.left(s2.length()-4);
348
349 QString destDir=OUTPUT_DIR+FILE_SEP+"timetables"+FILE_SEP+s2+"-multi";
350
351 QString s=tr("You can see the generated timetables on the hard disk, in HTML and XML formats and the soft conflicts in text format,"
352 " or the latest timetable (or the latest highest-stage timetable, if no timetable was completed) in the Timetable/View menu."
353 " The directory %1 must be emptied and deleted before proceeding.").arg(QDir::toNativeSeparators(destDir))
354 +"\n\n"
355 +tr("Note that, for large data, each timetable might occupy more megabytes of hard disk space, so make sure you have enough space"
356 " (you can check the dimension of a single timetable as a precaution). Each attempted timetable will correspond to a folder in %1"
357 " that contains information about the random seed that was used, but only completed timetables will contain the full set of timetable files.")
358 .arg(QDir::toNativeSeparators(destDir))
359 +"\n\n"
360 +tr("For finished timetables, there are also saved the timetables in .fet format (data + constraints to lock the timetable), so that you"
361 " can open each of them later.")
362 +"\n\n"
363 +tr("If you get an impossible timetable, please enter menu Generate (single) and see the initial order of evaluation of activities; this might help.")
364 +"\n\n"
365 +tr("You can limit the search time, by specifying the maximum number of minutes allowed to spend for each timetable (option %1).").arg("'"+tr("Limit each")+"'")
366 +" "+tr("The maximum and also the predefined value is %1 minutes, which means %2 hours, so virtually unlimited.").arg(600000).arg(10000)
367 +"\n\n"
368 +tr("Note that if you start the multiple generation with the same global seed, the timetables will be identical (if you let the generations finish).")
369 +" "+tr("The seed of the first thread will be the global seed multiplied in each component with the number of threads (modulo m1=%1 and respectively m2=%2),"
370 " for the second thread the seed will be the same as for the first thread, but +1 in each component (modulo m1 and respectively m2), for the third thread the"
371 " seed will be the same as for the first thread, but +2 in each component, and so on.")
372 .arg(gen.rng.m1).arg(gen.rng.m2)
373 +" "+tr("(If the component of a such computed seed will be all zeroes, we will add to the components of that seed the total number of threads instead"
374 " of the thread number starting from zero.)")
375 +" "+tr("This method was suggested by %1.").arg("Chichi Lalescu")
376 +" "+tr("After generating multiple, the global seed will become equal to the seed of the first thread.")
377 +"\n\n"
378 +tr("The number of threads is limited by your computer processor(s). If you have for example an 8 core/16 thread processor, the maximum allowed"
379 " number of threads is 16. In this case you can make a comparison of generation time with 8 threads or with 16 threads. If you generate on a single thread,"
380 " the speed of generation of a timetable will be in general a bit higher than that of obtaining a single timetable by generating on multiple threads,"
381 " because the processor slows down if you are using more threads, but you will obtain more timetables in a comparable time.")
382 +"\n\n"
383 +tr("WARNING: As you use more threads, the processor will be used to a greater extent and it might overheat. Also, the system might become"
384 " slow or nonreponsive.")
385 +"\n\n"
386 +tr("Note: If your file is very big, containing a very large number of teachers or total subgroups, generating on more threads might consume the"
387 " processor cache and the generation might become very slow compared to generating on a single thread.")
388 ;
389
390 LongTextMessageBox::largeInformation(this, tr("FET information"), s);
391 }
392
start()393 void TimetableGenerateMultipleForm::start(){
394 int nThreads=nThreadsSpinBox->value();
395 assert(nThreads>=1);
396 genMultiMatrix.resize(nThreads);
397 timetablingThreads.resize(nThreads);
398 process_start_time.resize(nThreads);
399 //generateMultipleThread.resize(nThreads);
400 //generateMultipleWorker.resize(nThreads);
401 semaphoreTimetableStarted.resize(nThreads);
402 semaphoreTimetableFinished.resize(nThreads);
403 //cvTimetableStarted.resize(nThreads);
404 //cvTimetableFinished.resize(nThreads);
405 allNThreads=nThreads;
406
407 for(Solution* sol : qAsConst(highestStageSolutions))
408 delete sol;
409 highestStageSolutions.clear();
410 nTimetableForHighestStageSolutions.clear();
411 nThreadForHighest.clear();
412 simulationTimedOutForHighest.clear();
413
414 for(int t=0; t<nThreads; t++){
415 /*genMultiMatrix[t].rng.s10=(gen.rng.s10*nThreads+t)%gen.rng.m1;
416 genMultiMatrix[t].rng.s11=(gen.rng.s11*nThreads+t)%gen.rng.m1;
417 genMultiMatrix[t].rng.s12=(gen.rng.s12*nThreads+t)%gen.rng.m1;
418 if(genMultiMatrix[t].rng.s10==0 && genMultiMatrix[t].rng.s11==0 && genMultiMatrix[t].rng.s12==0){
419 assert(t!=0);
420 genMultiMatrix[t].rng.s10=(gen.rng.s10*nThreads+nThreads)%gen.rng.m1;
421 genMultiMatrix[t].rng.s11=(gen.rng.s11*nThreads+nThreads)%gen.rng.m1;
422 genMultiMatrix[t].rng.s12=(gen.rng.s12*nThreads+nThreads)%gen.rng.m1;
423 }
424 assert(genMultiMatrix[t].rng.s10!=0 || genMultiMatrix[t].rng.s11!=0 || genMultiMatrix[t].rng.s12!=0);
425
426 genMultiMatrix[t].rng.s20=(gen.rng.s20*nThreads+t)%gen.rng.m2;
427 genMultiMatrix[t].rng.s21=(gen.rng.s21*nThreads+t)%gen.rng.m2;
428 genMultiMatrix[t].rng.s22=(gen.rng.s22*nThreads+t)%gen.rng.m2;
429 if(genMultiMatrix[t].rng.s20==0 && genMultiMatrix[t].rng.s21==0 && genMultiMatrix[t].rng.s22==0){
430 assert(t!=0);
431 genMultiMatrix[t].rng.s20=(gen.rng.s20*nThreads+nThreads)%gen.rng.m2;
432 genMultiMatrix[t].rng.s21=(gen.rng.s21*nThreads+nThreads)%gen.rng.m2;
433 genMultiMatrix[t].rng.s22=(gen.rng.s22*nThreads+nThreads)%gen.rng.m2;
434 }
435 assert(genMultiMatrix[t].rng.s20!=0 || genMultiMatrix[t].rng.s21!=0 || genMultiMatrix[t].rng.s22!=0);*/
436
437 //init method suggested by Chichi Lalescu
438 qint64 s10=(gen.rng.s10*nThreads+t)%gen.rng.m1;
439 qint64 s11=(gen.rng.s11*nThreads+t)%gen.rng.m1;
440 qint64 s12=(gen.rng.s12*nThreads+t)%gen.rng.m1;
441 if(s10==0 && s11==0 && s12==0){
442 assert(t!=0);
443 s10=(gen.rng.s10*nThreads+nThreads)%gen.rng.m1;
444 s11=(gen.rng.s11*nThreads+nThreads)%gen.rng.m1;
445 s12=(gen.rng.s12*nThreads+nThreads)%gen.rng.m1;
446 }
447
448 qint64 s20=(gen.rng.s20*nThreads+t)%gen.rng.m2;
449 qint64 s21=(gen.rng.s21*nThreads+t)%gen.rng.m2;
450 qint64 s22=(gen.rng.s22*nThreads+t)%gen.rng.m2;
451 if(s20==0 && s21==0 && s22==0){
452 assert(t!=0);
453 s20=(gen.rng.s20*nThreads+nThreads)%gen.rng.m2;
454 s21=(gen.rng.s21*nThreads+nThreads)%gen.rng.m2;
455 s22=(gen.rng.s22*nThreads+nThreads)%gen.rng.m2;
456 }
457
458 genMultiMatrix[t].rng.initializeMRG32k3a(s10, s11, s12, s20, s21, s22);
459 }
460
461 nTimetables=timetablesSpinBox->value();
462 assert(nTimetables>0);
463 timeLimit=60*minutesSpinBox->value(); //seconds
464 assert(timeLimit>0);
465
466 QDir dir;
467 QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
468
469 if(s2.right(4)==".fet")
470 s2=s2.left(s2.length()-4);
471
472 QString destDir=OUTPUT_DIR+FILE_SEP+"timetables"+FILE_SEP+s2+"-multi";
473 if(dir.exists(destDir)){
474 QMessageBox::warning(this, tr("FET information"), tr("The directory %1 exists and might not be empty"
475 " (it might contain old files). You need to manually remove all contents of this directory AND the directory itself (or rename it)"
476 " and then you can generate multiple timetables.")
477 .arg(QDir::toNativeSeparators(destDir)));
478
479 return;
480 }
481
482 if(!gt.rules.internalStructureComputed){
483 if(!gt.rules.computeInternalStructure(this)){
484 QMessageBox::warning(this, TimetableGenerateMultipleForm::tr("FET warning"), TimetableGenerateMultipleForm::tr("Data is wrong. Please correct and try again"));
485 return;
486 }
487 }
488
489 if(!gt.rules.initialized || gt.rules.activitiesList.isEmpty()){
490 QMessageBox::critical(this, TimetableGenerateMultipleForm::tr("FET information"),
491 TimetableGenerateMultipleForm::tr("You have entered simulation with uninitialized rules or 0 activities...aborting"));
492 assert(0);
493 exit(1);
494 return;
495 }
496
497 currentResultsTextEdit->setPlainText("");
498
499 bool ok=genMultiMatrix[0].precompute(this);
500 if(!ok){
501 currentResultsTextEdit->setPlainText(TimetableGenerateMultipleForm::tr("Cannot optimize - please modify your data"));
502 currentResultsTextEdit->update();
503
504 QMessageBox::information(this, TimetableGenerateMultipleForm::tr("FET information"),
505 TimetableGenerateMultipleForm::tr("Your data cannot be processed - please modify it as instructed."));
506
507 return;
508 }
509
510 //assert(controllersList.count()==0);
511 for(int t=0; t<nThreads; t++){
512 //generateMultipleWorker[t].disconnect(); //disconnect all connections for this QThread
513 genMultiMatrix[t].disconnect(); //disconnect all connections for this Generate
514
515 //Controller* controller=new Controller();
516 timetablingThreads[t].disconnect();
517
518 //connect(controller, SIGNAL(timetableStarted(int, int)), this, SLOT(timetableStarted(int, int)));
519 //connect(controller, SIGNAL(timetableGenerated(int, int, const QString&, bool)), this, SLOT(timetableGenerated(int, int, const QString&, bool)));
520
521 //controllersList.append(controller);
522
523 timetablingThreads[t]._nThread=t;
524
525 genMultiMatrix[t].nThread=t;
526 genMultiMatrix[t].isRunning=false;
527
528 connect(&genMultiMatrix[t], SIGNAL(activityPlaced(int, int)), this, SLOT(activityPlaced(int, int)));
529 connect(&timetablingThreads[t], SIGNAL(timetableGenerated(int, int, const QString&, bool)), this, SLOT(timetableGenerated(int, int, const QString&, bool)));
530 }
531
532 startPushButton->setDisabled(true);
533 stopPushButton->setEnabled(true);
534 minutesGroupBox->setDisabled(true);
535 timetablesGroupBox->setDisabled(true);
536 closePushButton->setDisabled(true);
537 threadsGroupBox->setDisabled(true);
538
539 nGeneratedTimetables=0;
540 nSuccessfullyGeneratedTimetables=0;
541 highestPlacedActivities=0;
542
543 simulation_running_multi=true;
544
545 time(&all_processes_start_time);
546
547 //assert(controllersList.count()==nThreads);
548
549 //for(int t=0; t<nThreads; t++)
550 // controllersList.at(t)->_nThread=t;
551
552 for(int t=0; t<nThreads; t++){
553 genMultiMatrix[t].c.makeUnallocated(gt.rules);
554
555 timetablingThreads[t].nOverallTimetable=t;
556
557 //Controller* tc=controllersList.at(t);
558 //tc->nOverallTimetable=t;
559
560 timetableStarted(t, timetablingThreads[t].nOverallTimetable+1);
561
562 semaphoreTimetableStarted[t].acquire();
563 /*std::mutex mtx;
564 std::unique_lock<std::mutex> lck(mtx);
565 cvTimetableStarted[t].wait(lck);*/
566
567 //timetablingThreads[t].startGenerating();
568 timetablingThreads[t]._internalGeneratingThread=std::thread([=]{timetablingThreads[t].startGenerating();});
569 timetablingThreads[t]._internalGeneratingThread.detach();
570 }
571 }
572
timetableStarted(int nThread,int timetable)573 void TimetableGenerateMultipleForm::timetableStarted(int nThread, int timetable)
574 {
575 TimetableExport::writeRandomSeed(this, genMultiMatrix[nThread].rng, timetable, true); //true represents 'before' state
576
577 semaphoreTimetableStarted[nThread].release();
578 //cvTimetableStarted[nThread].notify_one();
579 }
580
timetableGenerated(int nThread,int timetable,const QString & description,bool ok)581 void TimetableGenerateMultipleForm::timetableGenerated(int nThread, int timetable, const QString& description, bool ok)
582 {
583 nGeneratedTimetables++;
584 numberOfGeneratedTimetablesLabel->setText(tr("Generated: %1").arg(nGeneratedTimetables));
585 if(ok){
586 nSuccessfullyGeneratedTimetables++;
587 numberOfSuccessfullyGeneratedTimetablesLabel->setText(tr("Successfully: %1").arg(nSuccessfullyGeneratedTimetables));
588 highestPlacedActivities=genMultiMatrix[nThread].maxActivitiesPlaced;
589 assert(highestPlacedActivities==gt.rules.nInternalActivities);
590 }
591 else{
592 if(highestPlacedActivities<genMultiMatrix[nThread].maxActivitiesPlaced){
593 highestPlacedActivities=genMultiMatrix[nThread].maxActivitiesPlaced;
594 for(Solution* sol : qAsConst(highestStageSolutions))
595 delete sol;
596 highestStageSolutions.clear();
597 nTimetableForHighestStageSolutions.clear();
598 nThreadForHighest.clear();
599 simulationTimedOutForHighest.clear();
600 Solution* sol=new Solution();
601 sol->copyForHighestStage(gt.rules, genMultiMatrix[nThread].highestStageSolution);
602 highestStageSolutions.append(sol);
603 nTimetableForHighestStageSolutions.append(timetable);
604 nThreadForHighest.append(nThread);
605 simulationTimedOutForHighest.append(true);
606 }
607 else if(highestPlacedActivities==genMultiMatrix[nThread].maxActivitiesPlaced){
608 Solution* sol=new Solution();
609 sol->copyForHighestStage(gt.rules, genMultiMatrix[nThread].highestStageSolution);
610 highestStageSolutions.append(sol);
611 nTimetableForHighestStageSolutions.append(timetable);
612 nThreadForHighest.append(nThread);
613 simulationTimedOutForHighest.append(true);
614 }
615 }
616
617 if(nGeneratedTimetables<=nTimetables){
618 if(ok)
619 TimetableExport::writeRandomSeed(this, genMultiMatrix[nThread].rng, timetable, false); //false represents 'before' state
620
621 QString s=QString("");
622 s+=tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description").arg(timetable).arg(description);
623 currentResultsTextEdit->appendPlainText(s);
624
625 bool begin;
626 if(nGeneratedTimetables==1)
627 begin=true;
628 else
629 begin=false;
630 TimetableExport::writeReportForMultiple(this, s, begin);
631
632 if(ok){
633 //needed to get the conflicts string
634 FakeString tmp;
635 fitnessMutexInThreads.lock();
636 genMultiMatrix[nThread].c.fitness(gt.rules, &tmp);
637 fitnessMutexInThreads.unlock();
638
639 /*TimetableExport::getStudentsTimetable(genMultiMatrix[nThread].c);
640 TimetableExport::getTeachersTimetable(genMultiMatrix[nThread].c);
641 TimetableExport::getRoomsTimetable(genMultiMatrix[nThread].c);*/
642 TimetableExport::getStudentsTeachersRoomsTimetable(genMultiMatrix[nThread].c);
643
644 TimetableExport::writeSimulationResults(this, timetable);
645
646 //update the string representing the conflicts
647 conflictsStringTitle=tr("Soft conflicts", "Title of dialog");
648 conflictsString = "";
649
650 conflictsString+=tr("Number of broken soft constraints: %1").arg(best_solution.conflictsWeightList.count());
651
652 conflictsString+="\n";
653
654 conflictsString+=tr("Total soft conflicts: %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(best_solution.conflictsTotal));
655
656 conflictsString+="\n";
657 conflictsString+=tr("Soft conflicts listing (in decreasing order):")+"\n";
658
659 for(const QString& t : qAsConst(best_solution.conflictsDescriptionList))
660 conflictsString+=t+"\n";
661
662 updateAllTimetableViewDialogs();
663 }
664 }
665
666 //semaphoreTimetableFinished.release();
667
668 semaphoreTimetableFinished[nThread].release();
669 //cvTimetableFinished[nThread].notify_one();
670 //generateMultipleThread[nThread].quit();
671 //generateMultipleThread[nThread].wait();
672 //while(generateMultipleThread[nThread].isRunning()){
673 //wait for the thread to finish.
674 //}
675 //assert(generateMultipleThread[nThread].isFinished());
676 if(nGeneratedTimetables<nTimetables && simulation_running_multi){
677 if(timetablingThreads[nThread]._internalGeneratingThread.joinable())
678 timetablingThreads[nThread]._internalGeneratingThread.join();
679
680 //Controller* oldController=controllersList.at(nThread);
681 //controllersList.removeAt(nThread);
682 //int toldn=oldController->nOverallTimetable;
683 int toldn=timetablingThreads[nThread].nOverallTimetable;
684 //delete oldController;
685 //oldController->deleteLater();
686
687 genMultiMatrix[nThread].c.makeUnallocated(gt.rules);
688
689 //Controller* controller=new Controller();
690 //controllersList[nThread]=controller;
691
692 //controller->_nThread=nThread;
693 //controller->nOverallTimetable=toldn+allNThreads;
694 timetablingThreads[nThread].nOverallTimetable=toldn+allNThreads;
695
696 //connect(controller, SIGNAL(timetableStarted(int, int)), this, SLOT(timetableStarted(int, int)));
697
698 //connect(controller, SIGNAL(timetableGenerated(int, int, const QString&, bool)), this, SLOT(timetableGenerated(int, int, const QString&, bool)));
699
700 //timetableStarted(nThread, controller->nOverallTimetable+1/*+allNThreads*/);
701 timetableStarted(nThread, timetablingThreads[nThread].nOverallTimetable+1);
702 semaphoreTimetableStarted[nThread].acquire();
703 /*std::mutex mtx;
704 std::unique_lock<std::mutex> lck(mtx);
705 cvTimetableStarted[nThread].wait(lck);*/
706
707 //timetablingThreads[nThread]._internalGeneratingThread=std::thread(timetablingThreads[nThread].startGenerating);
708 timetablingThreads[nThread]._internalGeneratingThread=std::thread([=]{timetablingThreads[nThread].startGenerating();});
709 timetablingThreads[nThread]._internalGeneratingThread.detach();
710 //controller->startOperate(nThread);
711 }
712 else if(simulation_running_multi){
713 //assert(controllersList.count()==allNThreads);
714 for(int t=0; t<allNThreads; t++){
715 //This is always done (1 || stuff) because we need to disconnnect the Generate.
716 //if(1 || generateMultipleThread[t].isRunning()){
717 genMultiMatrix[t].myMutex.lock();
718 genMultiMatrix[t].abortOptimization=true;
719 //genMultiMatrix[t].disconnect();
720 genMultiMatrix[t].myMutex.unlock();
721
722 //delete controllersList[t];
723 //}
724
725 if(timetablingThreads[t]._internalGeneratingThread.joinable())
726 timetablingThreads[t]._internalGeneratingThread.join();
727
728 if(highestPlacedActivities<genMultiMatrix[t].maxActivitiesPlaced){
729 highestPlacedActivities=genMultiMatrix[t].maxActivitiesPlaced;
730 for(Solution* sol : qAsConst(highestStageSolutions))
731 delete sol;
732 highestStageSolutions.clear();
733 nTimetableForHighestStageSolutions.clear();
734 nThreadForHighest.clear();
735 simulationTimedOutForHighest.clear();
736 Solution* sol=new Solution();
737 sol->copyForHighestStage(gt.rules, genMultiMatrix[t].highestStageSolution);
738 highestStageSolutions.append(sol);
739 nTimetableForHighestStageSolutions.append(timetablingThreads[t].nOverallTimetable+1);
740 nThreadForHighest.append(t);
741 simulationTimedOutForHighest.append(true);
742 }
743 else if(highestPlacedActivities==genMultiMatrix[t].maxActivitiesPlaced){
744 Solution* sol=new Solution();
745 sol->copyForHighestStage(gt.rules, genMultiMatrix[t].highestStageSolution);
746 highestStageSolutions.append(sol);
747 nTimetableForHighestStageSolutions.append(timetablingThreads[t].nOverallTimetable+1);
748 nThreadForHighest.append(t);
749 simulationTimedOutForHighest.append(true);
750 }
751 }
752 simulationFinished();
753 }
754 }
755
stop()756 void TimetableGenerateMultipleForm::stop()
757 {
758 if(!simulation_running_multi){
759 return;
760 }
761
762 simulation_running_multi=false;
763
764 //assert(controllersList.count()==nThreadsSpinBox->value());
765
766 for(int t=0; t<nThreadsSpinBox->value(); t++){
767 //cout<<"1. t="<<t<<endl;
768 genMultiMatrix[t].myMutex.lock();
769 genMultiMatrix[t].abortOptimization=true;
770 genMultiMatrix[t].myMutex.unlock();
771 //cout<<"2. t="<<t<<endl;
772 if(timetablingThreads[t]._internalGeneratingThread.joinable())
773 timetablingThreads[t]._internalGeneratingThread.join();
774 //cout<<"3. t="<<t<<endl;
775 //if(timetablingThreads[t].joinable())
776 // timetablingThreads[t].join();
777
778 //if(controllersList.at(t)->workerThread.isRunning()){
779 // genMultiMatrix[t].myMutex.lock();
780
781 // genMultiMatrix[t].abortOptimization=true;
782 if(nSuccessfullyGeneratedTimetables==0){
783 if(highestPlacedActivities<genMultiMatrix[t].maxActivitiesPlaced){
784 highestPlacedActivities=genMultiMatrix[t].maxActivitiesPlaced;
785 for(Solution* sol : qAsConst(highestStageSolutions))
786 delete sol;
787 highestStageSolutions.clear();
788 nTimetableForHighestStageSolutions.clear();
789 nThreadForHighest.clear();
790 simulationTimedOutForHighest.clear();
791 Solution* sol=new Solution();
792 sol->copyForHighestStage(gt.rules, genMultiMatrix[t].highestStageSolution);
793 highestStageSolutions.append(sol);
794 //nTimetableForHighestStageSolutions.append(controllersList.at(t)->nOverallTimetable+1);
795 nTimetableForHighestStageSolutions.append(timetablingThreads[t].nOverallTimetable+1);
796 nThreadForHighest.append(t);
797 simulationTimedOutForHighest.append(false);
798 }
799 else if(highestPlacedActivities==genMultiMatrix[t].maxActivitiesPlaced){
800 Solution* sol=new Solution();
801 sol->copyForHighestStage(gt.rules, genMultiMatrix[t].highestStageSolution);
802 highestStageSolutions.append(sol);
803 //nTimetableForHighestStageSolutions.append(controllersList.at(t)->nOverallTimetable+1);
804 nTimetableForHighestStageSolutions.append(timetablingThreads[t].nOverallTimetable+1);
805 nThreadForHighest.append(t);
806 simulationTimedOutForHighest.append(false);
807 }
808 }
809
810 //genMultiMatrix[t].myMutex.unlock();
811 }
812
813 QString s=TimetableGenerateMultipleForm::tr("Simulation interrupted!");
814
815 QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
816
817 if(s2.right(4)==".fet")
818 s2=s2.left(s2.length()-4);
819
820 QString destDir=OUTPUT_DIR+FILE_SEP+"timetables"+FILE_SEP+s2+"-multi";
821
822 time_t final_time;
823 time(&final_time);
824 int sec=int(difftime(final_time, all_processes_start_time));
825 int h=sec/3600;
826 sec%=3600;
827 int m=sec/60;
828 sec%=60;
829
830 QDate dat=QDate::currentDate();
831 QTime tim=QTime::currentTime();
832 QLocale loc(FET_LANGUAGE);
833 QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
834 QString ms2=QString("");
835
836 QString stringForDisk=s;
837
838 QString ms3=QString("");
839 if(nSuccessfullyGeneratedTimetables>=1){
840 s+=QString("\n\n");
841 s+=TimetableGenerateMultipleForm::tr("From the interface you can access the last successfully generated timetable.");
842 }
843 else{
844 s+=QString("\n\n");
845 s+=TimetableGenerateMultipleForm::tr("From the interface you can access the last highest-stage timetable and on the disk"
846 " there were saved all the highest-stage timetables (the highest number of activities reached is %1, reached in %2 timetables).")
847 .arg(highestPlacedActivities).arg(highestStageSolutions.count());
848
849 stringForDisk+=QString("\n\n");
850 stringForDisk+=TimetableGenerateMultipleForm::tr("On the disk there were saved all the highest-stage timetables (the highest number"
851 " of activities reached is %1, reached in %2 timetables).").arg(highestPlacedActivities).arg(highestStageSolutions.count());
852
853 for(int i=0; i<highestStageSolutions.count(); i++){
854 Solution* sol=highestStageSolutions.at(i);
855 int nTimetable=nTimetableForHighestStageSolutions.at(i);
856 int nThread=nThreadForHighest.at(i);
857 bool timedOut=simulationTimedOutForHighest.at(i);
858 QString description=tr("(Thread %1)").arg(nThread+1);
859 description+=QString(" ");
860 if(timedOut)
861 description+=tr("Time exceeded.");
862 else
863 description+=tr("Simulation stopped.");
864 description+=QString(" ");
865 description+=tr("Maximum placed activities: %1.").arg(highestPlacedActivities);
866 if(i>=1 || currentResultsTextEdit->toPlainText().isEmpty())
867 currentResultsTextEdit->appendPlainText(tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
868 .arg(nTimetable).arg(description));
869 else
870 currentResultsTextEdit->appendPlainText(QString("\n")+tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
871 .arg(nTimetable).arg(description));
872 ms3+=tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
873 .arg(nTimetable).arg(description)+QString("\n");
874
875 //needed to get the conflicts string
876 FakeString tmp;
877 fitnessMutexInThreads.lock();
878 sol->fitness(gt.rules, &tmp);
879 fitnessMutexInThreads.unlock();
880
881 /*TimetableExport::getStudentsTimetable(*sol);
882 TimetableExport::getTeachersTimetable(*sol);
883 TimetableExport::getRoomsTimetable(*sol);*/
884 TimetableExport::getStudentsTeachersRoomsTimetable(*sol);
885
886 TimetableExport::writeSimulationResults(this, nTimetable, true);
887
888 //update the string representing the conflicts
889 conflictsStringTitle=tr("Conflicts", "Title of dialog");
890 conflictsString = "";
891
892 conflictsString+=tr("Number of broken constraints: %1").arg(sol->conflictsWeightList.count());
893
894 conflictsString+="\n";
895
896 conflictsString+=tr("Total conflicts: %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(sol->conflictsTotal));
897
898 conflictsString+="\n";
899 conflictsString+=tr("Conflicts listing (in decreasing order):")+"\n";
900
901 for(const QString& t : qAsConst(sol->conflictsDescriptionList))
902 conflictsString+=t+"\n";
903 }
904 updateAllTimetableViewDialogs();
905 }
906
907 if(!ms3.isEmpty())
908 ms3+=QString("\n");
909
910 s+="\n\n";
911 s+=TimetableGenerateMultipleForm::tr("The results were saved in the directory %1.").arg(QDir::toNativeSeparators(destDir));
912 s+="\n\n";
913 s+=tr("Total searching time was: %1h %2m %3s.").arg(h).arg(m).arg(sec);
914
915 stringForDisk+="\n\n";
916 stringForDisk+=TimetableGenerateMultipleForm::tr("The results were saved in the directory %1.").arg(QDir::toNativeSeparators(destDir));
917 stringForDisk+="\n\n";
918 stringForDisk+=tr("Total searching time was: %1h %2m %3s.").arg(h).arg(m).arg(sec);
919
920 ms2+=QString("\n\n");
921 ms2+=TimetableGenerateMultipleForm::tr("This file was automatically generated by FET %1 on %2.",
922 "%1 is the FET version, %2 is the date and time when this file was generated.").arg(FET_VERSION).arg(sTime);
923
924 if(nGeneratedTimetables>=1)
925 TimetableExport::writeReportForMultiple(this, QString("\n")+ms3+stringForDisk+ms2, false);
926 else
927 TimetableExport::writeReportForMultiple(this, ms3+stringForDisk+ms2, true);
928
929 QMessageBox::information(this, tr("FET information"), s);
930
931 startPushButton->setEnabled(true);
932 stopPushButton->setDisabled(true);
933 minutesGroupBox->setEnabled(true);
934 timetablesGroupBox->setEnabled(true);
935 closePushButton->setEnabled(true);
936 threadsGroupBox->setEnabled(true);
937
938 /*gen.rng.s10=genMultiMatrix[0].rng.s10;
939 gen.rng.s11=genMultiMatrix[0].rng.s11;
940 gen.rng.s12=genMultiMatrix[0].rng.s12;
941 gen.rng.s20=genMultiMatrix[0].rng.s20;
942 gen.rng.s21=genMultiMatrix[0].rng.s21;
943 gen.rng.s22=genMultiMatrix[0].rng.s22;
944 assert(gen.rng.s10!=0 || gen.rng.s11!=0 || gen.rng.s12!=0);
945 assert(gen.rng.s20!=0 || gen.rng.s21!=0 || gen.rng.s22!=0);*/
946 gen.rng.initializeMRG32k3a(genMultiMatrix[0].rng.s10, genMultiMatrix[0].rng.s11, genMultiMatrix[0].rng.s12,
947 genMultiMatrix[0].rng.s20, genMultiMatrix[0].rng.s21, genMultiMatrix[0].rng.s22);
948
949 /*for(Controller* tc : qAsConst(controllersList))
950 //delete tc;
951 tc->deleteLater();
952 controllersList.clear();*/
953 }
954
simulationFinished()955 void TimetableGenerateMultipleForm::simulationFinished()
956 {
957 if(!simulation_running_multi){
958 return;
959 }
960
961 simulation_running_multi=false;
962
963 QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
964
965 if(s2.right(4)==".fet")
966 s2=s2.left(s2.length()-4);
967
968 QString destDir=OUTPUT_DIR+FILE_SEP+"timetables"+FILE_SEP+s2+"-multi";
969
970 time_t final_time;
971 time(&final_time);
972 int s=int(difftime(final_time, all_processes_start_time));
973 int h=s/3600;
974 s%=3600;
975 int m=s/60;
976 s%=60;
977
978 QString ms=QString("");
979 ms+=TimetableGenerateMultipleForm::tr("Simulation finished!");
980
981 QString stringForDisk=ms;
982
983 QString ms3=QString("");
984
985 if(nSuccessfullyGeneratedTimetables>=1){
986 ms+=QString("\n\n");
987 ms+=TimetableGenerateMultipleForm::tr("From the interface you can access the last successfully generated timetable.");
988 }
989 else{
990 ms+=QString("\n\n");
991 ms+=TimetableGenerateMultipleForm::tr("From the interface you can access the last highest-stage timetable and on the disk"
992 " there were saved all the highest-stage timetables (the highest number of activities reached is %1, reached in %2 timetables).")
993 .arg(highestPlacedActivities).arg(highestStageSolutions.count());
994
995 stringForDisk+=QString("\n\n");
996 stringForDisk+=TimetableGenerateMultipleForm::tr("On the disk there were saved all the highest-stage timetables (the highest number"
997 " of activities reached is %1, reached in %2 timetables).").arg(highestPlacedActivities).arg(highestStageSolutions.count());
998
999 for(int i=0; i<highestStageSolutions.count(); i++){
1000 Solution* sol=highestStageSolutions.at(i);
1001 int nTimetable=nTimetableForHighestStageSolutions.at(i);
1002 int nThread=nThreadForHighest.at(i);
1003 bool timedOut=simulationTimedOutForHighest.at(i);
1004 QString description=tr("(Thread %1)").arg(nThread+1);
1005 description+=QString(" ");
1006 if(timedOut)
1007 description+=tr("Time exceeded.");
1008 else
1009 description+=tr("Simulation stopped.");
1010 description+=QString(" ");
1011 description+=tr("Maximum placed activities: %1.").arg(highestPlacedActivities);
1012 if(i>=1 || currentResultsTextEdit->toPlainText().isEmpty())
1013 currentResultsTextEdit->appendPlainText(tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
1014 .arg(nTimetable).arg(description));
1015 else
1016 currentResultsTextEdit->appendPlainText(QString("\n")+tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
1017 .arg(nTimetable).arg(description));
1018 ms3+=tr("Timetable no: %1 => %2", "%1 is the number of this timetable when generating multiple timetables, %2 is its description")
1019 .arg(nTimetable).arg(description)+QString("\n");
1020
1021 //needed to get the conflicts string
1022 FakeString tmp;
1023 fitnessMutexInThreads.lock();
1024 sol->fitness(gt.rules, &tmp);
1025 fitnessMutexInThreads.unlock();
1026
1027 /*TimetableExport::getStudentsTimetable(*sol);
1028 TimetableExport::getTeachersTimetable(*sol);
1029 TimetableExport::getRoomsTimetable(*sol);*/
1030 TimetableExport::getStudentsTeachersRoomsTimetable(*sol);
1031
1032 TimetableExport::writeSimulationResults(this, nTimetable, true);
1033
1034 //update the string representing the conflicts
1035 conflictsStringTitle=tr("Conflicts", "Title of dialog");
1036 conflictsString = "";
1037
1038 conflictsString+=tr("Number of broken constraints: %1").arg(sol->conflictsWeightList.count());
1039
1040 conflictsString+="\n";
1041
1042 conflictsString+=tr("Total conflicts: %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(sol->conflictsTotal));
1043
1044 conflictsString+="\n";
1045 conflictsString+=tr("Conflicts listing (in decreasing order):")+"\n";
1046
1047 for(const QString& t : qAsConst(sol->conflictsDescriptionList))
1048 conflictsString+=t+"\n";
1049 }
1050 updateAllTimetableViewDialogs();
1051 }
1052
1053 if(!ms3.isEmpty())
1054 ms3+=QString("\n");
1055
1056 ms+=QString("\n\n");
1057 ms+=TimetableGenerateMultipleForm::tr("The results were saved in the directory %1.").arg(QDir::toNativeSeparators(destDir));
1058 ms+=QString("\n\n");
1059 ms+=TimetableGenerateMultipleForm::tr("Total searching time was %1h %2m %3s.").arg(h).arg(m).arg(s);
1060
1061 stringForDisk+=QString("\n\n");
1062 stringForDisk+=TimetableGenerateMultipleForm::tr("The results were saved in the directory %1.").arg(QDir::toNativeSeparators(destDir));
1063 stringForDisk+=QString("\n\n");
1064 stringForDisk+=TimetableGenerateMultipleForm::tr("Total searching time was %1h %2m %3s.").arg(h).arg(m).arg(s);
1065
1066 QDate dat=QDate::currentDate();
1067 QTime tim=QTime::currentTime();
1068 QLocale loc(FET_LANGUAGE);
1069 QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
1070 QString ms2=QString("\n\n");
1071 ms2+=TimetableGenerateMultipleForm::tr("This file was automatically generated by FET %1 on %2.",
1072 "%1 is the FET version, %2 is the date and time when this file was generated.").arg(FET_VERSION).arg(sTime);
1073
1074 assert(nGeneratedTimetables>=1);
1075 TimetableExport::writeReportForMultiple(this, QString("\n")+ms3+stringForDisk+ms2, false);
1076
1077 //Old comment below (2020-08-14).
1078 //On Windows we do not beep for Qt >= 5.14.1, because the QMessageBox below beeps itself.
1079 //It would be better to test at runtime, not at compile time, but it is easier/safer this way.
1080 //(The alternative would be to develop a parser for the function qVersion(), but I am not sure it will always respect the exact format "vM.vm.vp".)
1081 //We test the macro Q_OS_WIN32 because on the old Qt 4 it is the only available macro from these three below,
1082 //Q_OS_WIN, Q_OS_WIN32, and Q_OS_WIN64 (which are available on Qt 5.14.1). Yes, the test is redundant (because if QT_VERSION < 5.14.1
1083 //the condition is true and if QT_VERSION >= 5.14.1 then all these three macros are available), but this doesn't hurt.
1084 //#if (!defined(Q_OS_WIN) && !defined(Q_OS_WIN32) && !defined(Q_OS_WIN64)) || (QT_VERSION < QT_VERSION_CHECK(5,14,1))
1085 if(BEEP_AT_END_OF_GENERATION)
1086 QApplication::beep();
1087 //#endif
1088
1089 if(ENABLE_COMMAND_AT_END_OF_GENERATION){
1090 QString s=commandAtEndOfGeneration.simplified();
1091 if(!s.isEmpty()){
1092 QStringList sl=s.split(" ");
1093 assert(sl.count()>=1);
1094 QString command=sl.at(0);
1095 QStringList arguments;
1096 for(int i=1; i<sl.count(); i++)
1097 arguments.append(sl.at(i));
1098
1099 /*if(DETACHED_NOTIFICATION==false){
1100 QProcess* myProcess=new QProcess();
1101 if(terminateCommandAfterSeconds>0)
1102 QTimer::singleShot(terminateCommandAfterSeconds*1000, myProcess, SLOT(terminate()));
1103 if(killCommandAfterSeconds>0)
1104 QTimer::singleShot(killCommandAfterSeconds*1000, myProcess, SLOT(kill()));
1105
1106 //https://www.qtcentre.org/threads/43083-Freeing-a-QProcess-after-it-has-finished-using-deleteLater()
1107 connect(myProcess, SIGNAL(finished(int)), myProcess, SLOT(deleteLater()));
1108 myProcess->start(command, arguments);
1109 }*/
1110 //else{
1111 QProcess::startDetached(command, arguments);
1112 //}
1113 }
1114 }
1115
1116 //Trick so that the message box will be silent (the only sound is thus the beep above).
1117 QMessageBox msgBox(this);
1118 msgBox.setWindowTitle(TimetableGenerateMultipleForm::tr("FET information"));
1119 msgBox.setText(ms);
1120 msgBox.exec();
1121 //QMessageBox::information(this, TimetableGenerateMultipleForm::tr("FET information"), ms);
1122
1123 startPushButton->setEnabled(true);
1124 stopPushButton->setDisabled(true);
1125 minutesGroupBox->setEnabled(true);
1126 timetablesGroupBox->setEnabled(true);
1127 closePushButton->setEnabled(true);
1128 threadsGroupBox->setEnabled(true);
1129
1130 /*gen.rng.s10=genMultiMatrix[0].rng.s10;
1131 gen.rng.s11=genMultiMatrix[0].rng.s11;
1132 gen.rng.s12=genMultiMatrix[0].rng.s12;
1133 gen.rng.s20=genMultiMatrix[0].rng.s20;
1134 gen.rng.s21=genMultiMatrix[0].rng.s21;
1135 gen.rng.s22=genMultiMatrix[0].rng.s22;
1136 assert(gen.rng.s10!=0 || gen.rng.s11!=0 || gen.rng.s12!=0);
1137 assert(gen.rng.s20!=0 || gen.rng.s21!=0 || gen.rng.s22!=0);*/
1138 gen.rng.initializeMRG32k3a(genMultiMatrix[0].rng.s10, genMultiMatrix[0].rng.s11, genMultiMatrix[0].rng.s12,
1139 genMultiMatrix[0].rng.s20, genMultiMatrix[0].rng.s21, genMultiMatrix[0].rng.s22);
1140
1141 /*for(Controller* tc : qAsConst(controllersList))
1142 //delete tc;
1143 tc->deleteLater();
1144 controllersList.clear();*/
1145 }
1146
activityPlaced(int nThread,int na)1147 void TimetableGenerateMultipleForm::activityPlaced(int nThread, int na)
1148 {
1149 assert(nThread>=0);
1150 assert(nThread<nThreadsSpinBox->value());
1151
1152 genMultiMatrix[nThread].myMutex.lock();
1153 int seconds=genMultiMatrix[nThread].searchTime; //seconds
1154 int mact=genMultiMatrix[nThread].maxActivitiesPlaced;
1155 int mseconds=genMultiMatrix[nThread].timeToHighestStage;
1156 genMultiMatrix[nThread].myMutex.unlock();
1157
1158 ////////2011-05-26
1159 genMultiMatrix[nThread].semaphorePlacedActivity.release();
1160 //genMultiMatrix[nThread].cvForPlacedActivity.notify_one();
1161
1162 //time_t finish_time;
1163 //time(&finish_time);
1164 //int seconds=int(difftime(finish_time, start_time));
1165 //int seconds=int(difftime(finish_time, process_start_time[nThread]));
1166 int hours=seconds/3600;
1167 seconds%=3600;
1168 int minutes=seconds/60;
1169 seconds%=60;
1170
1171 QString s;
1172
1173 bool zero=false;
1174 if(mseconds==0)
1175 zero=true;
1176 int hh=mseconds/3600;
1177 mseconds%=3600;
1178 int mm=mseconds/60;
1179 mseconds%=60;
1180 int ss=mseconds;
1181
1182 QString tim;
1183 if(hh>0){
1184 tim+=" ";
1185 tim+=tr("%1 h", "hours").arg(hh);
1186 }
1187 if(mm>0){
1188 tim+=" ";
1189 tim+=tr("%1 m", "minutes").arg(mm);
1190 }
1191 if(ss>0 || zero){
1192 tim+=" ";
1193 tim+=tr("%1 s", "seconds").arg(ss);
1194 }
1195 tim.remove(0, 1);
1196 s+=QString("\n");
1197 s+=tr("Max placed activities: %1 (at %2)", "%1 represents the maximum number of activities placed, %2 is a time interval").arg(mact).arg(tim);
1198 ///////
1199
1200 labels[nThread]->setText(tr("Current timetable: %1 out of %2 activities placed, %3h %4m %5s")
1201 .arg(na)
1202 .arg(gt.rules.nInternalActivities)
1203 .arg(hours)
1204 .arg(minutes)
1205 .arg(seconds)+s);
1206 }
1207
closePressed()1208 void TimetableGenerateMultipleForm::closePressed()
1209 {
1210 if(!simulation_running_multi)
1211 this->close();
1212 }
1213