/*! * * 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 CanInterface.cpp * \author (last) Behrouz NematiPour * \date (last) 18-Jul-2023 * \author (original) Behrouz NematiPour * \date (original) 26-Aug-2020 * */ #include "CanInterface.h" // Qt #include #include #include #include // #include // stl #include // Project // #include "Logger.h" // #include "FrameInterface.h" namespace Can { static void qRegister() { qRegisterMetaType("QCanBusFrame"); } Q_COREAPP_STARTUP_FUNCTION(qRegister) /*! * \brief CanInterface::CanInterface * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ CanInterface::CanInterface(QObject *parent) : QObject(parent) { } /*! * \brief CanInterface::enableConsoleOut * \details Enables or Disables the console output and logs the status * \param vEnabled - Enable console output if true */ void CanInterface::enableConsoleOut(bool vEnabled) { if (_enableConsoleOut != vEnabled) { _enableConsoleOut = vEnabled; qDebug().noquote() << (_enableConsoleOut ? "Console out CanInterface enabled" : "Console out CanInterface disabled"); } } /*! * \brief CanInterface::deleteDevice * \details Disconnect the CANBus device and deletes the pointer */ void CanInterface::quitDevice() { if (_canDevice) { _canDevice->disconnectDevice(); _canDevice.reset(nullptr, &QObject::deleteLater); status(tr("Disconnected")); } } /*! * \brief CanInterface status * \details CanInterface status description * \return The current stores status */ QString CanInterface::status() const { return _canStatus; } /*! * \brief CanInterface Initialization * \details Initializes the CANBus and checks if can be connected * \return true if connected, false otherwise */ bool CanInterface::init() { if (_init) { return false; } _init = true; if (! initDevice()) { return false; } if (! testDevice()) { return false; } initConnections(); status(QString("Connected")); qDebug().noquote() << QString("UI,%1,%2").arg(QString("%1 Initialized").arg(metaObject()->className())).arg(status()); return true; } /*! * \brief CanInterface::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 CanInterface::init(QThread &vThread) { qDebug().noquote() << "*** CanInterface::init"; // SQ if (! init()) { return false; } initThread(vThread); return true; } /*! * \brief CanInterface quit * \details quits the class * Calls quitThread */ void CanInterface::quit() { quitThread(); // verified } /*! * \brief frameFlags * \details CANBus message frame type as flags * \param vFrame - CANBus message frame * \return Frame flag as QString */ QString CanInterface::frameFlags(const QCanBusFrame &vFrame) { QString result = QLatin1String(" --- "); if (vFrame.hasBitrateSwitch()) { result[1] = QLatin1Char('B'); } if (vFrame.hasErrorStateIndicator()) { result[2] = QLatin1Char('E'); } if (vFrame.hasLocalEcho()) { result[3] = QLatin1Char('L'); } return result; } /*! * \brief CanInterface console Output messaging * \details Sends out formatted CANBus message to the console * for debugging purposes. * \param vFrame - The CANBus frame to be sent out */ void CanInterface::consoleOut(const QCanBusFrame &vFrame, const QString &vFrameCount) { if (! _enableConsoleOut) { return; } const QString time = QString::fromLatin1("%1.%2 ") .arg(vFrame.timeStamp().seconds(), 10, 10, QLatin1Char(' ')) .arg(vFrame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0')); const QString flags = frameFlags(vFrame); QString view; if (vFrame.frameType() == QCanBusFrame::ErrorFrame) { if (_canDevice) { view = _canDevice->interpretErrorFrame(vFrame); } } else { view = vFrame.payload().toHex('.').replace(QByteArray("a5"),QByteArray("\033[1;33mA5\033[0m")); } // the fprintf is used for the colored output fprintf(stderr, "%s %s %s %i %s\n", vFrameCount.toLatin1().constData(), time.toLatin1().constData(), flags.toLatin1().constData(), vFrame.frameId(), view.toLatin1().constData()); } /*! * \brief CanInterface connections definition * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. */ void CanInterface::initConnections() { if (_canDevice) { connect(_canDevice.get(), &QCanBusDevice::framesReceived, this, &CanInterface::onFrameReceive); connect(_canDevice.get(), &QCanBusDevice::errorOccurred, this, &CanInterface::onFrameError); connect(_canDevice.get(), &QCanBusDevice::framesWritten, this, &CanInterface::onFrameWritten); } // connect(&_FrameInterface, SIGNAL(didFrameTransmit(QCanBusFrame)), // this , SLOT( onFrameTransmit(QCanBusFrame))); } /*! * \brief CanInterface::createDevice * \details Creates the CANBus device * \return false if cannot create the device */ bool CanInterface::initDevice() { QString mError; _canDevice.reset(QCanBus::instance()->createDevice(_canType, _canInterface, &mError), &QObject::deleteLater); if (!_canDevice) { status(tr("Device Creation"), mError); qDebug().noquote() << status(); return false; } _canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey , _canFDKey ); _canDevice->setConfigurationParameter(QCanBusDevice::BitRateKey , _canBitRate ); return true; } /*! * \brief CanInterface::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 CanInterface::initThread(QThread &vThread) { qDebug().noquote() << "*** CanInterface::initThread"; // SQ // runs in main thread Q_ASSERT_X(QThread::currentThread() == qApp->thread() , __func__, "The Class initialization must be done in Main Thread" ); vThread.setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, &QCoreApplication::aboutToQuit, this, &CanInterface::quit); moveToThread(&vThread); vThread.start(); } /*! * \brief CanInterface::quitThread * \details Moves this object to main thread to be handled by QApplication * And to be destroyed there. */ void CanInterface::quitThread() { // runs in thread if (QThread::currentThread() == qApp->thread()) { moveToThread(qApp->thread()); // verified } } /*! * \brief CanInterface status * \details Sets the Can interface status description * \param vDescription - Description about the CANBus Interface errors * \param vError - Qt CANBus Interface Error */ void CanInterface::status(const QString &vDescription, QString vError) { const QString mError = _canDevice ? _canDevice->errorString() + vError : vError; _canStatus = QString("%1 '%2[%3]', %4").arg(vDescription).arg(_canType).arg(_canInterface).arg(mError); } /*! * \brief CanInterface::checkDevice * \details Checks if the device has been connected. * \return true on successful device test and connection. */ bool CanInterface::testDevice() { if (_canDevice && !_canDevice->connectDevice()) { status(tr("Connection")); qDebug().noquote() << status(); _canDevice.reset(nullptr, &QObject::deleteLater); return false; } return true; } /*! * \brief CanInterface send * \details send a frame over the CANBus * \param vFrame - CANBus message frame */ bool CanInterface::transmit(const QCanBusFrame &vFrame) { return _canDevice ? _canDevice->writeFrame(vFrame) : false; } /*! * \brief CanInterface::rxCount * \details count received frames up the size of the FrameCount type size * \return frame count */ FrameCount CanInterface::rxCount() { Types::safeIncrement(_rxFrameCount); return _rxFrameCount; } /*! * \brief CanInterface::txCount * \details count transmitted frames up the size of the FrameCount type size * \return frame count */ FrameCount CanInterface::txCount() { Types::safeIncrement(_txFrameCount); return _txFrameCount; } /*! * \brief CanInterface::erCount * \details count errors happened * \return error count */ FrameCount CanInterface::erCount() { Types::safeIncrement(_erFrameCount); return _erFrameCount; } /*! * \brief CanInterface onError * \details CANBus error handler which sets the can status description * \param vError - CANBus error */ void CanInterface::onFrameError(QCanBusDevice::CanBusError vError) { erCount(); switch (vError) { case QCanBusDevice::ReadError: case QCanBusDevice::WriteError: case QCanBusDevice::ConnectionError: case QCanBusDevice::ConfigurationError: case QCanBusDevice::UnknownError: // log the error each 100k frame and if error is different if (_canStatus != (_canDevice ? _canDevice->errorString() : "") || !(_erFrameCount % 100000)) { _canStatus = _canDevice ? _canDevice->errorString() : ""; qDebug().noquote() << QString("%1 - %2").arg(_erFrameCount).arg(_canStatus); } break; default: break; } emit didFrameError(_canStatus); } /*! * \brief CanInterface::onFrameWritten * \details This is the slot connected to the signal * which is emitted every time a payload of frames * has been written to the CANBus bus. * \param vFramesCount - The framesCount argument is set to the number of frames * that were written in this payload. */ void CanInterface::onFrameWritten(qint64 vFramesCount) { static FrameCount mFrameCount = 0; Types::safeIncrement(mFrameCount, vFramesCount); emit didFrameWritten(vFramesCount); } /*! * \brief CanInterface onFrameReceived * \details CANBus message read handler */ void CanInterface::onFrameReceive() { while (_canDevice && _canDevice->framesAvailable()) { const QCanBusFrame frame = _canDevice->readFrame(); rxCount(); if (_enableConsoleOut) { consoleOut(frame, QString("Rx:%1").arg(_rxFrameCount)); } emit didFrameReceive(frame); } } /*! * \brief CanInterface onActionPerform * \details sends a CANBus message frame of the CANBus message of the performed action * This is a response from application UI to HD device * \param vFrame - CANBus message frame */ void CanInterface::onFrameTransmit(const QCanBusFrame &vFrame) { const bool ok = transmit(vFrame); txCount(); if (_enableConsoleOut) { consoleOut(vFrame, QString("Tx:%1").arg(_txFrameCount)); } emit didFrameTransmit(ok); } } // namespace Can