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