Fraxinus  18.10
An IGT application
cxTrackingSystemIGSTKService.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 
12 #define _USE_MATH_DEFINES
13 
15 
16 #include <QDir>
17 #include <QList>
18 #include <QMetaType>
19 #include <QFileInfo>
20 #include <vtkDoubleArray.h>
21 #include <QCoreApplication>
22 
24 #include "cxLogger.h"
25 #include "cxTypeConversions.h"
26 #include "cxPositionStorageFile.h"
27 #include "cxTime.h"
28 #include "cxEnumConverter.h"
29 #include "cxDummyTool.h"
30 #include "cxToolUsingIGSTK.h"
31 #include "cxIgstkTracker.h"
33 #include "cxManualToolAdapter.h"
34 #include "cxSettings.h"
35 #include "cxDataLocations.h"
36 #include "cxIgstkTrackerThread.h"
37 #include "cxPlaybackTool.h"
38 
39 #include "cxPlaybackTime.h"
41 #include "cxXMLNodeWrapper.h"
43 #include "cxProfile.h"
44 
45 namespace cx
46 {
47 
49 {
50  connect(settings(), SIGNAL(valueChangedFor(QString)), this, SLOT(globalConfigurationFileChangedSlot(QString)));
51  // initialize config file
52  this->setConfigurationFile(profile()->getToolConfigFilePath());
53 }
54 
56 {
57  this->destroyTrackerThread();
58 }
59 
61 {
62  return mTools;
63 }
64 
66 {
67  this->internalSetState(val);
68 }
69 
70 void TrackingSystemIGSTKService::configure()
71 {
72  if (mConfigurationFilePath.isEmpty() || !QFile::exists(mConfigurationFilePath))
73  {
74  reportWarning(QString("Configuration file [%1] is not valid, could not configure the toolmanager.").arg(mConfigurationFilePath));
75  return;
76  }
77 
78  //parse
80 
81  if(!configParser.getTrackingSystemImplementation().contains(TRACKING_SYSTEM_IMPLEMENTATION_IGSTK, Qt::CaseInsensitive))
82  {
83  CX_LOG_DEBUG() << "TrackingSystemIGSTKService::configure(): Not using IGSTK tracking.";
84  return;
85  }
86 
87  std::vector<ToolFileParser::TrackerInternalStructure> trackers = configParser.getTrackers();
88 
89  if (trackers.empty())
90  {
91  reportWarning("Failed to configure tracking.");
92  return;
93  }
94 
95  ToolFileParser::TrackerInternalStructure trackerStructure = trackers[0]; //we only support one tracker atm
96 
97  ToolFileParser::ToolInternalStructurePtr referenceToolStructure;
98  std::vector<ToolFileParser::ToolInternalStructurePtr> toolStructures;
99  QString referenceToolFile = configParser.getAbsoluteReferenceFilePath();
100  std::vector<QString> toolfiles = configParser.getAbsoluteToolFilePaths();
101  for (std::vector<QString>::iterator it = toolfiles.begin(); it != toolfiles.end(); ++it)
102  {
103  ToolFileParser toolParser(*it, mLoggingFolder);
104  ToolFileParser::ToolInternalStructurePtr internalTool = toolParser.getTool();
105  if ((*it) == referenceToolFile)
106  referenceToolStructure = internalTool;
107  else
108  toolStructures.push_back(internalTool);
109  }
110 
111  //new thread
112  mTrackerThread.reset(new IgstkTrackerThread(trackerStructure, toolStructures, referenceToolStructure));
113 
114  connect(mTrackerThread.get(), SIGNAL(configured(bool)), this, SLOT(trackerConfiguredSlot(bool)));
115  connect(mTrackerThread.get(), SIGNAL(initialized(bool)), this, SLOT(initializedSlot(bool)));
116  connect(mTrackerThread.get(), SIGNAL(tracking(bool)), this, SLOT(trackerTrackingSlot(bool)));
117  connect(mTrackerThread.get(), SIGNAL(error()), this, SLOT(uninitialize()));
118 
119  //start threads
120  if (mTrackerThread)
121  mTrackerThread->start();
122 }
123 
124 void TrackingSystemIGSTKService::trackerConfiguredSlot(bool on)
125 {
126  if (!on)
127  {
128  this->deconfigure();
129  return;
130  }
131 
132  if (!mTrackerThread)
133  {
134  reportDebug("Received a configured signal in ToolManager, but we don't have a mTrackerThread, this should never happen, contact programmer.");
135  return;
136  }
137 
138  //new all tools
139  mTools.clear();
140  std::map<QString, IgstkToolPtr> igstkTools = mTrackerThread->getTools();
141  IgstkToolPtr reference = mTrackerThread->getRefereceTool();
142  std::map<QString, IgstkToolPtr>::iterator it = igstkTools.begin();
143  for (; it != igstkTools.end(); ++it)
144  {
145  IgstkToolPtr igstkTool = it->second;
146  ToolUsingIGSTKPtr tool(new ToolUsingIGSTK(igstkTool));
147  if (tool->isValid())
148  {
149  mTools.push_back(tool);
150  }
151  else
152  reportWarning("Creation of the cxTool " + it->second->getUid() + " failed.");
153  }
154 
156 
157  reportSuccess("IGSTK Tracking Service Configured.");
158  emit configured();
159  emit stateChanged();
160 }
161 
162 //ToolPtr TrackingSystemIGSTKService::getReferenceTool()
163 //{
164 // return mReference;
165 //}
166 
167 void TrackingSystemIGSTKService::deconfigure()
168 {
169  if (!this->isConfigured())
170  return;
171 
172  if (this->isInitialized())
173  {
174  connect(this, SIGNAL(uninitialized()), this, SLOT(deconfigureAfterUninitializedSlot()));
175  this->uninitialize();
176  return;
177  }
178  mTools.clear();
179 
180  this->destroyTrackerThread();
181 
182 // this->setActiveTool(this->getManualTool()->getUid());
183 
185  emit deconfigured();
186  emit stateChanged();
187  report("IGSTK Tracking Service is deconfigured.");
188 }
189 
190 void TrackingSystemIGSTKService::initialize()
191 {
192  if (!this->isConfigured())
193  {
194  connect(this, SIGNAL(configured()), this, SLOT(initializeAfterConfigSlot()));
195  this->configure();
196  return;
197  }
198 
199  if (!this->isConfigured())
200  {
201  reportWarning("Please configure before trying to initialize.");
202  return;
203  }
204 
205 #ifndef WIN32
206  if (!this->createSymlink())
207  {
208  reportError("Initialization of tracking failed.");
209  return;
210  }
211 #endif
212 
213  if (mTrackerThread)
214  mTrackerThread->initialize(true);
215  else
216  reportError("Cannot initialize the tracking system because the tracking thread does not exist.");
217 }
218 
219 void TrackingSystemIGSTKService::uninitialize()
220 {
221  if (this->isTracking())
222  {
223  connect(this, SIGNAL(trackingStopped()), this, SLOT(uninitializeAfterTrackingStoppedSlot()));
224  this->stopTracking();
225  return;
226  }
227 
228  if (!this->isInitialized())
229  {
230  return;
231  }
232  if (mTrackerThread)
233  mTrackerThread->initialize(false);
234 }
235 
236 #ifndef WIN32
237 
242 bool TrackingSystemIGSTKService::createSymlink()
243 {
244  bool retval = true;
245  QFileInfo symlink = this->getSymlink();
246  QDir linkDir(symlink.absolutePath());
247  QString linkfile = symlink.absoluteFilePath();
248  ;
249 
250  if (!linkDir.exists())
251  {
252  reportError(
253  QString("Folder %1 does not exist. It is required to exist and be writable in order to connect to IGSTK. Create it and set the correct permission.").arg(linkDir.path()));
254  return false;
255  }
256 
257  QDir devDir("/dev/");
258 
259  QStringList filters;
260  // cu* applies to Mac, ttyUSB applies to Linux
261  filters << "cu.usbserial*" << "cu.KeySerial*" << "serial" << "ttyUSB*"; //NOTE: only works with current hardware using aurora or polaris.
262 // filters << "cu.usbserial*" << "cu.KeySerial*" << "serial" << "serial/by-id/usb-NDI*" ; //NOTE: only works with current hardware using aurora or polaris.
263  QStringList files = devDir.entryList(filters, QDir::System);
264 
265  if (files.empty())
266  {
267  reportError(
268  QString("No usb connections found in /dev using filters %1").arg(filters.join(";")));
269  return false;
270  }
271  else
272  {
273  report(QString("Device files: %1").arg(files.join(",")));
274  if (files.size() > 1)
275  reportError(
276  QString("More than one tracker connected? Will only try to connect to: %1").arg(files[0]));
277  }
278 
279  QString device = devDir.filePath(files[0]);
280 // QString device = "/dev/serial/by-id/usb-NDI_NDI_Host_USB_Converter-if00-port0";
281 
282  QFile(linkfile).remove();
283  QFile devFile(device);
284  QFileInfo devFileInfo(device);
285  if (!devFileInfo.isWritable())
286  {
287  reportError(QString("Device %1 is not writable. Connection will fail.").arg(device));
288  retval = false;
289  }
290  // this call only succeeds if Custus is run as root.
291  bool val = devFile.link(linkfile);
292  if (!val)
293  {
294  reportError(
295  QString("Symlink %1 creation to device %2 failed with code %3").arg(linkfile).arg(device).arg(
296  devFile.error()));
297  retval = false;
298  }
299  else
300  {
301  report(QString("Created symlink %1 to device %2").arg(linkfile).arg(device));
302  }
303 
304  devFile.setPermissions(
305  QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::WriteGroup
306  | QFile::ExeGroup | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
307  return retval;
308 }
309 
310 QFileInfo TrackingSystemIGSTKService::getSymlink() const
311 {
312  QString name("/Library/CustusX/igstk.links");
313  QDir linkDir(name);
314  QDir::root().mkdir(name); // only works if run with sudo
315  QString linkFile = linkDir.path() + "/cu.CustusX.dev0";
316  return QFileInfo(linkDir, linkFile);
317 }
318 
321 void TrackingSystemIGSTKService::cleanupSymlink()
322 {
323  report("Cleaning up symlinks.");
324  QFile(this->getSymlink().absoluteFilePath()).remove();
325 }
326 #endif //WIN32
327 
328 void TrackingSystemIGSTKService::startTracking()
329 {
330  if (!this->isInitialized())
331  {
332  connect(this, SIGNAL(initialized()), this, SLOT(startTrackingAfterInitSlot()));
333  this->initialize();
334  return;
335  }
336 
337  if (mTrackerThread)
338  mTrackerThread->track(true);
339 }
340 
341 void TrackingSystemIGSTKService::stopTracking()
342 {
343  if (!this->isTracking())
344  {
345  return;
346  }
347  if (mTrackerThread)
348  mTrackerThread->track(false);
349 }
350 
351 void TrackingSystemIGSTKService::setConfigurationFile(QString configurationFile)
352 {
353  if (configurationFile == mConfigurationFilePath)
354  return;
355 
356  if (this->isConfigured())
357  {
358  connect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
359  this->deconfigure();
360  }
361 
362  mConfigurationFilePath = configurationFile;
363 }
364 
366 {
367  if (mLoggingFolder == loggingFolder)
368  return;
369 
370  if (this->isConfigured())
371  {
372  connect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
373  this->deconfigure();
374  }
375 
376  mLoggingFolder = loggingFolder;
377 }
378 
379 void TrackingSystemIGSTKService::initializedSlot(bool value)
380 {
381  if (value)
382  {
384  reportSuccess("IGSTK Tracking Service is initialized.");
385  emit stateChanged();
386  emit initialized();
387  }
388  else
389  {
391  report("IGSTK Tracking Service is uninitialized.");
392  emit stateChanged();
393  emit uninitialized();
394  }
395 }
396 
397 void TrackingSystemIGSTKService::trackerTrackingSlot(bool value)
398 {
399  if (value)
400  {
402  reportSuccess("IGSTK Tracking Service started tracking.");
403  emit stateChanged();
404  emit trackingStarted();
405  }
406  else
407  {
409  reportSuccess("IGSTK Tracking Service stopped tracking.");
410  emit stateChanged();
411  emit trackingStopped();
412  }
413 }
414 
415 void TrackingSystemIGSTKService::startTrackingAfterInitSlot()
416 {
417  disconnect(this, SIGNAL(initialized()), this, SLOT(startTrackingAfterInitSlot()));
418  this->startTracking();
419 }
420 
421 void TrackingSystemIGSTKService::initializeAfterConfigSlot()
422 {
423  disconnect(this, SIGNAL(configured()), this, SLOT(initializeAfterConfigSlot()));
424  this->initialize();
425 }
426 
427 void TrackingSystemIGSTKService::uninitializeAfterTrackingStoppedSlot()
428 {
429  disconnect(this, SIGNAL(trackingStopped()), this, SLOT(uninitializeAfterTrackingStoppedSlot()));
430  this->uninitialize();
431 }
432 
433 void TrackingSystemIGSTKService::deconfigureAfterUninitializedSlot()
434 {
435  disconnect(this, SIGNAL(uninitialized()), this, SLOT(deconfigureAfterUninitializedSlot()));
436  this->deconfigure();
437 }
438 
439 void TrackingSystemIGSTKService::configureAfterDeconfigureSlot()
440 {
441  disconnect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
442  this->configure();
443 }
444 
445 void TrackingSystemIGSTKService::globalConfigurationFileChangedSlot(QString key)
446 {
447  if (key == "toolConfigFile")
448  {
449  this->setConfigurationFile(profile()->getToolConfigFilePath());
450  }
451 }
452 
454 {
456  retval.reset(new TrackerConfigurationImpl());
457  retval->setTrackingSystemImplementation(TRACKING_SYSTEM_IMPLEMENTATION_IGSTK);
458  return retval;
459 }
460 
461 void TrackingSystemIGSTKService::destroyTrackerThread()
462 {
463  if (mTrackerThread)
464  {
465  mTrackerThread->quit();
466  mTrackerThread->wait(2000);
467  if (mTrackerThread->isRunning())
468  {
469  mTrackerThread->terminate();
470  mTrackerThread->wait(); // forever or until dead thread
471  }
472  QObject::disconnect(mTrackerThread.get());
473  mTrackerThread.reset();
474  }
475 }
476 
477 } //namespace cx
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
boost::shared_ptr< ToolInternalStructure > ToolInternalStructurePtr
void reportError(QString msg)
Definition: cxLogger.cpp:71
void uninitialized()
system is uninitialized
virtual TrackerConfigurationPtr getConfiguration()
boost::shared_ptr< class IgstkTool > IgstkToolPtr
QString mConfigurationFilePath
path to the configuration file
Class representing the tools a navigation system can recognize.
virtual void setLoggingFolder(QString loggingFolder)
configured with basic info
Definition: cxTool.h:75
boost::shared_ptr< class TrackerConfiguration > TrackerConfigurationPtr
#define TRACKING_SYSTEM_IMPLEMENTATION_IGSTK
Definition: cxDefinitions.h:25
QString mLoggingFolder
path to where logging should be saved
virtual ToolInternalStructurePtr getTool()
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
void trackingStarted()
system starts tracking
Class for reading the files defining a CustusX tool.
not available
Definition: cxTool.h:74
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
virtual bool isInitialized() const
void reportSuccess(QString msg)
Definition: cxLogger.cpp:72
connected to hardware, if any, ready to use
Definition: cxTool.h:76
void trackingStopped()
system stops tracking
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
void report(QString msg)
Definition: cxLogger.cpp:69
std::vector< QString > getAbsoluteToolFilePaths()
virtual bool isConfigured() const
std::vector< ToolFileParser::TrackerInternalStructure > getTrackers()
void initialized()
system is initialized
emitting tracking data
Definition: cxTool.h:77
Thread containing all of IGSTK.
Class for reading the files defining a CustusX tool.
void configured()
system is configured
boost::shared_ptr< ToolUsingIGSTK > ToolUsingIGSTKPtr
void internalSetState(Tool::State val)
virtual std::vector< ToolPtr > getTools()
virtual void setState(const Tool::State val)
asynchronously request a state. Wait for signal stateChanged()
void reportDebug(QString msg)
Definition: cxLogger.cpp:68
Namespace for all CustusX production code.