/*! * * 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 VAlarmStatus.cpp * \author (last) Behrouz NematiPour * \date (last) 17-Jan-2024 * \author (original) Behrouz NematiPour * \date (original) 26-Aug-2020 * */ #include "VAlarmStatus.h" // Qt #include #include // Project #include "GuiController.h" #include "Settings.h" VIEW_DEF_CLASS_ADJUSTMENT(VAlarmStatus) void VAlarmStatus::initConnections() { ACTION_VIEW_CONNECTION(AlarmStatusData ); ACTION_VIEW_CONNECTION(SettingsData ); ADJUST_VIEW_CONNECTION(AlarmSilenceRequestData ); ADJUST_VIEW_CONNECTION(AlarmUserActionRequestData ); connect(&_GuiController, SIGNAL(didActionReceive (GuiActionType, const QVariantList &)), this , SLOT( onActionReceive (GuiActionType, const QVariantList &))); _instructionsList.setRoleNames({ { eRole_Instruction , "instruction" }, { eRole_Image , "image" }, }); } /*! * \brief View::VAlarmStatus::doUserActionResume * \details send the message to notify that user chose the Resume Treatment button */ void View::VAlarmStatus::doUserActionResume() { AlarmUserActionRequestData data; data.action = Gui::GuiAlarmUserActions::ALARM_USER_ACTION_RESUME; emit didAdjustment(data); } /*! * \brief View::VAlarmStatus::doUserActionRinseback * \details send the message to notify that user chose the Rinse back button */ void View::VAlarmStatus::doUserActionRinseback() { AlarmUserActionRequestData data; data.action = Gui::GuiAlarmUserActions::ALARM_USER_ACTION_RINSEBACK; emit didAdjustment(data); } /*! * \brief View::VAlarmStatus::doUserActionEnd * \details send the message to notify that user chose the End Treatment button */ void View::VAlarmStatus::doUserActionEnd() { AlarmUserActionRequestData data; data.action = Gui::GuiAlarmUserActions::ALARM_USER_ACTION_END_TREATMENT; emit didAdjustment(data); } /*! * \brief View::VAlarmStatus::doUserActionEnd * \details send the message to notify that user chose the End Treatment button */ void View::VAlarmStatus::doUserActionTemporaryBreak() { AlarmUserActionRequestData data; data.action = Gui::GuiAlarmUserActions::ALARM_USER_ACTION_TEMPOARY_BREAK; emit didAdjustment(data); } /*! * \brief View::VAlarmStatus::doUserActionOk * \details send the message to notify that user chose the OK button */ void View::VAlarmStatus::doUserActionOk() { AlarmUserActionRequestData data; data.action = Gui::GuiAlarmUserActions::ALARM_USER_ACTION_ACK; emit didAdjustment(data); } /*! * \brief VAlarmStatus::doSilence * Send out a request to silence alarms */ void View::VAlarmStatus::doSilence() { AlarmSilenceRequestData data; if (_alarm_Flag_alarmsSilenced) data.silence = 0; // de-silence else data.silence = 1; // silence emit didAdjustment(data); } /*! * \brief VAlarmStatus::onActionReceive * \details the message received handler * \param vData - Data of the received message */ void VAlarmStatus::onActionReceive(const AlarmStatusData &vData) { GuiAlarmID alarmID = static_cast(vData.mTop); if (alarmID == GuiAlarmID::ALARM_ID_TD_COMM_TIMEOUT && gDisableTimeout) { LOG_DEBUG(tr("Suppressing HD communication timeout.")); return; } //// ------ HIDE THE DIALOG - if has NO the alarm ------ // DENBUG-182: Phantom Alarm Screen Appears After Clearing Last Alarm if (vData.mTop == GuiAlarmID::ALARM_ID_NO_ALARM) { hasAlarm(false); _alarm_AlarmID = 0; emit didAlarmEmpty(); return; } //// ----- SET PROPERTY VALUES ------ // Look for this tag: #First_Time_Message_Sent_With_Silenced // this has to be first to make sure in the Notification dialog when we decided to show the dialog, // we should also consider checking the alarms Silenced and if it is already silenced, // even though there should be a text but should not show the dialog // otherwise it bounces alarm_Flag_alarmsSilenced (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_ALARMS_SILENCED )); alarm_Priority (vData.mState ); alarm_AlarmID (vData.mTop ); alarm_MuteTimeout (vData.mMuteTimeout ); alarm_Flag_systemFault (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_SYSTEM_FAULT )); alarm_Flag_stop (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_STOP )); alarm_Flag_noClear (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_CLEAR )); alarm_Flag_noResume (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_RESUME )); alarm_Flag_noRinseback (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_RINSEBACK )); alarm_Flag_noEndTreatment (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_END_TREATMENT )); alarm_Flag_noTemporaryBreak (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_TEMPORARY_BREAK )); alarm_Flag_UserMustAck (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_OK_BUTTON_ONLY )); alarm_Flag_alarmsToEscalate (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_ALARMS_TO_ESCALATE )); // alarm_Flag_alarmsSilenced (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_ALARMS_SILENCED )); // look at the note @L112 alarm_Flag_alarmsLampOn (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_LAMP_ON )); alarm_Flag_noRetrigger (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_RETRIGGER )); // Added but not used yet. alarm_Flag_bloodRecirc (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_BLOOD_RECIRC )); // Added but not used yet. alarm_Flag_dialysateRecirc (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_DIALYSATE_RECIRC )); // Added but not used yet. // alarm_Flag_noMinimize (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_MINIMIZE )); // Used in the if's else block. // alarm_Flag_alarmsCondition (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_TOP_CONDITION )); // Is moved to be after the NoMinimize if ( gDisableAlarmNoMinimize ) { alarm_Flag_noMinimize (false); } else { alarm_Flag_noMinimize (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_NO_MINIMIZE )); } alarm_Flag_alarmsCondition (vData.mFlags.at(GuiAlarmFlags::ALARM_STATE_FLAG_BIT_POS_TOP_CONDITION )); hasAlarm(true); if ( // alarm id properties _alarm_AlarmIDChanged || _alarm_PriorityChanged // alarm buttons || _alarm_Flag_noResumeChanged || _alarm_Flag_noRinsebackChanged || _alarm_Flag_noEndTreatmentChanged || _alarm_Flag_UserMustAckChanged // alarm cannot be minimized || ( _alarm_Flag_noMinimizeChanged && _alarm_Flag_noMinimize ) // alarm is out of silence || ( _alarm_Flag_alarmsSilencedChanged && ! _alarm_Flag_alarmsSilenced ) ){ _instructionsList = _alarms[_alarm_AlarmID].instructions; emit didAlarmRaise(); } } /*! * \brief VAlarmStatus::alarmIDText * \details Looks up the alarm text from the alarm ID * \param vEnum - The Alarm ID * \return The alarm text (QString) */ QString VAlarmStatus::alarmIDText(GuiAlarmID vEnum) { return Model::MAlarmStatus::toText(vEnum); } /*! * \brief VAlarmStatus::text * \return Gets the alarm text from the alarm ID and returns it as a QString */ QString VAlarmStatus::text() { return alarmIDText(static_cast(alarm_AlarmID())); } QString VAlarmStatus::title () { if ( ! _alarm_AlarmID ) return {}; QString s = _alarms[_alarm_AlarmID].title ; if ( ! s.isEmpty()) return s; else return tr("Alarm") ; } /*! * \brief View::VAlarmStatus::onActionReceive * \details This function updates the alarm structure defined here to be used on the AlarmDialogs, * when the settings controller is done reading the Alarms.conf and signaling this class. * \todo This function with the same one in VActiveAlarmList needs to be moved to a Controller Model structure, for a better performance and memory management. */ void VAlarmStatus::onActionReceive(const SettingsData &) { QString category = Storage::Settings_Category_Alarms; QStringList groups = _Settings.groups(category); for (const auto &group : groups) { bool ok = true; quint32 id = group.toInt( &ok ); if ( ! ok ) { LOG_DEBUG(QString("Invalid Alarm ID [%1]").arg(group)); continue; } AlarmData alarmData; InstructionData instructionData; for (const QString &key : _Settings.keys(category, group)) { if (Storage::Settings::isKeyTitle ( key ) ) { alarmData.title = _Settings.value(category, group, key).toString(); } else if (Storage::Settings::isKeyListTitle ( key ) ) { // do nothing.. } else { const QString imagePath = QStringLiteral("%1%2").arg(_location) .arg(_Settings.value(category, group, key).toString()); QFileInfo fileInfo(imagePath); QHash instructionStep; QStringList updatedKey = updateReplacements(group, key, id, instructionData.count()); instructionStep.insert(eRole_Instruction, updatedKey); instructionStep.insert(eRole_Image, fileInfo.exists() && fileInfo.isFile() ? "file:" + imagePath : "") ; instructionData.append(instructionStep); alarmData.instructions = instructionData; } } _alarms[id] = alarmData; /// DEBUG: /// TODO: the MSettings model should do the same has been done here and use map instead of separate structure to iterate vertically, while map supports, keys, values. // qDebug() << "@" << id << _alarms[id].title << _alarms[id].message << _alarms[id].instructions.keys() << _alarms[id].instructions.values(); // for (const auto &key : _alarms[id].instructions.keys()) { // qDebug() << "-" << key << _alarms[id].instructions.value(key); // } } emit alarm_AlarmIDChanged(_alarm_AlarmID); // to get the dialog content in sync with the Alarm.conf in case there is an early alarm. } QStringList VAlarmStatus::updateReplacements(const QString &vGroup, const QString &vKey, const int &vAlarmID, const int &vIndex) { QRegExp regx("\\{\\s*\\d+\\s*:\\s*\\d+\\s*:\\s*\\w*\\s*:\\s*\\d*\\s*\\}"); QString key = vKey; int replacementCount = key.count(regx); QStringList keyList; for ( int j = 0; j < replacementCount; j++ ) { int pos = key.indexOf(regx); int len = regx.matchedLength(); QString blk = key.mid(pos+1, len-2); keyList += key.mid(0, pos); keyList += ""; // value placeholder key.remove(0, pos + regx.matchedLength()); QStringList lst = blk.split(":"); quint16 id = lst[0].toUInt(); quint16 ix = lst[1].toUInt(); QString ex = lst[2] ; quint16 rd = lst[3].toUInt(); Q_UNUSED(vGroup); TLocation loc; loc.group = vGroup ; loc.alarmID = vAlarmID ; loc.index = vIndex ; loc.locIndex = j * 2 + 1 ; loc.prmIndex = ix ; loc.prmExtra = ex ; loc.prmRound = rd ; _replacements[id] += loc; } keyList += key; return keyList; } /*! * \brief VSettings::onActionReceive * \details emits didActionReceive signal to notify other classes (Gui) * , an action has been received. * \param vAction - the action * \param vData - the action data */ void VAlarmStatus::onActionReceive (GuiActionType vAction, const QVariantList &vData) { bool isChanged = false; quint16 id = qFromBigEndian((quint16)vAction); quint32 alarmID; if (_replacements.contains(id)) { isChanged = true; for ( const auto &item : _replacements[id]) { QString value; quint16 len = vData.length(); if ( 0 < item.prmIndex && item.prmIndex <= len ) { if ( item.prmRound > 0 ) { value = QString::number(vData[item.prmIndex - 1].toFloat(), 'f', item.prmRound) + item.prmExtra; } else { value = vData[item.prmIndex - 1].toString() + item.prmExtra; } } else { qDebug() << QString("Given prmIndex %1 is out of message %2 parameters count %3 on alarm id '%4'") .arg(item.prmIndex).arg(id).arg(len).arg(item.alarmID); continue; } // update alarm instruction step alarmID = item.alarmID; QStringList step = _alarms[alarmID].instructions[item.index][eRole_Instruction].toStringList(); step[item.locIndex] = value; _alarms[alarmID].instructions[item.index][eRole_Instruction] = step; } } if ( isChanged ) emit alarm_AlarmIDChanged(alarmID); }