/*! * * Copyright (c) 2020-2024 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 FrameInterface.cpp * \author (last) Behrouz NematiPour * \date (last) 30-Jul-2023 * \author (original) Behrouz NematiPour * \date (original) 26-Aug-2020 * */ #include "FrameInterface.h" // Qt #include #include #include // Project // #include "Logger.h" // #include "MessageDispatcher.h" // #include "CanInterface.h" // namespace using namespace Can; /*! * \brief FrameInterface::FrameInterface * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ FrameInterface::FrameInterface(QObject *parent) : QObject(parent) { } /*! * \brief Message Handler initializer */ bool FrameInterface::init() { if (_init) { return false; } _init = true; initConnections(); startTimer(1, Qt::PreciseTimer); // LOG_DEBUG(QString("%1 Initialized").arg(metaObject()->className())); return true; } /*! * \brief FrameInterface::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 FrameInterface::init(QThread &vThread) { if (init() == false) { return false; } initThread(vThread); return true; } /*! * \brief FrameInterface::quit * \details quits the class * Calls quitThread */ void FrameInterface::quit() { quitThread(); // validated } /*! * \brief FrameInterface connections definition * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. */ void FrameInterface::initConnections() { // From GUI // connect(&_MessageDispatcher, &MessageDispatcher::didFrameTransmit, this, &FrameInterface::onFrameTransmit); // From CAN // connect(&_CanInterface, &CanInterface::didFrameReceive, this, &FrameInterface::onFrameReceive); // connect(&_CanInterface, &CanInterface::didFrameWritten, this, &FrameInteface::onFrameWritten); } /*! * \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 FrameInterface::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())); moveToThread(_thread); _thread->start(); } /*! * \brief FrameInterface::quitThread * \details Moves this object to main thread to be handled by QApplication and to be destroyed there. */ void FrameInterface::quitThread() { if (_thread) { moveToThread(qApp->thread()); // validated } } /*! * \brief FrameInterface::transmitFrame * \details Prepares a frame to be transmitted * and emit signal didFrameTransmit with the frame as its argument * \param vFrameId - Channel id of the CANBus frame * \param vData - The Data this frame is going to carry * \note This frame is created by MessageBuilder * and it can be one of the frames of a message * which has been chopped into frames. */ void FrameInterface::transmitFrame(CanId vCanId, const QByteArray &vData) { if (vData.length() > Can::eLenCanFrame) { // LOG_DEBUG(QString("Payload cannot be larger than %1 bytes").arg(Can::eLenCanFrame)); return; } QCanBusFrame mFrame; mFrame.setFrameId(vCanId); mFrame.setPayload(vData); emit didFrameTransmit(mFrame); } /*! * \brief FrameInterface::checkChannel * \details Checks for the channel id of the received frame * which needs to be handled or ignored. * \param vFrameId - Channel id of the frame * \param vOK - will be set to true if the channel * is valid and a variable has been passed to * \return The Category if the channels from the UI * perspective FrameInterface::ChannelGroup */ FrameInterface::ChannelGroup FrameInterface::checkChannel(quint32 vFrameId, bool *vOK, bool *vDebugChanngel) { bool ok = true; bool debugChannel = false; FrameInterface::ChannelGroup channelGroup = ChannelGroup::eChannel_Unknown; switch (vFrameId) { case eDialin_TD : case eTD_Dialin : case eDialin_DD : case eDD_Dialin : case eDialin_UI : case eUI_Dialin : // TODO if ( gDisableDialinUnhandled ) { // TODO channelGroup = ChannelGroup::eChannel_Ignores; // TODO } else { // TODO channelGroup = ChannelGroup::eChannel_Listens; // TODO } // TODO debugChannel = gDisableDialinUnhandled; // if debug channel is true, the raw can message in logged in the service log. // TODO ok = ! gDisableDialinUnhandled; // if ok is true then it will be interpreted as unhandled messages. channelGroup = ChannelGroup::eChannel_Listens; debugChannel = true; // TODO ok = true; // TODO break; // these channels will be used for testing for now. case eChlid_TD_DD : case eChlid_DD_TD : case eChlid_DD_FP : case eChlid_FP_DD : //channelGroup = ChannelGroup::eChannel_Ignores; //break; case eChlid_TD_UI : case eChlid_TD_Alarm : case eChlid_TD_Sync : //case eChlid_DD_UI : // has duplicate value as eChlid_DD_Sync case eChlid_DD_Alarm : case eChlid_DD_Sync : //case eChlid_FP_UI : // has duplicate value as eChlid_FP_Sync, Only in α, will be removed in ꞵ case eChlid_FP_Alarm : // Only in α, will be removed in ꞵ case eChlid_FP_Sync : // Only in α, will be removed in ꞵ channelGroup = ChannelGroup::eChannel_Listens; break; case eChlid_UI_Alarm : case eChlid_UI_Sync : case eChlid_UI_TD : //case eChlid_UI_DD : // has duplicate value as eChlid_UI_Sync channelGroup = ChannelGroup::eChannel_Outputs; break; default: ok = false; break; } if (vOK) *vOK = ok; if (vDebugChanngel) *vDebugChanngel = debugChannel; return channelGroup; } /*! * \brief FrameInterface::onFrameReceive * \details This the slot connected to the CanInterface didFrameReceive signal. * When a frame received over the CANBus, * this slot will be called to check the channel if should be listened to. * and will emit didFrameReceive if should be handled and ignored otherwise. * \param vFrame - The frame has to be sent */ void FrameInterface::onFrameReceive(const QCanBusFrame &vFrame) { bool ok = false; bool debugChannel = false; quint32 mFrameId = vFrame.frameId(); ChannelGroup channelGroup = checkChannel(mFrameId, &ok, &debugChannel); QString logMessage; if ( debugChannel ) { // logMessage = "Debug Channel\r\n" + // Format::toHexString(mFrameId, false, eLenChannelDigits) + " -- " + vFrame.payload().toHex(' '); // LOG_DEBUG(logMessage); } if ( ! ok ) { // logMessage = "Unexpected Channel\r\n" + // Format::toHexString(mFrameId, false, eLenChannelDigits) + " -- " + vFrame.payload().toHex(' '); // LOG_DEBUG(logMessage); return; } if ( channelGroup != ChannelGroup::eChannel_Listens) { return; } CanId mCanId = static_cast(mFrameId); emit didFrameReceive(mCanId, vFrame.payload()); } /*! * \brief FrameInterface::onFrameTransmit * \details This the slot connected to the MessageDispatcher didFrameTransmit signal. * When a frame needs to be send to CANBus, * this slot will call transmitFrame method to do the job. * \param vCanId - CANBus Can Id target of the frame. * \param vData - The data which this frame will carry. */ void FrameInterface::onFrameTransmit(CanId vCanId, const QByteArray &vData) { appendHead(vCanId, vData); // DEBUG: qDebug() << _timestamp << "apnd #" << _txFrameList.count(); } /*! * \brief FrameInterface::onFrameWritten * \param vCount */ void FrameInterface::onFrameWritten(qint64 /*vCount*/) { _transmitted = true; removeHead(); // DEBUG: qDebug() << _timestamp << "Sent #" << _txFrameList.count() << vCount; } /*! * \brief FrameInterface::timerEvent * \details This event handler is re-implemented in this subclass to receive timer events for this object. */ void FrameInterface::timerEvent(QTimerEvent *) { static quint8 count = 0; // TEST : _timestamp = QTime::currentTime().toString("HH:mm:ss.zzz"); if (++count != _interval) return; // DEBUG: qDebug() << _timestamp; count = 0; _transmitted = false; trnsmtHead(); } /*! * \brief FrameInterface::trnsmtHead * \details Transmits the head of the transmit buffer * Sends an empty frame with lowest priority if the transmit buffer is empty * to keep the UI board Can-driver awake. */ void FrameInterface::trnsmtHead() { if ( _txFrameList.isEmpty() ) { // TODO if ( gSendEmptyKeepAwake ) { transmitFrame(eChlid_LOWEST,QByteArray()); // Keep the CANBus awake. return; // TODO } } else { Frame frame = _txFrameList.first(); transmitFrame(frame.canId, frame.data); // DEBUG: qDebug() << _timestamp << "Tsmt #" << _txFrameList.count(); } } /*! * \brief FrameInterface::removeHead * \details Removes the frame from the head of the transmit buffer * in case transmission has been confirmed by the CANBus driver * in the FrameInterface::onFrameWritten slot * which is connected to CanInterface::didFrameWritten. */ void FrameInterface::removeHead() { if ( _txFrameList.isEmpty() ) { return; } _txFrameList.removeFirst(); } /*! * \brief FrameInterface::appendHead * \details Appends the frame to the transmit buffer to be sent later * \param vCanId - the CANBus id * \param vData - the data to be sent */ void FrameInterface::appendHead(CanId vCanId, const QByteArray &vData) { //DEBUG qDebug() << "F " << _txFrameList.count(); if (_txFrameList.count() >= _txFrameList_Max) { static quint32 i = 0; // if ( i % 60 == 0 ) { // log only for the first time and each minute. // LOG_DEBUG(QString("Transmit buffer overflow of %1").arg(_txFrameList_Max)); // } if ( i < UINT32_MAX - 1 ) i++ ; else i = 0; return; } // Frame frame = Frame(vCanId, vData); _txFrameList.append({ vCanId, vData }); }