CustusX  16.12-rc5
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxSessionStorageServiceImpl.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
34 #include "cxSettings.h"
35 #include "cxReporter.h"
36 #include <QTextStream>
37 #include "cxTime.h"
38 #include <QDir>
39 #include "cxConfig.h"
40 #include "cxFileHelpers.h"
41 #include "cxDataLocations.h"
42 #include <QApplication>
43 #include <QTimer>
44 #include "cxLogger.h"
45 #include "cxProfile.h"
46 
47 namespace cx
48 {
49 
51 {
52  this->clearCache();
53  mActivePatientFolder = this->getNoPatientFolder();
54  connect(this, &SessionStorageServiceImpl::sessionChanged, this, &SessionStorageServiceImpl::onSessionChanged);
55 
56  QTimer::singleShot(100, this, SLOT(startupLoadPatient())); // make sure this is called after application state change
57 }
58 
60 {
61  this->clearCache();
62 }
63 
65 {
66  return false;
67 }
68 
70 {
71  bool valid = this->isValidSessionFolder(dir);
72  bool exists = this->folderExists(dir);
73 
74  if (valid)
75  {
76  this->loadSession(dir);
77  }
78  else if (!exists)
79  {
80  this->initializeNewSession(dir);
81  }
82  else
83  {
84  reportError(QString("Failed to initialize session in existing folder with no valid session: %1").arg(dir));
85  }
86 }
87 
88 QString SessionStorageServiceImpl::getNoPatientFolder() const
89 {
90  return DataLocations::getCachePath() + "/NoPatient";
91 }
92 
93 
94 bool SessionStorageServiceImpl::isValidSessionFolder(QString dir) const
95 {
96  QString filename = QDir(dir).absoluteFilePath(this->getXmlFileName());
97  return QFileInfo::exists(filename);
98 }
99 
100 bool SessionStorageServiceImpl::folderExists(QString dir) const
101 {
102  return QFileInfo(dir).exists() && QFileInfo(dir).isDir();
103 }
104 
105 void SessionStorageServiceImpl::loadSession(QString dir)
106 {
107  if (this->isActivePatient(dir))
108  return;
109  this->loadPatientSilent(dir);
110  this->reportActivePatient();
111  this->writeRecentPatientData();
112 }
113 
114 void SessionStorageServiceImpl::initializeNewSession(QString dir)
115 {
116  this->clearPatientSilent();
117  dir = this->convertToValidFolderName(dir);
118  this->createPatientFolders(dir);
119  this->setActivePatient(dir);
120  this->save();
121  this->reportActivePatient();
122  this->writeRecentPatientData();
123 }
124 
125 QString SessionStorageServiceImpl::getXmlFileName() const
126 {
127  return "custusdoc.xml";
128 }
129 
137 {
138 public:
140  {
141 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
142  // set fixed hash seed
143  qSetGlobalQHashSeed(42);
144 #endif
145  mDoc = QDomDocument();
146  }
148  {
149 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
150  // reset hash seed with new random value.
151  qSetGlobalQHashSeed(-1);
152 #endif
153  }
154 
155  QDomDocument& doc() {return mDoc; }
156 private:
157  QDomDocument mDoc;
158 };
159 
161 {
162  if (!this->isValid())
163  return;
164 
165  //Gather all the information that needs to be saved
167  this->generateSaveDoc(doc.doc());
168 
169  QDomElement element = doc.doc().documentElement();
170  emit isSaving(element); // give all listeners a chance to add to the document
171 
172  QString filename = QDir(mActivePatientFolder).absoluteFilePath(this->getXmlFileName());
173  this->writeXmlFile(doc.doc(), filename);
174  report("Saved patient " + mActivePatientFolder);
175 }
176 
177 void SessionStorageServiceImpl::writeXmlFile(QDomDocument doc, QString filename)
178 {
179  QFile file(filename);
180  if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
181  {
182  QTextStream stream(&file);
183  stream << doc.toString(4);
184  file.close();
185  }
186  else
187  {
188  reportError("Could not open " + file.fileName() + " Error: " + file.errorString());
189  }
190 }
191 
193 {
194  this->clearPatientSilent();
195  this->reportActivePatient();
196  this->writeRecentPatientData();
197 }
198 
200 {
201  return !mActivePatientFolder.isEmpty() &&
202  (mActivePatientFolder != this->getNoPatientFolder());
203 }
204 
206 {
207  return mActivePatientFolder;
208 }
209 
210 void SessionStorageServiceImpl::reportActivePatient()
211 {
212  report("Set Active Patient: " + mActivePatientFolder);
213 }
214 
215 void SessionStorageServiceImpl::setActivePatient(const QString& activePatientFolder)
216 {
217  if (activePatientFolder == mActivePatientFolder)
218  return;
219 
220  mActivePatientFolder = activePatientFolder;
221 
222  emit sessionChanged();
223 }
224 
225 void SessionStorageServiceImpl::onSessionChanged()
226 {
227  reporter()->setLoggingFolder(this->getSubFolder("Logs/"));
228 }
229 
230 void SessionStorageServiceImpl::clearPatientSilent()
231 {
232  this->setActivePatient(this->getNoPatientFolder());
233  emit cleared();
234 }
235 
236 bool SessionStorageServiceImpl::isActivePatient(QString patient) const
237 {
238  return (patient == mActivePatientFolder);
239 }
240 
241 void SessionStorageServiceImpl::loadPatientSilent(QString choosenDir)
242 {
243  if (this->isActivePatient(choosenDir))
244  return;
245  this->clearPatientSilent();
246  if (choosenDir == QString::null)
247  return; // On cancel
248 
249  QString filename = QDir(choosenDir).absoluteFilePath(this->getXmlFileName());
250  QDomDocument doc = this->readXmlFile(filename);
251 
252  mActivePatientFolder = choosenDir; // must set path before emitting isLoading()
253 
254  if (!doc.isNull())
255  {
256  //Read the xml
257  QDomElement element = doc.documentElement();
258  emit isLoading(element);
259  emit isLoadingSecond(element);
260  }
261 
262  emit sessionChanged();
263 }
264 
265 QDomDocument SessionStorageServiceImpl::readXmlFile(QString filename)
266 {
267  QDomDocument retval;
268  QFile file(filename);
269  if (!file.open(QIODevice::ReadOnly))
270  {
271  reportError("Could not open XML file :" + file.fileName() + ".");
272  return QDomDocument();
273  }
274 
275  QString emsg;
276  int eline, ecolumn;
277  // Read the file
278  if (!retval.setContent(&file, false, &emsg, &eline, &ecolumn))
279  {
280  reportError("Could not parse XML file :" + file.fileName() + " because: " + emsg + "");
281  return QDomDocument();
282  }
283 
284  return retval;
285 }
286 
290 void SessionStorageServiceImpl::writeRecentPatientData()
291 {
292  settings()->setValue("startup/lastPatient", mActivePatientFolder);
293  settings()->setValue("startup/lastPatientSaveTime", QDateTime::currentDateTime().toString(timestampSecondsFormat()));
294 }
295 
296 void SessionStorageServiceImpl::generateSaveDoc(QDomDocument& doc)
297 {
298  doc.appendChild(doc.createProcessingInstruction("xml version =", "'1.0'"));
299 
300  QDomElement patientNode = doc.createElement("patient");
301 
302  // note: all nodes must be below <patient>. XML requires only one root node per file.
303  QDomElement versionName = doc.createElement("version_name");
304  versionName.appendChild(doc.createTextNode(this->getVersionName()));
305  patientNode.appendChild(versionName);
306 
307  QDomElement activePatientNode = doc.createElement("active_patient");
308  activePatientNode.appendChild(doc.createTextNode(mActivePatientFolder.toStdString().c_str()));
309  patientNode.appendChild(activePatientNode);
310  doc.appendChild(patientNode);
311 }
312 
313 QString SessionStorageServiceImpl::convertToValidFolderName(QString dir) const
314 {
315  if (!dir.endsWith(".cx3"))
316  dir.append(".cx3");
317  return dir;
318 }
319 
320 void SessionStorageServiceImpl::createPatientFolders(QString dir)
321 {
322  QDir().mkpath(dir);
323  QDir().mkpath(dir+"/Images");
324  QDir().mkpath(dir+"/Logs");
325  QDir().mkpath(dir+"/US_Acq");
326 }
327 
328 QString SessionStorageServiceImpl::getVersionName()
329 {
330  return QString("%1").arg(CustusX_VERSION_STRING);
331 }
332 
333 void SessionStorageServiceImpl::clearCache()
334 {
335 // std::cout << "DataLocations::getCachePath() " << DataLocations::getCachePath() << std::endl;
336  // clear the global cache used by app
338 }
339 
343 QString SessionStorageServiceImpl::getCommandLineStartupPatient()
344 {
345  int doLoad = QApplication::arguments().indexOf("--load");
346  if (doLoad < 0)
347  return "";
348  if (doLoad + 1 >= QApplication::arguments().size())
349  return "";
350 
351  QString folder = QApplication::arguments()[doLoad + 1];
352 
353  return folder;
354 }
355 
358 void SessionStorageServiceImpl::startupLoadPatient()
359 {
360  QString folder = this->getCommandLineStartupPatient();
361 
362  if (!folder.isEmpty())
363  {
364  report(QString("Startup Load [%1] from command line").arg(folder));
365  }
366 
367  if (folder.isEmpty() && settings()->value("Automation/autoLoadRecentPatient").toBool())
368  {
369  folder = settings()->value("startup/lastPatient").toString();
370  if(this->isValidSessionFolder(folder))
371  {
372  QDateTime lastSaveTime = QDateTime::fromString(settings()->value("startup/lastPatientSaveTime").toString(), timestampSecondsFormat());
373  double minsSinceLastSave = lastSaveTime.secsTo(QDateTime::currentDateTime())/60;
374  double autoLoadRecentPatientWithinHours = settings()->value("Automation/autoLoadRecentPatientWithinHours").toDouble();
375  int allowedMinsSinceLastSave = autoLoadRecentPatientWithinHours*60;
376  if (minsSinceLastSave > allowedMinsSinceLastSave) // if less than 8 hours, accept
377  {
378  report(
379  QString("Startup Load: Ignored recent patient because %1 hours since last save, limit is %2")
380  .arg(int(minsSinceLastSave/60))
381  .arg(int(allowedMinsSinceLastSave/60)));
382  folder = "";
383  }
384  if (!folder.isEmpty())
385  report(QString("Startup Load [%1] as recent patient").arg(folder));
386  }
387  else
388  {
389  report("Startup Load: Ignored recent patient because it is not valid anymore");
390  folder = "";
391  }
392 
393  }
394 
395  if (folder.isEmpty())
396  return;
397 
398  this->load(folder);
399 }
400 
401 } // namespace cx
void reportError(QString msg)
Definition: cxLogger.cpp:92
void isLoading(QDomElement &root)
emitted while loading a session. Xml storage is available, getRootFolder() is set to loaded value...
ReporterPtr reporter()
Definition: cxReporter.cpp:59
bool removeNonemptyDirRecursively(const QString &dirName)
void isLoadingSecond(QDomElement &root)
Emitted after the isLoading signal, to allow for structures that must be loaded after core structures...
virtual void save()
Save all application data to XML file.
virtual void load(QString dir)
load session from dir, or create new session in this location if none exist
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:87
QString timestampSecondsFormat()
Definition: cxTime.cpp:39
void sessionChanged()
emitted after change to a new session (new or loaded or cleared)
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:79
static QString getCachePath()
return path to a folder that is used during execution, will be cleared at start and stop...
QString getSubFolder(QString relative)
return and create a folder/path relative to root. Created if necessary.
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:42
void cleared()
emitted when session is cleared, before isLoading is called
void report(QString msg)
Definition: cxLogger.cpp:90
SessionStorageServiceImpl(ctkPluginContext *context)
void isSaving(QDomElement &root)
xml storage is available