CustusX  15.8
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<IgstkTracker::InternalStructure> trackers = configParser.getTrackers();
155 
156  if (trackers.empty())
157  {
158  reportWarning("Failed to configure tracking.");
159  return;
160  }
161 
162  IgstkTracker::InternalStructure trackerStructure = trackers[0]; //we only support one tracker atm
163 
164  IgstkTool::InternalStructure referenceToolStructure;
165  std::vector<IgstkTool::InternalStructure> 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  IgstkTool::InternalStructure 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  cxToolPtr tool(new ToolUsingIGSTK(igstkTool));
214  if (tool->isValid())
215  {
216  mTools.push_back(tool);
217  if (tool->getProbe())
218  emit newProbe(tool);
219  }
220  else
221  reportWarning("Creation of the cxTool " + it->second->getUid() + " failed.");
222  }
223 
224  mState = Tool::tsCONFIGURED;
225 
226  reportSuccess("IGSTK Tracking Service Configured.");
227  emit configured();
228  emit stateChanged();
229 }
230 
231 //ToolPtr TrackingSystemIGSTKService::getReferenceTool()
232 //{
233 // return mReference;
234 //}
235 
236 void TrackingSystemIGSTKService::deconfigure()
237 {
238  if (!this->isConfigured())
239  return;
240 
241  if (this->isInitialized())
242  {
243  connect(this, SIGNAL(uninitialized()), this, SLOT(deconfigureAfterUninitializedSlot()));
244  this->uninitialize();
245  return;
246  }
247  mTools.clear();
248 
249  this->destroyTrackerThread();
250 
251 // this->setActiveTool(this->getManualTool()->getUid());
252 
253  mState = Tool::tsNONE;
254  emit deconfigured();
255  emit stateChanged();
256  report("IGSTK Tracking Service is deconfigured.");
257 }
258 
259 void TrackingSystemIGSTKService::initialize()
260 {
261  if (!this->isConfigured())
262  {
263  connect(this, SIGNAL(configured()), this, SLOT(initializeAfterConfigSlot()));
264  this->configure();
265  return;
266  }
267 
268  if (!this->isConfigured())
269  {
270  reportWarning("Please configure before trying to initialize.");
271  return;
272  }
273 
274 #ifndef WIN32
275  if (!this->createSymlink())
276  {
277  reportError("Initialization of tracking failed.");
278  return;
279  }
280 #endif
281 
282  if (mTrackerThread)
283  mTrackerThread->initialize(true);
284  else
285  reportError("Cannot initialize the tracking system because the tracking thread does not exist.");
286 }
287 
288 void TrackingSystemIGSTKService::uninitialize()
289 {
290  if (this->isTracking())
291  {
292  connect(this, SIGNAL(trackingStopped()), this, SLOT(uninitializeAfterTrackingStoppedSlot()));
293  this->stopTracking();
294  return;
295  }
296 
297  if (!this->isInitialized())
298  {
299  return;
300  }
301  if (mTrackerThread)
302  mTrackerThread->initialize(false);
303 }
304 
305 #ifndef WIN32
306 
311 bool TrackingSystemIGSTKService::createSymlink()
312 {
313  bool retval = true;
314  QFileInfo symlink = this->getSymlink();
315  QDir linkDir(symlink.absolutePath());
316  QString linkfile = symlink.absoluteFilePath();
317  ;
318 
319  if (!linkDir.exists())
320  {
321  reportError(
322  QString("Folder %1 does not exist. System is not properly installed.").arg(linkDir.path()));
323  return false;
324  }
325 
326  QDir devDir("/dev/");
327 
328  QStringList filters;
329  // cu* applies to Mac, ttyUSB applies to Linux
330  filters << "cu.usbserial*" << "cu.KeySerial*" << "serial" << "ttyUSB*"; //NOTE: only works with current hardware using aurora or polaris.
331 // filters << "cu.usbserial*" << "cu.KeySerial*" << "serial" << "serial/by-id/usb-NDI*" ; //NOTE: only works with current hardware using aurora or polaris.
332  QStringList files = devDir.entryList(filters, QDir::System);
333 
334  if (files.empty())
335  {
336  reportError(
337  QString("No usb connections found in /dev using filters %1").arg(filters.join(";")));
338  return false;
339  }
340  else
341  {
342  report(QString("Device files: %1").arg(files.join(",")));
343  if (files.size() > 1)
344  reportError(
345  QString("More than one tracker connected? Will only try to connect to: %1").arg(files[0]));
346  }
347 
348  QString device = devDir.filePath(files[0]);
349 // QString device = "/dev/serial/by-id/usb-NDI_NDI_Host_USB_Converter-if00-port0";
350 
351  QFile(linkfile).remove();
352  QFile devFile(device);
353  QFileInfo devFileInfo(device);
354  if (!devFileInfo.isWritable())
355  {
356  reportError(QString("Device %1 is not writable. Connection will fail.").arg(device));
357  retval = false;
358  }
359  // this call only succeeds if Custus is run as root.
360  bool val = devFile.link(linkfile);
361  if (!val)
362  {
363  reportError(
364  QString("Symlink %1 creation to device %2 failed with code %3").arg(linkfile).arg(device).arg(
365  devFile.error()));
366  retval = false;
367  }
368  else
369  {
370  report(QString("Created symlink %1 to device %2").arg(linkfile).arg(device));
371  }
372 
373  devFile.setPermissions(
374  QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::WriteGroup
375  | QFile::ExeGroup | QFile::ReadOther | QFile::WriteOther | QFile::ExeOther);
376  return retval;
377 }
378 
379 QFileInfo TrackingSystemIGSTKService::getSymlink() const
380 {
381  QString name("/Library/CustusX/igstk.links");
382  QDir linkDir(name);
383  QDir::root().mkdir(name); // only works if run with sudo
384  QString linkFile = linkDir.path() + "/cu.CustusX.dev0";
385  return QFileInfo(linkDir, linkFile);
386 }
387 
390 void TrackingSystemIGSTKService::cleanupSymlink()
391 {
392  report("Cleaning up symlinks.");
393  QFile(this->getSymlink().absoluteFilePath()).remove();
394 }
395 #endif //WIN32
396 
397 void TrackingSystemIGSTKService::startTracking()
398 {
399  if (!this->isInitialized())
400  {
401  connect(this, SIGNAL(initialized()), this, SLOT(startTrackingAfterInitSlot()));
402  this->initialize();
403  return;
404  }
405 
406  if (mTrackerThread)
407  mTrackerThread->track(true);
408 }
409 
410 void TrackingSystemIGSTKService::stopTracking()
411 {
412  if (!this->isTracking())
413  {
414  return;
415  }
416  if (mTrackerThread)
417  mTrackerThread->track(false);
418 }
419 
420 void TrackingSystemIGSTKService::setConfigurationFile(QString configurationFile)
421 {
422  if (configurationFile == mConfigurationFilePath)
423  return;
424 
425  if (this->isConfigured())
426  {
427  connect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
428  this->deconfigure();
429  }
430 
431  mConfigurationFilePath = configurationFile;
432 }
433 
435 {
436  if (mLoggingFolder == loggingFolder)
437  return;
438 
439  if (this->isConfigured())
440  {
441  connect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
442  this->deconfigure();
443  }
444 
445  mLoggingFolder = loggingFolder;
446 }
447 
448 void TrackingSystemIGSTKService::initializedSlot(bool value)
449 {
450  if (value)
451  {
452  mState = Tool::tsINITIALIZED;
453  reportSuccess("IGSTK Tracking Service is initialized.");
454  emit stateChanged();
455  emit initialized();
456  }
457  else
458  {
459  mState = Tool::tsCONFIGURED;
460  report("IGSTK Tracking Service is uninitialized.");
461  emit stateChanged();
462  emit uninitialized();
463  }
464 }
465 
466 void TrackingSystemIGSTKService::trackerTrackingSlot(bool value)
467 {
468  if (value)
469  {
470  mState = Tool::tsTRACKING;
471  reportSuccess("IGSTK Tracking Service started tracking.");
472  emit stateChanged();
473  emit trackingStarted();
474  }
475  else
476  {
477  mState = Tool::tsINITIALIZED;
478  reportSuccess("IGSTK Tracking Service stopped tracking.");
479  emit stateChanged();
480  emit trackingStopped();
481  }
482 }
483 
484 void TrackingSystemIGSTKService::startTrackingAfterInitSlot()
485 {
486  disconnect(this, SIGNAL(initialized()), this, SLOT(startTrackingAfterInitSlot()));
487  this->startTracking();
488 }
489 
490 void TrackingSystemIGSTKService::initializeAfterConfigSlot()
491 {
492  disconnect(this, SIGNAL(configured()), this, SLOT(initializeAfterConfigSlot()));
493  this->initialize();
494 }
495 
496 void TrackingSystemIGSTKService::uninitializeAfterTrackingStoppedSlot()
497 {
498  disconnect(this, SIGNAL(trackingStopped()), this, SLOT(uninitializeAfterTrackingStoppedSlot()));
499  this->uninitialize();
500 }
501 
502 void TrackingSystemIGSTKService::deconfigureAfterUninitializedSlot()
503 {
504  disconnect(this, SIGNAL(uninitialized()), this, SLOT(deconfigureAfterUninitializedSlot()));
505  this->deconfigure();
506 }
507 
508 void TrackingSystemIGSTKService::configureAfterDeconfigureSlot()
509 {
510  disconnect(this, SIGNAL(deconfigured()), this, SLOT(configureAfterDeconfigureSlot()));
511  this->configure();
512 }
513 
514 void TrackingSystemIGSTKService::globalConfigurationFileChangedSlot(QString key)
515 {
516  if (key == "toolConfigFile")
517  {
518  this->setConfigurationFile(profile()->getToolConfigFilePath());
519  }
520 }
521 
523 {
525  retval.reset(new TrackerConfigurationImpl());
526  return retval;
527 }
528 
529 void TrackingSystemIGSTKService::destroyTrackerThread()
530 {
531  if (mTrackerThread)
532  {
533  mTrackerThread->quit();
534  mTrackerThread->wait(2000);
535  if (mTrackerThread->isRunning())
536  {
537  mTrackerThread->terminate();
538  mTrackerThread->wait(); // forever or until dead thread
539  }
540  QObject::disconnect(mTrackerThread.get());
541  mTrackerThread.reset();
542  }
543 }
544 
545 } //namespace cx
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:169
void reportError(QString msg)
Definition: cxLogger.cpp:92
tsNONE
Not specified.
void uninitialized()
system is uninitialized
boost::shared_ptr< IgstkTool > IgstkToolPtr
Definition: cxIgstkTool.h:60
virtual TrackerConfigurationPtr getConfiguration()
Interface to a tool, i.e. a pointer, US probe or similar.
Definition: cxTool.h:69
virtual void setLoggingFolder(QString loggingFolder)
configured with basic info
Definition: cxTool.h:81
boost::shared_ptr< class TrackerConfiguration > TrackerConfigurationPtr
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
void trackingStarted()
system starts tracking
not available
Definition: cxTool.h:80
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:82
void trackingStopped()
system stops tracking
void report(QString msg)
Definition: cxLogger.cpp:90
void newProbe(const ToolPtr probe)
void initialized()
system is initialized
emitting tracking data
Definition: cxTool.h:83
void configured()
system is configured
boost::shared_ptr< ToolUsingIGSTK > cxToolPtr
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