Files
giesCANne/framesenderobject.cpp
T
Collin Kidder 0c84b6998f Partial implementation of a more generic and multi-threaded version of
the frame sender. Now a simplified interface is attached to the main
window as well. Doesn't necessarily work just yet...
2024-02-12 21:20:54 -05:00

454 lines
16 KiB
C++

#include "framesenderobject.h"
#include "mainwindow.h"
FrameSenderObject::FrameSenderObject(const QVector<CANFrame> *frames)
{
mThread_p = new QThread();
statusCounter = 0;
modelFrames = frames;
dbcHandler = DBCHandler::getReference();
}
FrameSenderObject::~FrameSenderObject()
{
mThread_p->quit();
mThread_p->wait();
delete mThread_p;
}
void FrameSenderObject::piStart()
{
sendingTimer = new QTimer();
sendingTimer->setTimerType(Qt::PreciseTimer);
sendingTimer->setInterval(1);
connect(sendingTimer, &QTimer::timeout, this, &FrameSenderObject::timerTriggered);
}
void FrameSenderObject::piStop()
{
sendingTimer->stop();
delete sendingTimer;
}
void FrameSenderObject::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;
connect(MainWindow::getReference(), SIGNAL(framesUpdated(int)), this, SLOT(updatedFrames(int)));
/* in multithread case, this will be called before entering thread event loop */
return piStart();
}
void FrameSenderObject::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 FrameSenderObject::startSending()
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "startSending",
Qt::BlockingQueuedConnection);
return;
}
sendingElapsed.start();
sendingTimer->start();
}
void FrameSenderObject::stopSending()
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "stopPlayback",
Qt::BlockingQueuedConnection);
return;
}
sendingTimer->stop(); //pushing this button halts automatic playback
//emit statusUpdate(currentPosition);
}
void FrameSenderObject::addSendRecord(FrameSendData record)
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "addSendRecord",
Qt::BlockingQueuedConnection,
Q_ARG(FrameSendData, record));
return;
}
sendingData.append(record);
}
void FrameSenderObject::removeSendRecord(int idx)
{
/* make sure we execute in mThread context */
if( mThread_p && (mThread_p != QThread::currentThread()) ) {
QMetaObject::invokeMethod(this, "removeSendRecord",
Qt::BlockingQueuedConnection,
Q_ARG(int, idx));
return;
}
sendingData.removeAt(idx);
}
/*
* Note: Getting a reference to an item in a QList is a dangerous thing. If anyone
* or anything happens to reallocate the list you're screwed. So, call this, do the
* work, and drop the reference as soon as possible.
*/
FrameSendData *FrameSenderObject::getSendRecordRef(int idx)
{
if (idx < 0 || idx >= sendingData.count()) return nullptr;
return &sendingData[idx];
}
/// <summary>
/// Called every millisecond to set the system update figures and send frames if necessary.
/// </summary>
/// <param name="sender"></param>
/// <param name="timerEventArgs"></param>
///
void FrameSenderObject::timerTriggered()
{
FrameSendData *sendData;
Trigger *trigger;
sendingList.clear();
if(mutex.tryLock())
{
//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 = sendingElapsed.nsecsElapsed() / 1000;
if (elapsed == 0) elapsed = 1;
sendingElapsed.start();
sendingLastTimeStamp += elapsed;
//qDebug() << playbackLastTimeStamp;
//qDebug() << "El: " << elapsed;
statusCounter++;
for (int i = 0; i < sendingData.count(); i++)
{
sendData = &sendingData[i];
if (!sendData->enabled)
{
if (sendData->triggers.count() > 0)
{
for (int j = 0; j < sendData->triggers.count(); j++) //resetting currCount when line is disabled
{
sendData->triggers[j].currCount = 0;
}
}
continue; //abort any processing on this if it is not enabled.
}
if (sendData->triggers.count() == 0)
{
qDebug() << "No triggers to process";
break;
}
for (int j = 0; j < sendData->triggers.count(); j++)
{
trigger = &sendData->triggers[j];
if (trigger->currCount >= trigger->maxCount) continue; //don't process if we've sent max frames we were supposed to
if (!trigger->readyCount) continue; //don't tick if not ready to tick
//is it time to fire?
trigger->msCounter += elapsed; //gives proper tracking even if timer doesn't fire as fast as it should
if (trigger->msCounter >= trigger->milliseconds)
{
trigger->msCounter -= trigger->milliseconds;
sendData->count++;
trigger->currCount++;
doModifiers(i);
//updateGridRow(i);
//qDebug() << "About to try to send a frame";
//CANConManager::getInstance()->sendFrame(sendingData[i]);
sendingList.append(sendingData[i]); //queue it instead of immediate sending
if (trigger->ID > 0) trigger->readyCount = false; //reset flag if this is a timed ID trigger
}
}
}
//if we have any frames to send after the above then send as a batch
if (sendingList.count() > 0) CANConManager::getInstance()->sendFrames(sendingList);
mutex.unlock();
}
else
{
qDebug() << "framesenderobject::handleTick() couldn't get mutex, elapsed is: " << sendingElapsed.elapsed();
}
}
void FrameSenderObject::buildFrameCache()
{
CANFrame thisFrame;
frameCache.clear();
for (int i = 0; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!frameCache.contains(thisFrame.frameId()))
{
frameCache.insert(thisFrame.frameId(), thisFrame);
}
else
{
frameCache[thisFrame.frameId()] = thisFrame;
}
}
}
//remember, negative numbers are special -1 = all frames deleted, -2 = totally new set of frames.
void FrameSenderObject::updatedFrames(int numFrames)
{
CANFrame thisFrame;
if (numFrames == -1) //all frames deleted.
{
}
else if (numFrames == -2) //all new set of frames.
{
buildFrameCache();
}
else //just got some new frames. See if they are relevant.
{
if (numFrames > modelFrames->count()) return;
qDebug() << "New frames in sender window";
//run through the supposedly new frames in order
for (int i = modelFrames->count() - numFrames; i < modelFrames->count(); i++)
{
thisFrame = modelFrames->at(i);
if (!frameCache.contains(thisFrame.frameId()))
{
frameCache.insert(thisFrame.frameId(), thisFrame);
}
else
{
frameCache[thisFrame.frameId()] = thisFrame;
}
processIncomingFrame(&thisFrame);
}
}
}
void FrameSenderObject::processIncomingFrame(CANFrame *frame)
{
for (int sd = 0; sd < sendingData.count(); sd++)
{
if (sendingData[sd].triggers.count() == 0) continue;
bool passedChecks = true;
for (int trig = 0; trig < sendingData[sd].triggers.count(); trig++)
{
Trigger *thisTrigger = &sendingData[sd].triggers[trig];
//need to ensure that this trigger is actually related to frames incoming.
//Otherwise ignore it here. Only frames that have BUS and/or ID set as trigger will work here.
if (!(thisTrigger->triggerMask & (TriggerMask::TRG_BUS | TriggerMask::TRG_ID) ) ) continue;
passedChecks = true;
//qDebug() << "Trigger ID: " << thisTrigger->ID;
//qDebug() << "Frame ID: " << frame->frameId();
//Check to see if we have a bus trigger condition and if so does it match
if (thisTrigger->bus != frame->bus && (thisTrigger->triggerMask & TriggerMask::TRG_BUS) )
passedChecks = false;
//check to see if we have an ID trigger condition and if so does it match
if ((thisTrigger->triggerMask & TriggerMask::TRG_ID) && (uint32_t)thisTrigger->ID != frame->frameId() )
passedChecks = false;
//check to see if we're limiting the trigger by max count and have we reached that count?
if ( (thisTrigger->triggerMask & TriggerMask::TRG_COUNT) && (thisTrigger->currCount >= thisTrigger->maxCount) )
passedChecks = false;
//if the above passed then are we triggering not only on ID but also signal?
if (passedChecks && (thisTrigger->triggerMask & (TriggerMask::TRG_SIGNAL | TriggerMask::TRG_ID) ) )
{
bool sigCheckPassed = false;
DBC_MESSAGE *msg = dbcHandler->findMessage(thisTrigger->ID);
if (msg)
{
DBC_SIGNAL *sig = msg->sigHandler->findSignalByName(thisTrigger->sigName);
if (sig)
{
//first of all, is this signal really in this message we got?
if (sig->isSignalInMessage(*frame)) sigCheckPassed = true;
//if it was and we're also filtering on value then try that next
if (sigCheckPassed && (thisTrigger->triggerMask & TriggerMask::TRG_SIGVAL))
{
double sigval = 0.0;
if (sig->processAsDouble(*frame, sigval))
{
if (abs(sigval - thisTrigger->sigValueDbl) > 0.001)
{
sigCheckPassed = false;
}
}
else sigCheckPassed = false;
}
}
else sigCheckPassed = false;
}
else sigCheckPassed = false;
passedChecks &= sigCheckPassed; //passedChecks can only be true if both are
}
//if all the above says it's OK then we'll go ahead and send that message.
//If a message that comes through here has a MS value then we use it as a delay
//after the check passes. This allows for delaying the sending of the frame if that
//is required. Otherwise, just send it immediately.
if (passedChecks)
{
if (thisTrigger->milliseconds == 0) //immediate reply
{
thisTrigger->currCount++;
sendingData[sd].count++;
doModifiers(sd);
//updateGridRow(sd);
CANConManager::getInstance()->sendFrame(sendingData[sd]);
}
else //delayed sending frame
{
thisTrigger->readyCount = true;
}
}
}
}
}
/// <summary>
/// given an index into the sendingData list we run the modifiers that it has set up
/// </summary>
/// <param name="idx">The index into the sendingData list</param>
void FrameSenderObject::doModifiers(int idx)
{
int shadowReg = 0; //shadow register we use to accumulate results
int first=0, second=0;
FrameSendData *sendData = &sendingData[idx];
Modifier *mod;
ModifierOp op;
if (sendData->modifiers.count() == 0) return; //if no modifiers just leave right now
//qDebug() << "Executing mods";
for (int i = 0; i < sendData->modifiers.count(); i++)
{
mod = &sendData->modifiers[i];
for (int j = 0; j < mod->operations.count(); j++)
{
op = mod->operations.at(j);
if (op.first.ID == -1)
{
first = shadowReg;
}
else first = fetchOperand(idx, op.first);
second = fetchOperand(idx, op.second);
switch (op.operation)
{
case ADDITION:
shadowReg = first + second;
break;
case AND:
shadowReg = first & second;
break;
case DIVISION:
shadowReg = first / second;
break;
case MULTIPLICATION:
shadowReg = first * second;
break;
case OR:
shadowReg = first | second;
break;
case SUBTRACTION:
shadowReg = first - second;
break;
case XOR:
shadowReg = first ^ second;
break;
case MOD:
shadowReg = first % second;
}
}
//Finally, drop the result into the proper data byte
QByteArray newArr(sendData->payload());
newArr[mod->destByte] = (char) shadowReg;
sendData->setPayload(newArr);
}
}
int FrameSenderObject::fetchOperand(int idx, ModifierOperand op)
{
CANFrame *tempFrame = nullptr;
if (op.ID == 0) //numeric constant
{
if (op.notOper) return ~op.databyte;
else return op.databyte;
}
else if (op.ID == -2) //fetch data from a data byte within the output frame
{
if (op.notOper) return ~((unsigned char)sendingData.at(idx).payload()[op.databyte]);
else return (unsigned char)sendingData.at(idx).payload()[op.databyte];
}
else //look up external data byte
{
tempFrame = lookupFrame(op.ID, op.bus);
if (tempFrame != nullptr)
{
if (op.notOper) return ~((unsigned char)tempFrame->payload()[op.databyte]);
else return (unsigned char)tempFrame->payload()[op.databyte];
}
else return 0;
}
}
/// <summary>
/// Try to find the most recent frame given the input criteria
/// </summary>
/// <param name="ID">The ID to find</param>
/// <param name="bus">Which bus to look on (-1 if you don't care)</param>
/// <returns></returns>
CANFrame* FrameSenderObject::lookupFrame(int ID, int bus)
{
if (!frameCache.contains(ID)) return nullptr;
if (bus == -1 || frameCache[ID].bus == bus) return &frameCache[ID];
return nullptr;
}