OpenWalnut  1.5.0dev
WIGTLinkRemote.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <iostream>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include "WIGTLinkRemote.h"
31 #include "core/common/WIOTools.h"
32 #include "core/common/WLogger.h"
33 #include "core/dataHandler/WDataSet.h"
34 #include "core/dataHandler/WDataSetScalar.h"
35 #include "core/dataHandler/WGridRegular3D.h"
36 #include "core/dataHandler/WValueSet.h"
37 #include "igtlImageMessage.h"
38 #include "igtlImageMetaMessage.h"
39 #include "igtlOSUtil.h"
40 #include "igtlServerSocket.h"
41 #include "igtlTransformMessage.h"
42 
43 WIGTLinkRemote::WIGTLinkRemote()
44  : checkCRC( false ), port( 0 )
45 {
46  receiversCondition.reset( new WCondition );
47  statusCondition.reset( new WCondition );
48 }
49 
50 WIGTLinkRemote::~WIGTLinkRemote()
51 {
52  receiversMutex.lock();
53  //receiveQueue.clear();
54  receiversCondition->notify();
55  receiversMutex.unlock();
56  if( socket.IsNotNull() )
57  {
58  socket->CloseSocket();
59  }
60 }
61 
63 {
64  this->port = port;
65 }
66 
67 void WIGTLinkRemote::createSocketAndConnect( std::string hostname, uint32_t port )
68 {
69  std::cout << "setting up connection to: \"" << hostname << ":" << port << "\"" << std::endl;
70  this->port = 0;
71  socket = igtl::ClientSocket::New();
72  int r = socket->ConnectToServer( hostname.c_str(), port );
73  if( r != 0 )
74  {
75  throw WException( "Cannot connect to server" );
76  }
77  std::cout << "connection established" << std::endl;
78  statusCondition->notify(); // link established
79 }
80 
82 {
83  readDataLoop();
84 }
85 
87 {
88  igtl::ServerSocket::Pointer serverSocket;
89  serverSocket = igtl::ServerSocket::New();
90  serverSocket->CreateServer( port );
91 
92  // wait for connection
93  while( !m_shutdownFlag() && socket.IsNull() )
94  {
95  socket = serverSocket->WaitForConnection( 2000 );
96  }
97  if( !m_shutdownFlag() && socket.IsNotNull() )
98  {
99  statusCondition->notify(); // passive connection established
100  }
101 }
102 
104 {
105  igtl::MessageHeader::Pointer headerMsg;
106  headerMsg = igtl::MessageHeader::New();
107 
108  headerMsg->InitPack();
109 
110  if( port > 0 )
111  {
112  listenLoop();
113  }
114 
115  WAssert( socket.IsNotNull(), "Something failed when setting up the socket" );
116 
117  while( !m_shutdownFlag() )
118  {
119  try
120  {
121  std::cout << "Waiting for data." << std::endl;
122  int r = socket->Receive( headerMsg->GetPackPointer(), headerMsg->GetPackSize() );
123  if( r == 0 )
124  {
125  // socket failed!!!
126  throw WException( "Socket Failed" );
127  }
128  std::cout << "got something." << std::endl;
129 
130  if( r != headerMsg->GetPackSize() )
131  {
132  throw WException( "Invalid data size" );
133  }
134 
135  // deserialize the header
136  headerMsg->Unpack( checkCRC ? 1:0 );
137 
138  // check data type
139  if( strcmp( headerMsg->GetDeviceType(), "TRANSFORM" ) == 0 )
140  {
141  //debugLog() << "Received TRANSFORM";
142  receiveTransform( headerMsg );
143  }
144  else if( strcmp( headerMsg->GetDeviceType(), "IMAGE" ) == 0 )
145  {
146  std::cout << "got an image!!!" << std::endl;
147  WDataSetScalarSPtr ds = receiveImage( headerMsg );
148  receiversMutex.lock();
149  receiveQueue.push( ds );
150  receiversCondition->notify();
151  receiversMutex.unlock();
152  }
153  else if( strcmp( headerMsg->GetDeviceType(), "POSITION" ) == 0 )
154  {
155  //readPosition( headerMsg );
156  socket->Skip( headerMsg->GetBodySizeToRead(), 0 );
157  }
158  else if( strcmp( headerMsg->GetDeviceType(), "STATUS" ) == 0 )
159  {
160  //readStatus( headerMsg );
161  socket->Skip( headerMsg->GetBodySizeToRead(), 0 );
162  }
163  else
164  {
165  //debugLog() << "Unknown header received";
166  socket->Skip( headerMsg->GetBodySizeToRead(), 0 );
167  }
168  }
169  catch( const std::exception &e )
170  {
171  if( socket.IsNotNull() )
172  {
173  socket->CloseSocket();
174  }
175  if( port == 0 )
176  {
177  // we are in client mode and the connection failed
178  // notify the main program and quit this thread
179  // we have to start a new connection if this happens
180  throw e;
181  }
182  else
183  {
184  // just continue listening
185  socket = igtl::ClientSocket::Pointer();
186  listenLoop();
187  }
188  }
189  }
190 }
191 
192 void WIGTLinkRemote::receiveTransform( igtl::MessageHeader::Pointer headerMsg )
193 {
194  // TODO(mario): OpenWalnut needs a nice interface for transforms to display tracking devices etc
195 
196  // message body handler for transform
197  igtl::TransformMessage::Pointer transMsg;
198  transMsg = igtl::TransformMessage::New();
199  transMsg->SetMessageHeader( headerMsg );
200  transMsg->AllocatePack();
201 
202  socket->Receive( transMsg->GetPackBodyPointer(), transMsg->GetPackBodySize() );
203 
204  // deserialize the message body
205  int c = transMsg->Unpack( checkCRC ? 1:0 ); // 1 indicates to perform CRC
206  if( c & igtl::MessageHeader::UNPACK_BODY )
207  {
208  // if CRC check is OK, read transform data
209  igtl::Matrix4x4 matrix;
210  transMsg->GetMatrix( matrix );
211 
212  // TODO(mario): add better debugging
213  igtl::PrintMatrix( matrix );
214  }
215 }
216 
217 WDataSetScalarSPtr WIGTLinkRemote::receiveImage( igtl::MessageHeader::Pointer headerMsg )
218 {
219  igtl::ImageMessage::Pointer imgMsg;
220  imgMsg = igtl::ImageMessage::New();
221  imgMsg->SetMessageHeader( headerMsg );
222  imgMsg->AllocatePack();
223 
224  // receive data from socket
225  socket->Receive( imgMsg->GetPackBodyPointer(), imgMsg->GetPackBodySize() );
226 
227  // deserialize the data
228  int c = imgMsg->Unpack( checkCRC ? 1:0 ); // 1 stands for do CRC
229 
230  if( c & igtl::MessageHeader::UNPACK_BODY )
231  {
232  int size[ 3 ];
233  float spacing[ 3 ];
234  int svsize[ 3 ];
235  int svoffset[ 3 ];
236  igtl::Matrix4x4 mat;
237 
238  imgMsg->GetDimensions( size );
239  imgMsg->GetSpacing( spacing );
240  imgMsg->GetSubVolume( svsize, svoffset );
241  imgMsg->GetMatrix( mat );
242 
243  WMatrix<double> owmat( 4, 4 );
244  for( int i = 0; i < 4; ++i )
245  {
246  for( int j = 0; j < 4; ++j )
247  {
248  owmat( i, j ) = mat[ i ][ j ];
249  }
250  }
251  WGridRegular3D::SPtr grid( new WGridRegular3D( size[ 0 ], size[ 1 ], size[ 2 ], WGridTransformOrtho( owmat ) ) );
252 
253  std::shared_ptr< WValueSetBase > valueSet = createValueSet( imgMsg );
254 
255  WDataSetScalarSPtr ds( new WDataSetScalar( valueSet, grid ) );
256  // if( length( imgMsg->GetDeviceName() > 0 ) )
257  // {
258  // ds->setFilename( imgMsg->GetDeviceName() );
259  // }
260 
261  return ds;
262  }
263  else
264  {
265  std::cerr << "decoding image failed" << std::endl;
266  }
267  return WDataSetScalarSPtr ();
268 }
269 
271 {
272 }
273 
274 void WIGTLinkRemote::sendTransform( const std::string& name, const WMatrix<double> & matrix )
275 {
276  if( socket.IsNotNull() )
277  {
278  igtl::TransformMessage::Pointer transMsg;
279  transMsg = igtl::TransformMessage::New();
280  transMsg->SetDeviceName( name.c_str() );
281  igtl::Matrix4x4 igtlMatrix;
282  for( int i = 0; i < 4; ++i )
283  {
284  for( int j = 0; j < 4; ++j )
285  {
286  igtlMatrix[ i ][ j ] = matrix( i, j );
287  }
288  }
289  transMsg->SetMatrix( igtlMatrix );
290 
291  transMsg->Pack();
292 
293  socket->Send( transMsg->GetPackPointer(), transMsg->GetPackSize() );
294  }
295 }
296 
297 namespace
298 {
299  int convertTypeOWtoIGTL( int type )
300  {
301  switch( type )
302  {
303  case igtl::ImageMessage::TYPE_INT8:
304  return W_DT_INT8;
305  case igtl::ImageMessage::TYPE_UINT8:
306  return W_DT_UINT8;
307  case igtl::ImageMessage::TYPE_INT16:
308  return W_DT_INT16;
309  case igtl::ImageMessage::TYPE_UINT16:
310  return W_DT_UINT16;
311  case igtl::ImageMessage::TYPE_INT32:
312  return W_DT_INT16;
313  case igtl::ImageMessage::TYPE_UINT32:
314  return W_DT_UINT32;
315  default:
316  // TODO(mario): throw exception?
317  return W_DT_UNKNOWN;
318  }
319  }
320 }
321 
322 void WIGTLinkRemote::sendImageMetaData( const std::vector < WDataSetScalarSPtr >& dataSets )
323 {
324  igtl::ImageMetaMessage::Pointer imgMetaMsg;
325  imgMetaMsg = igtl::ImageMetaMessage::New();
326 
327  imgMetaMsg->SetDeviceName( "OpenWalnut" );
328 
329  // create meta data for each data set
330  for( size_t i = 0; i < dataSets.size(); ++i )
331  {
332  igtl::ImageMetaElement::Pointer imgMeta;
333  imgMeta = igtl::ImageMetaElement::New();
334  imgMeta->SetName( dataSets[ i ]->getFilename().c_str() );
335  imgMeta->SetDeviceName( dataSets[ i ]->getFilename().c_str() );
336  imgMeta->SetModality( "UNKNOWN_MODALITY" );
337  imgMeta->SetPatientName( dataSets[ i ]->getFilename().c_str() );
338  imgMeta->SetPatientID( "PATIENT_ID_0" );
339 
340  igtl::TimeStamp::Pointer ts0;
341  ts0 = igtl::TimeStamp::New();
342  ts0->SetTime( 123456.78 );
343 
344  imgMeta->SetTimeStamp( ts0 );
345  std::shared_ptr < WGridRegular3D > g3dr( std::dynamic_pointer_cast < WGridRegular3D > ( dataSets[ i ]->getGrid() ) );
346  imgMeta->SetSize( g3dr->getNbCoordsX(), g3dr->getNbCoordsY(), g3dr->getNbCoordsZ() );
347  imgMeta->SetScalarType( convertTypeOWtoIGTL( dataSets[ i ]->getValueSet()->getDataType() ) );
348  imgMetaMsg->AddImageMetaElement( imgMeta );
349  }
350  imgMetaMsg->Pack();
351 
352  socket->Send( imgMetaMsg->GetPackPointer(), imgMetaMsg->GetPackSize() );
353 }
354 
355 namespace Ugly
356 {
357  template < int DT >
358  size_t getRawSizeT( std::shared_ptr < WValueSetBase > valueSet )
359  {
360  typedef typename DataTypeRT<DT>::type type;
361  std::shared_ptr < WValueSet < type > > v = std::dynamic_pointer_cast < WValueSet < type > >( valueSet );
362  WAssert( v, "Type cast failed" );
363  return valueSet->rawSize() * sizeof( type );
364  }
365 
366  template < int DT >
367  const void* getRawPtrT( std::shared_ptr < WValueSetBase > valueSet )
368  {
369  typedef typename DataTypeRT<DT>::type type;
370  std::shared_ptr < WValueSet < type > > v;
371  v = std::dynamic_pointer_cast < WValueSet < type > >( valueSet );
372  WAssert( v, "Type cast failed" );
373  const void* ptr = v->rawData();
374  WAssert( ptr != 0, "Trying to query raw data, got null pointer" );
375  return ptr;
376  }
377 
378  size_t getRawSize( std::shared_ptr < WValueSetBase > valueSet )
379  {
380  int type = valueSet->getDataType();
381 #define CASE( A ) case A: return getRawSizeT < A > ( valueSet );
382  switch( type )
383  {
384  CASE( W_DT_UNSIGNED_CHAR );
385  CASE( W_DT_INT8 );
386  CASE( W_DT_SIGNED_SHORT ); // INT16
387  CASE( W_DT_SIGNED_INT ); // INT32
388  CASE( W_DT_FLOAT );
389  CASE( W_DT_DOUBLE );
390  CASE( W_DT_UINT16 );
391  CASE( W_DT_UINT32 );
392  CASE( W_DT_INT64 );
393  CASE( W_DT_UINT64 );
394  CASE( W_DT_FLOAT128 );
395  default:
396  throw WException( "Not implemented for given data type" );
397  }
398 #undef CASE
399  }
400 
401  const void* getRawPtr( std::shared_ptr < WValueSetBase > valueSet )
402  {
403  int type = valueSet->getDataType();
404 #define CASE( A ) case A: return getRawPtrT < A > ( valueSet );
405  switch( type )
406  {
407  CASE( W_DT_UNSIGNED_CHAR );
408  CASE( W_DT_INT8 );
409  CASE( W_DT_SIGNED_SHORT ); // INT16
410  CASE( W_DT_SIGNED_INT ); // INT32
411  CASE( W_DT_FLOAT );
412  CASE( W_DT_DOUBLE );
413  CASE( W_DT_UINT16 );
414  CASE( W_DT_UINT32 );
415  CASE( W_DT_INT64 );
416  CASE( W_DT_UINT64 );
417  CASE( W_DT_FLOAT128 );
418  default:
419  throw WException( "Not implemented for given data type" );
420  }
421 #undef CASE
422  }
423 }
424 
425 void WIGTLinkRemote::sendImageData( WDataSetScalarSPtr dataSetScalar )
426 {
427  std::shared_ptr< WValueSetBase > valueSet = dataSetScalar->getValueSet();
428  //size_t rawSize = valueSet->rawSize();
429 
430  int scalarType = 0;
431  switch( valueSet->getDataType() )
432  {
433  case W_DT_FLOAT:
434  scalarType = igtl::ImageMessage::TYPE_FLOAT32;
435  break;
436  case W_DT_DOUBLE:
437  scalarType = igtl::ImageMessage::TYPE_FLOAT64;
438  break;
439  case W_DT_UINT16:
440  scalarType = igtl::ImageMessage::TYPE_UINT16;
441  break;
442  case W_DT_UINT32:
443  scalarType = igtl::ImageMessage::TYPE_UINT32;
444  break;
445  case W_DT_UNSIGNED_CHAR:
446  scalarType = igtl::ImageMessage::TYPE_UINT8;
447  break;
448  case W_DT_INT8:
449  scalarType = igtl::ImageMessage::TYPE_INT8;
450  break;
451  case W_DT_INT64:
452  case W_DT_FLOAT128:
453  throw WException( "Unsupported scalar type: not supported by igtl?" );
454  break;
455  case W_DT_SIGNED_INT:
456  scalarType = igtl::ImageMessage::TYPE_INT32;
457  break;
458  case W_DT_SIGNED_SHORT:
459  scalarType = igtl::ImageMessage::TYPE_INT16;
460  break;
461  case W_DT_BINARY:
462  case W_DT_UINT64:
463  throw WException( "Unsupported scalar type" );
464  break;
465  case W_DT_RGB:
466  case W_DT_RGBA32:
467  throw WException( "RGB not supported" );
468  break;
469  case W_DT_COMPLEX:
470  case W_DT_COMPLEX128:
471  case W_DT_COMPLEX256:
472  throw WException( "Complex types are not supported, yet" );
473  break;
474  case W_DT_NONE:
475  throw WException( "W_DT_NONE should never occur as a type." );
476  break;
477  case W_DT_ALL:
478  throw WException( "W_DT_ALL should never occur as a type." );
479  break;
480  }
481 
482  if( socket.IsNotNull() )
483  {
484  int size[ 3 ];
485  std::shared_ptr < WGridRegular3D > g3dr( std::dynamic_pointer_cast < WGridRegular3D > ( dataSetScalar->getGrid() ) );
486  size[ 0 ] = g3dr->getNbCoordsX();
487  size[ 1 ] = g3dr->getNbCoordsY();
488  size[ 2 ] = g3dr->getNbCoordsZ();
489 
490  int svsize[ 3 ] = { size[ 0 ], size[ 1 ], size[ 2 ]};
491  int svoffset[] = { 0, 0, 0 };
492 
493  igtl::ImageMessage::Pointer imgMsg = igtl::ImageMessage::New();
494  imgMsg->SetDimensions( size );
495  imgMsg->SetSpacing( g3dr->getOffsetX(), g3dr->getOffsetY(), g3dr->getOffsetZ() );
496  imgMsg->SetDeviceName( "OpenWalnut" );
497  imgMsg->SetSubVolume( svsize, svoffset );
498  imgMsg->SetScalarType( scalarType );
499  imgMsg->AllocateScalars();
500 
501  size_t rawsize = Ugly::getRawSize( valueSet );
502  std::cout << "Transfering " << rawsize << " = " << imgMsg->GetImageSize() << " bytes of data." << std::endl;
503  memcpy( imgMsg->GetScalarPointer(), Ugly::getRawPtr( valueSet ), rawsize );
504 
505  igtl::Matrix4x4 matrix;
506  igtl::IdentityMatrix( matrix );
507  imgMsg->SetMatrix( matrix ); // TODO(mario): get the matrix from the data set
508  imgMsg->Pack();
509 
510  socket->Send( imgMsg->GetPackPointer(), imgMsg->GetPackSize() );
511  }
512 }
513 
514 std::shared_ptr < WValueSetBase > WIGTLinkRemote::createValueSet( const igtl::ImageMessage::Pointer& imgMsg )
515 {
516  std::shared_ptr<WValueSetBase> valueSet;
517  int size[ 3 ];
518  imgMsg->GetDimensions( size );
519  size_t sz = size[ 0 ] * size[ 1 ] * size[ 2 ];
520 #define CASE( igtltype, ctype, owtype )\
521  case igtltype: \
522  {\
523  std::shared_ptr < std::vector < ctype > > values( new std::vector < ctype >( sz ) );\
524  memcpy( ( void* )&( ( *values )[ 0 ] ), imgMsg->GetScalarPointer(), sizeof( ctype ) * sz );\
525  valueSet.reset( new WValueSet < ctype >( 0, 1, values, owtype ) );\
526  }\
527  break
528 
529  switch( imgMsg->GetScalarType() )
530  {
531  CASE( igtl::ImageMessage::TYPE_INT8, int8_t, W_DT_INT8 );
532  CASE( igtl::ImageMessage::TYPE_UINT8, uint8_t, W_DT_UINT8 );
533  CASE( igtl::ImageMessage::TYPE_INT16, int16_t, W_DT_INT16 );
534  CASE( igtl::ImageMessage::TYPE_UINT16, uint16_t, W_DT_UINT16 );
535  CASE( igtl::ImageMessage::TYPE_INT32, int32_t, W_DT_SIGNED_INT );
536  CASE( igtl::ImageMessage::TYPE_UINT32, uint32_t, W_DT_UINT32 );
537  default:
538  break;
539  // TODO(mario): throw exception?
540  }
541 #undef CASE
542  return valueSet;
543 }
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
This data set type contains scalars as values.
Basic exception handler.
Definition: WException.h:39
A grid that has parallelepiped cells which all have the same proportion.
std::shared_ptr< WGridRegular3DTemplate > SPtr
Convenience typedef for a std::shared_ptr< WGridRegular3DTemplate >.
Implements an orthogonal grid transformation.
void createSocketAndWaitForConnection(uint32_t port)
Use this as a server that waits for client connections.
igtl::ClientSocket::Pointer socket
Points to the socket used by this connection.
void receiveTransform(igtl::MessageHeader::Pointer headerMsg)
Receive a transform.
std::shared_ptr< WCondition > receiversCondition
condition to notify receivers that new data is waiting
bool checkCRC
true if we check CRC sums in incoming packets
void sendTransform(const std::string &name, const WMatrix< double > &matrix)
send a matrix as an igtl transform
std::shared_ptr< WValueSetBase > createValueSet(const igtl::ImageMessage::Pointer &imgMsg)
Internal helper to create a value set from a message.
void readDataLoop()
main loop for reading
std::shared_ptr< WCondition > statusCondition
condition to notify a status change
boost::mutex receiversMutex
mutex has to be locked during access to receiveQueue
uint32_t port
the port for listening connections
WDataSetScalarSPtr receiveImage(igtl::MessageHeader::Pointer headerMsg)
Receive an image.
void injectMessage()
TODO: inject a message in the send queue.
void createSocketAndConnect(std::string server, uint32_t port)
Use this as a client that connects to a remote server.
virtual void threadMain()
the main thread doing passive connections or receiving data
void sendImageMetaData(const std::vector< WDataSetScalarSPtr > &dataSets)
Send metadata of a list of data sets.
void sendImageData(WDataSetScalarSPtr dataSetScalar)
Send image data of a single data set.
void listenLoop()
setup listening socket and listen
std::queue< WDataSetScalarSPtr > receiveQueue
queue of received data sets that should be read by the module
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
Convert a runtime type to a C++ type.