/*! * * 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 messagebuilder.cpp * date 12/6/2019 * author Behrouz NematiPour * */ #include "messagebuilder.h" // Qt #include // Project #include "crc.h" #include "format.h" // namespace using namespace Can; MessageBuilder::MessageBuilder(QObject *parent) : QObject(parent) { } FrameList MessageBuilder::buildFrames(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; if (len % eLenCanFrame) ++frameCount; 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 MessageBuilder::addSyncByte(QByteArray &vPayload) { vPayload.append(ePayload_Sync); // Sync byte } void MessageBuilder::addActionId(QByteArray &vPayload, GuiActionType vAction) { if (vAction != GuiActionType::Unknown) { quint16 mAction = static_cast(vAction); vPayload += (mAction >> 8) & 0xFF;//high byte vPayload += mAction & 0xFF;// low byte } } void MessageBuilder::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) { if (vData.length() > eLenMaxData) { len = eLenMaxData ; } else { len = vData.length(); } } vPayload += len; vPayload += vData.mid(0, len); // Adding required Data } void MessageBuilder::addCRC(QByteArray &vPayload) { // sync byte should not be passed for crc calculation vPayload += calcCRC(vPayload.mid(1)); } void MessageBuilder::addPadding(QByteArray &vPayload) { vPayload = vPayload.leftJustified(eLenCanFrame, '\0'); } // TODO : This section better to be in the MessageModel quint8 MessageBuilder::calcCRC(const QByteArray &vData) { quint8 crc = crc8(vData); return crc; } // CRC is always next byte after Data // TODO : This section better to be in the MessageModel bool MessageBuilder::checkCRC(const QByteArray &vData) { #ifndef DISABLE_CRC int len = vData.length(); if ( ! len ) return false; quint8 crc = vData.back(); quint8 got = calcCRC(vData.mid(0, len - 1)); bool ok = got == crc; // it's very good but I'm not sure if it's correct. //bool ok = calcCRC(vData) == 0; if ( ! ok ) { qDebug() << "ch crc :" << hex << got << crc << vData.toHex('.'); } return ok; #else return true; #endif } bool MessageBuilder::buildMessage(const QByteArray &vPayload, Message &vMessage, Can_Id vCan_Id) { QByteArray mPayload = vPayload; if (vMessage.data.isEmpty()) { // message is empty so expected a header if (hasSyncByte(mPayload)) { // Got header consoleOut(vPayload, true, vCan_Id); vMessage.can_id = vCan_Id; vMessage.head = getHeader (mPayload); // keep header before taking it out of the payload. doesn't affect payload vMessage.actionId = getActionId (mPayload); vMessage.length = getLength (mPayload); vMessage.data = getData (mPayload, vMessage.length); vMessage.initialized = true; } else { // Expected Header but got pure data printPayload(vPayload, false ,vCan_Id); qDebug() << "ERROR :" << tr("Expected Header, got frame without Sync byte"); return false; } } else { consoleOut(vPayload, false ,vCan_Id); vMessage.data += vPayload.mid(0, vMessage.length - vMessage.data.length()); } // TODO : This section better to be in the MessageModel // and when Message model identifies the message is complete // will SIGNAL builder to check for crc. if (vMessage.isComplete()) { consoleOut("", false, Can_Id::eChlid_NONE); QByteArray crcData = vMessage.head + vMessage.data; if ( ! checkCRC(crcData) ) { // CRC is always next byte after Data qDebug() << "ERROR :" << tr("CRC error"); return false; } } return true; } bool MessageBuilder::hasSyncByte(QByteArray &vPayload) { quint8 mSyncByte = vPayload[0]; if (mSyncByte == ePayload_Sync) { vPayload = vPayload.mid(eLenSyncByte); return true; } return false; } QByteArray MessageBuilder::getHeader(const QByteArray &vPayload) { QByteArray headInfo; for (int i = 0; i < eLenHeaderInfo; i++) { headInfo += vPayload[i]; } return headInfo; } GuiActionType MessageBuilder::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); } int MessageBuilder::getLength(QByteArray &vPayload) { // on the line bellow it has to be cast to unsigned otherwise FF will be converted to -1 and + 1 becomes 0. int mlen = static_cast(vPayload[0]) + 1; // Add CRC to the length of data vPayload = vPayload.mid(eLenLength); return mlen; } QByteArray MessageBuilder::getData(QByteArray &vPayload, int vLen) { QByteArray mData; if (vLen <= eLenMaxHeaderData) { mData = vPayload.mid(0, vLen); } else { mData = vPayload; } return mData; } void MessageBuilder::printPayload(const QByteArray &vPayload, bool vIsHeader, Can_Id vCan_Id, bool vUseColor) { if (vCan_Id == Can_Id::eChlid_NONE) { qDebug() << " "; return; } QByteArray view; if (vUseColor) { QList byteList; byteList = vPayload.toHex('.').split('.'); for (int i = 0; i < byteList.length(); i++) { if (vIsHeader) { if(i == 0) { byteList[i] = QByteArray("\033[32m") + byteList[i].constData(); } if (i == 1 || i == 2) { byteList[i] = QByteArray("\033[33m") + byteList[i].constData(); } if (i > 2) { byteList[i] = QByteArray("\033[36m") + byteList[i].constData() + QByteArray("\033[0m"); } } else { byteList[i] = QByteArray("\033[36m") + byteList[i].constData() + QByteArray("\033[0m"); } } view = Format::toHexString(vCan_Id, false, 3).toLatin1() + " " + byteList.join('.'); fprintf(stderr, "%s\n", view.constData()); } else { view = Format::toHexString(vCan_Id, false, 3).toLatin1() + " " + vPayload.toHex('.'); fprintf(stderr, "%s\n", view.constData()); } } void MessageBuilder::consoleOut(const QByteArray &vPayload, bool vIsHeader, Can_Id vCan_Id, bool vUseColor) { if ( ! _enableConsoleOut) return; printPayload(vPayload, vIsHeader, vCan_Id, vUseColor); }