#include "frameplaybackobject.h" #include FramePlaybackObject::FramePlaybackObject() { mThread_p = new QThread(); currentPosition = 0; playbackInterval = 1; playbackBurst = 1; statusCounter = 0; numBuses = 0; playbackActive = false; playbackForward = true; useOrigTiming = true; whichBusSend = 0; currentSeqItem = nullptr; } FramePlaybackObject::~FramePlaybackObject() { mThread_p->quit(); mThread_p->wait(); delete mThread_p; } quint64 FramePlaybackObject::updatePosition(bool forward) { //qDebug() << "updatePosition"; if (!currentSeqItem) { playbackTimer->stop(); //pushing this button halts automatic playback playbackActive = false; currentPosition = 0; return 0; } //only send frame out if its ID is checked in the list. Otherwise discard it. CANFrame *thisFrame = ¤tSeqItem->data[currentPosition]; uint32_t originalBus = thisFrame->bus; if (currentSeqItem->idFilters.find(thisFrame->frameId()).value()) { if (whichBusSend > -1) { thisFrame->bus = whichBusSend; sendingBuffer.append(*thisFrame); } else if (whichBusSend == -1) { for (int c = 0; c < numBuses; c++) { thisFrame->bus = c; sendingBuffer.append(*thisFrame); } } else //from file so retain original bus and send as-is { sendingBuffer.append(*thisFrame); } thisFrame->bus = originalBus; } if (forward) { if (currentPosition < (currentSeqItem->data.count() - 1)) currentPosition++; //still in same file so keep going else //hit the end of the current file { qDebug() << "hit end of current sequence"; currentSeqItem->currentLoopCount++; currentPosition = 0; if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? { playbackActive = false; playbackTimer->stop(); emit EndOfFrameCache(); } } } else { if (currentPosition > 0) currentPosition--; else //hit the beginning of the current sequence { qDebug() << "hit start of current sequence"; currentSeqItem->currentLoopCount++; currentPosition = currentSeqItem->data.count() - 1; if (currentSeqItem->currentLoopCount == currentSeqItem->maxLoops) //have we looped enough times? { playbackActive = false; playbackTimer->stop(); emit EndOfFrameCache(); } } } return thisFrame->timeStamp().microSeconds(); } quint64 FramePlaybackObject::peekPosition(bool forward) { int peekCurrentPosition = currentPosition; if (forward) { if (peekCurrentPosition < (currentSeqItem->data.count() - 1)) peekCurrentPosition++; //still in same file so keep going else //hit the end of the current file { return 0xFFFFFFFFFFFFFFFFull; } } else { if (peekCurrentPosition > 0) peekCurrentPosition--; else //hit the beginning of the current sequence { return 0xFFFFFFFFFFFFFFFFull; } } CANFrame *thisFrame = ¤tSeqItem->data[peekCurrentPosition]; return thisFrame->timeStamp().microSeconds(); } void FramePlaybackObject::piStart() { playbackTimer = new QTimer(); playbackTimer->setTimerType(Qt::PreciseTimer); playbackTimer->setInterval(1); currentPosition = 0; playbackActive = false; playbackForward = true; whichBusSend = 0; connect(playbackTimer, &QTimer::timeout, this, &FramePlaybackObject::timerTriggered); } void FramePlaybackObject::piStop() { playbackTimer->stop(); delete playbackTimer; } void FramePlaybackObject::initialize() { if( mThread_p && (mThread_p != QThread::currentThread()) ) { /* move ourself to the thread */ moveToThread(mThread_p); /*TODO handle errors */ /* connect started() */ connect(mThread_p, SIGNAL(started()), this, SLOT(initialize())); /* start the thread */ mThread_p->start(QThread::HighPriority); return; } /* set started flag */ //mStarted = true; /* in multithread case, this will be called before entering thread event loop */ return piStart(); } void FramePlaybackObject::finalize() { /* 1) execute in mThread_p context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { /* if thread is finished, it means we call this function for the second time so we can leave */ if( !mThread_p->isFinished() ) { /* we need to call piStop() */ QMetaObject::invokeMethod(this, "finalize", Qt::BlockingQueuedConnection); /* 3) stop thread */ mThread_p->quit(); if(!mThread_p->wait()) { qDebug() << "can't stop thread"; } } return; } /* 2) call piStop in mThread context */ return piStop(); } void FramePlaybackObject::startPlaybackForward() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "startPlaybackForward", Qt::BlockingQueuedConnection); return; } playbackActive = true; playbackForward = true; if (useOrigTiming) { playbackTimer->setInterval(1); playbackElapsed.start(); if (currentSeqItem->data[currentPosition].timeStamp().microSeconds() > 2000) playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() - 2000; else playbackLastTimeStamp = 0; } playbackTimer->start(); } void FramePlaybackObject::startPlaybackBackward() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "startPlaybackBackward", Qt::BlockingQueuedConnection); return; } playbackActive = true; playbackForward = false; if (useOrigTiming) { playbackElapsed.start(); playbackTimer->setInterval(1); playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() + 2000; } playbackTimer->start(); } void FramePlaybackObject::stepPlaybackForward() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "stepPlaybackForward", Qt::BlockingQueuedConnection); return; } sendingBuffer.clear(); playbackTimer->stop(); playbackActive = false; updatePosition(true); CANConManager::getInstance()->sendFrames(sendingBuffer); emit statusUpdate(currentPosition); } void FramePlaybackObject::stepPlaybackBackward() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "stepPlaybackBackward", Qt::BlockingQueuedConnection); return; } sendingBuffer.clear(); playbackTimer->stop(); //pushing this button halts automatic playback playbackActive = false; updatePosition(false); CANConManager::getInstance()->sendFrames(sendingBuffer); emit statusUpdate(currentPosition); } void FramePlaybackObject::stopPlayback() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "stopPlayback", Qt::BlockingQueuedConnection); return; } playbackTimer->stop(); //pushing this button halts automatic playback playbackActive = false; currentPosition = 0; emit statusUpdate(currentPosition); } void FramePlaybackObject::pausePlayback() { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "pausePlayback", Qt::BlockingQueuedConnection); return; } playbackActive = false; playbackTimer->stop(); emit statusUpdate(currentPosition); } void FramePlaybackObject::seekPlayback(double timestamp) { if (currentSeqItem == NULL) return; /* make sure we execute in mThread context */ if (mThread_p && (mThread_p != QThread::currentThread())) { // TODO fix for QT5 from customizing branch // QMetaObject::invokeMethod(this, "seekPlayback", Qt::BlockingQueuedConnection, timestamp); return; } // playbackTimer->stop(); // playbackActive = false; timestamp *= 1000000; // convert to us size_t lowIdx = 0; size_t highIdx = currentSeqItem->data.count() - 1; while (lowIdx < highIdx) { int midIdx = lowIdx + (highIdx - lowIdx) / 2; CANFrame midFrame = currentSeqItem->data[midIdx]; if (midFrame.timeStamp().seconds() * 1000000 + midFrame.timeStamp().microSeconds() < timestamp) { lowIdx = midIdx + 1; } else { highIdx = midIdx - 1; } } currentPosition = lowIdx; emit statusUpdate(currentPosition); startPlaybackForward(); } void FramePlaybackObject::setSequenceObject(SequenceItem *item) { currentSeqItem = item; } void FramePlaybackObject::setUseOriginalTiming(bool state) { useOrigTiming = state; } void FramePlaybackObject::setSendingBus(int bus) { qDebug() << "Setting sending bus to " << bus; whichBusSend = bus; } void FramePlaybackObject::setPlaybackBurst(int burst) { playbackBurst = burst; } void FramePlaybackObject::setNumBuses(int buses) { numBuses = buses; } //any time you touch objects that live in a potentially different thread you must switch into that thread. //So this function is a bit different than the rest of the simple setters for that reason. void FramePlaybackObject::setPlaybackInterval(int interval) { /* make sure we execute in mThread context */ if( mThread_p && (mThread_p != QThread::currentThread()) ) { QMetaObject::invokeMethod(this, "setPlaybackInterval", Qt::BlockingQueuedConnection, Q_ARG(int , interval) ); return; } playbackInterval = interval; playbackTimer->setInterval(interval); } void FramePlaybackObject::timerTriggered() { sendingBuffer.clear(); if (useOrigTiming) { //get elapsed microseconds since last tick (in case timer skips or is otherwise inaccurate, though there are no guarantees about elapsed timer either) quint64 elapsed = playbackElapsed.nsecsElapsed() / 1000; playbackElapsed.start(); if (playbackForward) { playbackLastTimeStamp += elapsed; //qDebug() << playbackLastTimeStamp; //qDebug() << "El: " << elapsed; while (peekPosition(true) <= playbackLastTimeStamp) { updatePosition(true); } if (peekPosition(true) == 0xFFFFFFFFFFFFFFFFull) { updatePosition(true); //this'll go to the next log (if there is one) if (currentSeqItem->data[currentPosition].timeStamp().microSeconds() > 1000) playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() - 1000; else playbackLastTimeStamp = 0; } } else { if (playbackLastTimeStamp > elapsed) playbackLastTimeStamp -= elapsed; else playbackLastTimeStamp = 0; while (peekPosition(false) >= playbackLastTimeStamp && peekPosition(false) != 0xFFFFFFFFFFFFFFFFull) { updatePosition(false); } if (peekPosition(false) == 0xFFFFFFFFFFFFFFFFull) { updatePosition(false); //this'll go to the next log (if there is one) playbackLastTimeStamp = currentSeqItem->data[currentPosition].timeStamp().microSeconds() + 1000; } } statusCounter++; } else { for (int count = 0; count < playbackBurst; count++) { if (!playbackActive) { playbackTimer->stop(); return; } if (playbackForward) { updatePosition(true); } else { updatePosition(false); } } statusCounter += playbackInterval; } if (statusCounter > 100) { statusCounter = 0; emit statusUpdate(currentPosition); } //qDebug() << "sb: " << sendingBuffer.count(); if (sendingBuffer.count() > 0) CANConManager::getInstance()->sendFrames(sendingBuffer); }