MITK-IGT
IGT Extension of MITK
Loading...
Searching...
No Matches
mitkIGTLDevice.cpp
Go to the documentation of this file.
1/*============================================================================
2
3The Medical Imaging Interaction Toolkit (MITK)
4
5Copyright (c) German Cancer Research Center (DKFZ)
6All rights reserved.
7
8Use of this source code is governed by a 3-clause BSD license that can be
9found in the LICENSE file.
10
11============================================================================*/
12
13#include "mitkIGTLDevice.h"
14//#include "mitkIGTException.h"
15//#include "mitkIGTTimeStamp.h"
16#include <itkMultiThreaderBase.h>
17#include <itksys/SystemTools.hxx>
18#include <cstring>
19#include <thread>
20
21#include <igtlTransformMessage.h>
23
24#include <mitkIGTLStatus.h>
25
26//remove later
27#include <igtlTrackingDataMessage.h>
28
29namespace mitk
30{
31 itkEventMacroDefinition(MessageSentEvent, itk::AnyEvent);
32 itkEventMacroDefinition(MessageReceivedEvent, itk::AnyEvent);
33 itkEventMacroDefinition(CommandReceivedEvent, itk::AnyEvent);
34 itkEventMacroDefinition(NewClientConnectionEvent, itk::AnyEvent);
35 itkEventMacroDefinition(LostConnectionEvent, itk::AnyEvent);
36}
37
38//TODO: Which timeout is acceptable and also needed to transmit image data? Is there a maximum data limit?
39static const int SOCKET_SEND_RECEIVE_TIMEOUT_MSEC = 100;
40
42// m_Data(mitk::DeviceDataUnspecified),
43m_State(mitk::IGTLDevice::Setup),
44m_Name("Unspecified Device"),
45m_StopCommunication(false),
46m_Hostname("127.0.0.1"),
47m_PortNumber(-1),
48m_LogMessages(false)
49{
50 m_ReadFully = ReadFully;
51 // execution rights are owned by the application thread at the beginning
55 // m_Data = mitk::DeviceDataUnspecified;
56 // m_LatestMessage = igtl::MessageBase::New();
57
58 m_MessageFactory = mitk::IGTLMessageFactory::New();
59 m_MessageQueue = mitk::IGTLMessageQueue::New();
60}
61
63{
64 /* stop communication and disconnect from igtl device */
65 if (GetState() == Running)
66 {
67 this->StopCommunication();
68 this->CloseConnection();
69 }
70 else if (GetState() == Ready)
71 {
72 this->CloseConnection();
73 }
74 /* cleanup tracking thread */
75 if (m_SendThread.joinable())
76 m_SendThread.detach();
77
78 if (m_ReceiveThread.joinable())
79 m_ReceiveThread.detach();
80
81 if (m_ConnectThread.joinable())
82 m_ConnectThread.detach();
83}
84
86{
87 std::lock_guard<std::mutex> lock(m_StateMutex);
88 return m_State;
89}
90
92{
93 itkDebugMacro("setting m_State to " << state);
94
95 m_StateMutex.lock();
96 // MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex
97
98 if (m_State == state)
99 {
100 m_StateMutex.unlock();
101 return;
102 }
103 m_State = state;
104 m_StateMutex.unlock();
105 this->Modified();
106}
107
109{
110 return true;
111}
112
113unsigned int mitk::IGTLDevice::ReceivePrivate(igtl::Socket* socket)
114{
115 // Create a message buffer to receive header
116 igtl::MessageHeader::Pointer headerMsg;
117 headerMsg = igtl::MessageHeader::New();
118
119 // Initialize receive buffer
120 headerMsg->InitPack();
121
122 // Receive generic header from the socket
123 bool timeout = false;
124 auto r = socket->Receive(headerMsg->GetPackPointer(), headerMsg->GetPackSize(), timeout, 0);
125
126 //MITK_INFO << "Server received r = " << r;
127
128 //MITK_INFO << "Received r = " << r;
129
130 if (timeout == true) //timeout
131 {
132 // a timeout was received, this is no error state, thus, do nothing
134 }
135 else if (r == 0) //connection error
136 {
137 // an error was received, therefore the communication with this socket
138 // must be stoppedy
140 }
141 else if (r == headerMsg->GetPackSize())
142 {
143 // Deserialize the header and check the CRC
144 // ERROR HERE: This probably means the header data is corrupted...
145 int crcCheck = headerMsg->Unpack(1);
146
147 if (crcCheck & igtl::MessageHeader::UNPACK_HEADER)
148 {
149 // Allocate a time stamp
150 igtl::TimeStamp::Pointer ts;
151 ts = igtl::TimeStamp::New();
152
153 // Get time stamp
154 igtlUint32 sec;
155 igtlUint32 nanosec;
156
157 headerMsg->GetTimeStamp(ts);
158 ts->GetTimeStamp(&sec, &nanosec);
159
160 // std::cerr << "Time stamp: "
161 // << sec << "."
162 // << nanosec << std::endl;
163
164 // std::cerr << "Dev type and name: " << headerMsg->GetDeviceType() << " "
165 // << headerMsg->GetDeviceName() << std::endl;
166
167 // headerMsg->Print(std::cout);
168
169 //check the type of the received message
170 //if it is a GET_, STP_ or RTS_ command push it into the command queue
171 //otherwise continue reading the whole message from the socket
172 const char* curDevType = headerMsg->GetDeviceType();
173 if (std::strstr(curDevType, "GET_") != nullptr ||
174 std::strstr(curDevType, "STP_") != nullptr ||
175 std::strstr(curDevType, "RTS_") != nullptr)
176 {
177 this->m_MessageQueue->PushCommandMessage(headerMsg);
178 this->InvokeEvent(CommandReceivedEvent());
179 return IGTL_STATUS_OK;
180 }
181
182 //Create a message according to the header message
183 igtl::MessageBase::Pointer curMessage;
184 curMessage = m_MessageFactory->CreateInstance(headerMsg);
185
186 //check if the curMessage is created properly, if not the message type is
187 //not supported and the message has to be skipped
188 if (curMessage.IsNull())
189 {
190 socket->Skip(headerMsg->GetBodySizeToRead(), 0);
191 // MITK_ERROR("IGTLDevice") << "The received type is not supported. Please "
192 // "add it to the message factory.";
194 }
195
196 //insert the header to the message and allocate the pack
197 curMessage->SetMessageHeader(headerMsg);
198 curMessage->AllocatePack();
199
200 // Receive transform data from the socket
201 int receiveCheck = 0;
202 receiveCheck = socket->Receive(curMessage->GetPackBodyPointer(),
203 curMessage->GetPackBodySize(), m_ReadFully);
204
205 if (receiveCheck > 0)
206 {
207 int c = curMessage->Unpack(1);
208 if (!(c & igtl::MessageHeader::UNPACK_BODY))
209 {
211 }
212
213 //check the type of the received message
214 //if it is a command push it into the command queue
215 //otherwise into the normal receive queue
216 //STP_ commands are handled here because they implemented additional
217 //member variables that are not stored in the header message
218 if (std::strstr(curDevType, "STT_") != nullptr)
219 {
220 this->m_MessageQueue->PushCommandMessage(curMessage);
221 this->InvokeEvent(CommandReceivedEvent());
222 }
223 else
224 {
225 if(m_LogMessages)
226 MITK_INFO << "Received Message: " << mitk::IGTLMessage::New(curMessage)->ToString();
227 this->m_MessageQueue->PushMessage(curMessage);
228 this->InvokeEvent(MessageReceivedEvent());
229 }
230 return IGTL_STATUS_OK;
231 }
232 else
233 {
234 MITK_WARN("IGTLDevice") << "Received a valid header but could not "
235 << "read the whole message.";
237 }
238 }
239 else
240 {
241 //CRC check failed
242 MITK_WARN << "CRC Check failed";
244 }
245 }
246 else
247 {
248 //Message size information and actual data size don't match.
249 //this state is not suppossed to be reached, return unknown error
250 MITK_WARN << "IGTL status unknown";
252 }
253}
254
255void mitk::IGTLDevice::SendMessage(mitk::IGTLMessage::Pointer msg)
256{
257 m_MessageQueue->PushSendMessage(msg);
258}
259
260unsigned int mitk::IGTLDevice::SendMessagePrivate(mitk::IGTLMessage::Pointer msg,
261 igtl::Socket::Pointer socket)
262{
263 //check the input message
264 if (msg.IsNull())
265 {
266 MITK_ERROR("IGTLDevice") << "Could not send message because message is not "
267 "valid. Please check.";
268 return false;
269 }
270
271 igtl::MessageBase* sendMessage = msg->GetMessage();
272
273 // Pack (serialize) and send
274 sendMessage->Pack();
275
276 int sendSuccess = socket->Send(sendMessage->GetPackPointer(), sendMessage->GetPackSize());
277
278 if (sendSuccess)
279 {
280 if (m_LogMessages) { MITK_INFO << "Send IGTL message: " << msg->ToString(); }
281 this->InvokeEvent(MessageSentEvent());
282 return IGTL_STATUS_OK;
283 }
284 else
285 {
287 }
288}
289
290void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex)
291{
292 if (this->GetState() != Running)
293 return;
294
295 try
296 {
297 // keep lock until end of scope
298 std::lock_guard<std::mutex> communicationFinishedLockHolder(mutex);
299
300 // Because m_StopCommunication is used by two threads, access has to be guarded
301 // by a mutex. To minimize thread locking, a local copy is used here
302 bool localStopCommunication;
303
304 // update the local copy of m_StopCommunication
305 this->m_StopCommunicationMutex.lock();
306 localStopCommunication = this->m_StopCommunication;
307 this->m_StopCommunicationMutex.unlock();
308 while ((this->GetState() == Running) && (localStopCommunication == false))
309 {
310 (this->*ComFunction)();
311
312 /* Update the local copy of m_StopCommunication */
313 this->m_StopCommunicationMutex.lock();
314 localStopCommunication = m_StopCommunication;
315 this->m_StopCommunicationMutex.unlock();
316
317 // time to relax, this sets the maximum ever possible framerate to 1000 Hz
318 itksys::SystemTools::Delay(1);
319 }
320 }
321 catch (...)
322 {
323 mutex.unlock();
324 this->StopCommunication();
325 MITK_ERROR("IGTLDevice::RunCommunication") << "Error while communicating. Thread stopped.";
326 //mitkThrowException(mitk::IGTException) << "Error while communicating. Thread stopped.";
327 }
328 // StopCommunication was called, thus the mode should be changed back to Ready now
329 // that the tracking loop has ended.
330 //this->SetState(Ready); //this is done elsewhere
331 MITK_DEBUG("IGTLDevice::RunCommunication") << "Reached end of communication.";
332 // returning from this function (and ThreadStartCommunication())
333 // this will end the thread
334 return;
335}
336
338{
339 if (this->GetState() != Ready)
340 return false;
341
342 // go to mode Running
343 this->SetState(Running);
344
345 // set a timeout for the sending and receiving
346 this->m_Socket->SetTimeout(SOCKET_SEND_RECEIVE_TIMEOUT_MSEC);
347
348 // update the local copy of m_StopCommunication
349 this->m_StopCommunicationMutex.lock();
350 this->m_StopCommunication = false;
351 this->m_StopCommunicationMutex.unlock();
352
353 // transfer the execution rights to tracking thread
354 m_SendingFinishedMutex.unlock();
355 m_ReceivingFinishedMutex.unlock();
356 m_ConnectingFinishedMutex.unlock();
357
358 // start new threads that execute the communication
359 m_SendThread = std::thread(&IGTLDevice::ThreadStartSending, this);
360 m_ReceiveThread = std::thread(&IGTLDevice::ThreadStartReceiving, this);
361 m_ConnectThread = std::thread(&IGTLDevice::ThreadStartConnecting, this);
362 // mitk::IGTTimeStamp::GetInstance()->Start(this);
363 return true;
364}
365
367{
368 if (this->GetState() == Running) // Only if the object is in the correct state
369 {
370 // m_StopCommunication is used by two threads, so we have to ensure correct
371 // thread handling
372 m_StopCommunicationMutex.lock();
373 m_StopCommunication = true;
374 m_StopCommunicationMutex.unlock();
375 // we have to wait here that the other thread recognizes the STOP-command
376 // and executes it
377 m_SendingFinishedMutex.lock();
378 m_ReceivingFinishedMutex.lock();
379 m_ConnectingFinishedMutex.lock();
380 // mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock
381 // StopCommunication was called, thus the mode should be changed back
382 // to Ready now that the tracking loop has ended.
383 this->SetState(Ready);
384 }
385 return true;
386}
387
389{
390 if (this->GetState() == Setup)
391 {
392 return true;
393 }
394 else if (this->GetState() == Running)
395 {
396 this->StopCommunication();
397 }
398
399 m_Socket->CloseSocket();
400
401 /* return to setup mode */
402 this->SetState(Setup);
403
404 // this->InvokeEvent(mitk::LostConnectionEvent());
405
406 return true;
407}
408
410{
411 //construct the device type for the return message, it starts with RTS_ and
412 //continues with the requested type
413 std::string returnType("RTS_");
414 returnType.append(type);
415 //create a return message
416 igtl::MessageBase::Pointer rtsMsg =
417 this->m_MessageFactory->CreateInstance(returnType);
418 //if retMsg is nullptr there is no return message defined and thus it is not
419 //necessary to send one back
420 if (rtsMsg.IsNotNull())
421 {
422 this->SendMessage(mitk::IGTLMessage::New(rtsMsg));
423 return true;
424 }
425 else
426 {
427 return false;
428 }
429}
430
432{
433 MITK_DEBUG << "mitk::IGTLDevice::Connect();";
434}
435
436igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage2dMessage()
437{
438 return this->m_MessageQueue->PullImage2dMessage();
439}
440
441igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage3dMessage()
442{
443 return this->m_MessageQueue->PullImage3dMessage();
444}
445
446igtl::TransformMessage::Pointer mitk::IGTLDevice::GetNextTransformMessage()
447{
448 return this->m_MessageQueue->PullTransformMessage();
449}
450
451igtl::TrackingDataMessage::Pointer mitk::IGTLDevice::GetNextTrackingDataMessage()
452{
453 igtl::TrackingDataMessage::Pointer msg = this->m_MessageQueue->PullTrackingMessage();
454 return msg;
455}
456
457igtl::StringMessage::Pointer mitk::IGTLDevice::GetNextStringMessage()
458{
459 return this->m_MessageQueue->PullStringMessage();
460}
461
462igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextMiscMessage()
463{
464 return this->m_MessageQueue->PullMiscMessage();
465}
466
467igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextCommand()
468{
469 return m_MessageQueue->PullCommandMessage();
470}
472{
473 m_MessageQueue->EnableNoBufferingMode(enable);
474}
475
477 mitk::IGTLMessageQueue::Pointer queue,
478 bool enable)
479{
480 queue->EnableNoBufferingMode(enable);
481}
482
484{
485 this->RunCommunication(&IGTLDevice::Send, m_SendingFinishedMutex);
486}
487
489{
490 this->RunCommunication(&IGTLDevice::Receive, m_ReceivingFinishedMutex);
491}
492
494{
495 this->RunCommunication(&IGTLDevice::Connect, m_ConnectingFinishedMutex);
496}
Interface for all OpenIGTLink Devices.
std::mutex m_ReceivingFinishedMutex
void RunCommunication(void(IGTLDevice::*ComFunction)(void), std::mutex &mutex)
Continuously calls the given function.
void SendMessage(mitk::IGTLMessage::Pointer msg)
Adds the given message to the sending queue.
unsigned int SendMessagePrivate(mitk::IGTLMessage::Pointer msg, igtl::Socket::Pointer socket)
Sends a message.
std::mutex m_SendingFinishedMutex
igtl::StringMessage::Pointer GetNextStringMessage()
virtual void Connect()
Call this method to check for other devices that want to connect to this one.
virtual bool CloseConnection()
Closes the connection to the device.
void ThreadStartSending()
start method for the sending thread.
igtl::ImageMessage::Pointer GetNextImage3dMessage()
virtual bool TestConnection()
TestConnection() tries to connect to a IGTL device on the current ip and port.
mitk::IGTLMessageQueue::Pointer m_MessageQueue
igtl::TrackingDataMessage::Pointer GetNextTrackingDataMessage()
void ThreadStartReceiving()
start method for the receiving thread.
void ThreadStartConnecting()
start method for the connection thread.
bool StartCommunication()
Starts the communication between the two devices.
std::mutex m_ConnectingFinishedMutex
bool SendRTSMessage(const char *type)
Send RTS message of given type.
igtl::MessageBase::Pointer GetNextCommand()
Returns the oldest message in the command queue.
void EnableNoBufferingMode(mitk::IGTLMessageQueue::Pointer queue, bool enable=true)
Sets the buffering mode of the given queue.
unsigned int ReceivePrivate(igtl::Socket *device)
Call this method to receive a message from the given device.
IGTLDeviceState GetState() const
Returns current object state (Setup, Ready or Running)
virtual bool StopCommunication()
Stops the communication between the two devices.
virtual void Receive()=0
Call this method to receive a message.
virtual void Send()=0
Call this method to send a message. The message will be read from the queue.
igtl::TransformMessage::Pointer GetNextTransformMessage()
void SetState(IGTLDeviceState state)
change object state
igtl::MessageBase::Pointer GetNextMiscMessage()
igtl::ImageMessage::Pointer GetNextImage2dMessage()
Returns the oldest message in the receive queue.
IGTLDeviceState
Type for state variable. The IGTLDevice is always in one of these states.
mitk::IGTLMessageFactory::Pointer m_MessageFactory
#define IGTL_STATUS_CHECKSUM_ERROR
#define IGTL_STATUS_UNKNOWN_ERROR
#define IGTL_STATUS_TIME_OUT
#define IGTL_STATUS_OK
#define IGTL_STATUS_NOT_PRESENT
#define IGTL_STATUS_NOT_FOUND
IGT Exceptions.
itkEventMacroDefinition(MessageSentEvent, itk::AnyEvent)