PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusStealthLinkTracker.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 
8 #include "PlusConfigure.h"
10 #include "StealthLink/StealthLink.h"
11 
12 #include "vtkDICOMImageReader.h"
13 #include "vtkDirectory.h"
14 #include "vtkImageData.h"
15 #include "vtkImageFlip.h"
16 #include "vtkMath.h"
17 #include "vtkMatrix4x4.h"
18 #include "vtkPlusChannel.h"
19 #include "vtkPlusDataSource.h"
20 #include "vtkIGSIOTransformRepository.h"
21 
22 #include <iostream>
23 
24 static const int MAX_DEVICE_ID_LENGTH = 15; //for OpenIGTLink message sending purposes, the image id will be created off of device id. It is better for it to be short
25 
26 // Class for variables that are shared between the main thread and InternalUpdate thread
27 class vtkPlusStealthLinkTracker::vtkInternalShared
28 {
29 public:
30  vtkInternalShared(vtkPlusStealthLinkTracker* external)
31  : StealthLinkServer(NULL)
32  {
33  this->ExamIjkToRpiTransform = vtkSmartPointer<vtkMatrix4x4>::New();
34  this->ExamIjkToRasTransform = vtkSmartPointer<vtkMatrix4x4>::New();
35 
36  this->StealthLinkServerMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
37  this->ExamIjkToRpiMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
38  this->ExamIjkToRasMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
39  this->ExamValidMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
40  this->CurrentExamMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
41  this->CurrentRegistrationMutex = vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New();
42  }
43 
45  ~vtkInternalShared()
46  {
47  delete this->StealthLinkServer;
48  this->StealthLinkServer = NULL;
49  this->External = NULL;
50  }
51  // Begin - Thread Safe Set and Get Functions
52  // IjkToExamRpi Set and Get
53  void SetExamIjkToRpiTransformMatrix(vtkMatrix4x4* examIjkToRpiTransform)
54  {
55  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamIjkToRpiMutex);
56  this->ExamIjkToRpiTransform = examIjkToRpiTransform;
57  }
58 
59  void GetExamIjkToRpiTransformMatrix(vtkMatrix4x4* examIjkToRpiTransform)
60  {
61  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamIjkToRpiMutex);
62  examIjkToRpiTransform->DeepCopy(this->ExamIjkToRpiTransform);
63  }
64 
65  // IjkToRas Set and Get
66  void SetExamIjkToRasTransformMatrix(vtkMatrix4x4* examIjkToRasTransform)
67  {
68  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamIjkToRasMutex);
69  this->ExamIjkToRasTransform = examIjkToRasTransform;
70  }
71 
72  void GetExamIjkToRasTransformMatrix(vtkMatrix4x4* examIjkToRasTransform)
73  {
74  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamIjkToRasMutex);
75  examIjkToRasTransform->DeepCopy(this->ExamIjkToRasTransform);
76  }
77 
78  // ExamValid Set and Get
79  void SetExamValid(bool examValid)
80  {
81  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamValidMutex);
82  this->ExamValid = examValid;
83  }
84 
85  bool GetExamValid()
86  {
87  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->ExamValidMutex);
88  return this->ExamValid;
89  }
90 
91  //----------------------------------------------------------------------------
92  // Get for Navigation Data. It is called each time internalupdate is called
93  PlusStatus GetCurrentNavigationData(MNavStealthLink::NavData& navData)
94  {
95  MNavStealthLink::Error err;
96  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
97  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
98  if (!this->StealthLinkServer->get(navData, myDateTime, err))
99  {
100  LOG_ERROR(" Failed to acquire the navigation data from StealthLink Server: " << err.reason() << " " << err.what() << "\n");
101  return PLUS_FAIL;
102  }
103  return PLUS_SUCCESS;
104  }
105 
106  //----------------------------------------------------------------------------
107  // Get for Instrument Data. It is called each time internalupdate is called for any additional instruments that need to be tracked
108  PlusStatus GetInstrumentData(MNavStealthLink::Instrument& instrument, const std::string toolname)
109  {
110  MNavStealthLink::Error err;
111  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
112  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
113  if (!this->StealthLinkServer->get(toolname, instrument, myDateTime, err))
114  {
115  LOG_ERROR(" Failed to acquire the instrument data from StealthLink Server: " << err.reason() << " " << err.what() << "\n");
116  return PLUS_FAIL;
117  }
118  return PLUS_SUCCESS;
119  }
120 
121  //----------------------------------------------------------------------------
122  void GetCurrentExam(MNavStealthLink::Exam& exam)
123  {
124  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->CurrentExamMutex);
125  exam = this->CurrentExam;
126  }
127 
128  //----------------------------------------------------------------------------
129  void GetCurrentRegistration(MNavStealthLink::Registration& registration) // TODO make it thread safe
130  {
131  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->CurrentRegistrationMutex);
132  registration = this->CurrentRegistration;
133  }
134 
135  //----------------------------------------------------------------------------
136  //Port names that are present on the server
137  PlusStatus GetValidToolPortNames(std::vector<std::string>& validToolPortNames)
138  {
139  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
140  MNavStealthLink::Error err;
141  MNavStealthLink::InstrumentNameList instrumentNameList;
142  // Get the instrument list
143  if (!this->StealthLinkServer->get(instrumentNameList, err))
144  {
145  LOG_ERROR(" Could not retrieve the instument name list: " << err.reason() << std::endl);
146  return PLUS_FAIL;
147  }
148  MNavStealthLink::FrameNameList frameNameList;
149  // Get the frame name list
150  if (!this->StealthLinkServer->get(frameNameList, err))
151  {
152  LOG_ERROR(" Could not retrieve the instument name list: " << err.reason() << std::endl);
153  return PLUS_FAIL;
154  }
155  for (MNavStealthLink::InstrumentNameList::iterator instrumentNameIterator = instrumentNameList.begin(); instrumentNameIterator != instrumentNameList.end(); instrumentNameIterator++)
156  {
157  validToolPortNames.push_back(*instrumentNameIterator);
158  }
159  for (MNavStealthLink::FrameNameList::iterator frameNameIterator = frameNameList.begin(); frameNameIterator != frameNameList.end(); frameNameIterator++)
160  {
161  validToolPortNames.push_back(*frameNameIterator);
162  }
163  validToolPortNames.push_back(vtkInternalShared::GetRasRegistrationToolName());
164  return PLUS_SUCCESS;
165  }
166 
167  //----------------------------------------------------------------------------
168  // Get for current exam. The exam is only acquired from the server when GET_IMGMETA, GET_IMAGE or GET_EXAM_DATA commands are requested. This function will make sure that different threads
169  // do not modify the exam at the same time. Use GetCurrentExam function everytime the current exam is used.
170  PlusStatus UpdateCurrentExam()
171  {
172  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->CurrentExamMutex);
173  MNavStealthLink::Error err;
174  {
175  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
176  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
177  if (!this->StealthLinkServer->get(this->CurrentExam, myDateTime, err))
178  {
179  LOG_ERROR(" Failed to acquire the current exam: " << err.what() << "\n");
180  return PLUS_FAIL;
181  }
182  }
183  return PLUS_SUCCESS;
184  }
185 
186  //----------------------------------------------------------------------------
187  // Get for current registration. The registration is only acquired from the server when GET_IMAGE or GET_EXAM_DATA commands are requested. This function will make sure that different threads
188  // do not modify the registration at the same time. Use GetCurrentRegistration function everytime the current registration is used.
189  PlusStatus UpdateCurrentRegistration(bool imageTransferRequiresPatientRegistration)
190  {
191  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->CurrentRegistrationMutex);
192  MNavStealthLink::Error err;
193  {
194  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
195  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
196  if (!this->StealthLinkServer->get(this->CurrentRegistration, myDateTime, err))
197  {
198  if (imageTransferRequiresPatientRegistration)
199  {
200  LOG_ERROR(" Failed to acquire the current registration: " << err.what() << "\n");
201  return PLUS_FAIL;
202  }
203  else
204  {
205  LOG_WARNING(" Failed to acquire the current registration: " << err.what() << "\n Using default registration");
206  }
207  }
208  }
209  return PLUS_SUCCESS;
210  }
211 
212  //---------------------------------------------------------------------------
213  bool IsStealthServerConnected()
214  {
215  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
216  if (this->StealthLinkServer == NULL)
217  {
218  return false;
219  }
220  return true;
221  }
222 
223  //----------------------------------------------------------------------------
224  PlusStatus GetSdkVersion(std::string& version)
225  {
226  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
227  MNavStealthLink::Version serverVersion;
228  MNavStealthLink::Error err;
229  {
230  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
231  if (!this->StealthLinkServer->get(serverVersion, myDateTime, err))
232  {
233  LOG_ERROR("Failed to acquire the version of the StealthLinkServer " << err.reason() << "\n");
234  return PLUS_FAIL;
235  }
236  }
237  version = (unsigned) serverVersion.major + (unsigned) serverVersion.minor;
238  return PLUS_SUCCESS;
239  }
240 
241  //----------------------------------------------------------------------------
242  PlusStatus GetExamNameList(MNavStealthLink::ExamNameList& examNameList)
243  {
244  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
245  MNavStealthLink::Error err;
246  MNavStealthLink::DateTime myDateTime = this->StealthLinkServer->getServerTime();
247  if (!this->StealthLinkServer->get(examNameList, myDateTime, err))
248  {
249  LOG_ERROR("Failed to acquire the version of the StealthLinkServer " << err.reason() << "\n");
250  return PLUS_FAIL;
251  }
252  return PLUS_SUCCESS;
253  }
254 
255  //--------------------------------------------------------------------------
256  PlusStatus ConnectToStealthStation(std::string serverAddress, std::string serverPort)
257  {
258  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
259  delete this->StealthLinkServer;
260  this->StealthLinkServer = NULL;
261  this->StealthLinkServer = new MNavStealthLink::StealthServer(serverAddress, serverPort);
262 
263  MNavStealthLink::Error error;
264  if (!this->StealthLinkServer->connect(error))
265  {
266  LOG_ERROR(" Failed to connect to Stealth server application on host: " << error.what() << "\n");
267  return PLUS_FAIL;
268  }
269  return PLUS_SUCCESS;
270  }
271 
272  //-------------------------------------------------------------------------
273  PlusStatus IsLocalizerConnected(bool& connected)
274  {
275  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
276  MNavStealthLink::LocalizerInfo localizerInfo;
277  MNavStealthLink::Error err;
278  if (!this->StealthLinkServer->get(localizerInfo, err))
279  {
280  LOG_ERROR("Cannot retrieve the localizer info " << err.reason() << "\n");
281  connected = false;
282  return PLUS_FAIL;
283  }
284  if (localizerInfo.isConnected)
285  {
286  connected = true;
287  return PLUS_SUCCESS;
288  }
289  connected = false;
290  return PLUS_SUCCESS;
291  }
292 
293  //-------------------------------------------------------------------------
294  PlusStatus GetStealthStationServerTime(double& serverTime)
295  {
296  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
297  try
298  {
299  serverTime = (double) this->StealthLinkServer->getServerTime();
300  }
301  catch (MNavStealthLink::Error err)
302  {
303  LOG_ERROR("Cannot get server time " << err.reason());
304  return PLUS_FAIL;
305  }
306  return PLUS_SUCCESS;
307  }
308 
309  //-----------------------------------------------------------------------
310  void DisconnectFromStealthStation()
311  {
312  this->StealthLinkServer->disconnect();
313  delete this->StealthLinkServer;
314  this->StealthLinkServer = NULL;
315  }
316 
317  //----------------------------------------------------------------------
318  PlusStatus GetExamData(MNavStealthLink::Exam exam, std::string examImageDirectory)
319  {
320  igsioLockGuard<vtkIGSIORecursiveCriticalSection> updateMutexGuardedLock(this->StealthLinkServerMutex);
321  try
322  {
323  exam.getExamData(*(this->StealthLinkServer), examImageDirectory);
324  }
325  catch (MNavStealthLink::Error error)
326  {
327  LOG_ERROR("Error getting images from StealthLink: " << error.what() << "\n");
328  return PLUS_FAIL;
329  }
330  return PLUS_SUCCESS;
331  }
332 
333  //-----------------------------------------------------------------------
334  static std::string GetRasRegistrationToolName() { return "RasRegistration"; }
335 
336 private:
337  MNavStealthLink::StealthServer* StealthLinkServer;
338  MNavStealthLink::Registration CurrentRegistration;
339  MNavStealthLink::Exam CurrentExam;
340  // We receive ExamIjkToRpiTransform from StealthStation.
341  // StealthStation ignores the volume orientation (volume axis directions are RPI),
342  // and origin (origin is at 0 0 0) => this is the ExamRPI coordinate system.
343  vtkSmartPointer<vtkMatrix4x4> ExamIjkToRpiTransform;
344  // We read ijkToLps from the header of the DICOM files that we receive from StealthLink and compute ijkToRas
345  // from that.
346  vtkSmartPointer<vtkMatrix4x4> ExamIjkToRasTransform;
347 
348  // Necessary mutex variables
349  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> StealthLinkServerMutex;
350  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> ExamIjkToRpiMutex;
351  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> ExamIjkToRasMutex;
352  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> ExamValidMutex;
353  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> CurrentExamMutex;
354  vtkSmartPointer<vtkIGSIORecursiveCriticalSection> CurrentRegistrationMutex;
355 
356  bool ExamValid;
357 
358  vtkPlusStealthLinkTracker* External;
359 };
360 
361 //----------------------------------------------------------------------------
362 // Class for variables that are only accessed from the InternalUpdate thread and internalconnect
363 class vtkPlusStealthLinkTracker::vtkInternalUpdatePrivate
364 {
365 public:
368  vtkInternalUpdatePrivate(vtkPlusStealthLinkTracker* external)
369  : External(external)
370  {
371  }
372 private:
373  double ServerInitialTimeInMicroSeconds;
374  double TrackerTimeToSystemTimeSec;
375 
376  vtkPlusStealthLinkTracker* External;
377 };
378 
379 //----------------------------------------------------------------------------
380 // Class for variables that are shared only from main thread
381 class vtkPlusStealthLinkTracker::vtkInternal
382 {
383 public:
384  friend class vtkPlusStealthLinkTracker;
385 
386 private:
387  vtkPlusStealthLinkTracker* External;
388 
389  // This is used in GetImage() to calculate the transformation of the image in the demanded reference frame.
390  // The transform repository should be updated before calling GetImage() if the
391  vtkSmartPointer<vtkIGSIOTransformRepository> TransformRepository;
392 
393  std::pair<std::string, std::string> PairImageIdAndName; // unique name created by exam name patient name patient id...
394  std::string ServerAddress; // Host IP Address
395  std::string ServerPort; // Host Port Address
396  std::string DicomImagesOutputDirectory; //The folder where DICOM images that StealthLink sends will be saved
397 
399  int ImageMetaDatasetsCount;
400  bool KeepReceivedDicomFiles;
401 
402  /*~ Constructor ~*/
403  vtkInternal(vtkPlusStealthLinkTracker* external)
404  : External(external)
405  {
406 
407  this->TransformRepository = vtkSmartPointer<vtkIGSIOTransformRepository>::New();
408 
409  this->ServerAddress.clear();
410  this->ServerPort.clear();
411  this->DicomImagesOutputDirectory.clear();
412  }
413 
415  ~vtkInternal()
416  {
417  }
418  std::string GetExamAndPatientInformationAsString(MNavStealthLink::Exam exam)
419  {
420  std::string examAndPatientInformation;
421  examAndPatientInformation = exam.name + exam.description + exam.modality + exam.hospitalName + exam.examNumber + exam.patientId + exam.patientName + exam.patientDOB;
422  return examAndPatientInformation;
423  }
424 
426  static void GetInstrumentInformation(MNavStealthLink::Instrument instrument, ToolStatus& instrumentStatus, vtkMatrix4x4* insToTrackerTransform)
427  {
428  if (!(instrument.visibility == MNavStealthLink::Instrument::VISIBLE) && !(instrument.visibility == MNavStealthLink::Instrument::ALMOST_BLOCKED))
429  {
430  instrumentStatus = TOOL_OUT_OF_VIEW;
431  return;
432  }
433  else
434  {
435  instrumentStatus = TOOL_OK;
436  }
437  for (int col = 0; col < 4; col++)
438  {
439  for (int row = 0; row < 4; row++)
440  {
441  insToTrackerTransform->SetElement(row, col, instrument.localizer_T_instrument [row][col]);
442  }
443  }
444  }
446  static void GetInstrumentInformation(MNavStealthLink::NavData navData, ToolStatus& instrumentStatus, vtkMatrix4x4* insToTrackerTransform)
447  {
448  if (!(navData.instVisibility == MNavStealthLink::Instrument::VISIBLE) && !(navData.instVisibility == MNavStealthLink::Instrument::ALMOST_BLOCKED))
449  {
450  instrumentStatus = TOOL_OUT_OF_VIEW;
451  return;
452  }
453  else
454  {
455  instrumentStatus = TOOL_OK;
456  }
457  for (int col = 0; col < 4; col++)
458  {
459  for (int row = 0; row < 4; row++)
460  {
461  insToTrackerTransform->SetElement(row, col, navData.localizer_T_instrument [row][col]);
462  }
463  }
464  }
466  static void GetFrameInformation(MNavStealthLink::NavData navData, bool& frameOutOfView, vtkMatrix4x4* frameToTrackerTransform)
467  {
468  if (!(navData.frameVisibility == MNavStealthLink::Frame::VISIBLE) && !(navData.frameVisibility == MNavStealthLink::Frame::ALMOST_BLOCKED))
469  {
470  frameOutOfView = true;
471  return;
472  }
473  else
474  {
475  frameOutOfView = false;
476  }
477  vtkSmartPointer<vtkMatrix4x4> trackerToFrameTransform = vtkSmartPointer<vtkMatrix4x4>::New();
478  for (int col = 0; col < 4; col++)
479  {
480  for (int row = 0; row < 4; row++)
481  {
482  trackerToFrameTransform->SetElement(row, col, navData.frame_T_localizer [row][col]); // from localizer Space to frame space
483  }
484  }
485  //the transformation matrix given by NavData is from Localizer(Tracker) space to frame space and we need the inverse of it
486  vtkMatrix4x4::Invert(trackerToFrameTransform, frameToTrackerTransform);
487  }
488  /* Update the transformation maxtrix of the current image. Thread-safe. !*/
489  void GetRasToTrackerTransform(vtkMatrix4x4* frameToTrackerTransform, vtkMatrix4x4* rasToTrackerTransform)
490  {
491 
492  vtkSmartPointer<vtkMatrix4x4> frameToRegExamTransform = vtkSmartPointer<vtkMatrix4x4>::New(); // this is from frame to image used in the registration
493  vtkSmartPointer<vtkMatrix4x4> regExamToExamRpiTransform = vtkSmartPointer<vtkMatrix4x4>::New(); // from the exam image to registration image, if they are the same then the matrix is idendity
494  MNavStealthLink::Registration registration;
495  MNavStealthLink::Exam exam;
496  this->External->InternalShared->GetCurrentRegistration(registration); //this function is thread safe. If registration is being updated, GetCurrentRegistration function will wait
497  this->External->InternalShared->GetCurrentExam(exam); //this function is thread safe. If exam is being updated, GetCurrentExam function will wait
498  for (int col = 0; col < 4; col++)
499  {
500  for (int row = 0; row < 4; row++)
501  {
502  frameToRegExamTransform->SetElement(row, col, registration.regExamMM_T_frame [row][col]);
503  regExamToExamRpiTransform->SetElement(row, col, exam.examMM_T_regExamMM[row][col]);
504  }
505  }
506  //examRpiToFrame = regExamToFrame * examRpiToRegExam so we need the inverse of the two matrices
507  vtkSmartPointer<vtkMatrix4x4> regExamToFrameTransform = vtkSmartPointer<vtkMatrix4x4>::New();
508  vtkMatrix4x4::Invert(frameToRegExamTransform, regExamToFrameTransform);
509  vtkSmartPointer<vtkMatrix4x4> examRpiToRegExamTransform = vtkSmartPointer<vtkMatrix4x4>::New();
510  vtkMatrix4x4::Invert(regExamToExamRpiTransform, examRpiToRegExamTransform);
511 
512  //examToFrame = regExamToFrame * ExamToRegExam
513  vtkSmartPointer<vtkMatrix4x4> examRpiToFrameTransform = vtkSmartPointer<vtkMatrix4x4>::New();
514  vtkMatrix4x4::Multiply4x4(regExamToFrameTransform, examRpiToRegExamTransform, examRpiToFrameTransform);
515 
516  // medtronic stores the image in rpi and does not include orientation. so we need to do some extra math to include the orientation
517  // We need to get to RasToFrame. We have examRpiToFrame which is stored in rpi with no information of orientation. We have ijkToRas and ijkToExamRpi transformation matrices.
518  // RasToFrame = FrameFromRas = FrameFromExamRpi * ExamRpiFromRas
519  // = FrameFromExamRpi * ExamRpiFromIjk * IjkFromRas
520  // = ExamRpiToFrame * IjkToExamRpi * RasToIjk
521 
522  // we have ijkToRas so we need to invert it to have rasToIjk
523  vtkSmartPointer<vtkMatrix4x4> rasToIjkTransform = vtkSmartPointer<vtkMatrix4x4>::New();
524  vtkSmartPointer<vtkMatrix4x4> examIjkToRasTransform = vtkSmartPointer<vtkMatrix4x4>::New();
525  this->External->InternalShared->GetExamIjkToRasTransformMatrix(examIjkToRasTransform); //thread safe. If ijkToRas is being updated, GetIjkToRasTransformationMatrix will wait
526  vtkMatrix4x4::Invert(examIjkToRasTransform, rasToIjkTransform);
527 
528  //Now we just need to multiply the matrices
529  //First, let's multiplly examRpiToFrame and IjkToExamRpi: this will give us ijkToFrame
530  vtkSmartPointer<vtkMatrix4x4> ijkToFrameTransform = vtkSmartPointer<vtkMatrix4x4>::New();
531  vtkSmartPointer<vtkMatrix4x4> examIjkToRpiTransform = vtkSmartPointer<vtkMatrix4x4>::New();
532  this->External->InternalShared->GetExamIjkToRpiTransformMatrix(examIjkToRpiTransform); //thread safe. If ijkToExamRpi is being updated, GetIjkToExamRpiTransformationMatrix will wait
533  vtkMatrix4x4::Multiply4x4(examRpiToFrameTransform, examIjkToRpiTransform, ijkToFrameTransform);
534 
535  // we now multiply ijkToFrameTransform and RasToIjk which will give rasToFrame
536  vtkSmartPointer<vtkMatrix4x4> rasToFrameTransform = vtkSmartPointer<vtkMatrix4x4>::New();
537  vtkMatrix4x4::Multiply4x4(ijkToFrameTransform, rasToIjkTransform, rasToFrameTransform);
538 
539  //Final step is to get rasToTracker which is done by frameToTracker * rasToFrame
540  vtkSmartPointer<vtkMatrix4x4> lpsToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
541  vtkMatrix4x4::Multiply4x4(frameToTrackerTransform, rasToFrameTransform, rasToTrackerTransform);
542  }
543 };
544 
545 /****************************************************************************/
546 
548 
549 //----------------------------------------------------------------------------
551 {
552  this->Internal = new vtkInternal(this);
553  this->InternalShared = new vtkInternalShared(this);
554  this->InternalUpdatePrivate = new vtkInternalUpdatePrivate(this);
555 
557 
558  // No callback function provided by the device, so the data capture thread will be used to poll the hardware and add new items to the buffer
559  this->StartThreadForInternalUpdates = true;
560  this->AcquisitionRate = 30;
561 
563 }
564 
565 // Public Functions
566 //----------------------------------------------------------------------------
568 {
569  if (this->InternalShared->IsStealthServerConnected())
570  {
571  this->InternalDisconnect();
572  }
573  delete this->Internal;
574  this->Internal = NULL;
575 }
576 //---------------------------------------------------------------------------
577 void vtkPlusStealthLinkTracker::PrintSelf(ostream& os, vtkIndent indent)
578 {
579  this->Superclass::PrintSelf(os, indent);
580 }
581 //----------------------------------------------------------------------------
583 {
584  return this->InternalShared->GetSdkVersion(version);
585 }
586 //----------------------------------------------------------------------------
587 PlusStatus vtkPlusStealthLinkTracker::UpdateTransformRepository(vtkIGSIOTransformRepository* sharedTransformRepository)
588 {
589  if (sharedTransformRepository == NULL)
590  {
591  LOG_ERROR("vtkPlusVirtualVolumeReconstructor::UpdateTransformRepository: shared transform repository is invalid");
592  return PLUS_FAIL;
593  }
594  // Create a copy of the transform repository to allow using it for stealthlink while being also used in other threads
595  this->Internal->TransformRepository->DeepCopy(sharedTransformRepository, true);
596  return PLUS_SUCCESS;
597 }
598 
599 //--------------------------------------------------------------------
600 PlusStatus vtkPlusStealthLinkTracker::GetImageMetaData(igsioCommon::ImageMetaDataList& imageMetaData)
601 {
602  if (!this->InternalShared->UpdateCurrentExam()) //this function is thread safe
603  {
604  return PLUS_FAIL;
605  }
606  LOG_INFO("Acquiring the image meta data from the device with DeviceId: " << this->GetDeviceId());
607  igsioCommon::ImageMetaDataItem imageMetaDataItem;
608  MNavStealthLink::Exam exam;
609  this->InternalShared->GetCurrentExam(exam); // thread safe
610 
611  imageMetaDataItem.Id = this->GetImageMetaDatasetsCountAsString();
612  imageMetaDataItem.Description = exam.description;
613  imageMetaDataItem.Modality = exam.modality;
614  imageMetaDataItem.PatientId = exam.patientId;
615  imageMetaDataItem.PatientName = exam.patientName ;
616  imageMetaDataItem.ScalarType = 3; //TODO check this and correct this
617  imageMetaDataItem.Size[0] = exam.size[0];
618  imageMetaDataItem.Size[1] = exam.size[1];
619  imageMetaDataItem.Size[2] = exam.size[2];
620  imageMetaDataItem.TimeStampUtc = vtkIGSIOAccurateTimer::GetUniversalTime();
621  imageMetaData.push_back(imageMetaDataItem);
622  this->Internal->PairImageIdAndName.first = imageMetaDataItem.Id;
623  this->Internal->PairImageIdAndName.second = this->Internal->GetExamAndPatientInformationAsString(exam);
624  return PLUS_SUCCESS;
625 }
626 //-------------------------------------------------------------------
627 PlusStatus vtkPlusStealthLinkTracker::GetImage(const std::string& requestedImageId, std::string& assignedImageId, const std::string& imageReferencFrameName, vtkImageData* imageData, vtkMatrix4x4* ijkToReferenceTransform)
628 {
629  std::string examImageDirectoryToDelete;
630  {
631  if (!this->InternalShared->UpdateCurrentExam())
632  {
633  return PLUS_FAIL;
634  }
635  if (!this->InternalShared->UpdateCurrentRegistration(this->ImageTransferRequiresPatientRegistration))
636  {
637  return PLUS_FAIL;
638  }
639  if (!requestedImageId.empty()) // The command is GET_IMAGE
640  {
641  if (STRCASECMP(this->Internal->PairImageIdAndName.first.c_str(), requestedImageId.c_str()) != 0)
642  {
643  LOG_ERROR("The image requested does not belong to " << this->GetDeviceId());
644  return PLUS_FAIL;
645  }
646  MNavStealthLink::Exam exam;
647  this->InternalShared->GetCurrentExam(exam);
648  std::string examAndPatientInformation = this->Internal->GetExamAndPatientInformationAsString(exam);
649  if (STRCASECMP(this->Internal->PairImageIdAndName.second.c_str(), examAndPatientInformation.c_str()) != 0)
650  {
651  LOG_INFO("Current exam on the server does not match the exam you have requested. Please either update image meta data or select the exam you request on the server");
652  return PLUS_FAIL;
653  }
654  assignedImageId = requestedImageId;
655  }
656  else
657  {
658  assignedImageId = this->GetDeviceId() + std::string("-") + this->GetImageMetaDatasetsCountAsString();
659  }
660  std::string examImageDirectory;
661  if (!this->AcquireDicomImage(this->GetDicomImagesOutputDirectory(), examImageDirectory))
662  {
663  return PLUS_FAIL;
664  }
665  examImageDirectoryToDelete = examImageDirectory;
666 
667  vtkSmartPointer<vtkDICOMImageReader> reader = vtkSmartPointer<vtkDICOMImageReader>::New();
668  try
669  {
670  reader->SetDirectoryName(examImageDirectory.c_str());
671  reader->ReleaseDataFlagOn();
672 
673  //to go from the vtk orientation to lps orientation, the vtk image has to be flipped around y and z axis
674  vtkSmartPointer<vtkImageFlip> flipYFilter = vtkSmartPointer<vtkImageFlip>::New();
675  flipYFilter->SetFilteredAxis(1); // flip y axis
676  flipYFilter->ReleaseDataFlagOn();
677  flipYFilter->SetInputConnection(reader->GetOutputPort());
678 
679  vtkSmartPointer<vtkImageFlip> flipZFilter = vtkSmartPointer<vtkImageFlip>::New();
680  flipZFilter->SetFilteredAxis(2); // flip z axis
681  flipZFilter->ReleaseDataFlagOn();
682  flipZFilter->SetInputConnection(flipYFilter->GetOutputPort());
683  flipZFilter->Update();
684  imageData->DeepCopy(flipZFilter->GetOutput());
685  }
686  catch (const std::bad_alloc& e)
687  {
688  LOG_ERROR("Error when downloading image from StealthLink: " << e.what() << ". Image may be too large");
689  return PLUS_FAIL;
690  }
691 
692  float* ijkOrigin_LPS = reader->GetImagePositionPatient(); //(0020,0032) ImagePositionPatient
693  double* ijkVectorMagnitude_LPS = reader->GetPixelSpacing(); //(0020,0037) ImageOrientationPatient
694 
695  float* iDirectionVector_LPS = reader->GetImageOrientationPatient();
696  float* jDirectionVector_LPS = reader->GetImageOrientationPatient() + 3;
697  float kDirectionVector_LPS[3] = {0}; // the third cosine direction is the cross product of the other two vectors
698  vtkMath::Cross(iDirectionVector_LPS, jDirectionVector_LPS, kDirectionVector_LPS);
699 
700  vtkSmartPointer<vtkMatrix4x4> examIjkToRpiTransform = vtkSmartPointer<vtkMatrix4x4>::New(); // image to ExamRpi, medtronic exludes orientation
701  examIjkToRpiTransform->SetElement(0, 0, -1);
702  examIjkToRpiTransform->SetElement(2, 2, -1);
703  int xMin, xMax, yMin, yMax, zMin, zMax; //Dimensions, necessary to calculate the new origin in exam rpi
704  reader->GetDataExtent(xMin, xMax, yMin, yMax, zMin, zMax);
705  //the origin is shifted from lps to rpi aka shifted along x and z axes
706  double newOrigin_ExamRpi[3]; // medtronic uses rpi and considers the dicom origin to be zero and also the orientation to be idendity
707  newOrigin_ExamRpi[0] = (xMax - xMin + 1) * ijkVectorMagnitude_LPS[0];
708  newOrigin_ExamRpi[1] = 0;
709  newOrigin_ExamRpi[2] = (zMax - zMin + 1) * ijkVectorMagnitude_LPS[2];
710  examIjkToRpiTransform->SetElement(0, 3, newOrigin_ExamRpi[0]);
711  examIjkToRpiTransform->SetElement(1, 3, newOrigin_ExamRpi[1]);
712  examIjkToRpiTransform->SetElement(2, 3, newOrigin_ExamRpi[2]);
713 
714  vtkSmartPointer<vtkMatrix4x4> examIjkToRasTransform = vtkSmartPointer<vtkMatrix4x4>::New(); // ijkToRas: 3x3 direction cosines + origin 1X3
715  for (int i = 0; i < 3; i++)
716  {
717  //the dicom image is in lps. To translate it to ras, x and y are "flipped" aka multipled by -1
718  if (i == 0 || i == 1)
719  {
720  examIjkToRasTransform->SetElement(i, 0, -iDirectionVector_LPS[i]);
721  examIjkToRasTransform->SetElement(i, 1, -jDirectionVector_LPS[i]);
722  examIjkToRasTransform->SetElement(i, 2, -kDirectionVector_LPS[i]);
723  }
724  else
725  {
726  examIjkToRasTransform->SetElement(i, 0, iDirectionVector_LPS[i]);
727  examIjkToRasTransform->SetElement(i, 1, jDirectionVector_LPS[i]);
728  examIjkToRasTransform->SetElement(i, 2, kDirectionVector_LPS[i]);
729  }
730  }
731  //Set the elements of the transformation matrix, x and y are flipped here as well, multiplied by 1
732  examIjkToRasTransform->SetElement(0, 3, -ijkOrigin_LPS[0]);
733  examIjkToRasTransform->SetElement(1, 3, -ijkOrigin_LPS[1]);
734  examIjkToRasTransform->SetElement(2, 3, ijkOrigin_LPS[2]);
735 
736  //These matrices are needed to calculate rasToTracker
737  this->InternalShared->SetExamIjkToRpiTransformMatrix(examIjkToRpiTransform); //thread safe with get function
738  this->InternalShared->SetExamIjkToRasTransformMatrix(examIjkToRasTransform); //thread safe with get function
739  this->InternalShared->SetExamValid(true); // thread safe with get function
740 
741  if (imageReferencFrameName.compare("Ras") == 0)
742  {
743  for (int i = 0; i < 4; i++)
744  {
745  for (int j = 0; j < 4; j++)
746  {
747  ijkToReferenceTransform->SetElement(i, j, examIjkToRasTransform->GetElement(i, j));
748  }
749  }
750  }
751  else
752  {
753  MNavStealthLink::Exam exam;
754  this->InternalShared->GetCurrentExam(exam);
755  vtkIGSIOAccurateTimer::Delay(1); // Delay 1 second to make sure that this->Internal->RasToTracker is calculated correctly based on the new matrices, ijkToExamRpiTransform and ijkToRasTransform
756  const igsioTransformName rasToTrackerTransformName("Ras", "Tracker");
757 
758  MNavStealthLink::NavData navData;
759  if (!this->InternalShared->GetCurrentNavigationData(navData))
760  {
761  return PLUS_FAIL;
762  }
763 
764  vtkSmartPointer<vtkMatrix4x4> frameToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
765  bool frameOutOfView;
766  this->Internal->GetFrameInformation(navData, frameOutOfView, frameToTrackerTransform);
767 
768  vtkSmartPointer<vtkMatrix4x4> rasToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
769  if (frameOutOfView == true)
770  {
771  LOG_WARNING("The frame is out of view. If you requested the image in Stylus, Frame or Tracker coordinate system, this will give wrong results. Please make sure that the frame is visible");
772  }
773  else
774  {
775  this->Internal->GetRasToTrackerTransform(frameToTrackerTransform, rasToTrackerTransform); // thread-safe
776  }
777  this->Internal->TransformRepository->SetTransform(rasToTrackerTransformName, rasToTrackerTransform);
778  vtkSmartPointer<vtkMatrix4x4> rasToReferenceTransform = vtkSmartPointer<vtkMatrix4x4>::New();
779  const igsioTransformName rasToReferenceTransformName("Ras", imageReferencFrameName.c_str());
780  this->Internal->TransformRepository->GetTransform(rasToReferenceTransformName, rasToReferenceTransform);
781  vtkMatrix4x4::Multiply4x4(rasToReferenceTransform, examIjkToRasTransform, ijkToReferenceTransform);
782  }
783  }
784  this->Internal->DicomImagesOutputDirectory = vtkPlusConfig::GetInstance()->GetOutputDirectory() + std::string("/StealthLinkDicomOutput");
785  if (!this->Internal->KeepReceivedDicomFiles) //if it enters here then it means keepReceivedDicomFiles is false no need to set it to false again
786  {
787  return this->DeleteDicomImageOutputDirectory(examImageDirectoryToDelete);
788  }
789  this->SetKeepReceivedDicomFiles(false);
790  return PLUS_SUCCESS;
791 }
792 
793 //----------------------------------------------------------------------------
795 {
796  if (!this->InternalShared->IsStealthServerConnected())
797  {
798  LOG_ERROR("InternalStartRecording failed: StealthLinkServer has not been initialized");
799  return PLUS_FAIL;
800  }
801  return PLUS_SUCCESS;
802 }
803 //----------------------------------------------------------------------------
805 {
806  // No need to do anything here, as the StealthLinkServer only performs grabbing on request
807  return PLUS_SUCCESS;
808 }
809 //----------------------------------------------------------------------------
810 PlusStatus vtkPlusStealthLinkTracker::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
811 {
812  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
813 
814  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(ServerAddress, deviceConfig);
815  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(ServerPort, deviceConfig);
816  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(ImageTransferRequiresPatientRegistration, deviceConfig);
817 
818  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
819  for (int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); nestedElementIndex++)
820  {
821  vtkXMLDataElement* toolDataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
822  if (STRCASECMP(toolDataElement->GetName(), "DataSource") != 0)
823  {
824  // if this is not a data source element, skip it
825  continue;
826  }
827  if (toolDataElement->GetAttribute("Type") != NULL && STRCASECMP(toolDataElement->GetAttribute("Type"), "Tool") != 0)
828  {
829  // if this is not a Tool element, skip it
830  continue;
831  }
832 
833  const char* portName = toolDataElement->GetAttribute("PortName");
834  vtkPlusDataSource* trackerTool = NULL;
835  this->GetToolByPortName(portName, trackerTool);
836  if (trackerTool)
837  {
838  const char* alwaysTrackTool = toolDataElement->GetAttribute("AlwaysTrack");
839  if (alwaysTrackTool)
840  {
841  trackerTool->SetCustomProperty("AlwaysTrack", alwaysTrackTool);
842  }
843  else
844  {
845  trackerTool->SetCustomProperty("AlwaysTrack", "FALSE");
846  }
847  }
848  }
849 
850  if (this->GetDeviceId().size() > MAX_DEVICE_ID_LENGTH)
851  {
852  LOG_WARNING("The device id " << this->GetDeviceId() << " might be too long, as it may be used for generating identifiers for images that will be sent through OpenIGTLink. Consider choosing a shorter device Id. Example: SLD1");
853  }
854 
855  return PLUS_SUCCESS;
856 }
857 
858 //----------------------------------------------------------------------------
860 {
861  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceElement, rootConfig);
862  return PLUS_SUCCESS;
863 }
864 
865 //----------------------------------------------------------------------------
867 {
868  LOG_TRACE("vtkPlusStealthLinkTracker::InternalConnect");
869  if (this->InternalShared->IsStealthServerConnected())
870  {
871  LOG_DEBUG("Already connected to StealthLink");
872  return PLUS_SUCCESS;
873  }
874 
875  if (this->Internal->ServerAddress.empty() || this->Internal->ServerPort.empty())
876  {
877  LOG_ERROR("Cannot connect: Server Address or Server Port are not assigned\n");
878  return PLUS_FAIL;
879  }
880  LOG_TRACE("Server Address: " << this->Internal->ServerAddress << " " << "Server Port: " << this->Internal->ServerPort << "\n");
881 
882  if (!this->InternalShared->ConnectToStealthStation(this->Internal->ServerAddress, this->Internal->ServerPort))
883  {
884  return PLUS_FAIL;
885  }
886 
887  //Check if the instrument port names in the config file are valid
888  bool valid;
889  if (!this->AreInstrumentPortNamesValid(valid))
890  {
891  return PLUS_FAIL;
892  }
893  if (!valid)
894  { return PLUS_FAIL; }
895  //Check of the Localizer(Tracker) is connected
896  bool connected;
897  if (!this->InternalShared->IsLocalizerConnected(connected))
898  {
899  return PLUS_FAIL;
900  }
901  if (!connected)
902  {
903  LOG_ERROR("Localizer(Tracker) is not connected. Please check the StealthLink Server\n")
904  return PLUS_FAIL;
905  }
906 
907  //Get the time difference between the StealthServer and the vtkIGSIOAccurateTimer
908  double serverTimeInMicroSeconds = 0;
909  if (!this->InternalShared->GetStealthStationServerTime(serverTimeInMicroSeconds))
910  {
911  return PLUS_FAIL;
912  }
913  this->InternalUpdatePrivate->ServerInitialTimeInMicroSeconds = serverTimeInMicroSeconds;
914 
915  this->InternalUpdatePrivate->TrackerTimeToSystemTimeSec = vtkIGSIOAccurateTimer::GetSystemTime();
916 
917  this->Internal->DicomImagesOutputDirectory = vtkPlusConfig::GetInstance()->GetOutputDirectory() + std::string("/StealthLinkDicomOutput");
918  this->Internal->KeepReceivedDicomFiles = false;
919  this->Internal->ImageMetaDatasetsCount = 1;
920  this->InternalShared->SetExamValid(false);
921  return PLUS_SUCCESS;
922 }
923 //----------------------------------------------------------------------------
925 {
926  if (this->InternalShared->IsStealthServerConnected())
927  {
928  this->InternalShared->DisconnectFromStealthStation();
929  }
930  return PLUS_SUCCESS;
931 }
932 //-----------------------------------------------------------------------------
934 {
935  if (!this->InternalShared->IsStealthServerConnected()) //thread safe for stealthlink
936  {
937  LOG_ERROR("InternalUpdate failed: StealthLinkServer has not been initialized");
938  return PLUS_FAIL;
939  }
940 
941  double serverTimeInMicroSec;
942  if (!this->InternalShared->GetStealthStationServerTime(serverTimeInMicroSec))
943  {
944  return PLUS_FAIL;
945  }
946  double timeSystemSec = 0.0;
947  timeSystemSec = (serverTimeInMicroSec - this->InternalUpdatePrivate->ServerInitialTimeInMicroSeconds) / (1e6) + this->InternalUpdatePrivate->TrackerTimeToSystemTimeSec;
948  double unfilteredTime = vtkIGSIOAccurateTimer::GetSystemTime();
949  //----------------------------------------------------------
950  MNavStealthLink::NavData navData;
951  if (!this->InternalShared->GetCurrentNavigationData(navData)) //thread safe
952  {
953  return PLUS_FAIL;
954  }
955 
956  vtkSmartPointer<vtkMatrix4x4> frameToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
957  bool frameOutOfView = true;
958  vtkPlusStealthLinkTracker::vtkInternal::GetFrameInformation(navData, frameOutOfView, frameToTrackerTransform);
959 
960  for (DataSourceContainerConstIterator toolIterator = this->GetToolIteratorBegin(); toolIterator != this->GetToolIteratorEnd(); ++toolIterator)
961  {
962  //if the wanted transformation is the frameToTracker
963  if (toolIterator->second->GetPortName() == navData.frameName) // static!
964  {
965  if (frameOutOfView == false)
966  {
967  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), frameToTrackerTransform, TOOL_OK, unfilteredTime, timeSystemSec);
968  }
969  else if (frameOutOfView == true)
970  {
971  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), frameToTrackerTransform, TOOL_OUT_OF_VIEW, unfilteredTime, timeSystemSec);
972  }
973  }
974  // if the wanted transformation is rasToTracker
975  else if (toolIterator->second->GetPortName() == vtkInternalShared::GetRasRegistrationToolName()) // static!
976  {
977  // If frame is not out of view, the RasToTrackerTransform is invalid
978  bool examValid = this->InternalShared->GetExamValid(); //thread safe with the set function
979  if (frameOutOfView == false && examValid == true)
980  {
981  vtkSmartPointer<vtkMatrix4x4> rasToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
982  this->Internal->GetRasToTrackerTransform(frameToTrackerTransform, rasToTrackerTransform); // thread-safe
983  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), rasToTrackerTransform, TOOL_OK, unfilteredTime, timeSystemSec);
984  }
985  else
986  {
987  vtkSmartPointer<vtkMatrix4x4> rasToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
988  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), rasToTrackerTransform, TOOL_OUT_OF_VIEW, unfilteredTime, timeSystemSec);
989  }
990  }
991  // if the wanted Transform is the current tool to Tracker example sytlusToTracker, probeToTracker, etc
992  // the current tool is considered by the StealthStation to be the tool that is closest to the reference frame
993  else if (toolIterator->second->GetPortName() == navData.instrumentName)
994  {
995  vtkSmartPointer<vtkMatrix4x4> instrumentToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
996  ToolStatus instrumentStatus = TOOL_OUT_OF_VIEW;
997  vtkPlusStealthLinkTracker::vtkInternal::GetInstrumentInformation(navData, instrumentStatus, instrumentToTrackerTransform);
998  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), instrumentToTrackerTransform, instrumentStatus, unfilteredTime, timeSystemSec);
999  }
1000  // if the wanted Transform is any tool to Tracker that is not the closest to the reference frame
1001  else
1002  {
1003  // navData returned from stealthstation only contains updated information for the tool that is closest to the reference frame
1004  // if the tool is specified to always be tracked, another query is made to get updated information
1005  // making this extra query is an expensive operation, so it is only attempted if the user has specified that they would like a tool to be tracked at all times
1006  if (STRCASECMP(toolIterator->second->GetCustomProperty("AlwaysTrack").c_str(), "TRUE") == 0)
1007  {
1008  MNavStealthLink::Instrument instrument;
1009  if (this->InternalShared->GetInstrumentData(instrument, toolIterator->second->GetPortName()) == PLUS_FAIL) //thread safe
1010  {
1011  return PLUS_FAIL;
1012  }
1013 
1014  vtkSmartPointer<vtkMatrix4x4> instrumentToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
1015  ToolStatus instrumentStatus = TOOL_OUT_OF_VIEW;
1016  vtkPlusStealthLinkTracker::vtkInternal::GetInstrumentInformation(instrument, instrumentStatus, instrumentToTrackerTransform);
1017  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), instrumentToTrackerTransform, instrumentStatus, unfilteredTime, timeSystemSec);
1018  }
1019  // tools that are not closest to the reference frame and are not always tracked are considered to be out of view
1020  else
1021  {
1022  vtkSmartPointer<vtkMatrix4x4> transformMatrixForNotTrackedTool = vtkSmartPointer<vtkMatrix4x4>::New();
1023  this->ToolTimeStampedUpdateWithoutFiltering(toolIterator->second->GetSourceId(), transformMatrixForNotTrackedTool, TOOL_OUT_OF_VIEW, unfilteredTime, timeSystemSec);
1024  }
1025 
1026  }
1027  }
1028  return PLUS_SUCCESS;
1029 }
1030 
1031 //------------------------------------------------------------------------------
1032 
1033 //--
1035 {
1036  std::vector<int> spacePlaces;
1037  for (unsigned int i = 0; i < str.size(); i++)\
1038  {
1039  int asciiCode = (int) str[i];
1040  if (str[i] == ' ')
1041  {
1042  spacePlaces.push_back(i);
1043  }
1044  else if (str[i] == '\\' || str[i] == '/')
1045  {
1046  str[i] = '-';
1047  }
1048  else if (asciiCode < 48 || (asciiCode > 57 && asciiCode < 65) || (asciiCode > 90 && asciiCode < 97) || asciiCode > 122)
1049  {
1050  str[i] = ' ';
1051  spacePlaces.push_back(i);
1052  }
1053  }
1054  std::string subStrPatientName;
1055  int placement = 0;
1056  for (std::vector<int>::iterator it = spacePlaces.begin(); it != spacePlaces.end(); it++)
1057  {
1058  subStrPatientName = str.substr(*it + 1 - placement);
1059  str = str.substr(0, *it - placement);
1060  str = str + subStrPatientName;
1061  placement++;
1062  }
1063 }
1064 //---------------------------------------------------------------------------
1066 {
1067  return this->Internal->DicomImagesOutputDirectory;
1068 }
1069 void vtkPlusStealthLinkTracker::SetDicomImagesOutputDirectory(std::string dicomImagesOutputDirectory)
1070 {
1071  this->Internal->DicomImagesOutputDirectory = dicomImagesOutputDirectory;
1072 }
1073 //---------------------------------------------------------------------------
1075 {
1076  this->Internal->KeepReceivedDicomFiles = keepReceivedDicomFiles;
1077 }
1078 //----------------------------------------------------------------------------
1079 PlusStatus vtkPlusStealthLinkTracker::AcquireDicomImage(std::string dicomImagesOutputDirectory, std::string& examImageDirectory)
1080 {
1081 
1082  vtkDirectory::MakeDirectory(dicomImagesOutputDirectory.c_str());
1083 
1084  MNavStealthLink::Exam exam;
1085  this->InternalShared->GetCurrentExam(exam);
1086  if (exam.patientName.empty())
1087  {
1088  return PLUS_FAIL;
1089  }
1090  std::string description = exam.description;
1091  this->RemoveForbiddenCharacters(description);
1092  std::string patientName = exam.patientName;
1093  this->RemoveForbiddenCharacters(patientName);
1094  examImageDirectory = std::string(dicomImagesOutputDirectory) + std::string("/") + patientName + std::string("_") + description + std::string("_") + vtkIGSIOAccurateTimer::GetInstance()->GetDateAndTimeString();
1095  if (!this->InternalShared->GetExamData(exam, examImageDirectory))
1096  {
1097  return PLUS_FAIL;
1098  }
1099  return PLUS_SUCCESS;
1100 }
1101 //----------------------------------------------------------------------
1103 {
1104  std::vector<std::string> validToolPortNames;
1105  if (!this->InternalShared->GetValidToolPortNames(validToolPortNames))
1106  {
1107  valid = false;
1108  return PLUS_FAIL;
1109  }
1110  for (DataSourceContainerConstIterator toolIterator = this->GetToolIteratorBegin(); toolIterator != this->GetToolIteratorEnd(); ++toolIterator)
1111  {
1112  bool found = false;
1113  for (unsigned int i = 0; i < validToolPortNames.size(); i++)
1114  {
1115  if (validToolPortNames[i].compare(toolIterator->second->GetPortName()) == 0)
1116  {
1117  found = true;
1118  break;
1119  }
1120  }
1121  if (found == false)
1122  {
1123  LOG_ERROR(toolIterator->second->GetPortName() << " instrument is not available in the connected StealthStation. Please make sure that the port name matches the instrument name in StealthStation or remove the corresponding DataSource element from DataSources and OutputChannel elements in the Plus device set configuration file\n");
1124  valid = false;
1125  std::string strValidToolPortNames("Valid tool port names are:\n");
1126  for (unsigned int i = 0; i < validToolPortNames.size(); i++)
1127  {
1128  strValidToolPortNames += validToolPortNames[i] + std::string("\n");
1129  }
1130  LOG_INFO(strValidToolPortNames);
1131  return PLUS_FAIL;
1132  }
1133  found = false;
1134  }
1135  valid = true;
1136  return PLUS_SUCCESS;
1137 }
1138 //----------------------------------------------------------------------------
1140 {
1141  vtkSmartPointer<vtkDirectory> dicomDirectory = vtkSmartPointer<vtkDirectory>::New();
1142  if (!dicomDirectory->Open(examImageDirectory.c_str()))
1143  {
1144  LOG_ERROR("Cannot open the folder: " << examImageDirectory);
1145  return PLUS_FAIL;
1146  }
1147 
1148  // Delete the directory of the last exam
1149  vtkDirectory::DeleteDirectory(examImageDirectory.c_str());
1150 
1151  // Delete the parent directory if this was the only exam
1152  if (!dicomDirectory->Open(this->GetDicomImagesOutputDirectory().c_str()))
1153  {
1154  LOG_ERROR("Cannot open the folder: " << this->GetDicomImagesOutputDirectory());
1155  return PLUS_FAIL;
1156  }
1157  if (dicomDirectory->GetNumberOfFiles() == 2)
1158  {
1159  std::string file0(".");
1160  std::string file1("..");
1161  if ((file0.compare(dicomDirectory->GetFile(0)) == 0 && file1.compare(dicomDirectory->GetFile(1)) == 0) ||
1162  (file0.compare(dicomDirectory->GetFile(1)) == 0 && file1.compare(dicomDirectory->GetFile(0)) == 0))
1163  {
1164  vtkDirectory::DeleteDirectory(this->GetDicomImagesOutputDirectory().c_str());
1165  }
1166  }
1167 
1168  return PLUS_SUCCESS;
1169 }
1170 
1171 //----------------------------------------------------------------------------
1173 {
1174  std::ostringstream message;
1175  message << std::setw(3) << std::setfill('0') << this->Internal->ImageMetaDatasetsCount << std::ends;
1176  this->Internal->ImageMetaDatasetsCount++;
1177  return message.str();
1178 }
1179 
1180 //----------------------------------------------------------------------------
1181 void vtkPlusStealthLinkTracker::SetServerAddress(const char* serverAddress)
1182 {
1183  this->Internal->ServerAddress = serverAddress ? serverAddress : "";
1184 }
1185 
1186 //----------------------------------------------------------------------------
1187 void vtkPlusStealthLinkTracker::SetServerPort(const char* serverPort)
1188 {
1189  this->Internal->ServerPort = serverPort ? serverPort : "";
1190 }
DataSourceContainer::const_iterator DataSourceContainerConstIterator
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
void SetDicomImagesOutputDirectory(std::string dicomImagesOutputDirectory)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
virtual PlusStatus ToolTimeStampedUpdateWithoutFiltering(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, double unfilteredtimestamp, double filteredtimestamp, const igsioFieldMapType *customFields=NULL)
vtkStandardNewMacro(vtkPlusStealthLinkTracker)
PlusStatus DeleteDicomImageOutputDirectory(std::string examImageDirectory)
int
Definition: phidget22.h:3069
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *rootConfigElement)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
PlusStatus AcquireDicomImage(std::string dicomImagesOutputDirectory, std::string &examImageDirectory)
virtual std::string GetDeviceId() const
void SetServerPort(const char *serverPort)
bool RequirePortNameInDeviceSetConfiguration
for i
double AcquisitionRate
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual std::string GetSdkVersion()
static vtkPlusConfig * GetInstance()
void PrintSelf(ostream &os, vtkIndent indent)
void SetCustomProperty(const std::string &propertyName, const std::string &propertyValue)
virtual PlusStatus InternalStopRecording()
virtual PlusStatus InternalStartRecording()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PlusStatus GetToolByPortName(const char *aPortName, vtkPlusDataSource *&aSource)
std::string GetOutputDirectory()
PlusStatus AreInstrumentPortNamesValid(bool &valid)
PlusStatus UpdateTransformRepository(vtkIGSIOTransformRepository *sharedTransformRepository)
static void RemoveForbiddenCharacters(std::string &str)
PlusStatus IsLocalizerConnected(bool &)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
void SetServerAddress(const char *serverAddress)
virtual PlusStatus GetImageMetaData(igsioCommon::ImageMetaDataList &imageMetaData)
vtkInternalUpdatePrivate * InternalUpdatePrivate
bool StartThreadForInternalUpdates
DataSourceContainerConstIterator GetToolIteratorBegin() const
static const int MAX_DEVICE_ID_LENGTH
const char * message
Definition: phidget22.h:2457
DataSourceContainerConstIterator GetToolIteratorEnd() const
void SetKeepReceivedDicomFiles(bool keepReceivedDicomFiles)
virtual PlusStatus GetImage(const std::string &requestedImageId, std::string &assignedImageId, const std::string &imageReferenceFrameName, vtkImageData *imageData, vtkMatrix4x4 *ijkToReferenceTransform)
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
Interface to a 3D positioning tool, video source, or generalized data stream.