CustusX  20.03-rc1
An IGT application
cxNetworkHandler.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
12 #include "cxNetworkHandler.h"
13 
14 #include <QTimer>
15 
16 #include "igtlioLogic.h"
17 #include "igtlioImageDevice.h"
18 #include "igtlioTransformDevice.h"
19 #include "igtlioStatusDevice.h"
20 #include "igtlioStringDevice.h"
21 
22 #include "igtlioConnector.h"
23 
24 #include "igtlioImageConverter.h"
25 #include "igtlioTransformConverter.h"
26 #include "igtlioCommandConverter.h"
27 #include "igtlioStatusConverter.h"
28 #include "igtlioStringConverter.h"
29 #include "igtlioUsSectorDefinitions.h"
30 
31 #include "cxLogger.h"
32 #include "cxProbeSector.h"
33 #include "cxProbeDefinition.h"
34 
35 namespace cx
36 {
37 
38 NetworkHandler::NetworkHandler(igtlioLogicPointer logic) :
39  mTimer(new QTimer(this)),
40  mProbeDefinitionFromStringMessages(ProbeDefinitionFromStringMessagesPtr(new ProbeDefinitionFromStringMessages)),
41  mProbeDefinition(ProbeDefinitionPtr()),
42  mZeroesInImage(true),
43  mUSMask(nullptr),
44  mSkippedImages(0)
45 {
46  qRegisterMetaType<Transform3D>("Transform3D");
47  qRegisterMetaType<ImagePtr>("ImagePtr");
48 
49  mLogic = logic;
50 
52  this->connectToDeviceEvents();
53 
54  connect(mTimer, SIGNAL(timeout()), this, SLOT(periodicProcess()));
55  mTimer->start(5);
56 }
57 
59 {
60  mTimer->stop();
61 }
62 
63 igtlioSessionPointer NetworkHandler::requestConnectToServer(std::string serverHost, int serverPort, IGTLIO_SYNCHRONIZATION_TYPE sync, double timeout_s)
64 {
65  mSession = mLogic->ConnectToServer(serverHost, serverPort, sync, timeout_s);
66  return mSession;
67 }
68 
70 {
71  if (mSession->GetConnector() && mSession->GetConnector()->GetState()!=igtlioConnector::STATE_OFF)
72  {
73  CX_LOG_DEBUG() << "NetworkHandler: Disconnecting from server" << mSession->GetConnector()->GetName();
74  igtlioConnectorPointer connector = mSession->GetConnector();
75  connector->Stop();
76  mLogic->RemoveConnector(connector);
77  }
79 }
80 
81 void NetworkHandler::onDeviceReceived(vtkObject* caller_device, void* unknown, unsigned long event , void*)
82 {
83  Q_UNUSED(unknown);
84  Q_UNUSED(event);
85  vtkSmartPointer<igtlioDevice> receivedDevice(reinterpret_cast<igtlioDevice*>(caller_device));
86 
87  igtlioBaseConverter::HeaderData header = receivedDevice->GetHeader();
88  std::string device_type = receivedDevice->GetDeviceType();
89 
90 // CX_LOG_DEBUG() << "Device is modified, device type: " << device_type << " on device: " << receivedDevice->GetDeviceName() << " equipmentId: " << header.equipmentId;
91 
92  // Currently the only id available is the Device name defined in Plus xml. Looking like this: Probe_sToReference_s
93  // Use this for all message types for now, instead of equipmentId.
94  // Anser integration may send equipmentId, so this is checked for when we get a transform.
95  QString deviceName(receivedDevice->GetDeviceName().c_str());
96 
97  if(device_type == igtlioImageConverter::GetIGTLTypeName())
98  {
99  igtlioImageDevicePointer imageDevice = igtlioImageDevice::SafeDownCast(receivedDevice);
100 
101  igtlioImageConverter::ContentData content = imageDevice->GetContent();
102 
103 // QString deviceName(header.deviceName.c_str());
104 // QString deviceName(header.equipmentId.c_str());//Use equipmentId
105  ImagePtr cximage = ImagePtr(new Image(deviceName, content.image));
106  // get timestamp from igtl second-format:;
107  double timestampMS = header.timestamp * 1000;
108  //cximage->setAcquisitionTime( QDateTime::fromMSecsSinceEpoch(qint64(timestampMS)));
109  //TODO: Use incoming timestamps. Current implementation just restamp the messages at arrival to match CustusX time format.
110  cximage->setAcquisitionTime( QDateTime::currentDateTime() );
111  //this->decode_rMd(msg, retval);
112 
113 
114  //Use the igtlio meta data from the image message
115  std::string metaLabel;
116  std::string metaDataValue;
117  QStringList igtlioLabels;
118 
119  igtlioLabels << IGTLIO_KEY_PROBE_TYPE;
120  igtlioLabels << IGTLIO_KEY_ORIGIN;
121  igtlioLabels << IGTLIO_KEY_ANGLES;
122  igtlioLabels << IGTLIO_KEY_BOUNDING_BOX;
123  igtlioLabels << IGTLIO_KEY_DEPTHS;
124  igtlioLabels << IGTLIO_KEY_LINEAR_WIDTH;
125  igtlioLabels << IGTLIO_KEY_SPACING_X;
126  igtlioLabels << IGTLIO_KEY_SPACING_Y;
127  //TODO: Use deciveNameLong when this is defined in IGTLIO and sent with Plus
128 
129 
130  for (int i = 0; i < igtlioLabels.size(); ++i)
131  {
132  metaLabel = igtlioLabels[i].toStdString();
133  bool gotMetaData = receivedDevice->GetMetaDataElement(metaLabel, metaDataValue);
134  if(!gotMetaData)
135  {
136  //Also allow meta data sent as string messages
137  //CX_LOG_WARNING() << "Cannot get needed igtlio meta information: " << metaLabel;
138  }
139  else
140  mProbeDefinitionFromStringMessages->parseValue(metaLabel.c_str(), metaDataValue.c_str());
141  }
142 
143  mProbeDefinitionFromStringMessages->setImage(cximage);
144  processImageAndEmitProbeDefinition(cximage, deviceName);//Use equipmentId?
145  emit image(cximage);
146 
147  // CX-366: Currenly we don't use the transform from the image message, because there is no specification of what this transform should be.
148  // Only the transforms from the transform messages are used.
149 // double timestamp = header.timestamp;
150 // Transform3D cxtransform = Transform3D::fromVtkMatrix(content.transform);
151 // emit transform(deviceName, cxtransform, timestamp);
152  }
153  else if(device_type == igtlioTransformConverter::GetIGTLTypeName())
154  {
155  igtlioTransformDevicePointer transformDevice = igtlioTransformDevice::SafeDownCast(receivedDevice);
156  igtlioTransformConverter::ContentData content = transformDevice->GetContent();
157 
158 // QString deviceName(content.deviceName.c_str());
159 // QString deviceName(header.equipmentId.c_str());//Use equipmentId
160  //QString streamIdTo(content.streamIdTo.c_str());
161  //QString streamIdFrom(content.streamIdFrom.c_str());
162  Transform3D cxtransform = Transform3D::fromVtkMatrix(content.transform);
163 
164 // CX_LOG_DEBUG() << "TRANSFORM: " << " equipmentId: " << header.equipmentId
165 // << " streamIdTo: " << content.streamIdTo
166 // << " streamIdFrom: " << content.streamIdFrom
167 // << " deviceName: " << deviceName
168 // << " transform: " << cxtransform;
169 
170  double timestamp = header.timestamp;
171 // emit transform(deviceName, header.equipmentType, cxtransform, timestamp);
172  //test: Set all messages as type TRACKED_US_PROBE for now
173 // emit transform(deviceName, igtlioBaseConverter::TRACKED_US_PROBE, cxtransform, timestamp);
174 
175  // Try to use equipmentId from OpenIGTLink meta data. If not presnet use deviceName.
176  // Having equipmentId in OpenIGTLink meta data is something we would like to have a part of the OpenIGTLinkIO standard,
177  // and added to the messages from Plus.
178  std::string openigtlinktransformid;
179  bool gotTransformId = receivedDevice->GetMetaDataElement("equipmentId", openigtlinktransformid);
180 
181  if (gotTransformId)
182  emit transform(qstring_cast(openigtlinktransformid), cxtransform, timestamp);
183  else
184  emit transform(deviceName, cxtransform, timestamp);
185  }
186  else if(device_type == igtlioStatusConverter::GetIGTLTypeName())
187  {
188  igtlioStatusDevicePointer status = igtlioStatusDevice::SafeDownCast(receivedDevice);
189 
190  igtlioStatusConverter::ContentData content = status->GetContent();
191 
192  CX_LOG_DEBUG() << "STATUS: " << " code: " << content.code
193  << " subcode: " << content.subcode
194  << " errorname: " << content.errorname
195  << " statusstring: " << content.statusstring;
196 
197  }
198  else if(device_type == igtlioStringConverter::GetIGTLTypeName())
199  {
200  igtlioStringDevicePointer string = igtlioStringDevice::SafeDownCast(receivedDevice);
201 
202  igtlioStringConverter::ContentData content = string->GetContent();
203 
204 // CX_LOG_DEBUG() << "STRING: " << " equipmentId: " << header.equipmentId
205 // << " encoding: " << content.encoding
206 // << " string: " << content.string_msg;
207 
208  QString message(content.string_msg.c_str());
209  //Also allow meta data sent as string messages
210  mProbeDefinitionFromStringMessages->parseStringMessage(header, message);//Turning this off because we want to use meta info instead
211  emit string_message(message);
212  }
213  else
214  {
215  CX_LOG_WARNING() << "Found unhandled devicetype: " << device_type;
216  }
217 
218 }
219 
220 void NetworkHandler::onConnectionEvent(vtkObject* caller, void* connector, unsigned long event , void*)
221 {
222  Q_UNUSED(caller);
223  Q_UNUSED(connector);
224  if (event==igtlioLogic::ConnectionAddedEvent)
225  {
226  emit connected();
227  }
228  if (event==igtlioLogic::ConnectionAboutToBeRemovedEvent)
229  {
230  emit disconnected();
231  }
232 }
233 
234 void NetworkHandler::onDeviceAddedOrRemoved(vtkObject* caller, void* void_device, unsigned long event, void* callData)
235 {
236  Q_UNUSED(caller);
237  Q_UNUSED(callData);
238  if (event==igtlioLogic::NewDeviceEvent)
239  {
240  igtlioDevicePointer device(reinterpret_cast<igtlioDevice*>(void_device));
241  if(device)
242  {
243  CX_LOG_DEBUG() << " NetworkHandler is listening to " << device->GetDeviceName();
244  qvtkReconnect(NULL, device, igtlioDevice::ReceiveEvent, this, SLOT(onDeviceReceived(vtkObject*, void*, unsigned long, void*)));
245  }
246  }
247  if (event==igtlioLogic::RemovedDeviceEvent)
248  {
249  CX_LOG_WARNING() << "TODO: on remove device event, not implemented";
250  }
251 }
252 
253 void NetworkHandler::periodicProcess()
254 {
255  mLogic->PeriodicProcess();
256 }
257 
259 {
260  foreach(int eventId, QList<int>()
261  << igtlioLogic::ConnectionAddedEvent
262  << igtlioLogic::ConnectionAboutToBeRemovedEvent
263  )
264  {
265  qvtkReconnect(NULL, mLogic, eventId,
266  this, SLOT(onConnectionEvent(vtkObject*, void*, unsigned long, void*)));
267  }
268 }
269 
271 {
272  foreach(int eventId, QList<int>()
273  << igtlioLogic::NewDeviceEvent
274  << igtlioLogic::RemovedDeviceEvent
275  )
276  {
277  qvtkReconnect(NULL, mLogic, eventId,
278  this, SLOT(onDeviceAddedOrRemoved(vtkObject*, void*, unsigned long, void*)));
279  }
280 }
281 
282 //TODO: Consider moving these image changing functions out of the class
284 {
285  bool probeDefinitionHaveChanged = emitProbeDefinitionIfChanged(deviceName);
286 
287  if (probeDefinitionHaveChanged)
288  {
289  mZeroesInImage = true;
290  mSkippedImages = 0;
291  this->createMask(); //Only create mask once for each probeDefinition
292  }
293 
294  // Turn off zero conversion if we get a frame without zeroes. Recheck for zeroes every 30 images
295  if(mZeroesInImage || (mSkippedImages > 30))
296  {
297  // CX_LOG_DEBUG() << "*** Removing zeroes from US image ***";
299  mSkippedImages = 0;
300  }
301  else
302  {
303  ++mSkippedImages;
304  // CX_LOG_DEBUG() << "No zeroes in incoming US image";
305  }
306 }
307 
309 {
310  if (mProbeDefinitionFromStringMessages->haveValidValues() && mProbeDefinitionFromStringMessages->haveChanged())
311  {
312  mProbeDefinition = mProbeDefinitionFromStringMessages->createProbeDefintion(deviceName);
313  emit probedefinition(deviceName, mProbeDefinition);
314  return true;
315  }
316  return false;
317 }
318 
319 bool NetworkHandler::convertZeroesInsideSectorToOnes(ImagePtr cximage, int threshold, int newValue)
320 {
321  bool retval = false;
322  if(!mUSMask)
323  return retval;
324 
325  Eigen::Array3i maskDims(mUSMask->GetDimensions());
326  unsigned char* maskPtr = static_cast<unsigned char*> (mUSMask->GetScalarPointer());
327  unsigned char* imagePtr = static_cast<unsigned char*> (cximage->getBaseVtkImageData()->GetScalarPointer());
328  unsigned components = cximage->getBaseVtkImageData()->GetNumberOfScalarComponents();
329  unsigned dimX = maskDims[0];
330  unsigned dimY = maskDims[1];
331  for (unsigned x = 0; x < dimX; x++)
332  for (unsigned y = 0; y < dimY; y++)
333  {
334  unsigned pos = x + y * dimX;
335  unsigned imagePos = pos*components;
336  unsigned char maskVal = maskPtr[pos];
337  unsigned char imageVal = imagePtr[imagePos];
338  if (maskVal != 0 && imageVal <= threshold)
339  {
340  for(unsigned i=0; i < components; ++i)
341  if(i < 3) //Only set RGB components, not Alpha
342  {
343  imagePtr[imagePos + i] = newValue;
344  retval = true;
345  }
346  }
347  }
348  return retval;
349 }
350 
352 {
353  if(!mProbeDefinition)
354  {
355  CX_LOG_WARNING() << "No ProbeDefinition";
356  return false;
357  }
358  ProbeSector probeSector;
359  probeSector.setData(*mProbeDefinition.get());
360 
361  if(!mUSMask)
362  {
363  mUSMask = probeSector.getMask();
364  return true;
365  }
366  return false;
367 }
368 
369 } // namespace cx
QString qstring_cast(const T &val)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
igtlioSessionPointer requestConnectToServer(std::string serverHost, int serverPort=-1, IGTLIO_SYNCHRONIZATION_TYPE sync=IGTLIO_BLOCKING, double timeout_s=5)
igtlioLogicPointer mLogic
bool convertZeroesInsideSectorToOnes(ImagePtr cximage, int threshold=0, int newValue=1)
bool emitProbeDefinitionIfChanged(QString deviceName)
vtkImageDataPtr mUSMask
void string_message(QString message)
void processImageAndEmitProbeDefinition(ImagePtr cximage, QString deviceName)
igtlioSessionPointer mSession
ProbeDefinitionFromStringMessagesPtr mProbeDefinitionFromStringMessages
A volumetric data set.
Definition: cxImage.h:45
void transform(QString devicename, Transform3D transform, double timestamp)
void probedefinition(QString devicename, ProbeDefinitionPtr definition)
boost::shared_ptr< class ProbeDefinitionFromStringMessages > ProbeDefinitionFromStringMessagesPtr
#define CX_LOG_DEBUG
Definition: cxLogger.h:95
#define CX_LOG_WARNING
Definition: cxLogger.h:98
vtkImageDataPtr getMask()
NetworkHandler(igtlioLogicPointer logic)
Utility functions for drawing an US Probe sector.
Definition: cxProbeSector.h:38
boost::shared_ptr< class ProbeDefinition > ProbeDefinitionPtr
ProbeDefinitionPtr mProbeDefinition
void image(ImagePtr image)
void setData(ProbeDefinition data)
Namespace for all CustusX production code.