/*! * * 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 messagehandler.cpp * date 10/26/2019 * author Behrouz NematiPour * */ #include "messagehandler.h" // Qt #include #include // Project #include "maintimer.h" #include "applicationcontroller.h" // namespace using namespace Can; // Singleton SINGLETON_INIT(MessageHandler) /*! * \brief MessageHandler Constructor * \param parent */ MessageHandler::MessageHandler(QObject *parent) : QObject(parent) { } /*! * \brief Message Handler initializer */ void MessageHandler::init() { initConnections(); } /*! * \brief Message Handler connections definition * \details Initializes the required signal/slot connection between this class and other objects\n * to be able to communicate. */ void MessageHandler::initConnections() { // From GUI connect(_ApplicationController, SIGNAL(didActionTransmit(GuiActionType, const QByteArray &)), this , SLOT( onActionTransmit(GuiActionType, const QByteArray &))); // From HD/DG connect(_CanInterface , SIGNAL( didRead(QCanBusFrame)), this , SLOT(onFrameReceive (QCanBusFrame))); connect(_MainTimer , SIGNAL( didTimeout()), this , SLOT(onMainTimerTimeout())); } void MessageHandler::checked_in() { transmitFrame(eChlid_UI, GuiActions::Check_In); } void MessageHandler::transmitFrame(MessageHandler::Can_Id vCan_Id, GuiActionType vAction, const QByteArray &vData) { QCanBusFrame mFrame; mFrame.setFrameId(vCan_Id); QList mFrames = buildMessage(vAction,vData); for (auto frame : mFrames) { mFrame.setPayload(frame); //TODO : If it's necessary we can have a dispatcher // to schedule and send messages in another thread. emit didFrameTransmit(mFrame); } } bool MessageHandler::ignoreChannel(const QCanBusFrame &vFrame) { //TODO : There should be an easy way to find if the correct CAN ID has been received Can_Id mCanId = static_cast(vFrame.frameId()); switch (mCanId) { case eChlid_HD_DG: case eChlid_DG_HD: return true; break; case eChlid_HD: break; default: break; } return false; } QList MessageHandler::buildMessage(GuiActionType vAction, const QByteArray &vData) { QList mFrames; QByteArray mPayload ; addSyncByte (mPayload); // SyncByte addActionId (mPayload, vAction); // Message ID addData (mPayload, vAction, vData); // Regarding Payload Length, Adding required Data addCRC (mPayload); // CRC quint16 len = mPayload.length(); if (len > eLenCanFrame) { quint8 frameCount = len / eLenCanFrame; for (quint8 i = 0; i <= frameCount; i++) { mFrames += mPayload.mid(i*eLenCanFrame,eLenCanFrame); } } else { mFrames += mPayload; } addPadding (mFrames.last()); // Padded to 8 byte frame return mFrames; } void MessageHandler::addSyncByte(QByteArray &vPayload) { vPayload.append(ePayload_Sync); // Sync byte } void MessageHandler::addActionId(QByteArray &vPayload, GuiActionType vAction) { if (vAction != GuiActions::Unknown) { vPayload += (vAction >> 8) & 0xFF;//high byte vPayload += vAction & 0xFF;// low byte } } void MessageHandler::addData(QByteArray &vPayload, GuiActionType vAction, const QByteArray &vData) { quint8 len = payloadLen[vAction]; // if len has been set to max(255) // it means it has no limit and can be as long as 255 bytes if (len == eLenMaxData) { len = vData.length(); if (vData.length() > eLenMaxData) { len = eLenMaxData ; } } vPayload += len; vPayload += vData.mid(0, len); // Adding required Data } void MessageHandler::addPadding(QByteArray &vPayload) { vPayload = vPayload.leftJustified(eLenCanFrame, '\0'); } quint8 MessageHandler::calcCRC(const QByteArray &vData) { // TODO : calcCRC has not been used yet Q_UNUSED(vData) return '\0'; } bool MessageHandler::checkCRC(const QByteArray &vData, quint8 vCRC) { //TODO : enable when the calcCRC implemented Q_UNUSED(vData) Q_UNUSED(vCRC ) return true;//calcCRC(vData) == vCRC; } bool MessageHandler::stripMessage(const QCanBusFrame &vFrame, Message &vMessage) { QByteArray mPayload = vFrame.payload(); //if (messages[mCanId].isEmpty()) { // messages[mCanId].append(Message()); //} //if (messages[mCanId].last().isComplete()) { // messages[mCanId].append(Message()); //} // //Message message = messages[mCanId].last(); // when we are here the message is new or partial. bool ok = false; if (hasSyncByte(mPayload)) { vMessage.actionId = getActionId(mPayload); vMessage.length = getLength (mPayload); vMessage.data = getData (mPayload, vMessage.length, &ok); if (!ok) { qDebug() << "ERROR : CRC error"; return false; } } else { qDebug() << "ERROR : incomplete message or corrupted without sync byte (without header)"; return false; } //messages[mCanId].append(message); return true; } bool MessageHandler::hasSyncByte(QByteArray &vPayload) { quint8 mSyncByte = vPayload[0]; if (mSyncByte == ePayload_Sync) { vPayload = vPayload.mid(eLenSyncByte); return true; } return false; } GuiActionType MessageHandler::getActionId(QByteArray &vPayload) { quint16 mActionId; mActionId = (vPayload[0] << 8) | vPayload[1]; //TODO : It needs to be checked that the Action ID is Correct. //return GuiActionType::Unknown; vPayload = vPayload.mid(eLenActionId); return static_cast(mActionId); } quint8 MessageHandler::getLength(QByteArray &vPayload) { quint8 mlen = vPayload[0]; vPayload = vPayload.mid(eLenLength); return mlen; } QByteArray MessageHandler::getData(QByteArray &vPayload, quint8 vLen, bool *ok) { QByteArray mData; if (vLen <= eLenMaxHeaderData) { mData = vPayload.mid(0,vLen); if (ok) { *ok = checkCRC(mData,vPayload[vLen+1]); // CRC is always next byte after Data } } else { mData = vPayload; } return mData; } bool MessageHandler::interpretData(const Message &vMessage, GuiActionType &vActionId, GuiActionData &vData) { quint8 tmp; vActionId = vMessage.actionId; switch (vActionId) { // notice we are in receive mode case GuiActionType::PowerOff: tmp = vMessage.data[0]; vData = static_cast(tmp); //TODO: (MessageInterpreter) There should be an easy way to check if a correct data has been provided. break; default: vData = GuiActionData::Command; break; } return true; } void MessageHandler::addCRC(QByteArray &vPayload) { vPayload += calcCRC(vPayload); } /*! * \brief Process the requested action * \details Processes the requested action * \param vAction - User requested Action */ void MessageHandler::onActionTransmit(GuiActionType vAction, const QByteArray &vData) { qDebug() << vData; transmitFrame(eChlid_UI, vAction, vData); } /*! * \brief MessageHandler::onRead * \param vFrame */ void MessageHandler::onFrameReceive(const QCanBusFrame &vFrame) { if (ignoreChannel(vFrame)) { return; } Message mMessage; if (! stripMessage(vFrame,mMessage)) { return; } // FIXME: Needs MessageInterpreter class for data in each action [Sprint 8] GuiActionType mActionId; GuiActionData mData; if (! interpretData(mMessage, mActionId, mData)) { return; } emit didActionReceive (mActionId, mData); } void MessageHandler::onMainTimerTimeout() { //QMetaObject::invokeMethod(this, "checked_in"); checked_in(); }