Index: leahi.pro =================================================================== diff -u -r34290f9fb980b41d888c0d1d543a0a9a17d3d309 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- leahi.pro (.../leahi.pro) (revision 34290f9fb980b41d888c0d1d543a0a9a17d3d309) +++ leahi.pro (.../leahi.pro) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -190,6 +190,7 @@ sources/model/td/data/treatment/MTreatmentStatesData.h \ sources/model/td/data/treatment/MTreatmentTimeData.h \ sources/model/td/data/treatment/MTreatmentUltrafiltrationData.h \ + sources/storage/RxProfiles.h \ sources/storage/Settings.h \ sources/storage/TreatmentLog.h \ sources/bluetooth/BluetoothInterface.h \ @@ -203,6 +204,7 @@ \ # ---------- Models - TD - Data //// ----- @LEAHIZED sources/model/td/data/MTDOpModeData.h \ \ # ---------- Views - TD - Data - States //// ----- @LEAHIZED + sources/view/VRxProfiles.h \ sources/view/dd/data/VDDBloodLeakData.h \ sources/view/dd/data/VDDConcentratePumpData.h \ sources/view/dd/data/VDDConductivityData.h \ @@ -516,8 +518,10 @@ sources/model/td/data/treatment/MTreatmentTimeData.cpp \ sources/model/td/data/treatment/MTreatmentUltrafiltrationData.cpp \ sources/model/ui/data/MUIBloodPressureData.cpp \ + sources/storage/RxProfiles.cpp \ sources/storage/Settings.cpp \ sources/storage/TreatmentLog.cpp \ + sources/view/VRxProfiles.cpp \ sources/view/dd/data/VDDBloodLeakData.cpp \ sources/view/dd/data/VDDConcentratePumpData.cpp \ sources/view/dd/data/VDDConductivityData.cpp \ Index: leahi.qrc =================================================================== diff -u -rc8ca0550f364ddbff8f37a68ce49050dfbc0ec09 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- leahi.qrc (.../leahi.qrc) (revision c8ca0550f364ddbff8f37a68ce49050dfbc0ec09) +++ leahi.qrc (.../leahi.qrc) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -300,6 +300,8 @@ sources/gui/qml/pages/rxmanager/RxManagerHeader.qml - sources/gui/qml/pages/rxmanager/RxManagerList.qml + sources/gui/qml/pages/rxmanager/RxManagerList.qml + sources/gui/qml/pages/rxmanager/RxProfileComponent.qml + sources/gui/qml/pages/rxmanager/RxProfileContainer.qml Index: main.cpp =================================================================== diff -u -ra5760947d3ed0d2748ba023a1c25e3c6aa0b1de1 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- main.cpp (.../main.cpp) (revision a5760947d3ed0d2748ba023a1c25e3c6aa0b1de1) +++ main.cpp (.../main.cpp) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -52,7 +52,8 @@ #include "DeviceController.h" #include "BluetoothInterface.h" #include "CloudSyncController.h" -//AMIR +#include "RxProfiles.h" + #include "Settings.h" #include "Threads.h" Index: sources/device/DeviceController.cpp =================================================================== diff -u -rdeaef8b5bdfe9be7293e63fb6ac256a9ce3cd3f4 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision deaef8b5bdfe9be7293e63fb6ac256a9ce3cd3f4) +++ sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -30,6 +30,7 @@ #include "Logger.h" #include "CloudSyncController.h" #include "ApplicationController.h" +#include "RxProfiles.h" #include "FileHandler.h" #include "Settings.h" #include "encryption.h" @@ -126,6 +127,9 @@ connect(&_CloudSyncController , SIGNAL(didLogUpload (const QString &)), this , SLOT( onLogUpload (const QString &))); + connect(&_RxProfilesController , SIGNAL(didFilesList (const QString &, const QStringList &)), + this , SLOT( onFilesList (const QString &, const QStringList &))); + DEVICE_DEV_INIT_CONNECTIONS_LIST connect(this, SIGNAL(didEventThreadChange()), @@ -745,6 +749,14 @@ emit didLogUpload(ok, vFileName); } +void DeviceController::onFilesList(const QString &vFilesPath, const QStringList &vFilesFilters) +{ + QFileInfoList mFileInfoList = readFilesList(vFilesPath, vFilesFilters); + emit didReadFilesList (mFileInfoList); +} + + + /*! * \brief DeviceController::checkConfugurationMountReady * \details Cheks if the system is ready to mount the encrypted partition. @@ -1172,6 +1184,13 @@ emit didAttributeResponse(model.data()); } +QFileInfoList DeviceController::readFilesList(const QString &vPath, const QStringList &vFilter) +{ + QFileInfoList mFilesList = FileHandler::find(vPath, vFilter); + return mFilesList; + +} + /*! * \brief DeviceController::processWifiConnectResponse * \param vExitCode Index: sources/device/DeviceController.h =================================================================== diff -u -rdeaef8b5bdfe9be7293e63fb6ac256a9ce3cd3f4 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/device/DeviceController.h (.../DeviceController.h) (revision deaef8b5bdfe9be7293e63fb6ac256a9ce3cd3f4) +++ sources/device/DeviceController.h (.../DeviceController.h) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -261,6 +261,7 @@ void onLogBackup (const QString &vFileName ); void onLogUpload (const QString &vFileName ); + void onFilesList (const QString &vFilesPath, const QStringList &vFilesFilters); protected: void timerEvent(QTimerEvent *) override; @@ -444,6 +445,7 @@ */ void didWiFiIP ( const QString &vData ); + void didReadFilesList ( const QFileInfoList &vFilesList); private: // ----- USB void usbCheck (); @@ -462,6 +464,9 @@ void wifiInfoRequest(); void wifiConnectRequest(const DeviceWifiConnectRequestData &vData); + QFileInfoList readFilesList(const QString &vPath, const QStringList &vFilter); + + SAFE_CALL_EX2(doAddWatch, const QString &, bool) }; } Index: sources/gui/GuiGlobals.cpp =================================================================== diff -u -rb80f8f84f21f86ad4c962a2c690447c572498e0b -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision b80f8f84f21f86ad4c962a2c690447c572498e0b) +++ sources/gui/GuiGlobals.cpp (.../GuiGlobals.cpp) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -147,8 +147,9 @@ // ----- #include "VTreatmentAdjustmentEnd.h" +// ----- +#include "VRxProfiles.h" - namespace Gui { MainView *_viewer = nullptr; @@ -169,6 +170,8 @@ qRegisterMetaType ("GuiStringIndexMap" ); qRegisterMetaType ("GuiUint08IndexMap" ); + qRegisterMetaType ("QFileInfoList" ); + // Note that this Models are not used in the QML // but Qt needs them to be registered to be able to use them in between threads queue // by their metadata information. Index: sources/gui/qml/main.qml =================================================================== diff -u -r6a1e770e3d7a8729ab7acdb7b2e96a5b72f8bc9f -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/gui/qml/main.qml (.../main.qml) (revision 6a1e770e3d7a8729ab7acdb7b2e96a5b72f8bc9f) +++ sources/gui/qml/main.qml (.../main.qml) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -138,6 +138,10 @@ // Confirm import VConfirm 0.1 + +// rx profiles +import VRxProfiles 0.1 + // Qml imports import "qrc:/globals" import "qrc:/pages" @@ -154,6 +158,7 @@ Item { id : _mainItem width : Variables.applicationWidth height: Variables.applicationHeight + VRxProfiles { id: vRxProfiles } VConfirm { id: vConfirm } Index: sources/gui/qml/pages/ManagerStack.qml =================================================================== diff -u -raafaecb45feeb45210f7f6ec46ff58eb07420e37 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/gui/qml/pages/ManagerStack.qml (.../ManagerStack.qml) (revision aafaecb45feeb45210f7f6ec46ff58eb07420e37) +++ sources/gui/qml/pages/ManagerStack.qml (.../ManagerStack.qml) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -235,6 +235,7 @@ onVisibleChanged: { if (visible) { _mainMenu.hidden = false + vRxProfiles.doInitRxProfilesList() } else { stackView.initialItem = null Index: sources/storage/RxProfiles.cpp =================================================================== diff -u --- sources/storage/RxProfiles.cpp (revision 0) +++ sources/storage/RxProfiles.cpp (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -0,0 +1,386 @@ +/*! + * + * Copyright (c) 2021-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 RxProfiles.cpp + * \author (last) Behrouz NematiPour + * \date (last) 13-Mar-2024 + * \author (original) Behrouz NematiPour + * \date (original) 04-May-2021 + * + */ +#include "RxProfiles.h" + +// Qt +#include + +// Project +#include "StorageGlobals.h" +#include "FileHandler.h" +#include "ApplicationController.h" +#include "Logger.h" +#include "Settings.h" +#include "DeviceController.h" + +using namespace Storage; + +#define NONE "N/A" +#define FLOAT3 0,'f',3 +#define ADDTITLE(vTITLE) logContent += QString("[%1]\n").arg(vTITLE) +#define ADDALINE(vTEXT ) logContent += QString("%1\n" ).arg(vTEXT ) +#define ADDTOLOG( vINDEX ) index = vINDEX; logContent += title(index) + _sep + value(index) + _sep + unit(index) + "\n"; +#define ADDTOLOG_MT(vINDEX, vVALUE ) \ + index = vINDEX; \ + logContent += title(index) + _sep + \ + ( value(index).trimmed().isEmpty() ? vVALUE : value(index) ) + _sep + \ + unit(index) + "\n"; + +/*! + * \brief RxProfiles::RxProfiles + * The constructor to initial the Treatment Log + * \param parent + */ +RxProfiles::RxProfiles(QObject *parent) : QObject(parent) { + initConnections(); +} + + +/*! \brief Connection Initializer + \details All the class signal/slot connections are defined here. +*/ +void RxProfiles::initConnections() { + + connect(&_exportWatcher , SIGNAL(finished()), + this , SLOT(onExport())); + + connect(&_saveWatcher , SIGNAL(finished()), + this , SLOT(onSave ())); + + connect(&_importWatcher , SIGNAL(finished()), + this , SLOT(onImport())); + +// connect(&_DeviceController , SIGNAL(didReadFilesList(const QFileInfoList &)), +// this , SIGNAL(didRxProfileList(const QFileInfoList &))); + connect(&_DeviceController, &Device::DeviceController::didReadFilesList, [=] (const QFileInfoList & vFileList) { + qDebug()<< "1++MEOW++" << vFileList; + emit didRxProfileList(vFileList); + }); + +} + +void RxProfiles::doInitRxProfiles() +{ + emit didFilesList(rxProfilesPath(), { QString("*.%1").arg(_rxProfiles) }); +} + +QString RxProfiles::rxProfilesPath() +{ + if(_rxProfilesPath.trimmed().isEmpty()){ + _rxProfilesPath = QString("%1%2") + .arg(Storage::Rx_Folder_Base ) // TODO: Adjust to be real base path for RX + .arg(Storage::Rx_Folder_Profiles );} + + LOG_DEBUG(QString("Rx Profile folder has been set to %1" ).arg(_rxProfilesPath )); + + return _rxProfilesPath; +} + + + +void RxProfiles::initModel(const QString &vFile) +{ + QString content; + QFileInfo rxProfile(vFile); + Storage::FileHandler::read(vFile, content); + QStringList lines = content.split('\n'); + for ( const QString &line : lines ) { + QStringList fields = line.split(','); + if ( fields.count() >= Role::eCount ) { + QString key = QString(fields[0].trimmed()); + QString value = fields.mid(1).join(", ").trimmed(); // TODO: Remove comma adjustment when Acid and Bicarb structure change + + //TODO: Add to model structure and add error checking + _values[_rxKeyMap[key]] = QString(value); + } + } + _values[eProfileName] = rxProfile.baseName(); + _values[eFavorite] = false; // TODO: add conf file read here + _values[eLastModifiedDate] = rxProfile.lastModified().date().toString("MM-dd-yyyy"); + +} + + +// ----- Save +/*! + * \brief RxProfiles::doSave + * The save slot to be exposed to the UI to be able to request for save + */ +void RxProfiles::doSave(const QString &fileName) +{ + if (_saveWatcher.isRunning()) return; + isIdle(false); + saveLogConcurrent(fileName); +} + +/*! + * \brief RxProfiles::saveLogConcurrent + * The treatment log save which is using a thread pool to run the save process. + */ +void RxProfiles::saveLogConcurrent(const QString &fileName) +{ + LOG_DEBUG("Save Treatment Log Started"); + QFuture mFuture = QtConcurrent::run(this, &RxProfiles::saveLog, fileName); + _saveWatcher.setFuture(mFuture); +} + +/*! + * \brief RxProfiles::saveLog + * The actual treatment log save function which does the save into the Prescription log + * \return true on successful save and false otherwise. + */ +bool RxProfiles::saveLog(const QString &fileName) +{ +// _lastTxInfo.clear(); + bool ok = (unsigned)_values.count() >= eRxProfilesIndexCount; + if (!ok) return false; + + + QString logContent ; + QString csv = "%1" + _sep ; + QString end = "%1" ; + uint index = 0 ; + + ADDTITLE("Treatment Parameters" ); + ADDTOLOG( eTreatmentDuration ); + ADDTOLOG( eBloodFlowRate ); + ADDTOLOG( eDialysateFlowRate ); + ADDTOLOG( eAcidConcentrateType ); + ADDTOLOG( eBicarbonateConcentrateType ); + ADDTOLOG( eDialysateTemperature ); + ADDTOLOG( eDialyzerType ); + ADDTOLOG( eHeparinConcentration ); + ADDTOLOG( eHeparinBolusVolume ); + ADDTOLOG( eHeparinDispenseRate ); + ADDTOLOG( eHeparinStop ); + + //DEBUG qDebug() << _lastTxInfo.mFileName; + ok = Storage::FileHandler::makeFolder(_rxProfilesPath); + if ( ! ok ) { LOG_DEBUG(QString("Cannot create folder %1").arg(_rxProfilesPath )); return ok; } + ok = Storage::FileHandler::write(fileName, logContent, false); + if ( ! ok ) { LOG_DEBUG(QString("Cannot write to file %1").arg(fileName)); return ok; } + + return ok; +} + +/*! + * \brief RxProfiles::onSave + * The private save slot which is called after the save process is finished saving. + */ +void RxProfiles::onSave() +{ + LOG_DEBUG(QString("Save PresciptionLog Log Ended: %1").arg(_saveWatcher.result())); + isIdle(true); +} + +// ----- Export +/*! + * \brief RxProfiles::doExport + * The export treatment log slot to be exposed to the UI to be able to request for the export. + */ +void RxProfiles::doExport() +{ + if (_exportWatcher.isRunning()) return; + isIdle(false); + exportLogConcurrent(); +} + +/*! + * \brief RxProfiles::exportLogConcurrent + * The treatment log export which is using a thread pool to run the save process. + */ +void RxProfiles::exportLogConcurrent() +{ + LOG_DEBUG("Export Prescription Log Started"); + QFuture mFuture = QtConcurrent::run(this, &RxProfiles::exportLog); + _exportWatcher.setFuture(mFuture); +} + +/*! + * \brief RxProfiles::exportLog + * \details The actual treatment log export function which does the export of the treatment log into the USB drive. + * \return true on successful export. + */ +bool RxProfiles::exportLog() +{ + bool ok = true; + QString status = ""; + QString dstPath = Storage::USB_Mount_Point ; + dstPath += Storage::Rx_Folder_Profiles ; + QString srcFile = "meow.txr" ;//TODO: adjust for filename variable + QString srcFileName = QFileInfo(srcFile).fileName() ; + QString dstFile = dstPath + srcFileName; + // HERE: expose to the UI dialog as the rejection/notification result + if ( ! Storage::FileHandler::makeFolder ( dstPath ) ) { status = QString( "Couldn't create folder on USB drive to export TxLog" ); ok = false; goto lOut; } + if ( ! QFileInfo::exists ( srcFile ) ) { status = QString( "Prescription Log '%1' doesn't exist" ).arg( srcFile ); ok = false; goto lOut; } + if ( QFileInfo::exists ( dstFile ) ) { status = QString( "Prescription Log '%1' already exists" ).arg( dstFile ); ok = false; goto lOut; } + if ( ! QFile::copy (srcFile, dstFile ) ) { status = QString( "Unable to Export RxLog '%1' to '%2'" ).arg( srcFile ).arg( dstFile ); ok = false; goto lOut; } + +lOut: + if ( ! ok ) { + LOG_DEBUG(status); // The log debug order in this block is by design + status = "Unable to export Prescription log '" + srcFileName +"'"; + } + else { + status = "Prescription log '" + srcFileName + "' exported successfully"; + LOG_APPED_UI(status); + } + + emit didNotification(status); + return ok; +} + +/*! + * \brief RxProfiles::onExport + * The private export slot which is called after the export process is finished exporting. + */ +void RxProfiles::onExport() +{ + LOG_DEBUG(QString("Export Prescription Log Ended: %1").arg(_exportWatcher.result())); + isIdle(true); +} + + +// ----- Import +/*! + * \brief RxProfiles::doImport + * The export treatment log slot to be exposed to the UI to be able to request for the import. + */ +void RxProfiles::doImport() +{ + if (_importWatcher.isRunning()) return; + isIdle(false); + importLogConcurrent(); +} + +/*! + * \brief RxProfiles::importLogConcurrent + * The treatment log import which is using a thread pool to run the save process. + */ +void RxProfiles::importLogConcurrent() +{ + LOG_DEBUG("Import Prescription Log Started"); + QFuture mFuture = QtConcurrent::run(this, &RxProfiles::importLog); + _importWatcher.setFuture(mFuture); +} + +/*! + * \brief RxProfiles::importLog + * \details The actual treatment log import function which does the import of the treatment log into the USB drive. + * \return true on successful import. + */ +bool RxProfiles::importLog() +{ + bool ok = true; +// QString srcFile = Storage::USB_Mount_Point + Storage::Log_Folder_Rx; +// QString dstPath = _rxProfilesPath ; +// QString status = ""; + // TODO: implement import + return ok; +} + +/*! + * \brief RxProfiles::onImport + * The private import slot which is called after the import process is finished importing. + */ +void RxProfiles::onImport() +{ + LOG_DEBUG(QString("Import Prescription Log Ended: %1").arg(_exportWatcher.result())); + isIdle(true); +} + +/*! + * \brief RxProfiles::filelimitReached + * The actual function that checks if RX file limit has been reached for Duplicate, Add, and import events + */ +bool RxProfiles::fileLimitReached() +{ QDir RxProfilesDir(_rxProfilesPath); + int fileCount = RxProfilesDir.count(); + if (fileCount != _RxLimit){ return false; } + LOG_DEBUG(QString("Rx Limit has been reached")); + return true; +} + +/*! + * \brief RxProfiles::doDelete + * The actual function that deletes file from memory + * TODO: Ensure model and favorites.conf gets updated + */ +bool RxProfiles::doDelete(const QString &fileName) +{ + bool ok = true; + QString targetFile = _rxProfilesPath + "/" + fileName; + QString status = ""; + if (!QFileInfo::exists(targetFile)) {status = QString("File does not exist."); ok = false;goto lOut;} + + if (QFile::remove(targetFile)) {status = QString("Prescription '%1' deleted successfully.").arg(fileName); ok = true;goto lOut;} + else {status = QString("Failed to delete prescription '%1'.").arg(fileName); ok = false; goto lOut;} + +lOut: + if ( ! ok ) { + LOG_DEBUG(status); // The log debug order in this block is by design + status = "Unable to delete Prescription log '" + fileName +"'"; + } + else { + status = "Prescription log '" + fileName + "' deleted successfully"; + LOG_APPED_UI(status); + } + + emit didNotification(status); + return ok; +} + + +/*! + * \brief RxProfiles::doCreate + * The actual function that initiates creation of Rx profile + * TODO: Ensure model and favorites.conf gets updated + */ +bool RxProfiles::doCreate() +{ + bool ok = true; + if (! fileLimitReached()) { + return ok ; // TODO: create blank model + } + return ok; +} + +/*! + * \brief RxProfiles::doDuplicate + * The actual function that duplicates Rx profile + * TODO: Ensure model and favorites.conf gets updated + */ +bool RxProfiles::doDuplicate(const QString &fileName) +{ + bool ok = true; + if (! fileLimitReached()) { + initModel(fileName); + } + return ok; +} + +/*! + * \brief RxProfiles::doEdit + * The actual function that edit Rx profile + * TODO: Ensure model and favorites.conf gets updated + */ +bool RxProfiles::doEdit(const QString &fileName) +{ + bool ok = true; + if (! fileLimitReached()) { + initModel(fileName); + } + return ok; +} Index: sources/storage/RxProfiles.h =================================================================== diff -u --- sources/storage/RxProfiles.h (revision 0) +++ sources/storage/RxProfiles.h (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -0,0 +1,214 @@ +/*! + * + * Copyright (c) 2021-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 RxProfiles.h + * \author (last) Behrouz NematiPour + * \date (last) 13-Mar-2024 + * \author (original) Behrouz NematiPour + * \date (original) 04-May-2021 + * + */ +#pragma once + +// Qt +#include +#include +#include + +// Project +#include "main.h" // Doxygen : do not remove +#include "Logger.h" + +// define +#define _RxProfilesController Storage::RxProfiles::I() //TODO: Change name to be a controller + +namespace Storage { + + +/*! + * \brief The RxProfiles class + * This class is the controller which converts/saves/exports model to be used in the UI/RxProfiles + */ +class RxProfiles : public QObject +{ + Q_OBJECT + + const int _interval = 1000; // 1s + const int _pendingInterval= 60 ; // 1m + int _pendingCounter = 0 ; // + const int _RxLimit = 10 ; // SRS- + + const QString _sep = ","; + + QString _rxProfilesPath; + const char* _rxProfiles = "rxp"; + + QFutureWatcher _saveWatcher; + QFutureWatcher _exportWatcher; + QFutureWatcher _importWatcher; + + QString _dateFormat = "yyyy/MM/dd" ; + QString _timeFormat = "HH:mm" ; + QString _datetimeFormat = _dateFormat + " " + _timeFormat; + + // TODO : A Unit class needs to be created to have all the unit string and all the required conversions. + QString _unitTextFlowRate = tr("mL/min" ); + QString _unitTextDurationHour = tr("hour" ); + QString _unitTextDurationMin = tr("min" ); + QString _unitTextConcentration = tr("mEq/L" ); + QString _unitTextTemperature = tr("C" ); + QString _unitTextVolume = tr("L" ); + QString _unitTextFluid = tr("mL" ); + QString _unitTextHeparinConcentration = tr("IU/mL" ); + QString _unitTextDispensingRate = tr("mL/hr" ); + QString _unitTextBloodPressure = tr("mmHg" ); + + + + enum Role { + eTitle, + eValue, + eCount + }; + + enum RxProfilesIndex { + eBloodFlowRate , + eDialysateFlowRate , + eTreatmentDuration , + eAcidConcentrateType , + eBicarbonateConcentrateType , + eDialysateTemperature , + eDialyzerType , + eHeparinConcentration , + eHeparinBolusVolume , + eHeparinDispenseRate , + eHeparinStop , + eVitalSigns , + eProfileName , + eFavorite , + eLastModifiedDate , + eRxProfilesIndexCount + }; + + typedef QHash RxKeyMap; + + const QStringList _titles { + tr("Qb" ), // eBloodFlowRate + tr("Qd" ), // eDialysateFlowRate + tr("Dur" ), // eTreatmentDuration + tr("AC" ), // eAcidConcentrateType + tr("Bicarb" ), // eBicarbonateConcentrateType + tr("dT" ), // eDialysateTemperature + tr("Dialyzer" ), // eDialyzerType + tr("UFH Concentration" ), // eHeparinConcentration + tr("UFH Bolus Volume" ), // eHeparinBolusVolume + tr("UFH Dispense Rate" ), // eHeparinDispenseRate + tr("UFH Stop" ), // eHeparinStop + tr("VS" ), // eVitalSigns + }; + + + const QStringList _units { + _unitTextFlowRate , // eBloodFlowRate + _unitTextFlowRate , // eDialysateFlowRate + _unitTextDurationMin , // eTreatmentDuration + "" , // eAcidConcentrateType + "" , // eBicarbonateConcentrateType + _unitTextTemperature , // eDialysateTemperature + "" , // eDialyzerType + _unitTextHeparinConcentration , // eHeparinConcentration + _unitTextFluid , // eHeparinBolusVolume + _unitTextDispensingRate , // eHeparinDispenseRate + _unitTextDurationMin , // eHeparinStop + _unitTextDurationMin , // eVitalSigns + }; + + const RxKeyMap _rxKeyMap { + { "Qb" , eBloodFlowRate }, + { "Qd" , eDialysateFlowRate }, + { "Dur" , eTreatmentDuration }, + { "AC" , eAcidConcentrateType }, + { "Bicarb" , eBicarbonateConcentrateType }, + { "dT" , eDialysateTemperature }, + { "Dialyzer" , eDialyzerType }, + { "UFHCond" , eHeparinConcentration }, + { "UFHVol" , eHeparinBolusVolume }, + { "UFHRate" , eHeparinDispenseRate }, + { "UFHStop" , eHeparinStop }, + { "VS" , eVitalSigns } + }; + + QString string(quint8 vIndex , const QStringList &vStringList) const { + QString str = ""; + if (vIndex < vStringList.count()) { + str = vStringList[vIndex]; + str.remove(_sep); + } + return str; + } + + QString title(quint8 vIndex) const { return string(vIndex, _titles );} + QString value(quint8 vIndex) const { return string(vIndex, _values );} + QString unit (quint8 vIndex) const { return string(vIndex, _units );} + + // Lists + QStringList _values; + + void initConnections(); + + bool saveLog (const QString &fileName); + void saveLogConcurrent (const QString &fileName); + bool exportLog (); + void exportLogConcurrent(); + bool importLog (); + void importLogConcurrent(); + + bool fileLimitReached (); + + + NOTIFIER(isIdle) + SINGLETON(RxProfiles) + +//protected: +// void timerEvent(QTimerEvent *) override; + +public: + void doInitRxProfiles(); + void initModel(const QString &vFile); + void clearModel() { + _values.clear(); + for (int i = 0; i < eRxProfilesIndexCount; i++) _values << ""; + } + + const QStringList titles () const { return _titles ; } + const QStringList units () const { return _units ; } + const QStringList values () const { return _values ; } + + QString rxProfilesPath () ; + +private slots: + void onSave (); + void onExport (); + void onImport (); + +public slots: + bool doCreate (); // check if file limit reached and make a temp file + bool doEdit (const QString &fileName); //Open model to be current + bool doDelete (const QString &fileName); + bool doDuplicate (const QString &fileName); //Check if file limit has been reached and make temp file, then display new screen +// void doFavorite (); + void doSave (const QString &fileName); + void doExport (); + void doImport (); +signals: + void didNotification ( const QString &vNotification ); + + void didFilesList ( const QString &vRxProfileList , const QStringList &vRxProfile ); + void didRxProfileList ( const QFileInfoList &vRxProfileList ); +}; + +} Index: sources/storage/StorageGlobals.cpp =================================================================== diff -u -rad80adbfb49b4a352148f8ce833618a6264f4016 -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision ad80adbfb49b4a352148f8ce833618a6264f4016) +++ sources/storage/StorageGlobals.cpp (.../StorageGlobals.cpp) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -133,6 +133,8 @@ const char *Settings_Key_Confirm = "Confirm" ; const char *Settings_Key_Cancel = "Cancel" ; + const char *Prescriptions_Category_Favorites = "Prescriptions/Favorites" ; + // CloudSync credentials #ifdef BUILD_FOR_TARGET #ifdef LEAHI_DIGI_BOARD @@ -171,6 +173,9 @@ const char *Log_Folder_Service = "service/" ; // Service Log const char *Log_Folder_CloudSync = "cloudsync/log/" ; // CloudSync Log + const char *Rx_Folder_Base = SDCard_Base_Path_Name; // Rx manager base Rep + const char *Rx_Folder_Profiles = "prescriptions/" ; // Rx manager files Rep + // Txr_Max_Allowable_Trtmt_Space_Percent notice that is the folder not the path const char *Txr_Folder_Base = Settings_Path_Name ; // Base Txr Folder Index: sources/storage/StorageGlobals.h =================================================================== diff -u -r2aacff00f09521902b4c0e06eb16c69b2cc31eed -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision 2aacff00f09521902b4c0e06eb16c69b2cc31eed) +++ sources/storage/StorageGlobals.h (.../StorageGlobals.h) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -76,6 +76,8 @@ extern const char *Settings_Key_ListTitle ; extern const char *Settings_Key_Confirm ; extern const char *Settings_Key_Cancel ; + // Prescriptions - Favorites + extern const char *Prescriptions_Category_Favorites ; // CloudSync_Credentials_Path_Name extern const char *CloudSync_Base_Path_Name; @@ -90,6 +92,9 @@ extern const char *Log_Folder_Service; // Service Log extern const char *Log_Folder_CloudSync; // CloudSync Log + extern const char *Rx_Folder_Base; + extern const char *Rx_Folder_Profiles; // Rx Log + extern const char *Txr_Folder_Base; // Base Txr Folder extern const char *Txr_Folder_Treatment; // Treatment Rep Index: sources/view/VRxProfiles.cpp =================================================================== diff -u --- sources/view/VRxProfiles.cpp (revision 0) +++ sources/view/VRxProfiles.cpp (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -0,0 +1,208 @@ +/*! + * + * 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 VRxProfiles.cpp + * \author (last) Behrouz NematiPour + * \date (last) 21-Nov-2023 + * \author (original) Peter Lucia + * \date (original) 22-Sep-2020 + * + */ + +#include "VRxProfiles.h" + +// Qt + +// Project +#include "ApplicationController.h" +#include "RxProfiles.h" +#include "FileHandler.h" +#include "MsgDefs.h" +#include "TreatmentLog.h" + +using namespace Gui; +using namespace View; +using namespace Storage; + +VRxProfiles::VRxProfiles(QObject *parent) : QObject(parent) +{ + ADJUST_VIEW_CONNECTION(AdjustParametersValidationRequestData ) + ACTION_VIEW_CONNECTION(AdjustParametersValidationResponseData ) + ADJUST_VIEW_CONNECTION(AdjustParametersConfirmRequestData ) + connect (this, SIGNAL(didRxProfileList()), &_RxProfilesController, SIGNAL(doInitRxProfiles())); + +// ACTION_METHOD_BRIDGE_CONNECTION(RxProfileList, _RxProfilesController, QFileInfoList) + connect(&_RxProfilesController, SIGNAL(didRxProfileList(QFileInfoList)), this, SLOT(onRxProfileList(QFileInfoList))); + +} + + +/** + * \brief VCreateTreatment::doValidation + * Validates the treatment profile locally, then requests validation of it with FW + */ +void VRxProfiles::doValidation() { + + AdjustParametersValidationRequestData data; + + data.mBloodFlowRate = _bloodFlowRate ; + data.mDialysateFlowRate = _dialysateFlowRate ; + data.mTreatmentDuration = _treatmentDuration ; + data.mSalineBolusVolume = _salineBolusVolume ; + data.mHeparinStopTime = _heparinStopTime ; + data.mHeparinType = _heparinType ; + data.mAcidConcentrate = _acidConcentrate ; + data.mBicarbonateConcentrate = _bicarbonateConcentrate ; + data.mDialyzerType = _dialyzerType ; + data.mBloodPressureMeasureInterval = _bloodPressureMeasureInterval ; + data.mRinsebackFlowRate = _rinsebackFlowRate ; + data.mRinsebackVolume = _rinsebackVolume ; + data.mArterialPressureLimitWindow = _arterialPressureLimitWindow ; + data.mVenousPressureLimitWindow = _venousPressureLimitWindow ; + data.mVenousPressureLimitAsymtrc = _venousPressureLimitAsymtrc ; + data.mTrancembrncPressureLimitWindow = _trancembrncPressureLimitWindow ; + data.mDialysateTemp = _dialysateTemp ; + data.mHeparinDispensingRate = _heparinDispensingRate ; + data.mHeparinBolusVolume = _heparinBolusVolume ; + + emit didAdjustment(data); + parametersConfirmed(false); + parametersValidated(false); +} + +void VRxProfiles::doConfirm() { + AdjustParametersConfirmRequestData confirmTreatmentRequest; + confirmTreatmentRequest.requestedState = AdjustParametersConfirmRequestData::eConfirm; + emit didAdjustment(confirmTreatmentRequest); + parametersConfirmed(true); + +} + +void VRxProfiles::doCancel() +{ + AdjustParametersConfirmRequestData confirmTreatmentRequest; + confirmTreatmentRequest.requestedState = AdjustParametersConfirmRequestData::eCancel; + emit didAdjustment(confirmTreatmentRequest); + parametersValidated(false); +} + +void VRxProfiles::doInitRxProfilesList() +{ + emit didRxProfileList(); +} + +QString VRxProfiles::enumToString(GuiRequestReasons vEnum) +{ + return Gui::enumString(vEnum, "[%1] Unknown Rejection Reason"); +} + +void VRxProfiles::onRxProfileList(const QFileInfoList &vRxProfiles) +{ + qDebug() << "-meow-" << vRxProfiles; +} + +/** + * \brief VCreateTreatment::onFWValidationResponse + * Slot to handle a validation response from FW + * \param actionType The action type must be a create treatment response + * \param messageData The message data must contain the reject reason codes for all parameters + * \returns True if FW OK's treatment parameters, false otherwise + */ +void VRxProfiles::onActionReceive(const AdjustParametersValidationResponseData &data) { + if (! data.mAccepted) { + bloodFlowRateRejectReason (data.mBloodFlowRateRejectReason ); + dialysateFlowRateRejectReason (data.mDialysateFlowRateRejectReason ); + treatmentDurationRejectReason (data.mTreatmentDurationRejectReason ); + heparinDispensingRateRejectReason (data.mHeparinDispensingRateRejectReason ); + heparinBolusVolumeRejectReason (data.mHeparinBolusVolumeRejectReason ); + heparinStopTimeRejectReason (data.mHeparinStopTimeRejectReason ); + salineBolusVolumeRejectReason (data.mSalineBolusVolumeRejectReason ); + heparinTypeRejectReason (data.mHeparinTypeRejectReason ); + acidConcentrateRejectReason (data.mAcidConcentrateRejectReason ); + bicarbonateConcentrateRejectReason (data.mBicarbonateConcentrateRejectReason ); + dialyzerTypeRejectReason (data.mDialyzerTypeRejectReason ); + dialysateTempRejectReason (data.mDialysateTempRejectReason ); + arterialPressureLimitWindowRejectReason (data.mArterialPressureLimitWindowRejectReason ); + venousPressureLimitWindowRejectReason (data.mVenousPressureLimitWindowRejectReason ); + venousPressureLimitAsymtrcRejectReason (data.mVenousPressureLimitAsymtrcRejectReason ); + bloodPressureMeasureIntervalRejectReason (data.mBloodPressureMeasureIntervalRejectReason ); + rinsebackFlowRateRejectReason (data.mRinsebackFlowRateRejectReason ); + rinsebackVolumeRejectReason (data.mRinsebackVolumeRejectReason ); + trancembrncPressureLimitWindowRejectReason (data.mTrancembrncPressureLimitWindowRejectReason ); + + emit didValidationFail(); + parametersValidated(false); + } + else { + emit didValidationPass(); + parametersValidated(true); + } +} + +/*! + * \brief VCreateTreatment::doGetPrescriptionParameterValues + * \details Gets a list of the prescription parameter values + * \return (QStringList) The list of parameter values with units + */ +QStringList VRxProfiles::doGetPrescriptionParameterValues() { + return QStringList() << QString("%1 mL/min" ).arg(_bloodFlowRate ) + << QString("%1 mL/min" ).arg(_dialysateFlowRate ) + << QString("%1 min" ).arg(_treatmentDuration ) + << ( _heparinDispensingRate ? QString("%1 mL/hr" ).arg(_heparinDispensingRate) : tr("OFF") ) + << ( _heparinBolusVolume ? QString("%1 mL" ).arg(_heparinBolusVolume ) : tr("OFF") ) + << ( _heparinDispensingRate ? QString("%1 min" ).arg(_heparinStopTime ) : tr("OFF") ) + << QString("%1 mL" ).arg(_salineBolusVolume ) ; +} + +/*! + * \brief VCreateTreatment::doGetOperatingParameterValues + * \details Gets the operating parameter values + * \return (QStringList) The list of operating parameter values with units where applicable + */ +QStringList VRxProfiles::doGetOperatingParameterValues() +{ + QString mHeparinType ; + QString mAcidConcentrate ; + QString mBicarbonateConcentrate ; + QString mDialyzerType ; + + QStringList mHeparinTypeOptions ; + QStringList mAcidConcentrateOptions ; + QStringList mBicarbonateConcentrateOptions ; + QStringList mDialyzerTypeOptions ; + + QString mCategory = Storage::Settings_Category_DataList; + + if ( heparinTypeSet () ) { // this is managed on UI with heparinTypeSet() // && ( _heparinDispensingRate || _heparinBolusVolume ) + mHeparinTypeOptions = _Settings.keys(mCategory, "Heparin Type Options" ); + if ((int) _heparinType < mHeparinTypeOptions .length()) mHeparinType = mHeparinTypeOptions .at( _heparinType ); + } else { mHeparinType = tr("NONE"); } + + if ( acidConcentrateSet () ) { + mAcidConcentrateOptions = _Settings.keys(mCategory, "Acid Concentrate Options" ); + if ((int) _acidConcentrate < mAcidConcentrateOptions .length()) mAcidConcentrate = mAcidConcentrateOptions .at( _acidConcentrate ); + } else { mAcidConcentrate = tr("NONE"); } + + if ( bicarbonateConcentrateSet () ) { + mBicarbonateConcentrateOptions = _Settings.keys(mCategory, "Bicarbonate Concentrate Options" ); + if ((int) _bicarbonateConcentrate < mBicarbonateConcentrateOptions .length()) mBicarbonateConcentrate = mBicarbonateConcentrateOptions .at( _bicarbonateConcentrate ); + } else { mBicarbonateConcentrate = tr("NONE"); } + + if ( dialyzerTypeSet () ) { + mDialyzerTypeOptions = _Settings.keys(mCategory, "Dialyzer Type Options" ); + if ((int) _dialyzerType < mDialyzerTypeOptions .length()) mDialyzerType = mDialyzerTypeOptions .at( _dialyzerType ); + } else { mDialyzerType = tr("NONE"); } + + + return QStringList() << mHeparinType + << mAcidConcentrate + << mBicarbonateConcentrate + << mDialyzerType + << QString("%0 °C" ).arg(_dialysateTemp ) + << ( _bloodPressureMeasureInterval ? QString("%0 min").arg(_bloodPressureMeasureInterval) : tr("OFF") ) + ; +} Index: sources/view/VRxProfiles.h =================================================================== diff -u --- sources/view/VRxProfiles.h (revision 0) +++ sources/view/VRxProfiles.h (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -0,0 +1,126 @@ +/*! + * + * 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 VRxProfiles.h + * \author (last) Behrouz NematiPour + * \date (last) 24-Jan-2024 + * \author (original) Peter Lucia + * \date (original) 22-Sep-2020 + * + */ +#pragma once + +// Qt +#include +#include +#include +#include +#include +#include + +// Project +#include "MPreTreatmentAdjustParametersValidationResponse.h" + +#include "GuiController.h" +#include "StorageGlobals.h" +#include "VView.h" + +// forward declaration +class tst_views; + +using namespace Storage; +using namespace Gui; +using namespace Model; + +namespace View { + +class VRxProfiles : public QObject +{ + Q_OBJECT + + // friends + friend class::tst_views; + + PROPERTY(bool , heparinDispensingRateOff , true) + PROPERTY(bool , heparinBolusVolumeOff , true) + + VALUESET(quint32 , bloodFlowRate , 0) + VALUESET(quint32 , dialysateFlowRate , 0) + VALUESET(quint32 , treatmentDuration , 0) + VALUESET(quint32 , salineBolusVolume , 0) + VALUESET(quint32 , heparinStopTime , 0) + VALUESET(quint32 , heparinType , 0) + VALUESET(quint32 , acidConcentrate , 0) + VALUESET(quint32 , bicarbonateConcentrate , 0) + VALUESET(quint32 , dialyzerType , 0) + VALUESET(quint32 , bloodPressureMeasureInterval , 0) + VALUESET(quint32 , rinsebackFlowRate , 0) + VALUESET(quint32 , rinsebackVolume , 0) + VALUESET(qint32 , arterialPressureLimitWindow , 0) + VALUESET(qint32 , venousPressureLimitWindow , 0) + VALUESET(qint32 , venousPressureLimitAsymtrc , 0) + VALUESET(qint32 , trancembrncPressureLimitWindow , 0) + VALUESET(float , dialysateTemp , 0) + VALUESET(float , heparinDispensingRate , 0) + VALUESET(float , heparinBolusVolume , 0) + + VALUESET(bool , parametersValidated , 0) + VALUESET(bool , parametersConfirmed , 0) + + + TRIGGER(quint32 , bloodFlowRateRejectReason , 0) + TRIGGER(quint32 , dialysateFlowRateRejectReason , 0) + TRIGGER(quint32 , treatmentDurationRejectReason , 0) + TRIGGER(quint32 , heparinDispensingRateRejectReason , 0) + TRIGGER(quint32 , heparinBolusVolumeRejectReason , 0) + TRIGGER(quint32 , heparinStopTimeRejectReason , 0) + TRIGGER(quint32 , salineBolusVolumeRejectReason , 0) + TRIGGER(quint32 , heparinTypeRejectReason , 0) + TRIGGER(quint32 , acidConcentrateRejectReason , 0) + TRIGGER(quint32 , bicarbonateConcentrateRejectReason , 0) + TRIGGER(quint32 , dialyzerTypeRejectReason , 0) + TRIGGER(quint32 , dialysateTempRejectReason , 0) + TRIGGER(quint32 , arterialPressureLimitWindowRejectReason , 0) + TRIGGER(quint32 , venousPressureLimitWindowRejectReason , 0) + TRIGGER(quint32 , venousPressureLimitAsymtrcRejectReason , 0) + TRIGGER(quint32 , bloodPressureMeasureIntervalRejectReason , 0) + TRIGGER(quint32 , rinsebackFlowRateRejectReason , 0) + TRIGGER(quint32 , rinsebackVolumeRejectReason , 0) + TRIGGER(quint32 , trancembrncPressureLimitWindowRejectReason , 0) + + + VIEW_DEC_CLASS(VRxProfiles) + VIEW_DEC_SLOT(AdjustParametersValidationResponseData) + +private: + QString enumToString(GuiRequestReasons vEnum); + + +signals: + void didAdjustment(const AdjustParametersValidationRequestData &data); + void didAdjustment(const AdjustParametersConfirmRequestData &data); + + void didValidationFail(); + void didValidationPass(); + + void didRxProfileList(); + +private slots: + void onRxProfileList(const QFileInfoList &vRxProfiles); + + +public slots: + void doValidation (); + void doConfirm (); + void doCancel (); + void doInitRxProfilesList (); + + + QStringList doGetPrescriptionParameterValues(); + QStringList doGetOperatingParameterValues(); +}; +} Index: sources/view/VView.h =================================================================== diff -u -rb80f8f84f21f86ad4c962a2c690447c572498e0b -r8f4dcc80fadbccfc152b28ea09466a2ab2ee7333 --- sources/view/VView.h (.../VView.h) (revision b80f8f84f21f86ad4c962a2c690447c572498e0b) +++ sources/view/VView.h (.../VView.h) (revision 8f4dcc80fadbccfc152b28ea09466a2ab2ee7333) @@ -239,5 +239,7 @@ /* Disinfection */ \ REGISTER_TYPE( VDisinfectAdjustDisinfect ) \ REGISTER_TYPE( VAdjustDGCleaningUsage ) \ + /* rx manager */ \ + REGISTER_TYPE( VRxProfiles ) \ //--------------------------------------------------------------------------------//