CustusX  2023.01.05-dev+develop.0da12
An IGT application
cxToolConfigurationParser.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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
13 
14 #include <QFile>
15 #include <QDir>
16 #include <QFileInfo>
17 #include <QTextStream>
18 #include "cxLogger.h"
19 #include "cxTypeConversions.h"
20 #include "cxEnumConversion.h"
21 #include "cxDataLocations.h"
22 #include "cxProfile.h"
23 #include "cxFrame3D.h"
24 #include "cxTransformFile.h"
25 
26 namespace cx
27 {
28 
29 // names of necessary tags in the configuration file
30 #define CONFIG_TAG "configuration"
31 #define CONFIG_TRACKER_TAG "tracker"
32 #define CONFIG_TRACKER_TOOL_FILE "toolfile"
33 #define CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG "trackingsystemimplementation"
34 
35 // names of necessary attributes in the configuration file
36 #define TYPE_ATTRIBUTE "type"
37 #define CLINICAL_APP_ATTRIBUTE "clinical_app"
38 #define REFERENCE_ATTRIBUTE "reference"
39 #define OPENIGTLINK_TRANSFORM_ID_ATTRIBUTE "openigtlinktransformid"
40 #define OPENIGTLINK_IMAGE_ID_ATTRIBUTE "openigtlinkimageid"
41 #define OPENIGTLINK_APPLY_REF_TO_TOOLS_ATTRIBUTE "openigtlinkApplyRefToTools"
42 
43 ConfigurationFileParser::ConfigurationFileParser(QString absoluteConfigFilePath, QString loggingFolder) :
44  mConfigurationFilePath(absoluteConfigFilePath), mLoggingFolder(loggingFolder)
45 {
46  this->setConfigDocument(mConfigurationFilePath);
47 }
48 
50 {
51 }
52 
54 {
55  if (!this->isConfigFileValid())
56  return "";
57 
58  QDomNode configNode = mConfigureDoc.elementsByTagName(CONFIG_TAG).at(0);
59  QString retval = configNode.toElement().attribute(CLINICAL_APP_ATTRIBUTE);
60  return retval;
61 }
62 
64 {
65  QString retval;
66 
67  QDomNodeList trackingsystemImplementationNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG);
68  for (int i = 0; i < trackingsystemImplementationNodes.count(); ++i)
69  {
70  retval = trackingsystemImplementationNodes.at(i).toElement().attribute(TYPE_ATTRIBUTE);
71  }
72 
73  if (trackingsystemImplementationNodes.count() == 0)
74  {
75  //CX_LOG_DEBUG() << "Cannot find " << CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG << " tag. Selecting " << TRACKING_SYSTEM_IMPLEMENTATION_IGSTK;
76  retval = TRACKING_SYSTEM_IMPLEMENTATION_IGSTK;//Revert to igstk implementation for old config files
77  }
78  else if(trackingsystemImplementationNodes.count() > 1)
79  {
80  CX_LOG_ERROR() << "ConfigurationFileParser::getTrackingSystemImplementation(): Config file: " << mConfigurationFilePath
81  << " has more the one tracking system implementation. Only one is currently supported.";
82  }
83 
84  return retval;
85 }
86 
87 std::vector<ToolFileParser::TrackerInternalStructure> ConfigurationFileParser::getTrackers()
88 {
89  std::vector<ToolFileParser::TrackerInternalStructure> retval;
90 
91  if (!this->isConfigFileValid())
92  return retval;
93 
94  QDomNodeList trackerNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKER_TAG);
95  for (int i = 0; i < trackerNodes.count(); ++i)
96  {
98  QString trackerType = trackerNodes.at(i).toElement().attribute(TYPE_ATTRIBUTE);
99  internalStructure.mType = string2enum<TRACKING_SYSTEM>(trackerType);
100  internalStructure.mLoggingFolderName = mLoggingFolder;
101 
102  retval.push_back(internalStructure);
103  }
104 
105  if (retval.size() > 1)
106  reportError(
107  "Config file " + mConfigurationFilePath
108  + " has a invalid number of tracking systems, we only support 1 tracking system atm!");
109 
110  return retval;
111 }
112 
114 {
115  std::vector<QString> retval;
116 
117  if (!this->isConfigFileValid())
118  return retval;
119 
120  QDomNodeList toolFileNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKER_TOOL_FILE);
121  for (int i = 0; i < toolFileNodes.count(); ++i)
122  {
123  QString absoluteToolFilePath = this->getAbsoluteToolFilePath(toolFileNodes.at(i).toElement());
124  if (absoluteToolFilePath.isEmpty())
125  continue;
126 
127  retval.push_back(absoluteToolFilePath);
128  }
129 
130  return retval;
131 }
132 
134 {
135  QString retval;
136 
137  if (!this->isConfigFileValid())
138  return retval;
139 
140 // QFile configFile(mConfigurationFilePath);
141 // QString configFolderAbsolutePath = QFileInfo(configFile).dir().absolutePath()+"/";
142 // std::cout << "configFolderAbsolutePath " << configFolderAbsolutePath << std::endl;
143 
144  QDomNodeList toolFileNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKER_TOOL_FILE);
145  for (int i = 0; i < toolFileNodes.count(); ++i)
146  {
147  QString reference = toolFileNodes.at(i).toElement().attribute(REFERENCE_ATTRIBUTE);
148  if (reference.contains("yes", Qt::CaseInsensitive))
149  {
150 // std::cout << "Found yes..." << std::endl;
151  retval = this->getAbsoluteToolFilePath(toolFileNodes.at(i).toElement());
152  }
153  }
154  return retval;
155 }
156 
157 std::vector<ConfigurationFileParser::ToolStructure> ConfigurationFileParser::getToolListWithMetaInformation()
158 {
159  std::vector<ToolStructure> retval;
160 
161  if (!this->isConfigFileValid())
162  return retval;
163  bool applyRefToTools = this->getApplyRefToTools();
164 
165  QDomNodeList toolFileNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKER_TOOL_FILE);
166  for (int i = 0; i < toolFileNodes.count(); ++i)
167  {
168  ToolStructure toolStructure;
169  toolStructure.mAbsoluteToolFilePath = this->getAbsoluteToolFilePath(toolFileNodes.at(i).toElement());
170  toolStructure.mOpenIGTLinkTransformId = toolFileNodes.at(i).toElement().attribute(OPENIGTLINK_TRANSFORM_ID_ATTRIBUTE);
171  toolStructure.mOpenIGTLinkImageId = toolFileNodes.at(i).toElement().attribute(OPENIGTLINK_IMAGE_ID_ATTRIBUTE);
172  toolStructure.mApplyRefToTool = applyRefToTools;
173 
174  QString reference = toolFileNodes.at(i).toElement().attribute(REFERENCE_ATTRIBUTE);
175  if (reference.contains("yes", Qt::CaseInsensitive))
176  toolStructure.mReference = true;
177  else
178  toolStructure.mReference = false;
179  retval.push_back(toolStructure);
180  }
181  return retval;
182 }
183 
185 {
186  QString retval = DataLocations::getRootConfigPath() + "/tool/TEMPLATE_configuration.xml";
187  return retval;
188 }
189 
191 {
192  bool retval = false;
193  if (!this->isConfigFileValid())
194  return retval;
195 
196  QDomNodeList trackingsystemImplementationNodes = mConfigureDoc.elementsByTagName(CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG);
197  for (int i = 0; i < trackingsystemImplementationNodes.count(); ++i)
198  {
199  QString applyRef = trackingsystemImplementationNodes.at(i).toElement().attribute(OPENIGTLINK_APPLY_REF_TO_TOOLS_ATTRIBUTE);
200  if (applyRef.contains("yes", Qt::CaseInsensitive))
201  retval = true;
202  }
203  //Is it neccesary to check for number of tracking system implementations here?
204  //This is done in ConfigurationFileParser::getTrackingSystemImplementation()
205  if(trackingsystemImplementationNodes.count() > 1)
206  {
207  CX_LOG_ERROR() << "ConfigurationFileParser::getApplyRefToTools(): Config file: " << mConfigurationFilePath
208  << " has more the one tracking system implementation. Only one is currently supported.";
209  }
210  return retval;
211 }
212 
213 QString ConfigurationFileParser::convertToRelativeToolFilePath(QString configFilename, QString absoluteToolFilePath)
214 {
215  foreach (QString root, profile()->getAllRootConfigPaths())
216  {
217  QString configPath = getToolPathFromRoot(root);
218  if (!absoluteToolFilePath.contains(configPath))
219  continue;
220  absoluteToolFilePath.replace(configPath, "");
221  if (absoluteToolFilePath.startsWith("/"))
222  absoluteToolFilePath.remove(0, 1);
223  return absoluteToolFilePath;
224  }
225 
226  // file not in any of the standard locations: return absolute
227  return absoluteToolFilePath;
228 }
229 
230 QString ConfigurationFileParser::getToolPathFromRoot(QString root)
231 {
232  return root + "/tool/Tools/";
233 }
234 
236 {
237  QDomDocument doc;
238  doc.appendChild(doc.createProcessingInstruction("xml version =", "\"1.0\""));
239 
240  QDomElement configNode = doc.createElement(CONFIG_TAG);
241  configNode.setAttribute(CLINICAL_APP_ATTRIBUTE, config.mClinical_app);
242 
243  QDomElement trackingsystemImplementationNode = doc.createElement(CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG);
244  trackingsystemImplementationNode.setAttribute(TYPE_ATTRIBUTE, config.mTrackingSystemImplementation);
245 
246  if (config.mTrackingSystemImplementation.contains(TRACKING_SYSTEM_IMPLEMENTATION_IGTLINK, Qt::CaseInsensitive))
247  trackingsystemImplementationNode.setAttribute(OPENIGTLINK_APPLY_REF_TO_TOOLS_ATTRIBUTE, (config.mApplyRefToTools ? "yes" : "no"));
248 
249  configNode.appendChild(trackingsystemImplementationNode);
250 
251  TrackersAndToolsMap::iterator it1 = config.mTrackersAndTools.begin();
252  for (; it1 != config.mTrackersAndTools.end(); ++it1)
253  {
254  QString trackingSystemName = enum2string(it1->first);
255  if(trackingSystemName.isEmpty())
256  {
257  CX_LOG_WARNING() << "trackingSystemName is empty.";
258  trackingSystemName="";
259  }
260  QDomElement trackerTagNode = doc.createElement(CONFIG_TRACKER_TAG);
261  trackerTagNode.setAttribute(TYPE_ATTRIBUTE, trackingSystemName);
262 
263  ToolStructureVector::iterator it2 = it1->second.begin();
264  for (; it2 != it1->second.end(); ++it2)
265  {
266  QString absoluteToolFilePath = it2->mAbsoluteToolFilePath;
267  QString relativeToolFilePath = convertToRelativeToolFilePath(config.mFileName, absoluteToolFilePath);
268 
269  ToolFileParser toolparser(absoluteToolFilePath);
270  QString toolTrackerType = enum2string(toolparser.getTool()->mTrackerType);
271 
272  if (!trackingSystemName.contains(enum2string(toolparser.getTool()->mTrackerType), Qt::CaseInsensitive))
273  {
274  reportWarning("When saving configuration, skipping tool " + relativeToolFilePath + " of type "
275  + toolTrackerType + " because trackingSystemName is set to " + trackingSystemName);
276  continue;
277  }
278 
279  QDomElement toolFileNode = doc.createElement(CONFIG_TRACKER_TOOL_FILE);
280  toolFileNode.appendChild(doc.createTextNode(relativeToolFilePath));
281  createToolFileNode(it2, toolFileNode, toolparser);
282  trackerTagNode.appendChild(toolFileNode);
283  }
284  trackingsystemImplementationNode.appendChild(trackerTagNode);
285  }
286 
287  doc.appendChild(configNode);
288 
289  //write to file
290  QFile file(config.mFileName);
291  QDir().mkpath(QFileInfo(config.mFileName).absolutePath());
292 
293  if (!file.open(QIODevice::WriteOnly))
294  {
295  reportWarning("Could not open file " + file.fileName() + ", aborting writing of config.");
296  return;
297  }
298 
299  QTextStream stream(&file);
300  doc.save(stream, 4);
301  reportSuccess("Configuration file " + file.fileName() + " is written.");
302 }
303 
304 void ConfigurationFileParser::createToolFileNode(ToolStructureVector::iterator iter, QDomElement &toolFileNode, ToolFileParser &toolparser)
305 {
306  toolFileNode.setAttribute(REFERENCE_ATTRIBUTE, (iter->mReference ? "yes" : "no"));
307  //Use OpenIGTLink id values from tool config file if present
308  QString transformId = iter->mOpenIGTLinkTransformId;
309  QString imageId = iter->mOpenIGTLinkImageId;
310  //Otherwise use id values from tool file
311  if(transformId.isEmpty())
312  transformId = toolparser.getTool()->mOpenigtlinkTransformId;
313  if(imageId.isEmpty())
314  imageId = toolparser.getTool()->mOpenigtlinkImageId;
315  if(!transformId.isEmpty())
316  toolFileNode.setAttribute(OPENIGTLINK_TRANSFORM_ID_ATTRIBUTE, transformId);
317  if(!imageId.isEmpty())
318  toolFileNode.setAttribute(OPENIGTLINK_IMAGE_ID_ATTRIBUTE, imageId);
319 }
320 
321 void ConfigurationFileParser::setConfigDocument(QString configAbsoluteFilePath)
322 {
323  QFile configFile(configAbsoluteFilePath);
324  if (!configFile.exists())
325  {
326 // reportDebug("Configfile "+configAbsoluteFilePath+" does not exist.");
327  return;
328  }
329 
330  QString errorMessage;
331  int errorInLine = 0;
332  if (!mConfigureDoc.setContent(&configFile, &errorMessage, &errorInLine))
333  {
334  reportError("Could not set the xml content of the config file " + configAbsoluteFilePath);
335  CX_LOG_ERROR() << "Qt error message: " << errorMessage << " in line: " << errorInLine;
336  return;
337  }
338 }
339 
340 bool ConfigurationFileParser::isConfigFileValid()
341 {
342  //there can only be one config defined in every config.xml-file, that's why we say ...item(0)
343  QDomNode configNode = mConfigureDoc.elementsByTagName(CONFIG_TAG).item(0);
344  if (configNode.isNull())
345  {
346  //reportDebug("Configuration file \""+mConfigurationFilePath+"\" does not contain the tag <"+mConfigTag+">.");
347  return false;
348  }
349  return true;
350 }
351 
352 QString ConfigurationFileParser::findXmlFileWithDirNameInPath(QString path)
353 {
354  QDir dir(path);
355  QStringList filter;
356  filter << dir.dirName() + ".xml";
357  QStringList filepaths = dir.entryList(filter);
358  if (!filepaths.isEmpty())
359  return dir.absoluteFilePath(filter[0]);
360  return "";
361 }
362 
363 QString ConfigurationFileParser::searchForExistingToolFilePath(QString relativeToolFilePath)
364 {
365  // remove old-style paths (<= v3.7.0)
366  relativeToolFilePath.replace("../Tools/", "");
367 
368  foreach (QString root, profile()->getAllRootConfigPaths())
369  {
370  QString configPath = this->getToolPathFromRoot(root);
371  QFileInfo guess(configPath + "/" + relativeToolFilePath);
372  if (guess.exists())
373  return guess.canonicalFilePath();
374  }
375  return "";
376 }
377 
378 QString ConfigurationFileParser::getAbsoluteToolFilePath(QDomElement toolfileelement)
379 {
380  QString relativeToolFilePath = toolfileelement.text();
381  if (relativeToolFilePath.isEmpty())
382  return "";
383 
384  QString absoluteToolFilePath = this->searchForExistingToolFilePath(relativeToolFilePath);
385 
386  QFileInfo info(absoluteToolFilePath);
387  if (!info.exists())
388  reportError(QString("Tool file %1 in configuration %2 not found. Skipping.")
389  .arg(relativeToolFilePath)
390  .arg(mConfigurationFilePath));
391 
392  if (info.isDir())
393  absoluteToolFilePath = this->findXmlFileWithDirNameInPath(absoluteToolFilePath);
394  return absoluteToolFilePath;
395 }
396 //----------------------------------------------------------------------------------------------------------------------
397 
398 
399 //----------------------------------------------------------------------------------------------------------------------
400 
401 }//namespace cx
TrackersAndToolsMap mTrackersAndTools
the trackers and tools (relative path) that should be used in the config
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
QString mLoggingFolderName
path to where log should be saved
#define REFERENCE_ATTRIBUTE
void reportError(QString msg)
Definition: cxLogger.cpp:71
TRACKING_SYSTEM mType
the trackers type
QString mFileName
absolute path and filename for the new config file
ConfigurationFileParser(QString absoluteConfigFilePath, QString loggingFolder="")
#define OPENIGTLINK_APPLY_REF_TO_TOOLS_ATTRIBUTE
static void saveConfiguration(Configuration &config)
#define CONFIG_TRACKINGSYSTEMIMPLEMENTATION_TAG
const char * TRACKING_SYSTEM_IMPLEMENTATION_IGSTK
#define OPENIGTLINK_TRANSFORM_ID_ATTRIBUTE
#define CONFIG_TAG
virtual ToolInternalStructurePtr getTool()
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
#define TYPE_ATTRIBUTE
const char * TRACKING_SYSTEM_IMPLEMENTATION_IGTLINK
#define CX_LOG_ERROR
Definition: cxLogger.h:99
#define CONFIG_TRACKER_TOOL_FILE
void reportSuccess(QString msg)
Definition: cxLogger.cpp:72
static QString getRootConfigPath()
return path to root config folder. May be replaced with getExistingConfigPath()
std::vector< QString > getAbsoluteToolFilePaths()
#define CX_LOG_WARNING
Definition: cxLogger.h:98
QString mClinical_app
the clinical application this config is made for
std::vector< ToolFileParser::TrackerInternalStructure > getTrackers()
Class for reading the files defining a CustusX tool.
#define CLINICAL_APP_ATTRIBUTE
QString enum2string(const ENUM &val)
std::vector< ConfigurationFileParser::ToolStructure > getToolListWithMetaInformation()
#define CONFIG_TRACKER_TAG
#define OPENIGTLINK_IMAGE_ID_ATTRIBUTE
Namespace for all CustusX production code.