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