CustusX  15.8
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxImageReceiverThread.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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
33 #include "cxImageReceiverThread.h"
34 
35 #include "cxCyclicActionLogger.h"
36 #include "cxXmlOptionItem.h"
37 #include "cxStreamer.h"
38 #include "cxStreamerService.h"
39 #include "cxDirectlyLinkedSender.h"
40 #include "cxLogger.h"
41 #include "cxProfile.h"
42 
43 namespace cx
44 {
45 
47 {
48 public:
49  AbsDoubleLess(double center) : mCenter(center) { };
50 
51  bool operator()(const double& d1, const double& d2)
52  {
53  return fabs(d1 - mCenter) < fabs(d2 - mCenter);
54  }
55 
56  double mCenter;
57 };
58 
62 
63 ImageReceiverThread::ImageReceiverThread(StreamerServicePtr streamerInterface, QObject* parent) :
64  QObject(parent),
65  mStreamerInterface(streamerInterface)
66 {
67  this->setObjectName("imagereceiver worker");
68  mGeneratingTimeCalibration = false;
69  mLastReferenceTimestampDiff = 0.0;
70  mLastTimeStamps.reserve(20);
71 }
72 
74 {
75  if (!this->attemptInitialize())
76  {
77  // cleanup here in order to do less in the destructor... ??
78  mImageStreamer.reset();
79  mSender.reset();
80 
81  emit finished();
82  }
83 }
84 
85 bool ImageReceiverThread::attemptInitialize()
86 {
87  XmlOptionFile xmlFile = profile()->getXmlSettings().descend("video");
88  QDomElement element = xmlFile.getElement("video");
89  mImageStreamer = mStreamerInterface->createStreamer(element);
90  report(QString("Starting streamer: [%1]").arg(this->hostDescription()));
91 
92  if(!mImageStreamer)
93  {
94  return false;
95  }
96  mSender.reset(new DirectlyLinkedSender());
97 
98  connect(mSender.get(), &DirectlyLinkedSender::newImage, this, &ImageReceiverThread::addImageToQueueSlot, Qt::DirectConnection);
99  connect(mSender.get(), &DirectlyLinkedSender::newUSStatus, this, &ImageReceiverThread::addSonixStatusToQueueSlot, Qt::DirectConnection);
100 
101  mImageStreamer->startStreaming(mSender);
102 
103  return true;
104 }
105 
107 {
108  if (mImageStreamer)
109  {
110  report(QString("Stopping streamer: [%1]...").arg(this->hostDescription()));
111  mImageStreamer->stopStreaming();
112  report(QString("Stopped streamer: [%1]").arg(this->hostDescription()));
113  mImageStreamer.reset();
114  mSender.reset();
115  }
116 
117  emit finished();
118 }
119 
120 void ImageReceiverThread::addImageToQueueSlot()
121 {
122  this->addImageToQueue(mSender->popImage());
123 }
124 
125 void ImageReceiverThread::addSonixStatusToQueueSlot()
126 {
127  this->addSonixStatusToQueue(mSender->popUSStatus());
128 }
129 
130 
132 {
133  this->reportFPS(imgMsg->getUid());
134 // if(this->imageComesFromActiveVideoSource(imgMsg))
135 // this->reportFPS();
136 
137  bool needToCalibrateMsgTimeStamp = this->imageComesFromSonix(imgMsg);
138 
139  //Should only be needed if time stamp is set on another computer that is
140  //not synched with the one running this code: e.g. The Ultrasonix scanner
141  if (needToCalibrateMsgTimeStamp)
142  this->calibrateTimeStamp(imgMsg);
143 
144  QMutexLocker sentry(&mImageMutex);
145  mMutexedImageMessageQueue.push_back(imgMsg);
146  sentry.unlock();
147 
148  emit imageReceived(); // emit signal outside lock, catch possibly in another thread
149 }
150 
152 {
153  QMutexLocker sentry(&mSonixStatusMutex);
154  mMutexedSonixStatusMessageQueue.push_back(msg);
155  sentry.unlock();
156  emit sonixStatusReceived(); // emit signal outside lock, catch possibly in another thread
157 }
158 
160 {
161  QMutexLocker sentry(&mImageMutex);
162  if (mMutexedImageMessageQueue.empty())
163  return ImagePtr();
164  ImagePtr retval = mMutexedImageMessageQueue.front();
165  mMutexedImageMessageQueue.pop_front();
166 
167  // this happens when the main thread is busy. This is bad, but happens a lot during operation.
168  // Removed this in order to remove spam from console
169 // static int mQueueSizeOnLastGet = 0;
170 // int queueSize = mMutexedImageMessageQueue.size();
171 // mQueueSizeOnLastGet = queueSize;
172  return retval;
173 }
174 
176 {
177  QMutexLocker sentry(&mSonixStatusMutex);
178  if (mMutexedSonixStatusMessageQueue.empty())
179  return ProbeDefinitionPtr();
180  ProbeDefinitionPtr retval = mMutexedSonixStatusMessageQueue.front();
181  mMutexedSonixStatusMessageQueue.pop_front();
182  return retval;
183 }
184 
186 {
187  QDateTime timestamp_dt = imgMsg->getAcquisitionTime();
188  double timestamp_ms = timestamp_dt.toMSecsSinceEpoch();
189 
190  if (similar(mLastReferenceTimestampDiff, 0.0, 0.000001))
191  mLastReferenceTimestampDiff = timestamp_dt.msecsTo(QDateTime::currentDateTime());
192 
193  // Start collecting time stamps if 20 sec since last calibration time
194  if(mLastSyncTime.isNull() || ( mLastSyncTime.msecsTo(QDateTime::currentDateTime()) > 2000) )
195  mGeneratingTimeCalibration = true;
196 
197  if(mGeneratingTimeCalibration)
198  mLastTimeStamps.push_back(timestamp_dt.msecsTo(QDateTime::currentDateTime()));
199 
200  // Perform time calibration if enough time stamps have been collected
201  if(mLastTimeStamps.size() >= 20)
202  {
203  std::sort(mLastTimeStamps.begin(), mLastTimeStamps.end(), AbsDoubleLess(mLastReferenceTimestampDiff));
204 
205  mLastTimeStamps.resize(15);
206 
207  double sumTimes = 0;
208  for (std::vector<double>::const_iterator citer = mLastTimeStamps.begin(); citer != mLastTimeStamps.end(); ++citer)
209  {
210  sumTimes += *citer;
211  }
212  mLastReferenceTimestampDiff = sumTimes / 15.0;
213 
214  //Reset
215  mLastTimeStamps.clear();
216  mLastSyncTime = QDateTime::currentDateTime();
217  mGeneratingTimeCalibration = false;
218  }
219  imgMsg->setAcquisitionTime(QDateTime::fromMSecsSinceEpoch(timestamp_ms + mLastReferenceTimestampDiff));
220 
221 }
222 
223 void ImageReceiverThread::reportFPS(QString streamUid)
224 {
225  int timeout = 2000;
226  if (!mFPSTimer.count(streamUid))
227  {
228  mFPSTimer[streamUid].reset(new CyclicActionLogger());
229  mFPSTimer[streamUid]->reset(timeout);
230  }
231 
232  CyclicActionLoggerPtr logger = mFPSTimer[streamUid];
233 
234  logger->begin();
235  if (logger->intervalPassed())
236  {
237  emit fps(streamUid, logger->getFPS());
238  logger->reset(timeout);
239  }
240 }
241 
242 bool ImageReceiverThread::imageComesFromSonix(ImagePtr imgMsg)
243 {
244  return imgMsg->getUid().contains("Sonix", Qt::CaseInsensitive);
245 }
246 
248 {
249  if (!mStreamerInterface)
250  return "none";
251  return mStreamerInterface->getName();
252 }
253 
254 
255 } /* namespace cx */
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:169
void fps(QString, double)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
QDomElement getElement()
return the current element
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
virtual ImagePtr getLastImageMessage()
AbsDoubleLess(double center)
boost::shared_ptr< class CyclicActionLogger > CyclicActionLoggerPtr
void addSonixStatusToQueue(ProbeDefinitionPtr msg)
add the message to a thread-safe queue
virtual QString hostDescription() const
ImageReceiverThread(StreamerServicePtr streamerInterface, QObject *parent=NULL)
void report(QString msg)
Definition: cxLogger.cpp:90
void calibrateTimeStamp(ImagePtr imgMsg)
Calibrate the time stamps of the incoming message based on the computer clock. Calibration is based o...
virtual ProbeDefinitionPtr getLastSonixStatusMessage()
boost::shared_ptr< class StreamerService > StreamerServicePtr
bool operator()(const double &d1, const double &d2)
void addImageToQueue(ImagePtr imgMsg)
boost::shared_ptr< class ProbeDefinition > ProbeDefinitionPtr
Helper class for xml files used to store ssc/cx data.