OpenWalnut  1.5.0dev
WMOpenIGTLink.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 <memory>
26 #include <string>
27 
28 #include "WIGTLinkRemote.h"
29 #include "WMOpenIGTLink.h"
30 #include "core/dataHandler/WDataSetScalar.h"
31 #include "core/kernel/WKernel.h"
32 #include "core/kernel/WSelectionManager.h"
33 
34 #define PORT_SLICER_DEFAULT 18944
35 
36 // This line is needed by the module loader to actually find your module. Do not remove. Do NOT add a ";" here.
37 W_LOADABLE_MODULE( WMOpenIGTLinkSender )
38 
39 WMOpenIGTLinkSender::WMOpenIGTLinkSender():
40  WModule()
41 {
42 }
43 
44 WMOpenIGTLinkSender::~WMOpenIGTLinkSender()
45 {
46  // Cleanup!
47 }
48 
49 std::shared_ptr< WModule > WMOpenIGTLinkSender::factory() const
50 {
51  // See "src/modules/template/" for an extensively documented example.
52  return std::shared_ptr< WModule >( new WMOpenIGTLinkSender() );
53 }
54 
55 const char** WMOpenIGTLinkSender::getXPMIcon() const
56 {
57  return NULL; // Please put a real icon here.
58 }
59 const std::string WMOpenIGTLinkSender::getName() const
60 {
61  // Specify your module name here. This name must be UNIQUE!
62  return "OpenIGTLinkSender";
63 }
64 
65 const std::string WMOpenIGTLinkSender::getDescription() const
66 {
67  return "OpenIGT Link";
68 }
69 
71 {
72  m_input = WModuleInputData < WDataSetScalar > ::createAndAdd( shared_from_this(), "input", "A data set to transmit over the network" );
73  m_output = WModuleOutputData < WDataSetScalar > ::createAndAdd( shared_from_this(), "output", "A data set received from the network" );
75 }
76 
78 {
79  m_propCondition = std::shared_ptr < WCondition > ( new WCondition() );
80 
81  m_propActive = m_properties->addProperty( "Active",
82  "Set to true to start the connection or to start the server."
83  "To avoid random connections, an update of the remote IP address"
84  " and the remote or local port is only performed whenever the active flag changes.",
85  false, m_propCondition );
86 
87  m_propServerOrClientSelections = std::shared_ptr < WItemSelection > ( new WItemSelection() );
88  m_propServerOrClientSelections->addItem( "Server", "TCP/IP server listening for incoming connections." );
89  m_propServerOrClientSelections->addItem( "Client", "TCP/IP client activenly making a connection to the remote host." );
90  m_propServerOrClient = m_properties->addProperty( "Act as:", "Server or Client.",
91  m_propServerOrClientSelections->getSelectorFirst(), m_propCondition );
94 
95  m_propCheckCRC = m_properties->addProperty( "Check CRC", "Enable to perform CRC on incoming packets. Disable to ignore the checksum",
96  true, m_propCondition );
97 
98  m_hostname = m_properties->addProperty( "Hostname", "Remote host name to connect to. Enter a host name or an IP address.",
99  std::string( "127.0.0.1" ), m_propCondition );
100  m_port = m_properties->addProperty( "Port", "Local port to open or remote port to connect to. The default Slicer3D port is 18944.",
101  PORT_SLICER_DEFAULT, m_propCondition );
102  m_port->setMin( 1 );
103  m_port->setMax( 65535 );
104 
105  // The slice positions. We use these as a transform that is sent over the network. Maybe we should send it as a position instead?
106  m_xPos = m_properties->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropSagittalPos() );
107  m_yPos = m_properties->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropCoronalPos() );
108  m_zPos = m_properties->addProperty( WKernel::getRunningKernel()->getSelectionManager()->getPropAxialPos() );
109 
111 }
112 
114 {
115 }
116 
118 {
119  m_moduleState.setResetable( true, true );
120  m_moduleState.add( m_input->getDataChangedCondition() );
122  m_moduleState.add( m_xPos->getUpdateCondition() );
123  m_moduleState.add( m_yPos->getUpdateCondition() );
124  m_moduleState.add( m_zPos->getUpdateCondition() );
125 
126  ready();
127  debugLog() << "Module is now ready.";
128 
129  std::shared_ptr < WIGTLinkRemote > remote;
130 
131  if( m_propActive->get() )
132  {
133  //if( m_propConnectToRemote->get() )
134  if( m_propServerOrClient->get() == 0 )
135  {
136  debugLog() << "listen on local port";
137  remote.reset( new WIGTLinkRemote );
138  remote->createSocketAndWaitForConnection( m_port->get() );
139  }
140  //else if( m_propListenOnPort->get() )
141  else // client
142  {
143  debugLog() << "connect to remote host";
144  remote.reset( new WIGTLinkRemote );
145  try
146  {
147  remote->createSocketAndConnect( m_hostname->get(), m_port->get() );
148  }
149  catch( const std::exception& e )
150  {
151  infoLog() << "Connection failed";
152  infoLog() << e.what();
153  // TODO(mario): issue gui warning and possible interaction?
154  }
155  remote.reset();
156  }
157  }
158  else
159  {
160  debugLog() << "no active connection";
161  }
162 
163  if( remote )
164  {
165  remote->setCheckCRC( m_propCheckCRC->get() );
166  m_moduleState.add( remote->receiversCondition );
167  m_moduleState.add( remote->statusCondition );
168  remote->run();
169  }
170 
171  debugLog() << "Entering main loop";
172  while( !m_shutdownFlag() )
173  {
174  debugLog() << "Waiting ...";
176  debugLog() << "Awaken!";
177 
178  if( m_shutdownFlag() )
179  {
180  break;
181  }
182 
183  if( m_propActive->changed( true ) )
184  // we ignore the following changes as we only allow changes when the server becomes active or inactive
185  // || m_hostname->changed( true ) || m_port->changed( true )
186  // || m_propConnectToRemote->changed( true ) || m_propListenOnPort->changed( true ) )
187  {
188  if( remote )
189  {
190  m_moduleState.remove( remote->receiversCondition );
191  m_moduleState.remove( remote->statusCondition );
192  debugLog() << "Shutting down the server.";
193  remote->wait( true );
194 
195  // first, remove the remote server object
196  remote.reset();
197  debugLog() << "waiting one second.";
198 
199  // wait for an appropriate time for operation system to close connections and allowing access to the ports, again
200  sleep( 1 ); // wait 1 second
201  }
202 
203  debugLog() << "reengage server.";
204  if( m_propActive->get() )
205  {
206  //if( m_propConnectToRemote->get() )
207  if( m_propServerOrClient->get() == 0 )
208  {
209  debugLog() << "listen on local port";
210  remote.reset( new WIGTLinkRemote );
211  remote->createSocketAndWaitForConnection( m_port->get() );
212  }
213  //else if( m_propListenOnPort->get() )
214  else // client
215  {
216  debugLog() << "connect to remote host";
217  remote.reset( new WIGTLinkRemote );
218  try
219  {
220  remote->createSocketAndConnect( m_hostname->get(), m_port->get() );
221  }
222  catch( const std::exception& e )
223  {
224  infoLog() << "Connection failed";
225  infoLog() << e.what();
226  // TODO(mario): issue gui warning and possible interaction?
227  m_propActive->set( false ); // set active to disabled as this is what we are at that point
228  }
229  remote.reset();
230  }
231  }
232  else
233  {
234  debugLog() << "no active connection";
235  }
236 
237  if( remote )
238  {
239  remote->setCheckCRC( m_propCheckCRC->get() );
240  m_moduleState.add( remote->receiversCondition );
241  m_moduleState.add( remote->statusCondition );
242  remote->run();
243  debugLog() << "server running.";
244  }
245  }
246 
247  if( remote )
248  {
249  if( m_propCheckCRC->changed( true ) )
250  {
251  remote->setCheckCRC( m_propCheckCRC->get() );
252  }
253 
254  debugLog() << "checking receive queue:";
255  remote->receiversMutex.lock();
256  if( !remote->receiveQueue.empty() )
257  {
258  debugLog() << "something in queue for us";
259  WDataSetScalarSPtr ds = remote->receiveQueue.front();
260  remote->receiveQueue.pop();
261  if( ds )
262  {
263  debugLog() << "received data set";
264  m_output->updateData( ds );
265  }
266  else
267  {
268  debugLog() << "received empty data set";
269  }
270  }
271  else
272  {
273  debugLog() << "Queue empty";
274  }
275  remote->receiversMutex.unlock();
276  }
277 
278  if( remote && m_input->updated() )
279  {
280  debugLog() << "prepare sending new data";
281  WDataSetScalarSPtr dataSet = m_input->getData();
282  bool dataValid = ( dataSet != NULL );
283 
284  if( dataValid )
285  {
286  debugLog() << "sending new data";
287  remote->sendImageData( dataSet ); // TODO(mario): try to make this async
288  }
289  }
290 
291  // if the navigation slices changed, we send an update to whoevert is listening
292  if( ( m_xPos->changed( true ) || m_yPos->changed( true ) || m_zPos->changed( true ) ) && remote )
293  {
294  WMatrix<double> a( 4, 4 );
295  a( 0, 0 ) = 1.0; a( 0, 1 ) = 0.0; a( 0, 2 ) = 0.0; a( 0, 3 ) = m_xPos->get(); // NOLINT
296  a( 1, 0 ) = 0.0; a( 1, 1 ) = 1.0; a( 1, 2 ) = 0.0; a( 1, 3 ) = m_yPos->get(); // NOLINT
297  a( 2, 0 ) = 0.0; a( 2, 1 ) = 0.0; a( 2, 2 ) = 1.0; a( 2, 3 ) = m_zPos->get(); // NOLINT
298  a( 3, 0 ) = 0.0; a( 3, 1 ) = 0.0; a( 3, 2 ) = 0.0; a( 3, 3 ) = 1.0; // NOLINT
299 
300  remote->sendTransform( "Coordinage System", a );
301  }
302  }
303 
304  debugLog() << "about to shut down";
305  if( remote )
306  {
307  debugLog() << "about to shut down: waiting for connection to close.";
308  remote->wait( true );
309  debugLog() << "about to shut down: connection closed";
310  }
311 
312  debugLog() << "module shutting down.";
313 }
314 
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void remove(std::shared_ptr< WCondition > condition)
Removes the specified condition.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
Common base class for a IGT Link connection.
A class containing a list of named items.
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
This module provides an interface to OpenIGTLink, a protocol to exchange data remotely over IP networ...
Definition: WMOpenIGTLink.h:52
std::shared_ptr< WModuleInputData< WDataSetScalar > > m_input
input for data to send
WPropDouble m_xPos
x position of the slice
std::shared_ptr< WItemSelection > m_propServerOrClientSelections
whether we connect to the remote host or just listen
virtual void requirements()
Initialize requirements for this module.
virtual void moduleMain()
Entry point after loading the module.
WPropString m_hostname
hostname to connect to
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
WPropInt m_port
port to listen on or to connect to
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
virtual void connectors()
Initialize the connectors this module is using.
WPropDouble m_yPos
y position of the slice
WPropBool m_propCheckCRC
Check CRC.
virtual const std::string getDescription() const
Gives back a description of this module.
WPropSelection m_propServerOrClient
Pick whether to act as an TCPIP server or as an TCPIP client, i.e., whether to listen on a port for i...
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
WPropDouble m_zPos
z position of the slice
WPropBool m_propActive
Activation status of the server.
virtual const std::string getName() const
Gives back the name of this module.
virtual void properties()
Initialize the properties for this module.
std::shared_ptr< WModuleOutputData< WDataSetScalar > > m_output
output of received data
Class offering an instantiate-able data connection between modules.
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
void sleep(const int32_t t) const
Sets thread asleep.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
void addTo(WPropSelection prop)
Add the PC_NOTEMPTY constraint to the property.
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.