NorMIT-nav  2023.01.05-dev+develop.0da12
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
78  ConfigurationFileParser configParser(mConfigurationFilePath, mLoggingFolder);
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  emit updateTrackingSystemImplementation(configParser.getTrackingSystemImplementation());
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
cx::reportSuccess
void reportSuccess(QString msg)
Definition: cxLogger.cpp:72
cxLogger.h
cxPositionStorageFile.h
cxPlaybackTime.h
cx::TrackingSystemIGSTKService::~TrackingSystemIGSTKService
virtual ~TrackingSystemIGSTKService()
Definition: cxTrackingSystemIGSTKService.cpp:54
cx::TRACKING_SYSTEM_IMPLEMENTATION_IGSTK
const char * TRACKING_SYSTEM_IMPLEMENTATION_IGSTK
Definition: cxDefinitions.cpp:16
cx::TrackingSystemIGSTKService::initialized
void initialized()
system is initialized
cx::TrackingSystemIGSTKService::getTools
virtual std::vector< ToolPtr > getTools()
Definition: cxTrackingSystemIGSTKService.cpp:59
cxIgstkTrackerThread.h
cx
Namespace for all CustusX production code.
Definition: cx_dev_group_definitions.h:13
cx::IgstkToolPtr
boost::shared_ptr< class IgstkTool > IgstkToolPtr
Definition: cxOpenIGTLinkTool.h:35
cx::TrackerConfigurationPtr
boost::shared_ptr< class TrackerConfiguration > TrackerConfigurationPtr
Definition: cxTrackerConfiguration.h:24
cx::reportDebug
void reportDebug(QString msg)
Definition: cxLogger.cpp:68
cx::report
void report(QString msg)
Definition: cxLogger.cpp:69
cx::TrackingSystemService::isInitialized
virtual bool isInitialized() const
Definition: cxTrackingSystemService.cpp:34
cxDummyTool.h
cxToolUsingIGSTK.h
cxTrackerConfigurationImpl.h
cx::TrackerConfigurationImpl
Definition: cxTrackerConfigurationImpl.h:26
cx::TrackingSystemIGSTKService::trackingStarted
void trackingStarted()
system starts tracking
cx::ToolUsingIGSTKPtr
boost::shared_ptr< ToolUsingIGSTK > ToolUsingIGSTKPtr
Definition: cxToolUsingIGSTK.h:125
cx::TrackingSystemIGSTKService::deconfigured
void deconfigured()
cx::TrackingSystemIGSTKService::setLoggingFolder
virtual void setLoggingFolder(QString loggingFolder)
Definition: cxTrackingSystemIGSTKService.cpp:365
cx::TrackingSystemService::stateChanged
void stateChanged()
Reset time synchronization. Used for resetting time synchronization of incoming timestamps in OpenIGT...
cxProfile.h
cx::TrackingSystemService::mConfigurationFilePath
QString mConfigurationFilePath
path to the configuration file
Definition: cxTrackingSystemService.h:68
cxXMLNodeWrapper.h
cx::TrackingSystemIGSTKService::configured
void configured()
system is configured
cx::TrackingSystemIGSTKService::getConfiguration
virtual TrackerConfigurationPtr getConfiguration()
Definition: cxTrackingSystemIGSTKService.cpp:453
cxIgstkTracker.h
cxTypeConversions.h
cx::TrackingSystemService::updateTrackingSystemImplementation
void updateTrackingSystemImplementation(QString trackingSystemImplementation)
cx::Tool::tsNONE
@ tsNONE
not available
Definition: cxTool.h:74
cx::Tool::State
State
Definition: cxTool.h:72
cx::TrackingSystemService::isTracking
virtual bool isTracking() const
Definition: cxTrackingSystemService.cpp:39
cxSettings.h
cxTrackingSystemIGSTKService.h
cx::TrackingSystemIGSTKService::setState
virtual void setState(const Tool::State val)
asynchronously request a state. Wait for signal stateChanged()
Definition: cxTrackingSystemIGSTKService.cpp:64
cx::TrackingSystemIGSTKService::trackingStopped
void trackingStopped()
system stops tracking
cxRegistrationTransform.h
cx::Tool::tsCONFIGURED
@ tsCONFIGURED
configured with basic info
Definition: cxTool.h:75
cx::profile
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:160
cx::TrackingSystemService::mLoggingFolder
QString mLoggingFolder
path to where logging should be saved
Definition: cxTrackingSystemService.h:69
cx::TrackingSystemService::mState
Tool::State mState
Definition: cxTrackingSystemService.h:67
cx::Tool::tsINITIALIZED
@ tsINITIALIZED
connected to hardware, if any, ready to use
Definition: cxTool.h:76
cxTime.h
cxDataLocations.h
cx::TrackingSystemService::internalSetState
void internalSetState(Tool::State val)
Definition: cxTrackingSystemService.cpp:70
CX_LOG_DEBUG
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
cx::TrackingSystemIGSTKService::uninitialized
void uninitialized()
system is uninitialized
cx::TrackingSystemIGSTKService::TrackingSystemIGSTKService
TrackingSystemIGSTKService()
Definition: cxTrackingSystemIGSTKService.cpp:47
cx::TrackingSystemService::isConfigured
virtual bool isConfigured() const
Definition: cxTrackingSystemService.cpp:29
cxToolConfigurationParser.h
cxTrackingPositionFilter.h
cx::reportError
void reportError(QString msg)
Definition: cxLogger.cpp:71
cx::Tool::tsTRACKING
@ tsTRACKING
emitting tracking data
Definition: cxTool.h:77
cxManualToolAdapter.h
cx::ToolFileParser::ToolInternalStructurePtr
boost::shared_ptr< ToolInternalStructure > ToolInternalStructurePtr
Definition: cxToolFileParser.h:99
cx::reportWarning
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
cx::settings
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:21
cxPlaybackTool.h