PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusOpenIGTLinkServer.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 // Local includes
8 #include "PlusConfigure.h"
9 #include "PlusCommon.h"
10 #include "igsioTrackedFrame.h"
11 #include "vtkPlusChannel.h"
12 #include "vtkPlusCommand.h"
14 #include "vtkPlusDataCollector.h"
18 #include "vtkIGSIORecursiveCriticalSection.h"
19 #include "vtkIGSIOTrackedFrameList.h"
20 #include "vtkIGSIOTransformRepository.h"
21 
22 // VTK includes
23 #include <vtkImageData.h>
24 #include <vtkObjectFactory.h>
25 #include <vtkPoints.h>
26 #include <vtkPolyData.h>
27 #include <vtkPolyDataReader.h>
28 
29 // OpenIGTLink includes
30 #include <igtlCommandMessage.h>
31 #include <igtlImageMessage.h>
32 #include <igtlImageMetaMessage.h>
33 #include <igtlMessageHeader.h>
35 #include <igtlPointMessage.h>
36 #include <igtlPolyDataMessage.h>
37 #include <igtlStatusMessage.h>
38 #include <igtlStringMessage.h>
39 #include <igtlTrackingDataMessage.h>
40 
41 // OpenIGTLinkIO includes
42 #include <igtlioPolyDataConverter.h>
43 
44 #if defined(WIN32)
46 #elif defined(__APPLE__)
48 #elif defined(__linux__)
50 #endif
51 
52 // STL includes
53 #include <fstream>
54 #include <streambuf>
55 
56 namespace
57 {
58  const double DELAY_ON_SENDING_ERROR_SEC = 0.02;
59  const double DELAY_ON_NO_NEW_FRAMES_SEC = 0.005;
60  const int NUMBER_OF_RECENT_COMMAND_IDS_STORED = 10;
61  const int IGTL_EMPTY_DATA_SIZE = -1;
62  const double SERVER_START_CHECK_DELAY_SEC = 2.0;
63  const double SERVER_START_CHECK_DELAY_INTERVAL_SEC = 0.05;
64 
65  //----------------------------------------------------------------------------
66  // If a frame cannot be retrieved from the device buffers (because it was overwritten by new frames)
67  // then we skip a SAMPLING_SKIPPING_MARGIN_SEC long period to allow the application to catch up.
68  // This time should be long enough to comfortably retrieve a frame from the buffer.
69  const double SAMPLING_SKIPPING_MARGIN_SEC = 0.1;
70 }
71 
72 //----------------------------------------------------------------------------
73 
75 int vtkPlusOpenIGTLinkServer::ClientIdCounter = 1;
76 const float vtkPlusOpenIGTLinkServer::CLIENT_SOCKET_TIMEOUT_SEC = 0.5f;
77 
78 //----------------------------------------------------------------------------
80  : ServerSocket(igtl::ServerSocket::New())
81  , TransformRepository(NULL)
82  , DataCollector(NULL)
83  , Threader(vtkSmartPointer<vtkMultiThreader>::New())
84  , IGTLProtocolVersion(OpenIGTLink_PROTOCOL_VERSION)
85  , IGTLHeaderVersion(IGTL_HEADER_VERSION_2)
86  , ListeningPort(-1)
87  , NumberOfRetryAttempts(10)
88  , DelayBetweenRetryAttemptsSec(0.05)
89  , MaxNumberOfIgtlMessagesToSend(100)
90  , ConnectionReceiverThreadId(-1)
91  , DataSenderThreadId(-1)
92  , IgtlMessageFactory(vtkSmartPointer<vtkPlusIgtlMessageFactory>::New())
93  , IgtlClientsMutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
94  , LastSentTrackedFrameTimestamp(0)
95  , MaxTimeSpentWithProcessingMs(50)
96  , LastProcessingTimePerFrameMs(-1)
97  , SendValidTransformsOnly(true)
98  , DefaultClientSendTimeoutSec(CLIENT_SOCKET_TIMEOUT_SEC)
99  , DefaultClientReceiveTimeoutSec(CLIENT_SOCKET_TIMEOUT_SEC)
100  , IgtlMessageCrcCheckEnabled(0)
101  , PlusCommandProcessor(vtkSmartPointer<vtkPlusCommandProcessor>::New())
102  , MessageResponseQueueMutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
103  , BroadcastChannel(NULL)
104  , LogWarningOnNoDataAvailable(true)
105  , KeepAliveIntervalSec(CLIENT_SOCKET_TIMEOUT_SEC / 2.0)
106  , GracePeriodLogLevel(vtkPlusLogger::LOG_LEVEL_DEBUG)
107  , MissingInputGracePeriodSec(0.0)
108  , BroadcastStartTime(0.0)
109  , NewClientConnected(false)
110 {
111 
112 }
113 
114 //----------------------------------------------------------------------------
116 {
117  this->Stop();
118  this->SetTransformRepository(NULL);
119  this->SetDataCollector(NULL);
120  this->SetConfigFilename(NULL);
121 }
122 
123 //----------------------------------------------------------------------------
125 {
126  bool found(false);
127  {
128  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
129  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
130  {
131  if (clientIterator->ClientId == clientId)
132  {
133  found = true;
134  break;
135  }
136  }
137  }
138 
139  if (!found)
140  {
141  LOG_ERROR("Requested clientId " << clientId << " not found in list.");
142  return PLUS_FAIL;
143  }
144 
145  igsioLockGuard<vtkIGSIORecursiveCriticalSection> mutexGuardedLock(this->MessageResponseQueueMutex);
146  this->MessageResponseQueue[clientId].push_back(message);
147 
148  return PLUS_SUCCESS;
149 }
150 
151 //----------------------------------------------------------------------------
152 void vtkPlusOpenIGTLinkServer::PrintSelf(ostream& os, vtkIndent indent)
153 {
154  this->Superclass::PrintSelf(os, indent);
155 }
156 
157 //----------------------------------------------------------------------------
159 {
160  if (this->DataCollector == NULL)
161  {
162  LOG_WARNING("Tried to start OpenIGTLink server without a vtkPlusDataCollector");
163  return PLUS_FAIL;
164  }
165 
166  if (this->ConnectionReceiverThreadId < 0)
167  {
168  this->ConnectionActive.Request = true;
169  this->ConnectionReceiverThreadId = this->Threader->SpawnThread((vtkThreadFunctionType)&ConnectionReceiverThread, this);
170  }
171 
172  if (this->DataSenderThreadId < 0)
173  {
174  this->DataSenderActive.Request = true;
175  this->DataSenderThreadId = this->Threader->SpawnThread((vtkThreadFunctionType)&DataSenderThread, this);
176  }
177 
178  // Wait a short duration to see if both threads initialized properly, check at 50ms interval
179  RETRY_UNTIL_TRUE(this->ConnectionActive.Respond,
180  vtkMath::Round(SERVER_START_CHECK_DELAY_SEC / SERVER_START_CHECK_DELAY_INTERVAL_SEC),
181  SERVER_START_CHECK_DELAY_INTERVAL_SEC);
182  if (!this->ConnectionActive.Respond)
183  {
184  LOG_ERROR("Unable to initialize receiver and sender processes.");
185  return PLUS_FAIL;
186  }
187 
188  std::ostringstream ss;
189  ss << "Data sent by default: ";
190  this->DefaultClientInfo.PrintSelf(ss, vtkIndent(0));
191  LOG_DEBUG(ss.str());
192 
193  this->PlusCommandProcessor->SetPlusServer(this);
194 
195  this->BroadcastStartTime = vtkIGSIOAccurateTimer::GetSystemTime();
196 
197  return PLUS_SUCCESS;
198 }
199 
200 //----------------------------------------------------------------------------
202 {
203  // Stop connection receiver thread
204  if (this->ConnectionReceiverThreadId >= 0)
205  {
206  this->ConnectionActive.Request = false;
207  while (this->ConnectionActive.Respond)
208  {
209  // Wait until the thread stops
210  vtkIGSIOAccurateTimer::DelayWithEventProcessing(0.2);
211  }
212  this->ConnectionReceiverThreadId = -1;
213  LOG_DEBUG("ConnectionReceiverThread stopped");
214  }
215 
216  // Disconnect clients (stop receiving thread, close socket)
217  std::vector< int > clientIds;
218  {
219  // Get all the client ids and release the lock
220  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
221  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
222  {
223  clientIds.push_back(clientIterator->ClientId);
224  }
225  }
226  for (std::vector< int >::iterator it = clientIds.begin(); it != clientIds.end(); ++it)
227  {
228  DisconnectClient(*it);
229  }
230 
231  LOG_INFO("Plus OpenIGTLink server stopped.");
232 
233  return PLUS_SUCCESS;
234 }
235 
236 //----------------------------------------------------------------------------
237 void* vtkPlusOpenIGTLinkServer::ConnectionReceiverThread(vtkMultiThreader::ThreadInfo* data)
238 {
240 
241  int r = self->ServerSocket->CreateServer(self->ListeningPort);
242  if (r < 0)
243  {
244  LOG_ERROR("Cannot create a server socket.");
245  return NULL;
246  }
247 
248  PrintServerInfo(self);
249 
250  self->ConnectionActive.Respond = true;
251 
252  // Wait for connections until we want to stop the thread
253  while (self->ConnectionActive.Request)
254  {
255  igtl::ClientSocket::Pointer newClientSocket = self->ServerSocket->WaitForConnection(CLIENT_SOCKET_TIMEOUT_SEC * 1000);
256  if (newClientSocket.IsNotNull())
257  {
258  // Lock before we change the clients list
259  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self->IgtlClientsMutex);
260  ClientData newClient;
261  self->IgtlClients.push_back(newClient);
262  self->NewClientConnected = true;
263 
264  ClientData* client = &(self->IgtlClients.back()); // get a reference to the client data that is stored in the list
265  client->ClientId = self->ClientIdCounter;
266  self->ClientIdCounter++;
267  client->ClientSocket = newClientSocket;
268  client->ClientSocket->SetReceiveTimeout(self->DefaultClientReceiveTimeoutSec * 1000);
269  client->ClientSocket->SetSendTimeout(self->DefaultClientSendTimeoutSec * 1000);
270  client->ClientInfo = self->DefaultClientInfo;
271  client->Server = self;
272 
273  // Setup vtkIGSIOFrameConverters for each stream
274  for (std::vector<PlusIgtlClientInfo::ImageStream>::iterator imageStreamIterator = client->ClientInfo.ImageStreams.begin();
275  imageStreamIterator != client->ClientInfo.ImageStreams.end(); ++imageStreamIterator)
276  {
277  PlusIgtlClientInfo::ImageStream* imageStream = &(*imageStreamIterator);
278  if (!imageStream->FrameConverter)
279  {
280  imageStream->FrameConverter = vtkSmartPointer<vtkIGSIOFrameConverter>::New();
281  }
282  }
283  for (std::vector<PlusIgtlClientInfo::VideoStream>::iterator videoStreamIterator = client->ClientInfo.VideoStreams.begin();
284  videoStreamIterator != client->ClientInfo.VideoStreams.end(); ++videoStreamIterator)
285  {
286  PlusIgtlClientInfo::VideoStream* videoStream = &(*videoStreamIterator);
287  if (!videoStream->FrameConverter)
288  {
289  videoStream->FrameConverter = vtkSmartPointer<vtkIGSIOFrameConverter>::New();
290  }
291  }
292 
293  int port = 0;
294  std::string address = "unknown";
295 #if (OPENIGTLINK_VERSION_MAJOR > 1) || ( OPENIGTLINK_VERSION_MAJOR == 1 && OPENIGTLINK_VERSION_MINOR > 9 ) || ( OPENIGTLINK_VERSION_MAJOR == 1 && OPENIGTLINK_VERSION_MINOR == 9 && OPENIGTLINK_VERSION_PATCH > 4 )
296  newClientSocket->GetSocketAddressAndPort(address, port);
297 #endif
298  LOG_INFO("Received new client connection (client " << client->ClientId << " at " << address << ":" << port << "). Number of connected clients: " << self->GetNumberOfConnectedClients());
299 
300  client->DataReceiverActive.first = true;
301  client->DataReceiverThreadId = self->Threader->SpawnThread((vtkThreadFunctionType)&DataReceiverThread, client);
302  }
303  }
304 
305  // Close server socket
306  if (self->ServerSocket.IsNotNull())
307  {
308  self->ServerSocket->CloseSocket();
309  }
310 
311  // Close thread
312  self->ConnectionReceiverThreadId = -1;
313  self->ConnectionActive.Respond = false;
314  return NULL;
315 }
316 
317 //----------------------------------------------------------------------------
318 void* vtkPlusOpenIGTLinkServer::DataSenderThread(vtkMultiThreader::ThreadInfo* data)
319 {
321  self->DataSenderActive.Respond = true;
322 
323  vtkPlusDevice* aDevice(NULL);
324  vtkPlusChannel* aChannel(NULL);
325 
326  DeviceCollection aCollection;
327  if (self->DataCollector->GetDevices(aCollection) != PLUS_SUCCESS || aCollection.size() == 0)
328  {
329  LOG_ERROR("Unable to retrieve devices. Check configuration and connection.");
330  return NULL;
331  }
332 
333  // Find the requested channel ID in all the devices
334  for (DeviceCollectionIterator it = aCollection.begin(); it != aCollection.end(); ++it)
335  {
336  aDevice = *it;
337  if (aDevice->GetOutputChannelByName(aChannel, self->GetOutputChannelId()) == PLUS_SUCCESS)
338  {
339  break;
340  }
341  }
342 
343  if (aChannel == NULL)
344  {
345  // The requested channel ID is not found
346  if (!self->GetOutputChannelId().empty())
347  {
348  // the user explicitly requested a specific channel, but none was found by that name
349  // this is an error
350  LOG_ERROR("Unable to start data sending. OutputChannelId not found: " << self->GetOutputChannelId());
351  return NULL;
352  }
353  // the user did not specify any channel, so just use the first channel that can be found in any device
354  for (DeviceCollectionIterator it = aCollection.begin(); it != aCollection.end(); ++it)
355  {
356  aDevice = *it;
357  if (aDevice->OutputChannelCount() > 0)
358  {
359  aChannel = *(aDevice->GetOutputChannelsStart());
360  break;
361  }
362  }
363  }
364 
365  // If we didn't find any channel then return
366  if (aChannel == NULL)
367  {
368  LOG_WARNING("There are no channels to broadcast. Only command processing is available.");
369  }
370 
371  self->BroadcastChannel = aChannel;
372  if (self->BroadcastChannel)
373  {
374  self->BroadcastChannel->GetMostRecentTimestamp(self->LastSentTrackedFrameTimestamp);
375  }
376 
377  double elapsedTimeSinceLastPacketSentSec = 0;
378  while (self->ConnectionActive.Request && self->DataSenderActive.Request)
379  {
380  bool clientsConnected = false;
381  {
382  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self->IgtlClientsMutex);
383  if (!self->IgtlClients.empty())
384  {
385  clientsConnected = true;
386  }
387  }
388  if (!clientsConnected)
389  {
390  // No client connected, wait for a while
391  vtkIGSIOAccurateTimer::Delay(0.2);
392  self->LastSentTrackedFrameTimestamp = 0; // next time start sending from the most recent timestamp
393  continue;
394  }
395 
396  if (self->HasGracePeriodExpired())
397  {
398  self->GracePeriodLogLevel = vtkPlusLogger::LOG_LEVEL_WARNING;
399  }
400 
401  SendMessageResponses(*self);
402 
403  // Send remote command execution replies to clients before sending any images/transforms/etc...
404  SendCommandResponses(*self);
405 
406  // Send image/tracking/string data
407  SendLatestFramesToClients(*self, elapsedTimeSinceLastPacketSentSec);
408  }
409  // Close thread
410  self->DataSenderThreadId = -1;
411  self->DataSenderActive.Respond = false;
412  return NULL;
413 }
414 
415 //----------------------------------------------------------------------------
417 {
418  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackedFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
419  double startTimeSec = vtkIGSIOAccurateTimer::GetSystemTime();
420 
421  // Acquire tracked frames since last acquisition (minimum 1 frame)
422  if (self.LastProcessingTimePerFrameMs < 1)
423  {
424  // if processing was less than 1ms/frame then assume it was 1ms (1000FPS processing speed) to avoid division by zero
425  self.LastProcessingTimePerFrameMs = 1;
426  }
427  int numberOfFramesToGet = std::max(self.MaxTimeSpentWithProcessingMs / self.LastProcessingTimePerFrameMs, 1);
428  // Maximize the number of frames to send
429  numberOfFramesToGet = std::min(numberOfFramesToGet, self.MaxNumberOfIgtlMessagesToSend);
430 
431  if (self.BroadcastChannel != NULL)
432  {
433  if ((self.BroadcastChannel->HasVideoSource() && !self.BroadcastChannel->GetVideoDataAvailable())
434  || (self.BroadcastChannel->ToolCount() > 0 && !self.BroadcastChannel->GetTrackingDataAvailable())
435  || (self.BroadcastChannel->FieldCount() > 0 && !self.BroadcastChannel->GetFieldDataAvailable()))
436  {
437  if (self.LogWarningOnNoDataAvailable)
438  {
439  LOG_DYNAMIC("No data is broadcasted, as no data is available yet.", self.GracePeriodLogLevel);
440  }
441  }
442  else
443  {
444  double oldestDataTimestamp = 0;
445  if (self.BroadcastChannel->GetOldestTimestamp(oldestDataTimestamp) == PLUS_SUCCESS)
446  {
447  if (self.LastSentTrackedFrameTimestamp < oldestDataTimestamp)
448  {
449  LOG_INFO("OpenIGTLink broadcasting started. No data was available between " << self.LastSentTrackedFrameTimestamp << "-" << oldestDataTimestamp << "sec, therefore no data were broadcasted during this time period.");
450  self.LastSentTrackedFrameTimestamp = oldestDataTimestamp + SAMPLING_SKIPPING_MARGIN_SEC;
451  }
452  static vtkIGSIOLogHelper logHelper(60.0, 500000);
453  CUSTOM_RETURN_WITH_FAIL_IF(self.BroadcastChannel->GetTrackedFrameList(self.LastSentTrackedFrameTimestamp, trackedFrameList, numberOfFramesToGet) != PLUS_SUCCESS,
454  "Failed to get tracked frame list from data collector (last recorded timestamp: " << std::fixed << self.LastSentTrackedFrameTimestamp);
455  }
456  }
457  }
458 
459  // There is no new frame in the buffer
460  if (trackedFrameList->GetNumberOfTrackedFrames() == 0)
461  {
462  vtkIGSIOAccurateTimer::Delay(DELAY_ON_NO_NEW_FRAMES_SEC);
463  elapsedTimeSinceLastPacketSentSec += vtkIGSIOAccurateTimer::GetSystemTime() - startTimeSec;
464 
465  // Send keep alive packet to clients
466  if (elapsedTimeSinceLastPacketSentSec > self.KeepAliveIntervalSec)
467  {
468  self.KeepAlive();
469  elapsedTimeSinceLastPacketSentSec = 0;
470  return PLUS_SUCCESS;
471  }
472 
473  return PLUS_FAIL;
474  }
475 
476  for (unsigned int i = 0; i < trackedFrameList->GetNumberOfTrackedFrames(); ++i)
477  {
478  // Send tracked frame
479  self.SendTrackedFrame(*trackedFrameList->GetTrackedFrame(i));
480  elapsedTimeSinceLastPacketSentSec = 0;
481  }
482 
483  // Compute time spent with processing one frame in this round
484  double computationTimeMs = (vtkIGSIOAccurateTimer::GetSystemTime() - startTimeSec) * 1000.0;
485 
486  // Update last processing time if new tracked frames have been acquired
487  if (trackedFrameList->GetNumberOfTrackedFrames() > 0)
488  {
489  self.LastProcessingTimePerFrameMs = computationTimeMs / trackedFrameList->GetNumberOfTrackedFrames();
490  }
491  return PLUS_SUCCESS;
492 }
493 
494 //----------------------------------------------------------------------------
496 {
497  igsioLockGuard<vtkIGSIORecursiveCriticalSection> mutexGuardedLock(self.MessageResponseQueueMutex);
498  if (!self.MessageResponseQueue.empty())
499  {
500  for (ClientIdToMessageListMap::iterator it = self.MessageResponseQueue.begin(); it != self.MessageResponseQueue.end(); ++it)
501  {
502  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self.IgtlClientsMutex);
503  igtl::ClientSocket::Pointer clientSocket = NULL;
504 
505  for (std::list<ClientData>::iterator clientIterator = self.IgtlClients.begin(); clientIterator != self.IgtlClients.end(); ++clientIterator)
506  {
507  if (clientIterator->ClientId == it->first)
508  {
509  clientSocket = clientIterator->ClientSocket;
510  break;
511  }
512  }
513  if (clientSocket.IsNull())
514  {
515  LOG_WARNING("Message reply cannot be sent to client " << it->first << ", probably client has been disconnected.");
516  continue;
517  }
518 
519  for (std::vector<igtl::MessageBase::Pointer>::iterator messageIt = it->second.begin(); messageIt != it->second.end(); ++messageIt)
520  {
521  clientSocket->Send((*messageIt)->GetBufferPointer(), (*messageIt)->GetBufferSize());
522  }
523  }
524  self.MessageResponseQueue.clear();
525  }
526 
527  return PLUS_SUCCESS;
528 }
529 
530 //----------------------------------------------------------------------------
532 {
533  PlusCommandResponseList replies;
534  self.PlusCommandProcessor->PopCommandResponses(replies);
535  if (!replies.empty())
536  {
537  for (PlusCommandResponseList::iterator responseIt = replies.begin(); responseIt != replies.end(); responseIt++)
538  {
539  igtl::MessageBase::Pointer igtlResponseMessage = self.CreateIgtlMessageFromCommandResponse(*responseIt);
540  if (igtlResponseMessage.IsNull())
541  {
542  LOG_ERROR("Failed to create OpenIGTLink message from command response");
543  continue;
544  }
545  igtlResponseMessage->Pack();
546 
547  // Only send the response to the client that requested the command
548  LOG_DEBUG("Send command reply to client " << (*responseIt)->GetClientId() << ": " << igtlResponseMessage->GetDeviceName());
549  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self.IgtlClientsMutex);
550  igtl::ClientSocket::Pointer clientSocket = NULL;
551  for (std::list<ClientData>::iterator clientIterator = self.IgtlClients.begin(); clientIterator != self.IgtlClients.end(); ++clientIterator)
552  {
553  if (clientIterator->ClientId == (*responseIt)->GetClientId())
554  {
555  clientSocket = clientIterator->ClientSocket;
556  break;
557  }
558  }
559 
560  if (clientSocket.IsNull())
561  {
562  LOG_WARNING("Message reply cannot be sent to client " << (*responseIt)->GetClientId() << ", probably client has been disconnected");
563  continue;
564  }
565  clientSocket->Send(igtlResponseMessage->GetBufferPointer(), igtlResponseMessage->GetBufferSize());
566  }
567  }
568 
569  return PLUS_SUCCESS;
570 }
571 
572 //----------------------------------------------------------------------------
573 void* vtkPlusOpenIGTLinkServer::DataReceiverThread(vtkMultiThreader::ThreadInfo* data)
574 {
575  ClientData* client = (ClientData*)(data->UserData);
576  client->DataReceiverActive.second = true;
577  vtkPlusOpenIGTLinkServer* self = client->Server;
578 
580  std::deque<uint32_t> previousCommandIds;
581 
582  // Make copy of frequently used data to avoid locking of client data
583  igtl::ClientSocket::Pointer clientSocket = client->ClientSocket;
584  int clientId = client->ClientId;
585 
586  igtl::MessageHeader::Pointer headerMsg = self->IgtlMessageFactory->CreateHeaderMessage(IGTL_HEADER_VERSION_1);
587 
588  while (client->DataReceiverActive.first)
589  {
590  headerMsg->InitBuffer();
591 
592  // Receive generic header from the socket
593  bool timeout(false);
594  igtlUint64 bytesReceived = clientSocket->Receive(headerMsg->GetBufferPointer(), headerMsg->GetBufferSize(), timeout);
595  if (bytesReceived == IGTL_EMPTY_DATA_SIZE || bytesReceived != headerMsg->GetBufferSize())
596  {
597  vtkIGSIOAccurateTimer::Delay(0.1);
598  continue;
599  }
600 
601  headerMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
602 
603  {
604  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self->IgtlClientsMutex);
605  // Keep track of the highest known version of message ever sent by this client, this is the version that we reply with
606  // (upper bounded by the servers version)
607  if (headerMsg->GetHeaderVersion() > client->ClientInfo.GetClientHeaderVersion())
608  {
609  client->ClientInfo.SetClientHeaderVersion(std::min<int>(self->GetIGTLHeaderVersion(), headerMsg->GetHeaderVersion()));
610  }
611  }
612 
613  igtl::MessageBase::Pointer bodyMessage = self->IgtlMessageFactory->CreateReceiveMessage(headerMsg);
614  if (bodyMessage.IsNull())
615  {
616  LOG_ERROR("Unable to receive message from client: " << client->ClientId);
617  continue;
618  }
619 
620  if (typeid(*bodyMessage) == typeid(igtl::PlusClientInfoMessage))
621  {
622  igtl::PlusClientInfoMessage::Pointer clientInfoMsg = dynamic_cast<igtl::PlusClientInfoMessage*>(bodyMessage.GetPointer());
623  clientInfoMsg->SetMessageHeader(headerMsg);
624  clientInfoMsg->AllocateBuffer();
625 
626  bool timeout(false);
627  clientSocket->Receive(clientInfoMsg->GetBufferBodyPointer(), clientInfoMsg->GetBufferBodySize(), timeout);
628 
629  int c = clientInfoMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
630  if (c & igtl::MessageHeader::UNPACK_BODY || clientInfoMsg->GetBufferBodySize() == 0)
631  {
632  // Message received from client, need to lock to modify client info
633  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(self->IgtlClientsMutex);
634  client->ClientInfo = clientInfoMsg->GetClientInfo();
635  LOG_DEBUG("Client info message received from client " << clientId);
636  }
637  }
638  else if (typeid(*bodyMessage) == typeid(igtl::GetStatusMessage))
639  {
640  // Just ping server, we can skip message and respond
641  clientSocket->Skip(headerMsg->GetBodySizeToRead(), 0);
642 
643  igtl::StatusMessage::Pointer replyMsg = dynamic_cast<igtl::StatusMessage*>(self->IgtlMessageFactory->CreateSendMessage("STATUS", client->ClientInfo.GetClientHeaderVersion()).GetPointer());
644  replyMsg->SetCode(igtl::StatusMessage::STATUS_OK);
645  replyMsg->Pack();
646  clientSocket->Send(replyMsg->GetBufferPointer(), replyMsg->GetBufferSize());
647  }
648  else if (typeid(*bodyMessage) == typeid(igtl::StringMessage)
649  && vtkPlusCommand::IsCommandDeviceName(headerMsg->GetDeviceName()))
650  {
651  igtl::StringMessage::Pointer stringMsg = dynamic_cast<igtl::StringMessage*>(bodyMessage.GetPointer());
652  stringMsg->SetMessageHeader(headerMsg);
653  stringMsg->AllocateBuffer();
654  bool timeout(false);
655  clientSocket->Receive(stringMsg->GetBufferBodyPointer(), stringMsg->GetBufferBodySize(), timeout);
656 
657  // We are receiving old style commands, handle it
658  int c = stringMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
659  if (c & igtl::MessageHeader::UNPACK_BODY || stringMsg->GetBufferBodySize() == 0)
660  {
661  std::string deviceName(headerMsg->GetDeviceName());
662  if (deviceName.empty())
663  {
664  self->PlusCommandProcessor->QueueStringResponse(PLUS_FAIL, std::string(vtkPlusCommand::DEVICE_NAME_REPLY), clientId, "Unable to read DeviceName.");
665  continue;
666  }
667 
668  uint32_t uid(0);
669  try
670  {
671 #if (_MSC_VER == 1500)
673  ss >> uid;
674 #else
676 #endif
677  }
678  catch (std::invalid_argument e)
679  {
680  LOG_ERROR("Unable to extract command UID from device name string.");
681  // Removing support for malformed command strings, reply with error
682  self->PlusCommandProcessor->QueueStringResponse(PLUS_FAIL, std::string(vtkPlusCommand::DEVICE_NAME_REPLY), clientId, "Malformed DeviceName. Expected CMD_cmdId (ex: CMD_001)");
683  continue;
684  }
685 
687 
688  if (std::find(previousCommandIds.begin(), previousCommandIds.end(), uid) != previousCommandIds.end())
689  {
690  // Command already exists
691  LOG_WARNING("Already received a command with id = " << uid << " from client " << clientId << ". This repeated command will be ignored.");
692  continue;
693  }
694  // New command, remember its ID
695  previousCommandIds.push_back(uid);
696  if (previousCommandIds.size() > NUMBER_OF_RECENT_COMMAND_IDS_STORED)
697  {
698  previousCommandIds.pop_front();
699  }
700 
701  LOG_DEBUG("Received command from client " << clientId << ", device " << deviceName << " with UID " << uid << ": " << stringMsg->GetString());
702 
703  vtkSmartPointer<vtkXMLDataElement> cmdElement = vtkSmartPointer<vtkXMLDataElement>::Take(vtkXMLUtilities::ReadElementFromString(stringMsg->GetString()));
704  std::string commandName = std::string(cmdElement->GetAttribute("Name") == NULL ? "" : cmdElement->GetAttribute("Name"));
705 
706  self->PlusCommandProcessor->QueueCommand(false, clientId, commandName, stringMsg->GetString(), deviceName, uid, stringMsg->GetMetaData());
707  }
708 
709  }
710  else if (typeid(*bodyMessage) == typeid(igtl::CommandMessage))
711  {
712  igtl::CommandMessage::Pointer commandMsg = dynamic_cast<igtl::CommandMessage*>(bodyMessage.GetPointer());
713  commandMsg->SetMessageHeader(headerMsg);
714  commandMsg->AllocateBuffer();
715  bool timeout(false);
716  clientSocket->Receive(commandMsg->GetBufferBodyPointer(), commandMsg->GetBufferBodySize(), timeout);
717 
718  int c = commandMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
719  if (c & igtl::MessageHeader::UNPACK_BODY || commandMsg->GetBufferBodySize() == 0)
720  {
721  std::string deviceName(headerMsg->GetDeviceName());
722 
723  uint32_t uid;
724  uid = commandMsg->GetCommandId();
725 
726  if (std::find(previousCommandIds.begin(), previousCommandIds.end(), uid) != previousCommandIds.end())
727  {
728  // Command already exists
729  LOG_WARNING("Already received a command with id = " << uid << " from client " << clientId << ". This repeated command will be ignored.");
730  continue;
731  }
732  // New command, remember its ID
733  previousCommandIds.push_back(uid);
734  if (previousCommandIds.size() > NUMBER_OF_RECENT_COMMAND_IDS_STORED)
735  {
736  previousCommandIds.pop_front();
737  }
738 
739  LOG_DEBUG("Received header version " << commandMsg->GetHeaderVersion() << " command " << commandMsg->GetCommandName()
740  << " from client " << clientId << ", device " << deviceName << " with UID " << uid << ": " << commandMsg->GetCommandContent());
741 
742  self->PlusCommandProcessor->QueueCommand(true, clientId, commandMsg->GetCommandName(), commandMsg->GetCommandContent(), deviceName, uid, commandMsg->GetMetaData());
743  }
744  else
745  {
746  LOG_ERROR("STRING message unpacking failed for client " << clientId);
747  }
748  }
749  else if (typeid(*bodyMessage) == typeid(igtl::StartTrackingDataMessage))
750  {
751  std::string deviceName("");
752 
753  igtl::StartTrackingDataMessage::Pointer startTracking = dynamic_cast<igtl::StartTrackingDataMessage*>(bodyMessage.GetPointer());
754  startTracking->SetMessageHeader(headerMsg);
755  startTracking->AllocateBuffer();
756  bool timeout(false);
757  clientSocket->Receive(startTracking->GetBufferBodyPointer(), startTracking->GetBufferBodySize(), timeout);
758 
759  int c = startTracking->Unpack(self->IgtlMessageCrcCheckEnabled);
760  if (c & igtl::MessageHeader::UNPACK_BODY || startTracking->GetBufferBodySize() == 0)
761  {
762  client->ClientInfo.SetTDATAResolution(startTracking->GetResolution());
763  client->ClientInfo.SetTDATARequested(true);
764  }
765  else
766  {
767  LOG_ERROR("Client " << clientId << " STT_TDATA failed: could not retrieve startTracking message");
768  return NULL;
769  }
770 
771  igtl::MessageBase::Pointer msg = self->IgtlMessageFactory->CreateSendMessage("RTS_TDATA", client->ClientInfo.GetClientHeaderVersion());
772  igtl::RTSTrackingDataMessage* rtsMsg = dynamic_cast<igtl::RTSTrackingDataMessage*>(msg.GetPointer());
773  rtsMsg->SetStatus(0);
774  rtsMsg->Pack();
775  self->QueueMessageResponseForClient(client->ClientId, msg);
776  }
777  else if (typeid(*bodyMessage) == typeid(igtl::StopTrackingDataMessage))
778  {
779  igtl::StopTrackingDataMessage::Pointer stopTracking = dynamic_cast<igtl::StopTrackingDataMessage*>(bodyMessage.GetPointer());
780  stopTracking->SetMessageHeader(headerMsg);
781  stopTracking->AllocateBuffer();
782  bool timeout(false);
783  clientSocket->Receive(stopTracking->GetBufferBodyPointer(), stopTracking->GetBufferBodySize(), timeout);
784 
785  client->ClientInfo.SetTDATARequested(false);
786  igtl::MessageBase::Pointer msg = self->IgtlMessageFactory->CreateSendMessage("RTS_TDATA", client->ClientInfo.GetClientHeaderVersion());
787  igtl::RTSTrackingDataMessage* rtsMsg = dynamic_cast<igtl::RTSTrackingDataMessage*>(msg.GetPointer());
788  rtsMsg->SetStatus(0);
789  rtsMsg->Pack();
790  self->QueueMessageResponseForClient(client->ClientId, msg);
791  }
792  else if (typeid(*bodyMessage) == typeid(igtl::GetPolyDataMessage))
793  {
794  igtl::GetPolyDataMessage::Pointer polyDataMessage = dynamic_cast<igtl::GetPolyDataMessage*>(bodyMessage.GetPointer());
795  polyDataMessage->SetMessageHeader(headerMsg);
796  polyDataMessage->AllocateBuffer();
797  bool timeout(false);
798  clientSocket->Receive(polyDataMessage->GetBufferBodyPointer(), polyDataMessage->GetBufferBodySize(), timeout);
799 
800  int c = polyDataMessage->Unpack(self->IgtlMessageCrcCheckEnabled);
801  if (c & igtl::MessageHeader::UNPACK_BODY || polyDataMessage->GetBufferBodySize() == 0)
802  {
803  std::string fileName;
804  // Check metadata for requisite parameters, if absent, check deviceName
805  if (polyDataMessage->GetHeaderVersion() > IGTL_HEADER_VERSION_1)
806  {
807  if (!polyDataMessage->GetMetaDataElement("filename", fileName))
808  {
809  fileName = polyDataMessage->GetDeviceName();
810  if (fileName.empty())
811  {
812  LOG_ERROR("GetPolyData message sent with no filename in either metadata or deviceName field.");
813  continue;
814  }
815  }
816  }
817  else
818  {
819  fileName = polyDataMessage->GetDeviceName();
820  if (fileName.empty())
821  {
822  LOG_ERROR("GetPolyData message sent with no filename in either metadata or deviceName field.");
823  continue;
824  }
825  }
826 
827  vtkSmartPointer<vtkPolyDataReader> reader = vtkSmartPointer<vtkPolyDataReader>::New();
828  reader->SetFileName(fileName.c_str());
829  reader->Update();
830 
831  auto polyData = reader->GetOutput();
832  if (polyData != nullptr)
833  {
834  igtl::MessageBase::Pointer msg = self->IgtlMessageFactory->CreateSendMessage("POLYDATA", client->ClientInfo.GetClientHeaderVersion());
835  igtl::PolyDataMessage* polyMsg = dynamic_cast<igtl::PolyDataMessage*>(msg.GetPointer());
836 
837  igtlioPolyDataConverter::ContentData data;
838  data.deviceName = "PlusServer";
839  data.polydata = polyData;
840 
841  igtlioBaseConverter::HeaderData header;
842  header.deviceName = "PlusServer";
843 
844  igtlioPolyDataConverter::toIGTL(header, data, (igtl::PolyDataMessage::Pointer*)&msg);
845  if (!msg->SetMetaDataElement("fileName", IANA_TYPE_US_ASCII, fileName))
846  {
847  LOG_ERROR("Filename too long to be sent back to client. Aborting.");
848  continue;
849  }
850  self->QueueMessageResponseForClient(client->ClientId, msg);
851  continue;
852  }
853 
854  igtl::MessageBase::Pointer msg = self->IgtlMessageFactory->CreateSendMessage("RTS_POLYDATA", polyDataMessage->GetHeaderVersion());
855  igtl::RTSPolyDataMessage* rtsPolyMsg = dynamic_cast<igtl::RTSPolyDataMessage*>(msg.GetPointer());
856  rtsPolyMsg->SetStatus(false);
857  self->QueueMessageResponseForClient(client->ClientId, rtsPolyMsg);
858  }
859  else
860  {
861  LOG_ERROR("Client " << clientId << " GET_POLYDATA failed: could not retrieve message");
862  return NULL;
863  }
864  }
865  else if (typeid(*bodyMessage) == typeid(igtl::StatusMessage))
866  {
867  // status message is used as a keep-alive, don't do anything
868  clientSocket->Skip(headerMsg->GetBodySizeToRead(), 0);
869  }
870  else if (typeid(*bodyMessage) == typeid(igtl::GetImageMetaMessage))
871  {
872  igtl::GetImageMetaMessage::Pointer getImageMetaMsg = dynamic_cast<igtl::GetImageMetaMessage*>(bodyMessage.GetPointer());
873  getImageMetaMsg->SetMessageHeader(headerMsg);
874  getImageMetaMsg->AllocateBuffer();
875  bool timeout(false);
876  clientSocket->Receive(getImageMetaMsg->GetBufferBodyPointer(), getImageMetaMsg->GetBufferBodySize(), timeout);
877 
878  int c = getImageMetaMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
879  if (c & igtl::MessageHeader::UNPACK_BODY || getImageMetaMsg->GetBufferBodySize() == 0)
880  {
881  // Image meta message
882  std::string deviceName("");
883  if (headerMsg->GetDeviceName() != NULL)
884  {
885  deviceName = headerMsg->GetDeviceName();
886  }
887  self->PlusCommandProcessor->QueueGetImageMetaData(clientId, deviceName);
888  }
889  else
890  {
891  LOG_ERROR("Client " << clientId << " GET_IMGMETA failed: could not retrieve message");
892  return NULL;
893  }
894  }
895  else if (typeid(*bodyMessage) == typeid(igtl::GetImageMessage))
896  {
897  igtl::GetImageMessage::Pointer getImageMsg = dynamic_cast<igtl::GetImageMessage*>(bodyMessage.GetPointer());
898  getImageMsg->SetMessageHeader(headerMsg);
899  getImageMsg->AllocateBuffer();
900  bool timeout(false);
901  clientSocket->Receive(getImageMsg->GetBufferBodyPointer(), getImageMsg->GetBufferBodySize(), timeout);
902 
903  int c = getImageMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
904  if (c & igtl::MessageHeader::UNPACK_BODY || getImageMsg->GetBufferBodySize() == 0)
905  {
906  std::string deviceName("");
907  if (headerMsg->GetDeviceName() != NULL)
908  {
909  deviceName = headerMsg->GetDeviceName();
910  }
911  else
912  {
913  LOG_ERROR("Please select the image you want to acquire");
914  return NULL;
915  }
916  self->PlusCommandProcessor->QueueGetImage(clientId, deviceName);
917  }
918  else
919  {
920  LOG_ERROR("Client " << clientId << " GET_IMAGE failed: could not retrieve message");
921  return NULL;
922  }
923 
924  }
925  else if (typeid(*bodyMessage) == typeid(igtl::GetPointMessage))
926  {
927  igtl::GetPointMessage* getPointMsg = dynamic_cast<igtl::GetPointMessage*>(bodyMessage.GetPointer());
928  getPointMsg->SetMessageHeader(headerMsg);
929  getPointMsg->AllocateBuffer();
930  bool timeout(false);
931  clientSocket->Receive(getPointMsg->GetBufferBodyPointer(), getPointMsg->GetBufferBodySize(), timeout);
932 
933  int c = getPointMsg->Unpack(self->IgtlMessageCrcCheckEnabled);
934  if (c & igtl::MessageHeader::UNPACK_BODY || getPointMsg->GetBufferBodySize() == 0)
935  {
936  std::string fileName;
937  if (!getPointMsg->GetMetaDataElement("Filename", fileName))
938  {
939  fileName = getPointMsg->GetDeviceName();
940  }
941 
942  if (igsioCommon::Tail(fileName, 4) != "fcsv")
943  {
944  LOG_WARNING("Filename does not end in fcsv. GetPoint behaviour may not function correctly.");
945  }
946 
947  if (!vtksys::SystemTools::FileExists(fileName) &&
948  !vtksys::SystemTools::FileExists(vtkPlusConfig::GetInstance()->GetImagePath(fileName)))
949  {
950  LOG_ERROR("File: " << fileName << " requested but does not exist. Cannot get POINT data from it.");
951  return NULL;
952  }
953 
954  igtl::MessageBase::Pointer msg = self->IgtlMessageFactory->CreateSendMessage("POINT", client->ClientInfo.GetClientHeaderVersion());
955  igtl::PointMessage* pointMsg = dynamic_cast<igtl::PointMessage*>(msg.GetPointer());
956 
957  std::ifstream t(fileName);
958  if (!t.is_open())
959  {
960  t.open(vtkPlusConfig::GetInstance()->GetImagePath(fileName));
961  if (!t.is_open())
962  {
963  LOG_ERROR("Cannot read file: " << fileName);
964  return NULL;
965  }
966  }
967  std::stringstream buffer;
968  buffer << t.rdbuf();
969  std::vector<std::string> lines = igsioCommon::SplitStringIntoTokens(buffer.str(), '\n', false);
970  for (std::vector<std::string>::iterator it = lines.begin(); it != lines.end(); ++it)
971  {
972  std::string line = igsioCommon::Trim(*it);
973  if (line[0] == '#')
974  {
975  continue;
976  }
977 
978  std::vector<std::string> tokens = igsioCommon::SplitStringIntoTokens(line, ',', true);
979  igtl::PointElement::Pointer elem = igtl::PointElement::New();
980  elem->SetPosition(std::stof(tokens[1]), std::stof(tokens[2]), std::stof(tokens[3]));
981  elem->SetName(tokens[0].c_str());
982  elem->SetGroupName("Point");
983  pointMsg->AddPointElement(elem);
984  }
985 
986  self->QueueMessageResponseForClient(client->ClientId, pointMsg);
987  }
988  else
989  {
990  LOG_ERROR("Client " << clientId << " GET_POINT failed: could not retrieve message");
991  return NULL;
992  }
993  }
994  else
995  {
996  // if the device type is unknown, skip reading.
997  LOG_WARNING("Unknown OpenIGTLink message is received from client " << clientId << ". Device type: " << headerMsg->GetMessageType()
998  << ". Device name: " << headerMsg->GetDeviceName() << ".");
999  clientSocket->Skip(headerMsg->GetBodySizeToRead(), 0);
1000  continue;
1001  }
1002  } // ConnectionActive
1003 
1004  // Close thread
1005  client->DataReceiverActive.second = false;
1006  return NULL;
1007 }
1008 
1009 //----------------------------------------------------------------------------
1011 {
1012  int numberOfErrors = 0;
1013 
1014  // Update transform repository with the tracked frame
1015  if (this->TransformRepository != NULL)
1016  {
1017  if (this->TransformRepository->SetTransforms(trackedFrame) != PLUS_SUCCESS)
1018  {
1019  LOG_ERROR("Failed to set current transforms to transform repository");
1020  numberOfErrors++;
1021  }
1022  }
1023 
1024  // Convert relative timestamp to UTC
1025  double timestampSystem = trackedFrame.GetTimestamp(); // save original timestamp, we'll restore it later
1026  double timestampUniversal = vtkIGSIOAccurateTimer::GetUniversalTimeFromSystemTime(timestampSystem);
1027  trackedFrame.SetTimestamp(timestampUniversal);
1028 
1029  std::vector<int> disconnectedClientIds;
1030  {
1031  // Lock before we send message to the clients
1032  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1033  if (this->NewClientConnected)
1034  {
1035  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1036  {
1037  std::vector<PlusIgtlClientInfo::VideoStream> videoStreams = (*clientIterator).ClientInfo.VideoStreams;
1038  for (std::vector<PlusIgtlClientInfo::VideoStream>::iterator videoStream = videoStreams.begin(); videoStream != videoStreams.end(); ++videoStream)
1039  {
1040  vtkIGSIOFrameConverter* frameConverter = videoStream->FrameConverter;
1041  if (frameConverter)
1042  {
1043  frameConverter->RequestKeyFrameOn();
1044  }
1045  }
1046  }
1047  }
1048  this->NewClientConnected = false;
1049 
1050  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1051  {
1052  igtl::ClientSocket::Pointer clientSocket = (*clientIterator).ClientSocket;
1053 
1054  // Create IGT messages
1055  std::vector<igtl::MessageBase::Pointer> igtlMessages;
1056  std::vector<igtl::MessageBase::Pointer>::iterator igtlMessageIterator;
1057 
1058  if (this->IgtlMessageFactory->PackMessages(clientIterator->ClientId, clientIterator->ClientInfo, igtlMessages, trackedFrame, this->SendValidTransformsOnly, this->TransformRepository) != PLUS_SUCCESS)
1059  {
1060  LOG_WARNING("Failed to pack all IGT messages");
1061  }
1062 
1063  // Send all messages to a client
1064  for (igtlMessageIterator = igtlMessages.begin(); igtlMessageIterator != igtlMessages.end(); ++igtlMessageIterator)
1065  {
1066  igtl::MessageBase::Pointer igtlMessage = (*igtlMessageIterator);
1067  if (igtlMessage.IsNull())
1068  {
1069  continue;
1070  }
1071 
1072  int retValue = 0;
1073  RETRY_UNTIL_TRUE((retValue = clientSocket->Send(igtlMessage->GetBufferPointer(), igtlMessage->GetBufferSize())) != 0, this->NumberOfRetryAttempts, this->DelayBetweenRetryAttemptsSec);
1074  if (retValue == 0)
1075  {
1076  disconnectedClientIds.push_back(clientIterator->ClientId);
1077  auto ts = igtl::TimeStamp::New();
1078  igtlMessage->GetTimeStamp(ts);
1079  LOG_INFO("Client disconnected - could not send " << igtlMessage->GetMessageType() << " message to client (device name: " << igtlMessage->GetDeviceName()
1080  << " Timestamp: " << std::fixed << ts->GetTimeStamp() << ").");
1081  break;
1082  }
1083 
1084  // Update the TDATA timestamp, even if TDATA isn't sent (cheaper than checking for existing TDATA message type)
1085  clientIterator->ClientInfo.SetLastTDATASentTimeStamp(trackedFrame.GetTimestamp());
1086  }
1087  }
1088  }
1089 
1090  // Clean up disconnected clients
1091  for (std::vector< int >::iterator it = disconnectedClientIds.begin(); it != disconnectedClientIds.end(); ++it)
1092  {
1093  DisconnectClient(*it);
1094  }
1095 
1096  // restore original timestamp
1097  trackedFrame.SetTimestamp(timestampSystem);
1098 
1099  return (numberOfErrors == 0 ? PLUS_SUCCESS : PLUS_FAIL);
1100 }
1101 
1102 //----------------------------------------------------------------------------
1104 {
1105  // Stop the client's data receiver thread
1106  {
1107  // Request thread stop
1108  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1109  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1110  {
1111  if (clientIterator->ClientId != clientId)
1112  {
1113  continue;
1114  }
1115  clientIterator->DataReceiverActive.first = false;
1116  break;
1117  }
1118  }
1119 
1120  // Wait for the thread to stop
1121  bool clientDataReceiverThreadStillActive = false;
1122  do
1123  {
1124  clientDataReceiverThreadStillActive = false;
1125  {
1126  // check if any of the receiver threads are still active
1127  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1128  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1129  {
1130  if (clientIterator->ClientId != clientId)
1131  {
1132  continue;
1133  }
1134  if (clientIterator->DataReceiverThreadId >= 0)
1135  {
1136  if (clientIterator->DataReceiverActive.second)
1137  {
1138  // thread still running
1139  clientDataReceiverThreadStillActive = true;
1140  }
1141  else
1142  {
1143  // thread stopped
1144  // We still need to call vtkMultiThreader::TerminateThread, not to terminate the thread (as it is already stopped)
1145  // but to indicate to the multithreader that the thread ID can be reused. Without this, vtkMultiThreader runs out
1146  // of usable thread IDs after the number of connects/disconnects reaches VTK_MAX_THREADS
1147  this->Threader->TerminateThread(clientIterator->DataReceiverThreadId);
1148  clientIterator->DataReceiverThreadId = -1;
1149  }
1150  break;
1151  }
1152  }
1153  }
1154  if (clientDataReceiverThreadStillActive)
1155  {
1156  // give some time for the threads to finish
1157  vtkIGSIOAccurateTimer::DelayWithEventProcessing(0.2);
1158  }
1159  }
1160  while (clientDataReceiverThreadStillActive);
1161 
1162  // Close socket and remove client from the list
1163  int port = 0;
1164  std::string address = "unknown";
1165  {
1166  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1167  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1168  {
1169  if (clientIterator->ClientId != clientId)
1170  {
1171  continue;
1172  }
1173  if (clientIterator->ClientSocket.IsNotNull())
1174  {
1175 #if (OPENIGTLINK_VERSION_MAJOR > 1) || ( OPENIGTLINK_VERSION_MAJOR == 1 && OPENIGTLINK_VERSION_MINOR > 9 ) || ( OPENIGTLINK_VERSION_MAJOR == 1 && OPENIGTLINK_VERSION_MINOR == 9 && OPENIGTLINK_VERSION_PATCH > 4 )
1176  clientIterator->ClientSocket->GetSocketAddressAndPort(address, port);
1177 #endif
1178  clientIterator->ClientSocket->CloseSocket();
1179  }
1180  this->IgtlClients.erase(clientIterator);
1181  break;
1182  }
1183  }
1184 
1185  LOG_INFO("Client disconnected (" << address << ":" << port << "). Number of connected clients: " << GetNumberOfConnectedClients());
1186 }
1187 
1188 //----------------------------------------------------------------------------
1190 {
1191  LOG_TRACE("Keep alive packet sent to clients...");
1192 
1193  std::vector< int > disconnectedClientIds;
1194 
1195  {
1196  // Lock before we send message to the clients
1197  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1198 
1199  for (std::list<ClientData>::iterator clientIterator = this->IgtlClients.begin(); clientIterator != this->IgtlClients.end(); ++clientIterator)
1200  {
1201  auto replyMsg = igtl::StatusMessage::New();
1202  replyMsg->SetCode(igtl::StatusMessage::STATUS_OK);
1203  replyMsg->Pack();
1204 
1205  int retValue = 0;
1206  RETRY_UNTIL_TRUE(
1207  (retValue = clientIterator->ClientSocket->Send(replyMsg->GetPackPointer(), replyMsg->GetPackSize())) != 0,
1208  this->NumberOfRetryAttempts, this->DelayBetweenRetryAttemptsSec);
1209  if (retValue == 0)
1210  {
1211  disconnectedClientIds.push_back(clientIterator->ClientId);
1212  auto ts = igtl::TimeStamp::New();
1213  replyMsg->GetTimeStamp(ts);
1214 
1215  LOG_DEBUG("Client disconnected - could not send " << replyMsg->GetMessageType() << " message to client (device name: " << replyMsg->GetDeviceName()
1216  << " Timestamp: " << std::fixed << ts->GetTimeStamp() << ").");
1217  }
1218  } // clientIterator
1219  } // unlock client list
1220 
1221  // Clean up disconnected clients
1222  for (std::vector< int >::iterator it = disconnectedClientIds.begin(); it != disconnectedClientIds.end(); ++it)
1223  {
1224  DisconnectClient(*it);
1225  }
1226 }
1227 
1228 //------------------------------------------------------------------------------
1230 {
1231  // Lock before we send message to the clients
1232  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1233  return this->IgtlClients.size();
1234 }
1235 
1236 //------------------------------------------------------------------------------
1237 PlusStatus vtkPlusOpenIGTLinkServer::GetClientInfo(unsigned int clientId, PlusIgtlClientInfo& outClientInfo) const
1238 {
1239  igsioLockGuard<vtkIGSIORecursiveCriticalSection> igtlClientsMutexGuardedLock(this->IgtlClientsMutex);
1240  for (std::list<ClientData>::const_iterator it = this->IgtlClients.begin(); it != this->IgtlClients.end(); ++it)
1241  {
1242  if (it->ClientId == clientId)
1243  {
1244  outClientInfo = it->ClientInfo;
1245  return PLUS_SUCCESS;
1246  }
1247  }
1248 
1249  return PLUS_FAIL;
1250 }
1251 
1252 //------------------------------------------------------------------------------
1253 PlusStatus vtkPlusOpenIGTLinkServer::ReadConfiguration(vtkXMLDataElement* serverElement, const std::string& aFilename)
1254 {
1255  LOG_TRACE("vtkPlusOpenIGTLinkServer::ReadConfiguration");
1256 
1257  if (aFilename.empty())
1258  {
1259  LOG_ERROR("Unable to configure PlusServer without an acceptable config file submitted.");
1260  return PLUS_FAIL;
1261  }
1262  this->SetConfigFilename(aFilename);
1263 
1264  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, ListeningPort, serverElement);
1265  XML_READ_STRING_ATTRIBUTE_REQUIRED(OutputChannelId, serverElement);
1266  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, MissingInputGracePeriodSec, serverElement);
1267  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, MaxTimeSpentWithProcessingMs, serverElement);
1268  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, MaxNumberOfIgtlMessagesToSend, serverElement);
1269  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, NumberOfRetryAttempts, serverElement);
1270  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, DelayBetweenRetryAttemptsSec, serverElement);
1271  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, KeepAliveIntervalSec, serverElement);
1272  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(SendValidTransformsOnly, serverElement);
1273  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(IgtlMessageCrcCheckEnabled, serverElement);
1274  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(LogWarningOnNoDataAvailable, serverElement);
1275 
1276  this->DefaultClientInfo.IgtlMessageTypes.clear();
1277  this->DefaultClientInfo.TransformNames.clear();
1278  this->DefaultClientInfo.ImageStreams.clear();
1279  this->DefaultClientInfo.VideoStreams.clear();
1280  this->DefaultClientInfo.StringNames.clear();
1281  this->DefaultClientInfo.SetTDATAResolution(0);
1282  this->DefaultClientInfo.SetTDATARequested(false);
1283 
1284  vtkXMLDataElement* defaultClientInfo = serverElement->FindNestedElementWithName("DefaultClientInfo");
1285  if (defaultClientInfo != NULL)
1286  {
1287  if (this->DefaultClientInfo.SetClientInfoFromXmlData(defaultClientInfo) != PLUS_SUCCESS)
1288  {
1289  return PLUS_FAIL;
1290  }
1291  }
1292 
1293  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(float, DefaultClientSendTimeoutSec, serverElement);
1294  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(float, DefaultClientReceiveTimeoutSec, serverElement);
1295 
1296  return PLUS_SUCCESS;
1297 }
1298 
1299 //------------------------------------------------------------------------------
1301 {
1302  return this->PlusCommandProcessor->ExecuteCommands();
1303 }
1304 
1305 //------------------------------------------------------------------------------
1307 {
1308  return (vtkIGSIOAccurateTimer::GetSystemTime() - this->BroadcastStartTime) > this->MissingInputGracePeriodSec;
1309 }
1310 
1311 //------------------------------------------------------------------------------
1312 PlusStatus vtkPlusOpenIGTLinkServer::Start(vtkPlusDataCollector* dataCollector, vtkIGSIOTransformRepository* transformRepository, vtkXMLDataElement* serverElement, const std::string& configFilePath)
1313 {
1314  if (serverElement == NULL)
1315  {
1316  LOG_ERROR("NULL configuration sent to vtkPlusOpenIGTLinkServer::Start. Unable to start PlusServer.");
1317  return PLUS_FAIL;
1318  }
1319 
1320  this->SetDataCollector(dataCollector);
1321  if (this->ReadConfiguration(serverElement, configFilePath.c_str()) != PLUS_SUCCESS)
1322  {
1323  LOG_ERROR("Failed to read PlusOpenIGTLinkServer configuration");
1324  return PLUS_FAIL;
1325  }
1326 
1327  this->SetTransformRepository(transformRepository);
1328  if (this->StartOpenIGTLinkService() != PLUS_SUCCESS)
1329  {
1330  LOG_ERROR("Failed to start Plus OpenIGTLink server");
1331  return PLUS_FAIL;
1332  }
1333 
1334  return PLUS_SUCCESS;
1335 }
1336 
1337 //------------------------------------------------------------------------------
1339 {
1340  PlusStatus status = PLUS_SUCCESS;
1341 
1343  {
1344  status = PLUS_FAIL;
1345  }
1346 
1347  SetDataCollector(NULL);
1348 
1349  SetTransformRepository(NULL);
1350 
1351  return status;
1352 }
1353 
1354 //------------------------------------------------------------------------------
1356 {
1357  int replyHeaderVersion = IGTL_HEADER_VERSION_1;
1358  PlusIgtlClientInfo info;
1359  if (GetClientInfo(response->GetClientId(), info) == PLUS_FAIL)
1360  {
1361  LOG_ERROR("Unable to find client with ID: " << response->GetClientId());
1362  }
1363  else
1364  {
1365  replyHeaderVersion = info.GetClientHeaderVersion();
1366  }
1368  if (stringResponse)
1369  {
1370  igtl::StringMessage::Pointer igtlMessage = dynamic_cast<igtl::StringMessage*>(this->IgtlMessageFactory->CreateSendMessage("STRING", replyHeaderVersion).GetPointer());
1371  igtlMessage->SetDeviceName(stringResponse->GetDeviceName().c_str());
1372  igtlMessage->SetString(stringResponse->GetMessage());
1373  LOG_DEBUG("String response: " << stringResponse->GetMessage());
1374  return igtlMessage.GetPointer();
1375  }
1376 
1378  if (imageResponse)
1379  {
1380  std::string imageName = imageResponse->GetImageName();
1381  if (imageName.empty())
1382  {
1383  imageName = "PlusServerImage";
1384  }
1385 
1386  vtkSmartPointer<vtkMatrix4x4> imageToReferenceTransform = vtkSmartPointer<vtkMatrix4x4>::New();
1387  if (imageResponse->GetImageToReferenceTransform() != NULL)
1388  {
1389  imageToReferenceTransform = imageResponse->GetImageToReferenceTransform();
1390  }
1391 
1392  vtkImageData* imageData = imageResponse->GetImageData();
1393  if (imageData == NULL)
1394  {
1395  LOG_ERROR("Invalid image data in command response");
1396  return NULL;
1397  }
1398 
1399  igtl::ImageMessage::Pointer igtlMessage = dynamic_cast<igtl::ImageMessage*>(this->IgtlMessageFactory->CreateSendMessage("IMAGE", replyHeaderVersion).GetPointer());
1400  igtlMessage->SetDeviceName(imageName.c_str());
1401 
1402  if (vtkPlusIgtlMessageCommon::PackImageMessage(igtlMessage, imageData, *imageToReferenceTransform, vtkIGSIOAccurateTimer::GetSystemTime()) != PLUS_SUCCESS)
1403  {
1404  LOG_ERROR("Failed to create image mesage from command response");
1405  return NULL;
1406  }
1407  return igtlMessage.GetPointer();
1408  }
1409 
1411  if (polydataResponse)
1412  {
1413  std::string polydataName = polydataResponse->GetPolyDataName();
1414  if (polydataName.empty())
1415  {
1416  polydataName = "UnknownFile";
1417  }
1418 
1419  vtkSmartPointer<vtkPolyData> polyData = polydataResponse->GetPolyData();
1420  if (polyData == NULL)
1421  {
1422  LOG_ERROR("Missing polydata in command response");
1423  return NULL;
1424  }
1425 
1426  igtl::PolyDataMessage::Pointer igtlMessage = dynamic_cast<igtl::PolyDataMessage*>(this->IgtlMessageFactory->CreateSendMessage("POLYDATA", replyHeaderVersion).GetPointer());
1427  igtlMessage->SetDeviceName("PlusServer");
1428  igtlMessage->SetMetaDataElement("fileName", IANA_TYPE_US_ASCII, polydataName);
1429 
1430  if (vtkPlusIgtlMessageCommon::PackPolyDataMessage(igtlMessage, polyData, vtkIGSIOAccurateTimer::GetSystemTime()) != PLUS_SUCCESS)
1431  {
1432  LOG_ERROR("Failed to create polydata mesage from command response");
1433  return NULL;
1434  }
1435  return igtlMessage.GetPointer();
1436  }
1437 
1439  if (imageMetaDataResponse)
1440  {
1441  std::string imageMetaDataName = "PlusServerImageMetaData";
1442  igsioCommon::ImageMetaDataList imageMetaDataList;
1443  imageMetaDataResponse->GetImageMetaDataItems(imageMetaDataList);
1444  igtl::ImageMetaMessage::Pointer igtlMessage = dynamic_cast<igtl::ImageMetaMessage*>(this->IgtlMessageFactory->CreateSendMessage("IMGMETA", replyHeaderVersion).GetPointer());
1445  igtlMessage->SetDeviceName(imageMetaDataName.c_str());
1446  if (vtkPlusIgtlMessageCommon::PackImageMetaMessage(igtlMessage, imageMetaDataList) != PLUS_SUCCESS)
1447  {
1448  LOG_ERROR("Failed to create image mesage from command response");
1449  return NULL;
1450  }
1451  return igtlMessage.GetPointer();
1452  }
1453 
1455  if (commandResponse)
1456  {
1457  if (!commandResponse->GetRespondWithCommandMessage())
1458  {
1459  // Incoming command was a v1/v2 style command, reply as such
1460  igtl::StringMessage::Pointer igtlMessage = dynamic_cast<igtl::StringMessage*>(this->IgtlMessageFactory->CreateSendMessage("STRING", replyHeaderVersion).GetPointer());
1461  igtlMessage->SetDeviceName(vtkPlusCommand::GenerateReplyDeviceName(commandResponse->GetOriginalId()));
1462 
1463  std::ostringstream replyStr;
1464  replyStr << "<CommandReply";
1465  replyStr << " Status=\"" << (commandResponse->GetStatus() == PLUS_SUCCESS ? "SUCCESS" : "FAIL") << "\"";
1466  replyStr << " Message=\"";
1467  // Write to XML, encoding special characters, such as " ' \ < > &
1468  vtkXMLUtilities::EncodeString(commandResponse->GetResultString().c_str(), VTK_ENCODING_NONE, replyStr, VTK_ENCODING_NONE, 1 /* encode special characters */);
1469  replyStr << "\"";
1470  replyStr << " />";
1471 
1472  igtlMessage->SetString(replyStr.str().c_str());
1473  LOG_DEBUG("Command response: " << replyStr.str());
1474  return igtlMessage.GetPointer();
1475  }
1476  else
1477  {
1478  // Incoming command was a modern style command, reply using our latest
1479  igtl::RTSCommandMessage::Pointer igtlMessage = dynamic_cast<igtl::RTSCommandMessage*>(this->IgtlMessageFactory->CreateSendMessage("RTS_COMMAND", replyHeaderVersion).GetPointer());
1480  //TODO : should this device name be the name of the server?
1481  igtlMessage->SetDeviceName(commandResponse->GetDeviceName().c_str());
1482  igtlMessage->SetCommandName(commandResponse->GetCommandName());
1483  igtlMessage->SetCommandId(commandResponse->GetOriginalId());
1484 
1485  // Send command result details both in XML and in metadata, slowly phase towards metadata
1486  std::ostringstream replyStr;
1487  if (commandResponse->GetUseDefaultFormat())
1488  {
1489 
1490  replyStr << "<CommandReply";
1491  replyStr << " Name=\"" << commandResponse->GetCommandName() << "\"";
1492  replyStr << " Status=\"" << (commandResponse->GetStatus() ? "SUCCESS" : "FAIL") << "\"";
1493  if (commandResponse->GetStatus() == PLUS_FAIL)
1494  {
1495  replyStr << " Error=\"" << commandResponse->GetErrorString() << "\"";
1496  }
1497  replyStr << " Message=\"" << commandResponse->GetResultString() << "\"></CommandReply>";
1498  igtlMessage->SetMetaDataElement("Message", IANA_TYPE_US_ASCII, commandResponse->GetResultString());
1499  }
1500  else
1501  {
1502  replyStr << commandResponse->GetResultString();
1503  }
1504  igtlMessage->SetCommandContent(replyStr.str());
1505 
1506  for (igtl::MessageBase::MetaDataMap::const_iterator it = begin(commandResponse->GetParameters()); it != end(commandResponse->GetParameters()); ++it)
1507  {
1508  igtlMessage->SetMetaDataElement(it->first, it->second.first, it->second.second);
1509  }
1510 
1511  igtlMessage->SetMetaDataElement("Status", IANA_TYPE_US_ASCII, (commandResponse->GetStatus() ? "SUCCESS" : "FAIL"));
1512  if (commandResponse->GetStatus() == PLUS_FAIL)
1513  {
1514  igtlMessage->SetMetaDataElement("Error", IANA_TYPE_US_ASCII, commandResponse->GetErrorString());
1515  }
1516 
1517  LOG_DEBUG("Command response: " << replyStr.str());
1518  return igtlMessage.GetPointer();
1519  }
1520  }
1521 
1522  LOG_ERROR("vtkPlusOpenIGTLinkServer::CreateIgtlMessageFromCommandResponse failed: invalid command response");
1523  return NULL;
1524 }
static const double SAMPLING_SKIPPING_MARGIN_SEC
const uint32_t * data
Definition: phidget22.h:3971
static PlusStatus PackImageMessage(igtl::ImageMessage::Pointer imageMessage, igsioTrackedFrame &trackedFrame, const vtkMatrix4x4 &imageToReferenceTransform, vtkIGSIOFrameConverter *frameConverter=NULL)
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *serverElement, const std::string &aFilename)
static std::string GenerateReplyDeviceName(uint32_t uid)
bool HasVideoSource() const
Abstract interface for tracker and video devices.
Definition: vtkPlusDevice.h:60
static vtkPlusCommandImageMetaDataResponse * SafeDownCast(vtkObject *o)
static vtkPlusCommandStringResponse * SafeDownCast(vtkObject *o)
const char int line
Definition: phidget22.h:2458
static PlusStatus SendCommandResponses(vtkPlusOpenIGTLinkServer &self)
std::vector< ImageStream > ImageStreams
IGTL message helper class for PlusServer ClientInfo class.
Class to abstract away specific sequence file read/write details.
Definition: vtkPlusLogger.h:21
std::vector< std::string > IgtlMessageTypes
igsioStatus PlusStatus
Definition: PlusCommon.h:40
void PrintServerInfo(vtkPlusOpenIGTLinkServer *self)
void SetClientHeaderVersion(int version)
PlusStatus SetClientInfoFromXmlData(const char *strXmlData)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
std::vector< std::string > StringNames
vtkSmartPointer< vtkIGSIOFrameConverter > FrameConverter
std::vector< vtkPlusDevice * > DeviceCollection
Definition: vtkPlusDevice.h:46
const igtl::MessageBase::MetaDataMap & GetParameters() const
PlusIgtlClientInfo ClientInfo
for i
std::vector< igsioTransformName > TransformNames
Creates a PlusCommand from a string. If the commands are to be executed on the main thread then call ...
virtual uint32_t GetOriginalId()
vtkPlusOpenIGTLinkServer * Server
#define PLUS_FAIL
Definition: PlusCommon.h:43
std::pair< bool, bool > DataReceiverActive
Active flag for thread (first: request, second: respond )
int port
Definition: phidget22.h:2454
static vtkPlusConfig * GetInstance()
int ClientId
Unique client identifier. First valid value is 1.
virtual std::string GetErrorString()
static PlusStatus SendMessageResponses(vtkPlusOpenIGTLinkServer &self)
virtual vtkMatrix4x4 * GetImageToReferenceTransform()
static PlusStatus PackImageMetaMessage(igtl::ImageMetaMessage::Pointer imageMetaMessage, igsioCommon::ImageMetaDataList &imageMetaDataList)
PlusStatus GetOutputChannelByName(vtkPlusChannel *&aChannel, const char *aChannelId)
virtual PlusStatus SendTrackedFrame(igsioTrackedFrame &trackedFrame)
virtual std::string GetMessage()
Manages devices that record image or positional data.
const char ** deviceName
Definition: phidget22.h:1316
virtual PlusStatus GetClientInfo(unsigned int clientId, PlusIgtlClientInfo &outClientInfo) const
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PlusStatus Start(vtkPlusDataCollector *dataCollector, vtkIGSIOTransformRepository *transformRepository, vtkXMLDataElement *serverElement, const std::string &configFilePath)
static vtkPlusCommandImageResponse * SafeDownCast(vtkObject *o)
ChannelContainerConstIterator GetOutputChannelsStart() const
virtual std::string GetPolyDataName()
virtual bool GetRespondWithCommandMessage()
virtual PlusStatus GetMostRecentTimestamp(double &ts)
virtual vtkImageData * GetImageData()
virtual void SetDataCollector(vtkPlusDataCollector *)
PlusStatus GetTrackedFrameList(double &aTimestampOfLastFrameAlreadyGot, vtkIGSIOTrackedFrameList *aTrackedFrameList, int aMaxNumberOfFramesToAdd)
const char * address
Definition: phidget22.h:2552
virtual std::string GetCommandName()
vtkStandardNewMacro(vtkPlusOpenIGTLinkServer)
std::vector< VideoStream > VideoStreams
Factory class of supported OpenIGTLink message types.
virtual unsigned int GetClientId()
static void * DataReceiverThread(vtkMultiThreader::ThreadInfo *data)
virtual PlusStatus GetStatus()
int GetClientHeaderVersion() const
static void * ConnectionReceiverThread(vtkMultiThreader::ThreadInfo *data)
igtl::ClientSocket::Pointer ClientSocket
IGTL client socket instance.
void GetImageMetaDataItems(igsioCommon::ImageMetaDataList &list)
void SetTDATARequested(bool val)
virtual vtkPolyData * GetPolyData()
virtual std::string GetDeviceName()
PlusStatus QueueMessageResponseForClient(int clientId, igtl::MessageBase::Pointer message)
static std::string GetPrefixFromCommandDeviceName(const std::string &deviceName)
static bool IsCommandDeviceName(const std::string &deviceName)
static std::string GetUidFromCommandDeviceName(const std::string &deviceName)
virtual PlusStatus GetOldestTimestamp(double &ts)
virtual std::string GetResultString()
static vtkPlusCommandRTSCommandResponse * SafeDownCast(vtkObject *o)
std::vector< vtkPlusDevice * >::iterator DeviceCollectionIterator
Definition: vtkPlusDevice.h:47
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
static PlusStatus SendLatestFramesToClients(vtkPlusOpenIGTLinkServer &self, double &elapsedTimeSinceLastPacketSentSec)
virtual std::string GetImageName()
const char * message
Definition: phidget22.h:2457
static vtkPlusCommandPolydataResponse * SafeDownCast(vtkObject *o)
This class provides a network interface for data acquired by Plus as an OpenIGTLink server.
std::list< vtkSmartPointer< vtkPlusCommandResponse > > PlusCommandResponseList
virtual unsigned int GetNumberOfConnectedClients() const
This class provides client information for vtkPlusOpenIGTLinkServer.
int ToolCount() const
void SetTDATAResolution(int val)
for t
Definition: exploreFolders.m:9
int FieldCount() const
igtl::MessageBase::Pointer CreateIgtlMessageFromCommandResponse(vtkPlusCommandResponse *response)
virtual int OutputChannelCount() const
virtual void SetTransformRepository(vtkIGSIOTransformRepository *)
vtkSmartPointer< vtkIGSIOFrameConverter > FrameConverter
static PlusStatus PackPolyDataMessage(igtl::PolyDataMessage::Pointer polydataMessage, vtkSmartPointer< vtkPolyData > polyData, double timestamp)
static const std::string DEVICE_NAME_REPLY
static void * DataSenderThread(vtkMultiThreader::ThreadInfo *data)
virtual void PrintSelf(ostream &os, vtkIndent indent)