CustusX  15.4.0-beta
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(mImageStreamer->getType()));
91 
92  if(!mImageStreamer)
93  {
94  return false;
95  }
96  mSender.reset(new DirectlyLinkedSender());
97 
98  connect(mSender.get(), SIGNAL(newImage()), this, SLOT(addImageToQueueSlot()), Qt::DirectConnection);
99  connect(mSender.get(), SIGNAL(newUSStatus()), this, SLOT(addSonixStatusToQueueSlot()), Qt::DirectConnection);
100 
101  if(!mImageStreamer->startStreaming(mSender))
102  {
103  return false;
104  }
105 
106  return true;
107 }
108 
110 {
111  if (mImageStreamer)
112  {
113  report(QString("Stopping streamer: [%1]...").arg(mImageStreamer->getType()));
114  mImageStreamer->stopStreaming();
115  report(QString("Stopped streamer: [%1]").arg(mImageStreamer->getType()));
116  mImageStreamer.reset();
117  mSender.reset();
118  }
119 
120  emit finished();
121 }
122 
123 void ImageReceiverThread::addImageToQueueSlot()
124 {
125  this->addImageToQueue(mSender->popImage());
126 }
127 
128 void ImageReceiverThread::addSonixStatusToQueueSlot()
129 {
130  this->addSonixStatusToQueue(mSender->popUSStatus());
131 }
132 
133 
135 {
136  this->reportFPS(imgMsg->getUid());
137 // if(this->imageComesFromActiveVideoSource(imgMsg))
138 // this->reportFPS();
139 
140  bool needToCalibrateMsgTimeStamp = this->imageComesFromSonix(imgMsg);
141 
142  //Should only be needed if time stamp is set on another computer that is
143  //not synched with the one running this code: e.g. The Ultrasonix scanner
144  if (needToCalibrateMsgTimeStamp)
145  this->calibrateTimeStamp(imgMsg);
146 
147  QMutexLocker sentry(&mImageMutex);
148  mMutexedImageMessageQueue.push_back(imgMsg);
149  sentry.unlock();
150 
151  emit imageReceived(); // emit signal outside lock, catch possibly in another thread
152 }
153 
155 {
156  QMutexLocker sentry(&mSonixStatusMutex);
157  mMutexedSonixStatusMessageQueue.push_back(msg);
158  sentry.unlock();
159  emit sonixStatusReceived(); // emit signal outside lock, catch possibly in another thread
160 }
161 
163 {
164  QMutexLocker sentry(&mImageMutex);
165  if (mMutexedImageMessageQueue.empty())
166  return ImagePtr();
167  ImagePtr retval = mMutexedImageMessageQueue.front();
168  mMutexedImageMessageQueue.pop_front();
169 
170  // this happens when the main thread is busy. This is bad, but happens a lot during operation.
171  // Removed this in order to remove spam from console
172 // static int mQueueSizeOnLastGet = 0;
173 // int queueSize = mMutexedImageMessageQueue.size();
174 // mQueueSizeOnLastGet = queueSize;
175  return retval;
176 }
177 
179 {
180  QMutexLocker sentry(&mSonixStatusMutex);
181  if (mMutexedSonixStatusMessageQueue.empty())
182  return ProbeDefinitionPtr();
183  ProbeDefinitionPtr retval = mMutexedSonixStatusMessageQueue.front();
184  mMutexedSonixStatusMessageQueue.pop_front();
185  return retval;
186 }
187 
189 {
190  QDateTime timestamp_dt = imgMsg->getAcquisitionTime();
191  double timestamp_ms = timestamp_dt.toMSecsSinceEpoch();
192 
193  if (similar(mLastReferenceTimestampDiff, 0.0, 0.000001))
194  mLastReferenceTimestampDiff = timestamp_dt.msecsTo(QDateTime::currentDateTime());
195 
196  // Start collecting time stamps if 20 sec since last calibration time
197  if(mLastSyncTime.isNull() || ( mLastSyncTime.msecsTo(QDateTime::currentDateTime()) > 2000) )
198  mGeneratingTimeCalibration = true;
199 
200  if(mGeneratingTimeCalibration)
201  mLastTimeStamps.push_back(timestamp_dt.msecsTo(QDateTime::currentDateTime()));
202 
203  // Perform time calibration if enough time stamps have been collected
204  if(mLastTimeStamps.size() >= 20)
205  {
206  std::sort(mLastTimeStamps.begin(), mLastTimeStamps.end(), AbsDoubleLess(mLastReferenceTimestampDiff));
207 
208  mLastTimeStamps.resize(15);
209 
210  double sumTimes = 0;
211  for (std::vector<double>::const_iterator citer = mLastTimeStamps.begin(); citer != mLastTimeStamps.end(); ++citer)
212  {
213  sumTimes += *citer;
214  }
215  mLastReferenceTimestampDiff = sumTimes / 15.0;
216 
217  //Reset
218  mLastTimeStamps.clear();
219  mLastSyncTime = QDateTime::currentDateTime();
220  mGeneratingTimeCalibration = false;
221  }
222  imgMsg->setAcquisitionTime(QDateTime::fromMSecsSinceEpoch(timestamp_ms + mLastReferenceTimestampDiff));
223 
224 }
225 
226 void ImageReceiverThread::reportFPS(QString streamUid)
227 {
228  int timeout = 2000;
229  if (!mFPSTimer.count(streamUid))
230  {
231  mFPSTimer[streamUid].reset(new CyclicActionLogger());
232  mFPSTimer[streamUid]->reset(timeout);
233  }
234 
235  CyclicActionLoggerPtr logger = mFPSTimer[streamUid];
236 
237  logger->begin();
238  if (logger->intervalPassed())
239  {
240  emit fps(streamUid, logger->getFPS());
241  logger->reset(timeout);
242  }
243 }
244 
245 bool ImageReceiverThread::imageComesFromSonix(ImagePtr imgMsg)
246 {
247  return imgMsg->getUid().contains("Sonix", Qt::CaseInsensitive);
248 }
249 
251 {
252  if (!mStreamerInterface)
253  return "none";
254  return mStreamerInterface->getName();
255 }
256 
257 
258 } /* namespace cx */
cxResource_EXPORT ProfilePtr profile()
Definition: cxProfile.cpp:142
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.