VPTissue Reference Manual
CliController.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "CliController.h"
21 
24 #include "session/SimSession.h"
25 #include "util/misc/log_debug.h"
26 #include "util/misc/Exception.h"
27 #include "util/misc/StringUtils.h"
29 
30 #include <QApplication>
31 #include <QEventLoop>
32 #include <QFileInfo>
33 
34 #include <algorithm>
35 #include <cassert>
36 #include <csignal>
37 #include <sstream>
38 #include <string>
39 
40 namespace SimPT_Shell {
41 
42 using namespace std;
43 using namespace std::chrono;
44 using namespace std::placeholders;
45 using namespace SimPT_Sim::Util;
46 
47 
48 CliController::CliController(shared_ptr<Ws::CliWorkspace> workspace_model, bool quiet)
49  : m_workspace_model(move(workspace_model)), m_quiet(quiet),
50  m_sig_int_adaptor(bind(&CliController::SigIntHandler, this, placeholders::_1)),
51 #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
52  m_sig_qt_adaptor(bind(&CliController::SigQtHandler, this, placeholders::_1, placeholders::_2, placeholders::_3))
53 #else
54  m_sig_qt_adaptor(bind(&CliController::SigQtHandler, this, placeholders::_1, placeholders::_2))
55 #endif
56 {
57  // register interrupt handler (adaptor auto-converts to function pointer).
58  signal(SIGINT, m_sig_int_adaptor);
59 
60  // Register Qt error handler (adaptor auto-converts to function pointer).
61 #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
62  qInstallMessageHandler(m_sig_qt_adaptor);
63 #else
64  qInstallMsgHandler(m_sig_qt_adaptor);
65 #endif
66 }
67 
68 CliController::~CliController()
69 {
70  signal(SIGINT, SIG_DFL);
71 #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
72  qInstallMessageHandler(0);
73 #else
74  qInstallMsgHandler(0);
75 #endif
76 }
77 
78 shared_ptr<CliController> CliController::Create(const string& workspace, bool quiet)
79 {
80  const string here = string(VL_HERE) + " exception:\n";
81  shared_ptr<CliController> ptr;
82 
83  try {
84  // QApplication must have been instantiated for the Qt services we use.
85  // QApp i.o. QCoreApp because of production of graphics output.
86  if (static_cast<QApplication*>(QApplication::instance()) == nullptr) {
87  throw Exception(here + "No QApplication instantiated in calling context!");
88  }
89 
90  // Create workspace model.
91  auto workspace_model = make_shared<Ws::CliWorkspace>(workspace);
92  if (!quiet) {
93  UserMessage("Successfully opened workspace " + workspace_model->GetPath());
94  }
95  ptr = shared_ptr<CliController>(new CliController(move(workspace_model), quiet));
96  if (ptr == nullptr) {
97  throw Exception(here + "Produced nullptr for " + workspace);
98  }
99 
100  } catch (exception& e) {
101  UserError(here + e.what());
102  throw;
103  } catch(...) {
104  UserError(here + "Unknown exception!");
105  }
106 
107  return ptr;
108 }
109 
110 shared_ptr<CliController> CliController::Create(shared_ptr<Ws::CliWorkspace> workspace, bool quiet)
111 {
112  const string here = string(VL_HERE) + " exception:\n";
113  shared_ptr<CliController> ptr;
114  assert((workspace != nullptr) && "Workspace pointer cannot be nullptr.");
115 
116  try {
117  // QApplication probably exists because a workspace pointer has been acquired.
118  // Still we check.
119  if (static_cast<QApplication*>(QApplication::instance()) == nullptr) {
120  throw Exception(here + "No QApplication instantiated in calling context!");
121  }
122 
123  ptr = shared_ptr<CliController>(new CliController(move(workspace), quiet));
124  if (ptr == nullptr) {
125  throw Exception(here + "Produced nullptr for " + workspace->GetPath());
126  }
127 
128  } catch (exception& e) {
129  UserError(here + e.what());
130  throw;
131  } catch(...) {
132  UserError(here + "Unknown exception!");
133  }
134 
135  assert((ptr != nullptr) && "CliController pointer cannot be nullptr.");
136  return ptr;
137 }
138 
139 int CliController::Execute(CliConverterTask task)
140 {
141  int exit_status = EXIT_SUCCESS;
142  Session::SimSession::Stopclock chrono_total("converter", true);
143 
144  StepSelection s;
146 
147  auto project = m_workspace_model->Get(task.GetProjectName());
148 
149  std::string inputfilter= task.GetInputFormatFilter();
150  std::transform(inputfilter.begin(), inputfilter.end(), inputfilter.begin(), ::tolower);
151 
152  auto format = task.GetOutputFormat();
153  auto prefs = SimShell::Ws::MergedPreferences::Create(m_workspace_model, project);
154  string prefix = "leaf";
155 
156  vector<int> timesteps;
157  for (auto& file : *project) {
158  bool valid_inputformat=true;
159 
160  if (inputfilter!="")
161  {
162  auto tissue_file = static_pointer_cast<Ws::StartupFileBase>(file.second);
163  std::string path = tissue_file->GetPath();
164  std::transform(path.begin(), path.end(), path.begin(), ::tolower);
165 
166  if (path.length()>=inputfilter.length()) {
167  valid_inputformat = path.substr(
168  path.length()-inputfilter.length(), inputfilter.length())==inputfilter;
169  }
170  }
171  if (valid_inputformat) {
172  for (int step : static_pointer_cast<Ws::StartupFileBase>(file.second)->GetTimeSteps()) {
173  timesteps.push_back(step);
174  }
175  }
176  }
177 
178  vector<int> filtered_timesteps;
179  for (int step : timesteps) {
180  if (s.Contains(step)) {
181  filtered_timesteps.push_back(step);
182  }
183  }
184 
185  // Instantiate file converter object.
186  FileConversion converter(
187  static_cast<Ws::Project*>(project.get()), filtered_timesteps, prefs,
188  format, project->GetPath(), prefix);
189 
190  // Execute the converter.
191  converter.Run(
192  [&](int i) { UserMessage("Converted " + ToString(i) + " files."); }
193  );
194 
195  m_timings.Record(chrono_total.GetName(), chrono_total.Get());
196  return exit_status;
197 }
198 
199 
200 int CliController::Execute(CliSimulationTask task)
201 {
202  int exit_status = EXIT_SUCCESS;
203  Session::SimSession::Stopclock chrono_total("total", true);
204 
205  try {
206  exit_status = SimulatorRun(task);
207  }
208  catch (std::exception& e) {
209  exit_status = EXIT_FAILURE;
210  throw SimPT_Sim::Util::Exception(e.what());
211  }
212 
213  m_timings.Record(chrono_total.GetName(), chrono_total.Get());
214  return exit_status;
215 }
216 
217 CliController::Timings CliController::GetTimings() const
218 {
219  return m_timings.GetRecords();
220 }
221 
222 bool CliController::IsQuiet() const
223 {
224  return m_quiet;
225 }
226 
227 void CliController::SigIntHandler(int )
228 {
229  signal(SIGINT, SIG_DFL);
230  emit TerminationRequested();
231 }
232 
233 #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
234 void CliController::SigQtHandler(QtMsgType type, const QMessageLogContext& /*context*/, const QString& msg)
235 {
236  switch (type) {
237  case QtDebugMsg:
238  UserMessage("Qt debug: " + msg.toStdString());
239  break;
240  #if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
241  case QtInfoMsg:
242  UserMessage("Qt info: " + msg.toStdString());
243  break;
244  #endif
245  case QtWarningMsg:
246  UserMessage("Qt warning: " + msg.toStdString());
247  break;
248  case QtCriticalMsg:
249  UserError("Qt critical: " + msg.toStdString());
250  break;
251  case QtFatalMsg:
252  UserError("Qt fatal: " + msg.toStdString());
253  abort();
254  break;
255  }
256 }
257 #else
258 void CliController::SigQtHandler(QtMsgType type, const char* msg)
259 {
260  switch (type) {
261  case QtDebugMsg:
262  UserMessage("Qt debug: " + string(msg));
263  break;
264  case QtWarningMsg:
265  UserMessage("Qt warning: " + string(msg));
266  break;
267  case QtCriticalMsg:
268  UserError("Qt critical: " + string(msg));
269  break;
270  case QtFatalMsg:
271  UserError("Qt fatal: " + string(msg));
272  abort();
273  break;
274  }
275 }
276 #endif
277 
278 void CliController::SimulationError(const std::string& error)
279 {
280  UserError(error);
281  emit TerminationRequested();
282 }
283 
284 void CliController::SimulationInfo(const std::string& message,
285  const Session::ISession::InfoMessageReason& reason)
286 {
287  switch (reason) {
288  case Session::ISession::InfoMessageReason::Stepped:
289  if (!IsQuiet()) {
290  UserMessage(message);
291  }
292  break;
293  case Session::ISession::InfoMessageReason::Stopped:
294  emit TerminationRequested();
295  break;
296  case Session::ISession::InfoMessageReason::Terminated:
297  emit TerminationRequested();
298  break;
299  default:
300  break;
301  }
302 }
303 
304 int CliController::SimulatorRun(CliSimulationTask task)
305 {
306  int exit_status = EXIT_FAILURE;
307  const string here = string(VL_HERE) + "> ";
308  const string project_name = task.GetProjectName();
309  auto project = m_workspace_model->Get(project_name);
310  const string file = task.IsLeafSet() ? task.GetLeaf() : (--project->end())->first;
311 
312  project->Open(file);
313  if (project->IsOpened()) {
314  // Create root viewer and initialize its subviewers
315  auto rootViewer = project->Session().CreateRootViewer();
316 
317  connect(&project->Session(), SIGNAL(InfoMessage(const std::string&, const InfoMessageReason&)),
318  this, SLOT(SimulationInfo(const std::string&, const InfoMessageReason&)));
319  connect(&project->Session(), SIGNAL(ErrorMessage(const std::string&)),
320  this, SLOT(SimulationError(const std::string&)));
321  if (!IsQuiet()) {
322  UserMessage("Opened project " + project_name + " and " + file);
323  }
324 
325  // Run simulation inside an event loop, so this thread can receive signals from
326  // possibly other threads.
327  // If TerminationRequested() would for some reason already be emitted during
328  // StartSimulation, then the connection to quit will be queued, so that the quit()
329  // slot of eventLoop will not be called before the call to exec()
330  QEventLoop eventLoop(this);
331  connect(this, SIGNAL(TerminationRequested()), &eventLoop, SLOT(quit()), Qt::QueuedConnection);
332  project->Session().StartSimulation(task.IsNumStepsSet() ? task.GetNumSteps() : -1);
333  eventLoop.exec();
334 
335  exit_status = EXIT_SUCCESS;
336  // Get timings from simulation.
337  m_timings.Merge(project->Session().GetTimings());
338 
339  project->Close();
340  } else {
341  exit_status = EXIT_FAILURE;
342  UserError(here + "Failed to open project " + project_name + " and " + file);
343  }
344 
345  return exit_status;
346 }
347 
348 void CliController::Terminate()
349 {
350  emit TerminationRequested();
351 }
352 
353 void CliController::UserError(const string& msg)
354 {
355  std::cerr << msg << std::endl;
356 }
357 
358 void CliController::UserMessage(const string& msg)
359 {
360  std::cout << msg << std::endl;
361 }
362 
363 } // namespace
bool Contains(int step) const
Checks whether the selection contains a specified step.
Interface for CliController.
STL namespace.
A CliConverterTask represents an invocation of the converter from the command line.
Interface for Conversion.
Namespace for miscellaneous utilities.
Definition: PTreeFile.cpp:44
string ToString(Type w)
Converts a WallType::Type value to corresponding name.
Definition: WallType.cpp:49
Namespace for SimPT shell package.
Definition: Client.cpp:50
std::string GetName() const
Return name of this stopwatch.
Definition: Stopwatch.h:88
const std::string GetProjectName() const
Plain getter.
Interface for StepSelection.
void SetSelectionText(const std::string &ranges)
Sets the selected ranges of steps.
Extremely simple Exception root class.
Definition: Exception.h:28
Macro defs for debug and logging.
Command line interface application controller.
Definition: CliController.h:38
const std::string GetTimeStepFilter() const
Plain getter.
const std::string GetInputFormatFilter() const
Plain getter.
virtual const std::string & GetPath() const
Interface for MergedPreferences.
Interfaces for simulator session.
T::duration Get() const
Returns the accumulated value without altering the stopwatch state.
Definition: Stopwatch.h:94
Base class representing the file types used to initiate a session.
String manipulation utilities.
Provides a stopwatch interface to time: it accumulates time between start/stop pairs.
Definition: Stopwatch.h:39
Header file for Exception class.
A CliTask represents an invocation of the program from the command line.
Class handling user input of ranges of steps.
Definition: StepSelection.h:32
Manages file conversion operations specifications.
IConverterFormat * GetOutputFormat() const
Plain getter.