PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusOpenIGTLinkDevice.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"
10 #include "vtkPlusDataSource.h"
13 
14 // OS includes
15 #ifdef _WIN32
16  #include <Winsock2.h>
17 #endif
18 
19 //----------------------------------------------------------------------------
21  : ServerPort(-1)
22  , IgtlMessageCrcCheckEnabled(0)
23  , ReceiveTimeoutSec(0.5)
24  , SendTimeoutSec(0.5)
25  , NumberOfRetryAttempts(3) // try a few times, but adding of data items is blocked while trying to reconnect, so don't make it too long
26  , DelayBetweenRetryAttemptsSec(0.100) // there is already a delay with a CLIENT_SOCKET_TIMEOUT_MSEC timeout, so we just add a little extra idle delay
27  , MessageFactory(vtkSmartPointer<vtkPlusIgtlMessageFactory>::New())
28  , SocketMutex(vtkSmartPointer<vtkIGSIORecursiveCriticalSection>::New())
29  , ClientSocket(igtl::ClientSocket::New())
30  , ReconnectOnReceiveTimeout(true)
31  , UseReceivedTimestamps(true)
32 {
33  // 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
34  this->StartThreadForInternalUpdates = true;
35  this->AcquisitionRate = 30;
36 }
37 
38 //----------------------------------------------------------------------------
40 {
41  if (this->Recording)
42  {
43  this->StopRecording();
44  }
45  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
46  this->ClientSocket = NULL;
47 }
48 
49 //----------------------------------------------------------------------------
50 void vtkPlusOpenIGTLinkDevice::PrintSelf(ostream& os, vtkIndent indent)
51 {
52  this->Superclass::PrintSelf(os, indent);
53  if (!this->ServerAddress.empty())
54  {
55  os << indent << "Server address: " << this->ServerAddress << "\n";
56  }
57  os << indent << "Server port: " << this->ServerPort << "\n";
58  if (!this->MessageType.empty())
59  {
60  os << indent << "Message type: " << this->MessageType << "\n";
61  }
62  if (this->ImageMessageEmbeddedTransformName.IsValid())
63  {
64  os << indent << "Image stream: " << this->ImageMessageEmbeddedTransformName.GetTransformName() << "\n";
65  }
66 }
67 //----------------------------------------------------------------------------
69 {
70  std::ostringstream version;
71  version << "OpenIGTLink v" << OPENIGTLINK_VERSION;
72  return version.str();
73 }
74 
75 //----------------------------------------------------------------------------
77 {
78  LOG_TRACE("vtkPlusOpenIGTLinkDevice::InternalConnect");
79 
80  // Clear buffers on connect
81  this->ClearAllBuffers();
82 
83  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
84  if (this->ClientSocket->GetConnected())
85  {
86  return PLUS_SUCCESS;
87  }
88 
89  return ClientSocketReconnect();
90 }
91 
92 //----------------------------------------------------------------------------
94 {
95  LOG_TRACE("vtkPlusOpenIGTLinkDevice::Disconnect");
96 
97  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
98  this->ClientSocket->CloseSocket();
99  return this->StopRecording();
100 }
101 
102 //----------------------------------------------------------------------------
104 {
105  LOG_TRACE("vtkPlusOpenIGTLinkDevice::Probe");
106 
107  PlusStatus status = PLUS_FAIL;
108  if (this->Connect() == PLUS_SUCCESS)
109  {
110  status = PLUS_SUCCESS;
111  this->Disconnect();
112  }
113 
114  return status;
115 }
116 
117 //----------------------------------------------------------------------------
119 {
120  LOG_DEBUG("Attempt to connect to client socket in device " << this->GetDeviceId());
121 
122  {
123  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
124  if (this->ClientSocket->GetConnected())
125  {
126  this->ClientSocket->CloseSocket();
127  }
128  }
129 
130  if (this->ServerAddress.empty())
131  {
132  LOG_ERROR("Unable to connect OpenIGTLink server - server address is undefined");
133  return PLUS_FAIL;
134  }
135 
136  if (this->ServerPort < 0)
137  {
138  LOG_ERROR("Unable to connect OpenIGTLink server - server port is invalid: " << this->ServerPort);
139  return PLUS_FAIL;
140  }
141 
142  int errorCode = 0; // 0 means success
143  {
144  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
145  RETRY_UNTIL_TRUE(
146  (errorCode = this->ClientSocket->ConnectToServer(this->ServerAddress.c_str(), this->ServerPort)) == 0,
148  }
149 
150  if (errorCode != 0)
151  {
152  LOG_ERROR("Cannot connect to the server (" << this->ServerAddress << ":" << this->ServerPort << ").");
153  return PLUS_FAIL;
154  }
155  else
156  {
157  LOG_DEBUG("Client successfully connected to server (" << this->ServerAddress << ":" << this->ServerPort << ").");
158  }
159 
160  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
161  this->ClientSocket->SetReceiveTimeout(this->ReceiveTimeoutSec * 1000.0); // *1000 because SetReceiveTimeout expects msec
162  this->ClientSocket->SetSendTimeout(this->SendTimeoutSec * 1000.0); // *1000 because SetSendTimeout expects msec
163 
164  return SendRequestedMessageTypes();
165 }
166 
167 //----------------------------------------------------------------------------
169 {
170  // If we specified message type, try to send it to the server
171  if (this->MessageType.empty())
172  {
173  return PLUS_SUCCESS;
174  }
175 
176  // Send client info request to the server
177  PlusIgtlClientInfo clientInfo;
178 
179  // Set message type
180  clientInfo.IgtlMessageTypes.push_back(this->MessageType);
181 
182  // Set any requested image streams
183  if (this->ImageMessageEmbeddedTransformName.IsValid())
184  {
186  is.Name = this->ImageMessageEmbeddedTransformName.From();
188  clientInfo.ImageStreams.push_back(is);
189  }
190 
191  // We need the following tool names from the server
192  for (DataSourceContainerConstIterator it = this->GetToolIteratorBegin(); it != this->GetToolIteratorEnd(); ++it)
193  {
194  igsioTransformName tName(it->second->GetId());
195  clientInfo.TransformNames.push_back(tName);
196  }
197 
198  // Pack client info message
199  auto clientInfoMsg = igtl::PlusClientInfoMessage::New();
200  clientInfoMsg->SetClientInfo(clientInfo);
201  clientInfoMsg->Pack();
202 
203  // Send message to server
204  int retValue = 0;
205  {
206  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
207  RETRY_UNTIL_TRUE(
208  (retValue = this->ClientSocket->Send(clientInfoMsg->GetBufferPointer(), clientInfoMsg->GetBufferSize())) != 0,
210  }
211 
212  if (retValue == 0)
213  {
214  LOG_ERROR("Failed to send PlusClientInfo message to server!");
215  return PLUS_FAIL;
216  }
217 
218  return PLUS_SUCCESS;
219 }
220 
221 //----------------------------------------------------------------------------
223 {
224  if (this->GetReconnectOnReceiveTimeout())
225  {
226  LOG_WARNING("No OpenIGTLink message has been received in device " << this->GetDeviceId() << ": failed to receive OpenIGTLink transforms. Attempt to reconnect.");
228  }
229 }
230 
231 //----------------------------------------------------------------------------
232 void vtkPlusOpenIGTLinkDevice::ReceiveMessageHeaderWithErrorHandling(igtl::MessageHeader::Pointer& headerMsg)
233 {
234  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
235  PlusStatus socketStatus = ReceiveMessageHeader(headerMsg);
236  if (socketStatus == PLUS_FAIL || !this->ClientSocket->GetConnected())
237  {
238  // There is a socket error
239  if (this->GetReconnectOnReceiveTimeout())
240  {
241  LOG_ERROR("Socket error in device " << this->GetDeviceId() << ": failed to receive OpenIGTLink transforms. Attempt to reconnect.");
243  }
244  else
245  {
246  LOG_ERROR("Socket error in device " << this->GetDeviceId() << ": failed to receive OpenIGTLink transforms");
247  }
248  }
249 }
250 
251 //----------------------------------------------------------------------------
252 PlusStatus vtkPlusOpenIGTLinkDevice::ReceiveMessageHeader(igtl::MessageHeader::Pointer& headerMsg)
253 {
254  headerMsg = this->MessageFactory->CreateHeaderMessage(IGTL_HEADER_VERSION_1);
255 
256  int numOfBytesReceived = 0;
257  {
258  bool timeout(false);
259  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
260  RETRY_UNTIL_TRUE(
261  (numOfBytesReceived = this->ClientSocket->Receive(headerMsg->GetBufferPointer(), headerMsg->GetBufferSize(), timeout)) != 0,
263  }
264 
265  if (numOfBytesReceived > 0)
266  {
267  // Data is received
268  if (numOfBytesReceived != headerMsg->GetBufferSize())
269  {
270  // Received data is not as we expected
271  LOG_ERROR("Couldn't receive data from OpenIGTLink device " << this->GetDeviceId() << ": (unexpected header size)");
272  headerMsg = NULL;
273  return PLUS_FAIL;
274  }
275  return PLUS_SUCCESS;
276  }
277 
278  // No data has been received
279  headerMsg = NULL; // this will indicate the caller that no data has been read
280 
281  bool socketError = numOfBytesReceived < 0; /* -1 == SOCKET_ERROR */
282 #ifdef _WIN32
283  // On Windows try to get some more details about the socket error
284  if (socketError)
285  {
286  int socketErrorCode = WSAGetLastError();
287  if (socketErrorCode == WSAETIMEDOUT)
288  {
289  // timeout, it just means that no data was received, no need to reconnect
290  LOG_TRACE("No data coming from OpenIGTLink device " << this->GetDeviceId() << ": (timeout)");
291  socketError = false;
292  }
293  else
294  {
295  LPTSTR errorMsgPtr = 0;
296  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, socketErrorCode, 0, (LPTSTR)&errorMsgPtr, 0, NULL) != 0)
297  {
298  LOG_DEBUG("No data coming from OpenIGTLink device " << this->GetDeviceId() << ": (socket error: " << errorMsgPtr << ")");
299  }
300  else
301  {
302  LOG_DEBUG("No data coming from OpenIGTLink device " << this->GetDeviceId() << ": (unknown socket error)");
303  }
304  }
305  }
306  else
307  {
308  LOG_DEBUG("No data coming from OpenIGTLink device " << this->GetDeviceId());
309  }
310 #else
311  LOG_DEBUG("No data coming from OpenIGTLink device " << this->GetDeviceId());
312 #endif
313 
314  return socketError ? PLUS_FAIL : PLUS_SUCCESS;
315 }
316 
317 //-----------------------------------------------------------------------------
318 PlusStatus vtkPlusOpenIGTLinkDevice::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
319 {
320  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
321  XML_READ_STRING_ATTRIBUTE_REQUIRED(ServerAddress, deviceConfig);
322  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, ServerPort, deviceConfig);
323  XML_READ_STRING_ATTRIBUTE_OPTIONAL(MessageType, deviceConfig);
324  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, ReceiveTimeoutSec, deviceConfig);
325  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, SendTimeoutSec, deviceConfig);
326  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(IgtlMessageCrcCheckEnabled, deviceConfig);
327  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(UseReceivedTimestamps, deviceConfig);
328  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(ReconnectOnReceiveTimeout, deviceConfig);
329  return PLUS_SUCCESS;
330 }
331 
332 //----------------------------------------------------------------------------
333 PlusStatus vtkPlusOpenIGTLinkDevice::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
334 {
335  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
336  XML_WRITE_STRING_ATTRIBUTE_IF_NOT_EMPTY(ServerAddress, rootConfigElement);
337  deviceConfig->SetIntAttribute("ServerPort", this->ServerPort);
338  XML_WRITE_STRING_ATTRIBUTE_IF_NOT_EMPTY(MessageType, rootConfigElement);
339  deviceConfig->SetDoubleAttribute("ReceiveTimeoutSec", this->ReceiveTimeoutSec);
340  deviceConfig->SetDoubleAttribute("SendTimeoutSec", this->SendTimeoutSec);
341  deviceConfig->SetAttribute("IgtlMessageCrcCheckEnabled", this->IgtlMessageCrcCheckEnabled ? "true" : "false");
342  deviceConfig->SetAttribute("UseReceivedTimestamps", this->UseReceivedTimestamps ? "true" : "false");
343  deviceConfig->SetAttribute("ReconnectOnReceiveTimeout", this->ReconnectOnReceiveTimeout ? "true" : "false");
344  return PLUS_SUCCESS;
345 }
346 
347 //----------------------------------------------------------------------------
348 bool vtkPlusOpenIGTLinkDevice::SendMessage(igtl::MessageBase::Pointer packedMessage)
349 {
350  int success = 0;
351  {
352  igsioLockGuard<vtkIGSIORecursiveCriticalSection> socketGuard(this->SocketMutex);
353  success = this->ClientSocket->Send(packedMessage->GetBufferPointer(), packedMessage->GetBufferSize());
354  }
355  if (!success)
356  {
357  LOG_ERROR("OpenIGTLink client couldn't send message to server.");
358  return false;
359  }
360  return true;
361 }
DataSourceContainer::const_iterator DataSourceContainerConstIterator
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
void ReceiveMessageHeaderWithErrorHandling(igtl::MessageHeader::Pointer &headerMsg)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
std::vector< ImageStream > ImageStreams
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
std::vector< std::string > IgtlMessageTypes
igsioStatus PlusStatus
Definition: PlusCommon.h:40
vtkSmartPointer< vtkPlusIgtlMessageFactory > MessageFactory
virtual std::string GetDeviceId() const
virtual PlusStatus InternalDisconnect()
vtkSmartPointer< vtkIGSIORecursiveCriticalSection > SocketMutex
std::vector< igsioTransformName > TransformNames
igsioTransformName ImageMessageEmbeddedTransformName
double AcquisitionRate
#define PLUS_FAIL
Definition: PlusCommon.h:43
bool SendMessage(igtl::MessageBase::Pointer packedMessage)
virtual PlusStatus Disconnect()
virtual PlusStatus SendRequestedMessageTypes()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual std::string GetSdkVersion()
virtual PlusStatus Connect()
virtual PlusStatus InternalConnect()
virtual PlusStatus ReceiveMessageHeader(igtl::MessageHeader::Pointer &headerMsg)
Factory class of supported OpenIGTLink message types.
virtual PlusStatus StopRecording()
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
bool StartThreadForInternalUpdates
igtl::ClientSocket::Pointer ClientSocket
DataSourceContainerConstIterator GetToolIteratorBegin() const
virtual PlusStatus ClientSocketReconnect()
This class provides client information for vtkPlusOpenIGTLinkServer.
virtual bool GetReconnectOnReceiveTimeout()
DataSourceContainerConstIterator GetToolIteratorEnd() const
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)