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