Fraxinus  16.5.0-fx-rc9
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 
131 {
132  if (!this->isValid())
133  return;
134 
135  //Gather all the information that needs to be saved
136  QDomDocument doc;
137  this->generateSaveDoc(doc);
138 
139  QDomElement element = doc.documentElement();
140  emit isSaving(element); // give all listeners a chance to add to the document
141 
142  QString filename = QDir(mActivePatientFolder).absoluteFilePath(this->getXmlFileName());
143  this->writeXmlFile(doc, filename);
144 
145  report("Saved patient " + mActivePatientFolder);
146 }
147 
148 void SessionStorageServiceImpl::writeXmlFile(QDomDocument doc, QString filename)
149 {
150  QFile file(filename);
151  if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
152  {
153  QTextStream stream(&file);
154  stream << doc.toString(4);
155  file.close();
156  }
157  else
158  {
159  reportError("Could not open " + file.fileName() + " Error: " + file.errorString());
160  }
161 }
162 
164 {
165  this->clearPatientSilent();
166  this->reportActivePatient();
167  this->writeRecentPatientData();
168 }
169 
171 {
172  return !mActivePatientFolder.isEmpty() &&
173  (mActivePatientFolder != this->getNoPatientFolder());
174 }
175 
177 {
178  return mActivePatientFolder;
179 }
180 
181 void SessionStorageServiceImpl::reportActivePatient()
182 {
183  report("Set Active Patient: " + mActivePatientFolder);
184 }
185 
186 void SessionStorageServiceImpl::setActivePatient(const QString& activePatientFolder)
187 {
188  if (activePatientFolder == mActivePatientFolder)
189  return;
190 
191  mActivePatientFolder = activePatientFolder;
192 
193  emit sessionChanged();
194 }
195 
196 void SessionStorageServiceImpl::onSessionChanged()
197 {
198  reporter()->setLoggingFolder(this->getSubFolder("Logs/"));
199 }
200 
201 void SessionStorageServiceImpl::clearPatientSilent()
202 {
203  this->setActivePatient(this->getNoPatientFolder());
204  emit cleared();
205 }
206 
207 bool SessionStorageServiceImpl::isActivePatient(QString patient) const
208 {
209  return (patient == mActivePatientFolder);
210 }
211 
212 void SessionStorageServiceImpl::loadPatientSilent(QString choosenDir)
213 {
214  if (this->isActivePatient(choosenDir))
215  return;
216  this->clearPatientSilent();
217  if (choosenDir == QString::null)
218  return; // On cancel
219 
220  QString filename = QDir(choosenDir).absoluteFilePath(this->getXmlFileName());
221  QDomDocument doc = this->readXmlFile(filename);
222 
223  mActivePatientFolder = choosenDir; // must set path before emitting isLoading()
224 
225  if (!doc.isNull())
226  {
227  //Read the xml
228  QDomElement element = doc.documentElement();
229  emit isLoading(element);
230  emit isLoadingSecond(element);
231  }
232 
233  emit sessionChanged();
234 }
235 
236 QDomDocument SessionStorageServiceImpl::readXmlFile(QString filename)
237 {
238  QDomDocument retval;
239  QFile file(filename);
240  if (!file.open(QIODevice::ReadOnly))
241  {
242  reportError("Could not open XML file :" + file.fileName() + ".");
243  return QDomDocument();
244  }
245 
246  QString emsg;
247  int eline, ecolumn;
248  // Read the file
249  if (!retval.setContent(&file, false, &emsg, &eline, &ecolumn))
250  {
251  reportError("Could not parse XML file :" + file.fileName() + " because: " + emsg + "");
252  return QDomDocument();
253  }
254 
255  return retval;
256 }
257 
261 void SessionStorageServiceImpl::writeRecentPatientData()
262 {
263  settings()->setValue("startup/lastPatient", mActivePatientFolder);
264  settings()->setValue("startup/lastPatientSaveTime", QDateTime::currentDateTime().toString(timestampSecondsFormat()));
265 }
266 
267 void SessionStorageServiceImpl::generateSaveDoc(QDomDocument& doc)
268 {
269  doc.appendChild(doc.createProcessingInstruction("xml version =", "'1.0'"));
270 
271  QDomElement patientNode = doc.createElement("patient");
272 
273  // note: all nodes must be below <patient>. XML requires only one root node per file.
274  QDomElement versionName = doc.createElement("version_name");
275  versionName.appendChild(doc.createTextNode(this->getVersionName()));
276  patientNode.appendChild(versionName);
277 
278  QDomElement activePatientNode = doc.createElement("active_patient");
279  activePatientNode.appendChild(doc.createTextNode(mActivePatientFolder.toStdString().c_str()));
280  patientNode.appendChild(activePatientNode);
281  doc.appendChild(patientNode);
282 }
283 
284 QString SessionStorageServiceImpl::convertToValidFolderName(QString dir) const
285 {
286  if (!dir.endsWith(".cx3"))
287  dir.append(".cx3");
288  return dir;
289 }
290 
291 void SessionStorageServiceImpl::createPatientFolders(QString dir)
292 {
293  QDir().mkpath(dir);
294  QDir().mkpath(dir+"/Images");
295  QDir().mkpath(dir+"/Logs");
296  QDir().mkpath(dir+"/US_Acq");
297 }
298 
299 QString SessionStorageServiceImpl::getVersionName()
300 {
301  return QString("%1").arg(CustusX_VERSION_STRING);
302 }
303 
304 void SessionStorageServiceImpl::clearCache()
305 {
306 // std::cout << "DataLocations::getCachePath() " << DataLocations::getCachePath() << std::endl;
307  // clear the global cache used by app
309 }
310 
314 QString SessionStorageServiceImpl::getCommandLineStartupPatient()
315 {
316  int doLoad = QApplication::arguments().indexOf("--load");
317  if (doLoad < 0)
318  return "";
319  if (doLoad + 1 >= QApplication::arguments().size())
320  return "";
321 
322  QString folder = QApplication::arguments()[doLoad + 1];
323 
324  return folder;
325 }
326 
329 void SessionStorageServiceImpl::startupLoadPatient()
330 {
331  QString folder = this->getCommandLineStartupPatient();
332 
333  if (!folder.isEmpty())
334  {
335  report(QString("Startup Load [%1] from command line").arg(folder));
336  }
337 
338  if (folder.isEmpty() && settings()->value("Automation/autoLoadRecentPatient").toBool())
339  {
340  folder = settings()->value("startup/lastPatient").toString();
341  if(this->isValidSessionFolder(folder))
342  {
343  QDateTime lastSaveTime = QDateTime::fromString(settings()->value("startup/lastPatientSaveTime").toString(), timestampSecondsFormat());
344  double minsSinceLastSave = lastSaveTime.secsTo(QDateTime::currentDateTime())/60;
345  double autoLoadRecentPatientWithinHours = settings()->value("Automation/autoLoadRecentPatientWithinHours").toDouble();
346  int allowedMinsSinceLastSave = autoLoadRecentPatientWithinHours*60;
347  if (minsSinceLastSave > allowedMinsSinceLastSave) // if less than 8 hours, accept
348  {
349  report(
350  QString("Startup Load: Ignored recent patient because %1 hours since last save, limit is %2")
351  .arg(int(minsSinceLastSave/60))
352  .arg(int(allowedMinsSinceLastSave/60)));
353  folder = "";
354  }
355  if (!folder.isEmpty())
356  report(QString("Startup Load [%1] as recent patient").arg(folder));
357  }
358  else
359  {
360  report("Startup Load: Ignored recent patient because it is not valid anymore");
361  folder = "";
362  }
363 
364  }
365 
366  if (folder.isEmpty())
367  return;
368 
369  this->load(folder);
370 }
371 
372 } // 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