/*! * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, * IN PART OR IN WHOLE, * WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * file messagepacker.cpp * date 12/9/2019 * author Behrouz NematiPour * */ #include "messagedispatcher.h" // Qt #include #include // Project #include "logger.h" #include "applicationcontroller.h" #include "frameinterface.h" #define DEBUG_ACKBACK_HD_TO_UI using namespace Can; /*! * \brief MessageDispatcher::MessageDispatcher * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ MessageDispatcher::MessageDispatcher(QObject *parent) : QObject(parent) { } /*! * \brief Message Handler initializer */ bool MessageDispatcher::init() { if ( _init ) return false; _init = true; // runs in USBWatcher thread initConnections(); LOG_EVENT(QObject::tr("%1 Initialized").arg(metaObject()->className())); return true; } /*! * \brief MessageDispatcher::init * \details Initialized the Class by calling the init() method first * And initializes the thread vThread by calling initThread * on success init(). * \param vThread - the thread * \return returns the return value of the init() method */ bool MessageDispatcher::init(QThread &vThread) { if ( ! init() ) return false; initThread(vThread); return true; } /*! * \brief MessageDispatcher::quit * \details quits the class * Calls quitThread */ void MessageDispatcher::quit() { quitThread(); } /*! * \brief Message Handler connections definition * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. */ void MessageDispatcher::initConnections() { // From GUI connect(&_ApplicationController, SIGNAL(didActionTransmit(GuiActionType, const QVariantList &)), this , SLOT( onActionTransmit(GuiActionType, const QVariantList &))); // From HD connect(&_FrameInterface , SIGNAL(didFrameReceive ( Can_Id, const QByteArray &)), this , SLOT( onFrameReceive ( Can_Id, const QByteArray &))); connect(this ,SIGNAL(didFramesTransmit( Can_Id, const FrameList &)), this , SLOT( onFramesTransmit( Can_Id, const FrameList &))); } /*! * \brief ApplicationController::initThread * \details Moves this object into the thread vThread. * And checks that this method is called from main thread. * Also connects quitThread to application aboutToQuit. * \param vThread - the thread */ void MessageDispatcher::initThread(QThread &vThread) { // runs in main thread Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); _thread = &vThread; _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); _thread->start(); moveToThread(_thread); } /*! * \brief MessageDispatcher::quitThread * \details Moves this object to main thread to be handled by QApplicaiton * And to be destroyed there. */ void MessageDispatcher::quitThread() { if ( ! _thread ) return; // runs in thread moveToThread(qApp->thread()); } /*! * \brief MessageDispatcher::onFrameReceive * \details Upon message has been received over CANBUS this slot will be called * by FrameInterface::didFrameReceive signal to process the frame * Upon completion of collected all the required frames * on successful interpretation of the message, emits didActionReceived signal. * The message will be removed from list of the channel vCan_Id messages. * \param vCan_Id - CANBUS channel of the frame * \param vPayload - Payload of the frame */ void MessageDispatcher::onFrameReceive(Can_Id vCan_Id, const QByteArray &vPayload) { // Append a message to the list if (_messageList[vCan_Id].isEmpty() || _messageList[vCan_Id].last().isComplete()) { _messageList[vCan_Id].append(Message()); } // build the message and check. if (! buildMessage(vCan_Id, vPayload)) { return; } Message mMessage = _messageList[vCan_Id].last(); // TODO : must be moved to a MessageModel class if (mMessage.isComplete()) { rxCount(); #ifdef QT_DEBUG if (_rxSequence != mMessage.sequence) { qDebug() << tr("Out of Sync : %1 , %2").arg(_rxSequence).arg(mMessage.sequence); } #endif interpretMessage(mMessage); } } /*! * \brief MessageDispatcher::onFramesTransmit * \details this slots calls the framesTransmit to emit the didFrameTransmit signal * to queue the frame(s) to be sent * \param vCan_Id - CanBus channel * \param vFrameList - frame(s) to be sent */ void MessageDispatcher::onFramesTransmit(Can_Id vCan_Id, const FrameList &vFrameList) { framesTransmit(vCan_Id, vFrameList); } /*! * \brief MessageDispatcher::onActionTransmit * \details This slot will be called by ApplicationController::didActionTransmit * upon UI message transmit request and calls MessageDispatcher::actionTransmit method. * \param vActionId - The ActionID of the message * \param vData - The data of the Message */ void MessageDispatcher::onActionTransmit(GuiActionType vActionId, const QVariantList &vData) { actionTransmit(vActionId, vData, txCount()); } /*! * \brief MessageDispatcher::actionTransmit * \details This method is called by slot MessageDispatcher::onActionTransmit * which emits didFrameTransmit on successful interpretation of the requested message * and successfully creating of frame(s). * \param vActionId - The ActionID of the message * \param vData - The data of the Message */ void MessageDispatcher::actionTransmit(GuiActionType vActionId, const QVariantList &vData, Sequence vSequence) { QByteArray mData; if (! _interpreter.interpretMessage(vActionId, vData, mData)) { LOG_ERROR(tr("Incorrect Message, can't be interpreted")); return; } // TODO : Create a buildFrames method FrameList frameList; Sequence mSequence = vSequence; bool mNeedsAcknow = needsAcknow(vActionId); if (mNeedsAcknow) mSequence = -mSequence; if ( ! _builder.buildFrames(vActionId, mData, frameList, mSequence) ) { LOG_ERROR(tr("Incorrect Message can't be built")); return; } if (mNeedsAcknow) { // NOTE : here vSequence should be used which is not negative // because when we get the Acknow it is not the negative // since it does not need Re-Acknow // and this is the sequence number which will be used // to remove the message from the Acknow list. emit didAcknowTransmit(vSequence, eChlid_UI_HD, frameList); } framesTransmit(eChlid_UI_HD, frameList); } /*! * \brief MessageDispatcher::framesTransmit * \details iterates through all the frames and emits to send the frames * \param vCan_Id - The channel to send the frames to * \param vFrameList - List of the frames to be sent */ void MessageDispatcher::framesTransmit(Can_Id vCan_Id, const FrameList &vFrameList) { for (const auto &frame : vFrameList) { emit didFrameTransmit(vCan_Id, frame); } } /*! * \brief MessageDispatcher::buildMessage * \details Calls the messageBuilder buildMessage method. * \param vCan_Id - CANBUS channel of the frame * \param vPayload - Payload of the frame * \return false on error */ bool MessageDispatcher::buildMessage(Can_Id vCan_Id, const QByteArray &vPayload) { if (vPayload.length() < eLenCanFrame) { LOG_ERROR(tr("Incorrect frame length. Exp:%1,got:%2").arg(eLenCanFrame).arg(vPayload.length())); return false; } if (! _builder.buildMessage(vPayload, _messageList[vCan_Id].last(), vCan_Id)) { _messageList[vCan_Id].removeLast(); return false; } return true; } /*! * \brief MessageDispatcher::interpretMessage * \details Calls the MessageInterpreter interpretMessage method * Regarding the Message Id and the sequence emit different signals * to handle the normal or acknowledge messages. * \param vMessage - The Message * \return false on error */ bool MessageDispatcher::interpretMessage(const Message &vMessage) { bool ok = false; QVariantList mData; if (_interpreter.interpretMessage(vMessage, mData)) { ok = true; GuiActionType mActionId = vMessage.actionId; switch (mActionId) { case GuiActionType::Acknow: emit didAcknowReceive(vMessage.sequence); break; default: if (vMessage.sequence < 0) { actionTransmit(GuiActionType::Acknow, {}, vMessage.sequence); qDebug() << tr(" ~~~~~~~~~~ Ack Back : %1~~~~~~~~~~ ").arg(vMessage.sequence); } emit didActionReceive(mActionId, mData); break; } } _messageList[vMessage.can_id].removeLast(); return ok; } /*! * \brief MessageDispatcher::rxCount * \details count received messages up the size of the Sequence type size * \return message count */ Sequence MessageDispatcher::rxCount() { if ( _rxSequence < SEQUENCE_MAX ) { ++_rxSequence; } else { _rxSequence = 1; } return _rxSequence; } /*! * \brief MessageDispatcher::txCount * \details count transmitted messages up the size of the Sequence type size * \return message count */ Sequence MessageDispatcher::txCount() { if ( _txSequence < SEQUENCE_MAX ) { ++_txSequence; } else { _txSequence = 1; } return _txSequence; } /*! * \brief MessageDispatcher::needsAcknow * \details List of the Action types which need Acknow * \param vActionId - Action Type id * \return true if needs an Acknow */ bool MessageDispatcher::needsAcknow(GuiActionType vActionId) { switch(vActionId) { case GuiActionType::Acknow: // this is the Acknow itself // since the actionTransmit will make any needsAcknow sequence to negative // and the Ack Back should be in positive after receiving it in negative // actually this make it positive and send it back. return true; default: // TEST : this code is for test. #ifdef DEBUG_ACKBACK_HD_TO_UI return ( ! (_txSequence % 21) ); #else return false; #endif } return false; }