/*! * * 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 #include // 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) { } bool MessageInterpreter::isType(const Message &vMessage, Gui::GuiActionType vType) const { if ( vMessage.actionId != vType ) { return false; } return true; } bool MessageInterpreter::isPayloadLenValid(const Message &vMessage, Gui::GuiActionType vType) const { if ( vMessage.data.length() < payloadLen[vType] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } return true; } /*! * \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 count = vData.count(); switch (vActionId) { // notice we are in transmit mode case Gui::GuiActionType::PowerOff: if (count) { vPayload += vData[0].toUInt(); } 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. // // Note : added this line to be able to do the Fake Test if (count) { vPayload = Format::fromVariant(vData[0]); } 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: if (count) { vPayload = Format::fromVariant(vData[0]); } break; case Gui::GuiActionType::AdjustBloodDialysateReq: case Gui::GuiActionType::AdjustDurationReq: if (count) { vPayload = Format::fromVariant(vData); } break; case Gui::GuiActionType::AlarmSilenceReq: if (count) { vPayload = Format::fromVariant(vData); } 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; // coco begin validated: Is a placeholder and There is no definition/implementation of DG communication with UI. case eChlid_DG_UI: //case eChlid_DG_Alarm: //case eChlid_DG_Sync: ok = interpretMessage_DG(vMessage, vData); break; default: break; // coco end } 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) { if ( gDisableHunhandledReport ) return; QString mActionIdHexString = Format::toHexString(vMessage.actionId, false, eLenMessageIDDigits); QString logMessage = tr("Unhandled Message ID (HD)") + '\n' + QString("%1 # %2 %3") .arg(vMessage.can_id, 3, 16, QChar('0')) .arg(mActionIdHexString) .arg(QString(vMessage.data.toHex('.'))); LOG_ERROR(logMessage); } /*! * \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 = false; vData.clear(); switch (vMessage.actionId) { // notice we are in receive mode case Gui::GuiActionType::PowerOff: { ok = powerOffData (vMessage, vData); break; } case Gui::GuiActionType::Acknow: ok = true; break; case Gui::GuiActionType::PowerOffBroadcast: ok = true; break; case Gui::GuiActionType::BloodFlow: ok = bloodFlowData (vMessage, vData); break; case Gui::GuiActionType::DialysateInletFlow: ok = dialysateInletFlowData (vMessage, vData); break; case Gui::GuiActionType::DialysateOutletFlow: ok = dialysateOutletFlowData (vMessage, vData); break; case Gui::GuiActionType::TreatmentTime: ok = treatmentTime (vMessage, vData); break; case Gui::GuiActionType::AlarmStatus: ok = alarmStatus (vMessage, vData); break; case Gui::GuiActionType::PressureOcclusion: ok = pressureOcclusionData (vMessage, vData); break; case Gui::GuiActionType::TreatmentRanges: ok = treatmentRangesData (vMessage, vData); break; case Gui::GuiActionType::AdjustBloodDialysateRsp: ok = adjustBloodDialysateData (vMessage, vData); break; case Gui::GuiActionType::AdjustDurationRsp: ok = adjustDurationData (vMessage, vData); break; // unhandles messages: these will only be logged as received message // there has nothing been defined for these messages. case Gui::GuiActionType::AlarmTriggered: printUnhandled (vMessage); ok = true; break; case Gui::GuiActionType::AlarmCleared: printUnhandled (vMessage); ok = true; break; case Gui::GuiActionType::AlarmSilenceRsp: printUnhandled (vMessage); ok = true; break; case Gui::GuiActionType::TreatmentState: printUnhandled (vMessage); ok = true; 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) { // coco begin validated: No data have been interpreted from DG yet Q_UNUSED(vMessage ); Q_UNUSED(vData ); return false; } // coco end /*! * \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 vData - The BloodFlow data * \return true if the data can be extracted as defined for PowerOff Message ID */ bool MessageInterpreter::getPowerOffData(const Message &vMessage, Model::MPowerOff &vData) { if ( vMessage.actionId != Gui::GuiActionType::PowerOff) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::PowerOff] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } vData.fromByteArray(vMessage.data); 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::powerOffData(const Message &vMessage, QVariantList &vData) { bool ok; Model::MPowerOff mData; ok = getPowerOffData(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } 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 vData - The BloodFlow data * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getBloodFlowData(const Message &vMessage, Model::MBloodFlow &vData) { 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; } vData.fromByteArray(vMessage.data); 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; Model::MBloodFlow mData; ok = getBloodFlowData(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } return ok; } /*! * \brief MessageInterpreter::getDialysateInletFlowData * \details This is the method which interprets the Dialysate Inlet 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 vData - The dialydate inlet flow data * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getDialysateInletFlowData(const Message &vMessage, Model::MDialysateFlow &vData) { if ( vMessage.actionId != Gui::GuiActionType::DialysateInletFlow ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::DialysateInletFlow] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } vData.fromByteArray(vMessage.data); return true; } /*! * \brief MessageInterpreter::dialysateInletFlowData * \details Used the getDialysateInletFlowData 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 getDialysateInletFlowData */ bool MessageInterpreter::dialysateInletFlowData(const Message &vMessage, QVariantList &vData) { bool ok; Model::MDialysateFlow mData; ok = getDialysateInletFlowData(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } return ok; } /*! * \brief MessageInterpreter::getDialysateOutletFlowData * \details This is the method which interprets the Dialysate Outlet 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 vData - The dialydate outlet flow data (ultrafiltration) * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getDialysateOutletFlowData(const Message &vMessage, Model::MOutletFlow &vData) { if ( vMessage.actionId != Gui::GuiActionType::DialysateOutletFlow ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::DialysateOutletFlow] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } vData.fromByteArray(vMessage.data); return true; } /*! * \brief MessageInterpreter::dialysateOutletFlowData * \details Used the getDialysateOutletFlowData 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 getDialysateOutletFlowData */ bool MessageInterpreter::dialysateOutletFlowData(const Message &vMessage, QVariantList &vData) { bool ok; Model::MOutletFlow mData; ok = getDialysateOutletFlowData(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } return ok; } /*! * \brief MessageInterpreter::getDialysateOutletFlowData * \param vMessage - The vMessage of type Message which contains all the data, require to be interpreted. * \param vData - Treatment Time data * \return true if the message can be successfully converted to the Blood Flow data elements. */ bool MessageInterpreter::getTreatmentTime( const Message &vMessage , Model::MTreatmentTime &vData) { if ( vMessage.actionId != Gui::GuiActionType::TreatmentTime ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::TreatmentTime] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } vData.fromByteArray(vMessage.data); return true; } /*! * \brief MessageInterpreter::treatmentTime * \details Used the getTreatmentTime 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 getDialysateOutletFlowData */ bool MessageInterpreter::treatmentTime(const Message &vMessage, QVariantList &vData) { bool ok; Model::MTreatmentTime mData; ok = getTreatmentTime(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } 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 vData - Alarm Status dta * \return true if the data can be extracted as defined for AlarmStatus Message ID */ bool MessageInterpreter::getAlarmStatus(const Message &vMessage, Model::MAlarmStatus &vData) { 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; } vData.fromByteArray(vMessage.data); 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; Model::MAlarmStatus mData; ok = getAlarmStatus(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } return ok; // --- 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; } /*! * \brief MessageInterpreter::getPressureOcclusionData * \details This method interprets Pressure Occlusion message data * in vMessage of type Message. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vData - Pressure Occlusion data * \return true if the data can be extracted as defined for PressureOcclusion Message ID */ bool MessageInterpreter::getPressureOcclusionData(const Message &vMessage, Model::MPressureOcclusion &vData) { if ( vMessage.actionId != Gui::GuiActionType::PressureOcclusion ) { return false; } if ( vMessage.data.length() < payloadLen[Gui::GuiActionType::PressureOcclusion] ) { QString mActionIdHexString = Format::toHexString(vMessage.actionId); LOG_ERROR(tr("Incorrect data for Message ID (HD) '%1'").arg(mActionIdHexString)); return false; } vData.fromByteArray(vMessage.data); return true; } /*! * \brief MessageInterpreter::pressureOcclusionData * \details Used the getPressureOcclusionData 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 getPressureOcclusionData */ bool MessageInterpreter::pressureOcclusionData(const Message &vMessage, QVariantList &vData) { bool ok; Model::MPressureOcclusion mData; ok = getPressureOcclusionData(vMessage, mData); LOG_DATUM(mData.toString()); if (ok) { mData.toVariantList(vData); emit didActionReceive(mData.data()); } return ok; } /*! * \brief MessageInterpreter::getTreatmentRangesData * \details This method interprets Treatment Ranges message data * in vMessage of type Message. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vData - Treatment Ranges data * \return true if the data can be extracted as defined for Treatment Ranges Message ID */ bool MessageInterpreter::treatmentRangesData(const Message &vMessage, QVariantList &vData) { // TODO : review other methods bool ok = false; if ( ! isType (vMessage, Gui::GuiActionType::TreatmentRanges) ) return ok; if ( ! isPayloadLenValid(vMessage, Gui::GuiActionType::TreatmentRanges) ) return ok; Model::MTreatmentRanges mData; ok = mData.fromByteArray(vMessage.data); LOG_DATUM(mData.toString()); mData.toVariantList(vData); emit didActionReceive(mData.data()); return ok; } /*! * \brief MessageInterpreter::adjustBloodDialysateData * \details This method interprets AdjustBlood Dialysate Response message data * in vMessage of type Message. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vData - AdjustBlood Dialysate Response data * \return true if the data can be extracted as defined for AdjustBlood Dialysate Response Message ID */ bool MessageInterpreter::adjustBloodDialysateData(const Message &vMessage, QVariantList &vData) { // TODO : review other methods bool ok = false; if ( ! isType (vMessage, Gui::GuiActionType::AdjustBloodDialysateRsp) ) return ok; if ( ! isPayloadLenValid(vMessage, Gui::GuiActionType::AdjustBloodDialysateRsp) ) return ok; Model::MAdjustBloodDialysateResponse mData; ok = mData.fromByteArray(vMessage.data); LOG_DATUM(mData.toString()); mData.toVariantList(vData); emit didActionReceive(mData.data()); return ok; } /*! * \brief MessageInterpreter::adjustDurationData * \details This method interprets Treatment Duration Adjustment Response message data * in vMessage of type Message. * \param vMessage - The vMessage of type Message which contains all the data, * require to be interpreted. * \param vData - Treatment Duration Adjustment Response data * \return true if the data can be extracted as defined for Treatment Duration Adjustment Response Message ID */ bool MessageInterpreter::adjustDurationData(const Message &vMessage, QVariantList &vData) { bool ok = false; if ( ! isType (vMessage, Gui::GuiActionType::AdjustDurationRsp) ) return ok; if ( ! isPayloadLenValid(vMessage, Gui::GuiActionType::AdjustDurationRsp) ) return ok; Model::MAdjustDurationResponse mData; ok = mData.fromByteArray(vMessage.data); LOG_DATUM(mData.toString()); mData.toVariantList(vData); emit didActionReceive(mData.data()); return ok; }