Fraxinus  17.12
An IGT application
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 #include "cxOrderedQDomDocument.h"
47 #include "cxXmlFileHandler.h"
48 
49 
50 namespace cx
51 {
52 
54 {
55  this->clearCache();
56  mActivePatientFolder = this->getNoPatientFolder();
57  connect(this, &SessionStorageServiceImpl::sessionChanged, this, &SessionStorageServiceImpl::onSessionChanged);
58 
59  QTimer::singleShot(100, this, SLOT(startupLoadPatient())); // make sure this is called after application state change
60 }
61 
63 {
64  this->clearCache();
65 }
66 
68 {
69  return false;
70 }
71 
73 {
74  bool valid = this->isValidSessionFolder(dir);
75  bool exists = this->folderExists(dir);
76 
77  if (valid)
78  {
79  this->loadSession(dir);
80  }
81  else if (!exists)
82  {
83  this->initializeNewSession(dir);
84  }
85  else
86  {
87  reportError(QString("Failed to initialize session in existing folder with no valid session: %1").arg(dir));
88  }
89 }
90 
91 QString SessionStorageServiceImpl::getNoPatientFolder() const
92 {
93  return DataLocations::getCachePath() + "/NoPatient";
94 }
95 
96 
97 bool SessionStorageServiceImpl::isValidSessionFolder(QString dir) const
98 {
99  QString filename = QDir(dir).absoluteFilePath(this->getXmlFileName());
100  return QFileInfo::exists(filename);
101 }
102 
103 bool SessionStorageServiceImpl::folderExists(QString dir) const
104 {
105  return QFileInfo(dir).exists() && QFileInfo(dir).isDir();
106 }
107 
108 void SessionStorageServiceImpl::loadSession(QString dir)
109 {
110  if (this->isActivePatient(dir))
111  return;
112  this->loadPatientSilent(dir);
113  this->reportActivePatient();
114  this->writeRecentPatientData();
115 }
116 
117 void SessionStorageServiceImpl::initializeNewSession(QString dir)
118 {
119  this->clearPatientSilent();
120  dir = this->convertToValidFolderName(dir);
121  this->createPatientFolders(dir);
122  this->setActivePatient(dir);
123  this->save();
124  this->reportActivePatient();
125  this->writeRecentPatientData();
126 }
127 
128 QString SessionStorageServiceImpl::getXmlFileName() const
129 {
130  return "custusdoc.xml";
131 }
132 
134 {
135  if (!this->isValid())
136  return;
137 
138  //Gather all the information that needs to be saved
140  this->generateSaveDoc(doc.doc());
141 
142  QDomElement element = doc.doc().documentElement();
143  emit isSaving(element); // give all listeners a chance to add to the document
144 
145  QString filename = QDir(mActivePatientFolder).absoluteFilePath(this->getXmlFileName());
146  XmlFileHandler::writeXmlFile(doc.doc(), filename);
147  report("Saved patient " + mActivePatientFolder);
148 }
149 
151 {
152  this->clearPatientSilent();
153  this->reportActivePatient();
154  this->writeRecentPatientData();
155 }
156 
158 {
159  return !mActivePatientFolder.isEmpty() &&
160  (mActivePatientFolder != this->getNoPatientFolder());
161 }
162 
164 {
165  return mActivePatientFolder;
166 }
167 
168 void SessionStorageServiceImpl::reportActivePatient()
169 {
170  report("Set Active Patient: " + mActivePatientFolder);
171 }
172 
173 void SessionStorageServiceImpl::setActivePatient(const QString& activePatientFolder)
174 {
175  if (activePatientFolder == mActivePatientFolder)
176  return;
177 
178  mActivePatientFolder = activePatientFolder;
179 
180  emit sessionChanged();
181 }
182 
183 void SessionStorageServiceImpl::onSessionChanged()
184 {
185  reporter()->setLoggingFolder(this->getSubFolder("Logs/"));
186 }
187 
188 void SessionStorageServiceImpl::clearPatientSilent()
189 {
190  this->setActivePatient(this->getNoPatientFolder());
191  emit cleared();
192 }
193 
194 bool SessionStorageServiceImpl::isActivePatient(QString patient) const
195 {
196  return (patient == mActivePatientFolder);
197 }
198 
199 void SessionStorageServiceImpl::loadPatientSilent(QString choosenDir)
200 {
201  if (this->isActivePatient(choosenDir))
202  return;
203  this->clearPatientSilent();
204  if (choosenDir == QString::null)
205  return; // On cancel
206 
207  QString filename = QDir(choosenDir).absoluteFilePath(this->getXmlFileName());
208  QDomDocument doc = XmlFileHandler::readXmlFile(filename);
209  mActivePatientFolder = choosenDir; // must set path before emitting isLoading()
210 
211  if (!doc.isNull())
212  {
213  //Read the xml
214  QDomElement element = doc.documentElement();
215  emit isLoading(element);
216  emit isLoadingSecond(element);
217  }
218 
219  emit sessionChanged();
220 }
221 
225 void SessionStorageServiceImpl::writeRecentPatientData()
226 {
227  settings()->setValue("startup/lastPatient", mActivePatientFolder);
228  settings()->setValue("startup/lastPatientSaveTime", QDateTime::currentDateTime().toString(timestampSecondsFormat()));
229 }
230 
231 void SessionStorageServiceImpl::generateSaveDoc(QDomDocument& doc)
232 {
233  doc.appendChild(doc.createProcessingInstruction("xml version =", "'1.0'"));
234 
235  QDomElement patientNode = doc.createElement("patient");
236 
237  // note: all nodes must be below <patient>. XML requires only one root node per file.
238  QDomElement versionName = doc.createElement("version_name");
239  versionName.appendChild(doc.createTextNode(this->getVersionName()));
240  patientNode.appendChild(versionName);
241 
242  QDomElement activePatientNode = doc.createElement("active_patient");
243  activePatientNode.appendChild(doc.createTextNode(mActivePatientFolder.toStdString().c_str()));
244  patientNode.appendChild(activePatientNode);
245  doc.appendChild(patientNode);
246 }
247 
248 QString SessionStorageServiceImpl::convertToValidFolderName(QString dir) const
249 {
250  if (!dir.endsWith(".cx3"))
251  dir.append(".cx3");
252  return dir;
253 }
254 
255 void SessionStorageServiceImpl::createPatientFolders(QString dir)
256 {
257  QDir().mkpath(dir);
258  QDir().mkpath(dir+"/Images");
259  QDir().mkpath(dir+"/Logs");
260  QDir().mkpath(dir+"/US_Acq");
261 }
262 
263 QString SessionStorageServiceImpl::getVersionName()
264 {
265  return QString("%1").arg(CustusX_VERSION_STRING);
266 }
267 
268 void SessionStorageServiceImpl::clearCache()
269 {
270 // std::cout << "DataLocations::getCachePath() " << DataLocations::getCachePath() << std::endl;
271  // clear the global cache used by app
273 }
274 
278 QString SessionStorageServiceImpl::getCommandLineStartupPatient()
279 {
280  int doLoad = QApplication::arguments().indexOf("--load");
281  if (doLoad < 0)
282  return "";
283  if (doLoad + 1 >= QApplication::arguments().size())
284  return "";
285 
286  QString folder = QApplication::arguments()[doLoad + 1];
287 
288  return folder;
289 }
290 
293 void SessionStorageServiceImpl::startupLoadPatient()
294 {
295  QString folder = this->getCommandLineStartupPatient();
296 
297  if (!folder.isEmpty())
298  {
299  report(QString("Startup Load [%1] from command line").arg(folder));
300  }
301 
302  if (folder.isEmpty() && settings()->value("Automation/autoLoadRecentPatient").toBool())
303  {
304  folder = settings()->value("startup/lastPatient").toString();
305  if(this->isValidSessionFolder(folder))
306  {
307  QDateTime lastSaveTime = QDateTime::fromString(settings()->value("startup/lastPatientSaveTime").toString(), timestampSecondsFormat());
308  double minsSinceLastSave = lastSaveTime.secsTo(QDateTime::currentDateTime())/60;
309  double autoLoadRecentPatientWithinHours = settings()->value("Automation/autoLoadRecentPatientWithinHours").toDouble();
310  int allowedMinsSinceLastSave = autoLoadRecentPatientWithinHours*60;
311  if (minsSinceLastSave > allowedMinsSinceLastSave) // if less than 8 hours, accept
312  {
313  report(
314  QString("Startup Load: Ignored recent patient because %1 hours since last save, limit is %2")
315  .arg(int(minsSinceLastSave/60))
316  .arg(int(allowedMinsSinceLastSave/60)));
317  folder = "";
318  }
319  if (!folder.isEmpty())
320  report(QString("Startup Load [%1] as recent patient").arg(folder));
321  }
322  else
323  {
324  report("Startup Load: Ignored recent patient because it is not valid anymore");
325  folder = "";
326  }
327 
328  }
329 
330  if (folder.isEmpty())
331  return;
332 
333  this->load(folder);
334 }
335 
336 } // namespace cx
static void writeXmlFile(QDomDocument &doc, QString &filename)
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
std::string toString(T const &value)
converts any type to a string
Definition: catch.hpp:755
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
static QDomDocument readXmlFile(QString &filename)
void report(QString msg)
Definition: cxLogger.cpp:90
SessionStorageServiceImpl(ctkPluginContext *context)
void isSaving(QDomElement &root)
xml storage is available
Namespace for all CustusX production code.