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