/*! * * 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 messageinterpreter.cpp * date 12/11/2019 * author Behrouz NematiPour * */ #include "messageinterpreter.h" // Qt // Project #include "logger.h" #include "format.h" using namespace Can; /*! * \brief MessageInterpreter::MessageInterpreter * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ MessageInterpreter::MessageInterpreter(QObject *parent) : QObject(parent) { } /*! * \brief MessageInterpreter::interpretMessage * \details This method will be called * to interpret messages from UI regarding vActionId. * \param vActionId - The ActionID of type GuiActionType * to be interpreted to hex representation of Message ID. * \param vData - The data which has to be sent over the CANBUS. * \param vPayload - The Payload of the frame of Type QByteArray of hex values * Which has been interpreted from vData of Type QVariantList * \return true if the vActionId is valid. * This return value will be used later for error handling. */ bool MessageInterpreter::interpretMessage(const Gui::GuiActionType &vActionId, const QVariantList &vData, QByteArray &vPayload) { bool ok = true; vPayload.clear(); int l = vData.length(); quint8 ix = 0; switch (vActionId) { // notice we are in transmit mode case Gui::GuiActionType::PowerOff: ix = static_cast(Gui::GuiActionIndx::PowerOff_Response); if (l >= ix + 1) { quint8 tmp = vData[ix].toUInt(); vPayload += tmp; } else { QString mActionIdHexString = Format::toHexString(vActionId); LOG_ERROR(tr("Incorrect data for Message ID (UI) '%1'").arg(mActionIdHexString)); ok = false; } break; case Gui::GuiActionType::KeepAlive: // Nothing needs to be done. // KeepAlive has No data. // Mentioned in the switch/case to be registered as a valid message. break; case Gui::GuiActionType::Acknow: // Nothing needs to be done. // Acknow has No data. // Mentioned in the switch/case to be registered as a valid message. break; case Gui::GuiActionType::String: vPayload = Format::fromVariant(vData[0]); break; default: QString mActionIdHexString = Format::toHexString(vActionId); LOG_ERROR(tr("Unknown Message ID (UI) '%1'").arg(mActionIdHexString)); ok = false; break; } return ok; } /*! * \brief MessageInterpreter::interpretMessage * \details This method will call appropriate message interpreter * for received messages from HD or DG regarding the Can_Id. * \param vCan_Id - The Channel Id of the CANBUS frame. * \param vMessage - The complete message of type Message which needs to be interpreted. * \param vActionId - The ActionId of GuiActionType which will be extracted from vMessage. * \param vData - The values of QVariantList which is understandable for UI * and has been extracted from hex values of the CANBUS Message Payload * regarding each Message Id definition. * \return true if the message channel is in the range which can be interpreted, false otherwise. * This return value will be used later to emit MessageDispatcher::didActionReceive signal or not */ bool MessageInterpreter::interpretMessage(const Message &vMessage, QVariantList &vData) { bool ok = false; switch (vMessage.can_id) { case eChlid_HD_UI: case eChlid_HD_Alarm: case eChlid_HD_Sync: ok = interpretMessage_HD(vMessage, vData); break; case eChlid_DG_UI: //case eChlid_DG_Alarm: //case eChlid_DG_Sync: ok = interpretMessage_DG(vMessage, vData); break; default: break; } return ok; } /*! * \brief MessageInterpreter::printUnhandled * \details Prints out the formatted string of the vMessage of type Message * In case the Message ID of received CANBUS message * is known to the interpreter but has not been handled/implemented. * \param vMessage - The message contains Unhandled Message ID */ void MessageInterpreter::printUnhandled(const Message &vMessage) { QString mActionIdHexString = Format::toHexString(vMessage.actionId, false, eLenMessageIDDigits); qDebug() << "WARNG :" << tr("Unhandled Message ID (HD)"); qDebug().noquote() << QString(mActionIdHexString + " " + vMessage.data.toHex('.')).toLatin1(); qDebug() << ""; } /*! * \brief MessageInterpreter::interpretMessage_HD * \details This method will be called * for received messages from HD to interpret the vMessage of type Message * to vData of type QVariantList which UI understands regarding the Can_Id. * \param vMessage - The complete message of type Message which needs to be interpreted. * \param vActionId - The ActionId of GuiActionType which will be extracted from vMessage. * \param vData - The values of QVariantList which is understandable for UI * and has been extracted from hex values of the CANBUS Message Payload * in vMessage of type Message regarding each Message Id definition. * \return true if the message CANBUS channel is in the range which can be interpreted, false otherwise. * This return value will be used later to emit MessageDispatcher::didActionReceive signal or not */ bool MessageInterpreter::interpretMessage_HD(const Message &vMessage, QVariantList &vData) { bool ok = true; vData.clear(); switch (vMessage.actionId) { // notice we are in receive mode case Gui::GuiActionType::PowerOff: { quint8 mShowHide; ok = getPowerOffData(vMessage, mShowHide); if (ok) { vData += mShowHide; } LOG_DATUM(QString("Power Off (%1)").arg(mShowHide)); break; } case Gui::GuiActionType::Acknow: ok = true; break; case Gui::GuiActionType::BloodFlow: ok = bloodFlowData (vMessage, vData); break; case Gui::GuiActionType::DialysateFlow: ok = dialysateFlowData (vMessage, vData); break; case Gui::GuiActionType::AlarmStatus: ok = alarmStatus (vMessage, vData); break; case Gui::GuiActionType::AlarmTriggered: printUnhandled (vMessage); break; case Gui::GuiActionType::AlarmCleared: printUnhandled (vMessage); break; default: printUnhandled (vMessage); break; } return ok; } /*! * \brief MessageInterpreter::interpretMessage_DG * \details This method will be called * for received messages from DG to interpret the vMessage of type Message * to vData of type QVariantList which UI understands regarding the Can_Id. * \param vMessage - The complete message of type Message which needs to be interpreted. * \param vActionId - The ActionId of GuiActionType which will be extracted from vMessage. * \param vData - The values of QVariantList which is understandable for UI * and has been extracted from hex values of the CANBUS Message Payload * in vMessage of type Message regarding each Message Id definition. * \return true if the message CANBUS channel is in the range which can be interpreted, false otherwise. * This return value will be used later to emit MessageDispatcher::didActionReceive signal or not */ bool MessageInterpreter::interpretMessage_DG(const Message &vMessage, QVariantList &vData) { Q_UNUSED(vMessage ); Q_UNUSED(vData ); // No data have been interpreted from DG yet return false; } /*! * \brief MessageInterpreter::getPowerOffData * \details This is the method which interprets the PowerOff message data * in vMessage of type Message. * to its elements of data. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vShowHide - The return value of extracted fro * \return true if the data can be extracted as defined for PowerOff Message ID */ bool MessageInterpreter::getPowerOffData(const Message &vMessage, quint8 &vShowHide) { bool ok = true; int l = vMessage.data.length(); quint8 ix = static_cast(Gui::GuiActionIndx::PowerOff_ShowHide); if (l >= ix + 1) { quint8 tmp = vMessage.data[ix]; vShowHide = tmp; } else { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); ok = false; } return ok; } /*! * \brief MessageInterpreter::getBloodFlowData * \details This is the method which interprets the Blood Flow message data in vMessage of type Message * to its elements of data. * \param vMessage - The vMessage of type Message which contains all the data, require to be interpreted. * \param vFlowSetPoint - Flow Set Point value of type signed int extracted out * \param vMeasuredFlow - Measured Flow value of type float extracted out * \param vRotorSpeed - Rotor Speed value of type float extracted out * \param vMotorSpeed - Motor Speed value of type float extracted out * \param vMotorCtlSpeed - Motor Controller Speed value of type float extracted out * \param vMotorCtlCurrent - Motor Controller Current value of type float extracted out * \param vPWMDtCycle - PWM Duty Cycle in % value of type float extracted out * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getBloodFlowData( const Message &vMessage , Types::S32 &vFlowSetPoint , Types::F32 &vMeasuredFlow , Types::F32 &vRotorSpeed , Types::F32 &vMotorSpeed, Types::F32 &vMotorCtlSpeed , Types::F32 &vMotorCtlCurrent , Types::F32 &vPWMDtCycle) { if ( vMessage.actionId != Gui::GuiActionType::BloodFlow ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::BloodFlow] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } int index = 0; // message data start position Types::getValue<>(vMessage.data, index, vFlowSetPoint ); Types::getValue<>(vMessage.data, index, vMeasuredFlow ); Types::getValue<>(vMessage.data, index, vRotorSpeed ); Types::getValue<>(vMessage.data, index, vMotorSpeed ); Types::getValue<>(vMessage.data, index, vMotorCtlSpeed ); Types::getValue<>(vMessage.data, index, vMotorCtlCurrent); Types::getValue<>(vMessage.data, index, vPWMDtCycle ); return true; } /*! * \brief MessageInterpreter::bloodFlowData * \details Used the getBloodFlowData method and converts each parameter * in vData of type QVaranitList, to be used in the GUI * Also logs the data * \param vMessage - The message * \param vData - the output data * \return return value of the method getBloodFlowData */ bool MessageInterpreter::bloodFlowData(const Message &vMessage, QVariantList &vData) { bool ok; Types::S32 mFlowSetPoint ; Types::F32 mMeasuredFlow ; Types::F32 mRotorSpeed ; Types::F32 mMotorSpeed ; Types::F32 mMotorCtlSpeed ; Types::F32 mMotorCtlCurrent ; Types::F32 mPWMDtCycle ; ok = getBloodFlowData(vMessage , mFlowSetPoint , mMeasuredFlow , mRotorSpeed , mMotorSpeed , mMotorCtlSpeed , mMotorCtlCurrent , mPWMDtCycle ); LOG_DATUM(QString("Blood Flow(%1, %2, %3, %4, %5, %6, %7)") .arg(mFlowSetPoint .value) .arg(mMeasuredFlow .value) .arg(mRotorSpeed .value) .arg(mMotorSpeed .value) .arg(mMotorCtlSpeed .value) .arg(mMotorCtlCurrent .value) .arg(mPWMDtCycle .value) ); if (ok) { vData += mFlowSetPoint .value; vData += mMeasuredFlow .value; vData += mRotorSpeed .value; vData += mMotorSpeed .value; vData += mMotorCtlSpeed .value; vData += mMotorCtlCurrent.value; vData += mPWMDtCycle .value; } return ok; } /*! * \brief MessageInterpreter::getDialysateFlowData * \details This is the method which interprets the Dialysate Flow message data in vMessage of type Message * to its elements of data. * \param vMessage - The vMessage of type Message which contains all the data, require to be interpreted. * \param vFlowSetPoint - Flow Set Point value of type signed int extracted out * \param vMeasuredFlow - Measured Flow value of type float extracted out * \param vRotorSpeed - Rotor Speed value of type float extracted out * \param vMotorSpeed - Motor Speed value of type float extracted out * \param vMotorCtlSpeed - Motor Controller Speed value of type float extracted out * \param vMotorCtlCurrent - Motor Controller Current value of type float extracted out * \param vPWMDtCycle - PWM Duty Cycle in % value of type float extracted out * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getDialysateFlowData( const Message &vMessage , Types::S32 &vFlowSetPoint , Types::F32 &vMeasuredFlow , Types::F32 &vRotorSpeed , Types::F32 &vMotorSpeed, Types::F32 &vMotorCtlSpeed , Types::F32 &vMotorCtlCurrent , Types::F32 &vPWMDtCycle) { if ( vMessage.actionId != Gui::GuiActionType::DialysateFlow ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::DialysateFlow] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } int index = 0; // message data start position Types::getValue<>(vMessage.data, index, vFlowSetPoint ); Types::getValue<>(vMessage.data, index, vMeasuredFlow ); Types::getValue<>(vMessage.data, index, vRotorSpeed ); Types::getValue<>(vMessage.data, index, vMotorSpeed ); Types::getValue<>(vMessage.data, index, vMotorCtlSpeed ); Types::getValue<>(vMessage.data, index, vMotorCtlCurrent); Types::getValue<>(vMessage.data, index, vPWMDtCycle ); return true; } /*! * \brief MessageInterpreter::dialysateFlowData * \details Used the getDialysateFlowData method and converts each parameter * in vData of type QVaranitList, to be used in the GUI * Also logs the data * \param vMessage - The message * \param vData - the output data * \return return value of the method getDialysateFlowData */ bool MessageInterpreter::dialysateFlowData(const Message &vMessage, QVariantList &vData) { bool ok; Types::S32 mFlowSetPoint ; Types::F32 mMeasuredFlow ; Types::F32 mRotorSpeed ; Types::F32 mMotorSpeed ; Types::F32 mMotorCtlSpeed ; Types::F32 mMotorCtlCurrent ; Types::F32 mPWMDtCycle ; ok = getBloodFlowData(vMessage , mFlowSetPoint , mMeasuredFlow , mRotorSpeed , mMotorSpeed , mMotorCtlSpeed , mMotorCtlCurrent , mPWMDtCycle ); LOG_DATUM(QString("Dialysate Flow(%1, %2, %3, %4, %5, %6, %7)") .arg(mFlowSetPoint .value) .arg(mMeasuredFlow .value) .arg(mRotorSpeed .value) .arg(mMotorSpeed .value) .arg(mMotorCtlSpeed .value) .arg(mMotorCtlCurrent .value) .arg(mPWMDtCycle .value) ); if (ok) { vData += mFlowSetPoint .value; vData += mMeasuredFlow .value; vData += mRotorSpeed .value; vData += mMotorSpeed .value; vData += mMotorCtlSpeed .value; vData += mMotorCtlCurrent.value; vData += mPWMDtCycle .value; } return ok; } /*! * \brief MessageInterpreter::getAlarmStatus * \details This method interprets AlarmStatus message data * in vMessage of type Message. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vState - Alarm Priority value of type unsigned int as 4 bytes extracted out * \param vTop - Top value of type unsigned int as 4 bytes extracted out * \param vMuteTimeout - MuteTimeout value of type unsigned int as 4 bytes extracted out * \param vEscalatesIn - EscalatesIn value of type unsigned int as 4 bytes extracted out * \param vFlags - Flags value of type unsigned int as 1 byte extracted out * \return true if the data can be extracted as defined for PowerOff Message ID */ bool MessageInterpreter::getAlarmStatus(const Message &vMessage, Types::U32 &vState , Types::U32 &vTop , Types::U32 &vMuteTimeout, Types::U32 &vEscalatesIn, Types::Flags &vFlags) { if ( vMessage.actionId != Gui::GuiActionType::AlarmStatus ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::AlarmStatus] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } int index = 0; // message data start position Types::getValue<>(vMessage.data, index, vState ); Types::getValue<>(vMessage.data, index, vTop ); Types::getValue<>(vMessage.data, index, vMuteTimeout ); Types::getValue<>(vMessage.data, index, vEscalatesIn ); Types::getBits (vMessage.data, index, vFlags, static_cast(Gui::GuiActionIndx::AlarmStatus_Flag_Bits_Length)); return true; } /*! * \brief MessageInterpreter::alarmStatus * \details Used the getAlarmStatus method and converts each parameter * in vData of type QVaranitList, to be used in the GUI * Also logs the data * \param vMessage - The message * \param vData - the output data * \return return value of the method getAlarmStatus */ bool MessageInterpreter::alarmStatus(const Message &vMessage, QVariantList &vData) { bool ok; Types::U32 mState ; Types::U32 mTop ; Types::U32 mMuteTimeout ; Types::U32 mEscalatesIn ; Types::Flags mFlags ; ok = getAlarmStatus(vMessage , mState , mTop , mMuteTimeout , mEscalatesIn , mFlags ); LOG_DATUM(QString("Alarm Status(%1, %2, %3, %4, %5)") .arg(mState .value ) .arg(mTop .value ) .arg(mMuteTimeout .value ) .arg(mEscalatesIn .value ) .arg(mFlags .toString()) ); if (ok) { vData += mState .value ; vData += mTop .value ; vData += mMuteTimeout .value ; vData += mEscalatesIn .value ; for (int i = 0; i < mFlags.count(); i++) { vData += mFlags.at(i); } } // --- an example of unit test --- // // Types::Flags flag; // int i = 0; // QByteArray ba; // ba += 0x83; ba += 0xf8; ba += 0x28; ba += 0xa1; // Types::getBits(ba, i, flag, 32); // qDebug() << '@' << flag << flag.toString() << ba; return ok; }