/*! * * 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; }