Index: sources/cloudsync/CloudSyncController.cpp =================================================================== diff -u -r2b671abf713c1e0cdaab0ebafdbf7087aebab176 -r6c6f1f5d466badd9b4fd67be7c907234c342b2a2 --- sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision 2b671abf713c1e0cdaab0ebafdbf7087aebab176) +++ sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision 6c6f1f5d466badd9b4fd67be7c907234c342b2a2) @@ -1,13 +1,13 @@ /*! * - * Copyright (c) 2021-2022 Diality Inc. - All Rights Reserved. + * Copyright (c) 2021-2023 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 CloudSyncController.cpp * \author (last) Behrouz NematiPour - * \date (last) 13-Oct-2022 + * \date (last) 07-Apr-2023 * \author (original) Behrouz NematiPour * \date (original) 14-Oct-2021 * @@ -23,6 +23,7 @@ #include "DeviceController.h" #include "FileHandler.h" #include "TreatmentLog.h" +#include "crc.h" SINGLETON_DISABLE(CloudSyncController) @@ -80,6 +81,15 @@ // disabled coco end /*! + * \brief CloudSyncController::doRegister + * \details Send the register device command. + */ +void CloudSyncController::doRegister() +{ + sendDeviceRegister(); +} + +/*! * \brief CloudSyncController::initConnections * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. @@ -90,12 +100,18 @@ SINGLETON_DISABLE_CONNECT(didPOSTCloudSync) } + connect(&_ApplicationController , SIGNAL(didPOSTCloudSync(bool)), + this , SLOT( onPOSTCloudSync(bool))); + + connect(&_DeviceController , SIGNAL(didCryptSetupMount(bool)), + this , SLOT( onCryptSetupMount(bool))); + connect(&_DeviceController , SIGNAL(didWatchFileChange (const QString &)), this , SLOT( onWatchFileChange (const QString &))); connect(&_MessageDispatcher , SIGNAL(didActionReceive (GuiActionType , const QVariantList &)), this , SLOT( onActionReceive (GuiActionType , const QVariantList &))); - connect(&_TreatmentLog , SIGNAL(didTreatmentLogSave(const QString &, const QString &, const QString &)), - this , SLOT( onTreatmentLogSave(const QString &, const QString &, const QString &))); + connect(&_TreatmentLog , SIGNAL(didTxPending (const QString &)), + this , SLOT( onTxPending (const QString &))); connect(this , SIGNAL(didInitComplete ()), this , SLOT( onInitComplete ()),Qt::QueuedConnection); // it has to be queued connection, don't remove it. } @@ -164,14 +180,36 @@ } /*! - * \brief CloudSyncController::onInitComplete - * \details The slot to be called when the CloudSync initialization is complete, to call the testDeviceRegister. + * \brief CloudSyncController::onInitComplete + * \details The slot to be called when the CloudSync initialization is complete + * Does nothing for now */ void CloudSyncController::onInitComplete() { - testDeviceRegister(); + // Nothing for now } /*! + * \brief CloudSyncController::onPOSTCloudSync + * \details This the handler for the ApplicationController::didPOSTCloudSync(bool) + * \param vPass - will be true if the POST test of CloudSync passed and it is running. + */ +void CloudSyncController::onPOSTCloudSync(bool vPass) +{ + _postPass = vPass; +} + +/*! + * \brief CloudSyncController::onCryptSetupMount + * \details This the handler for the DeviceController::onCryptSetupMount() + */ +void CloudSyncController::onCryptSetupMount(bool vPass) +{ + if ( vPass ) { + testReady(); + } +} + +/*! * \brief CloudSyncController::onWatchFileChange * \details This slot will be called when the Device Controller identifies any changes in the watched files. * \param vFile - watched file @@ -232,7 +270,7 @@ Message message; QStringList items; - quint8 id; + quint32 id; if ( vContent.isEmpty() ) { error = eError_OutFileEmpty; ok = false; goto lErr; } @@ -322,13 +360,15 @@ case eError_LogFolder : text = tr( "CS The log folder cannot be created." ) ; break; case eError_LogFileInp : text = tr( "CS Error writing to the input file." ) ; break; case eError_CredentialFile : text = tr( "CS The credentials file does not exist." ) ; break; - case eError_CredentialMake : text = tr( "CS The credentials folder make failed." ) ; break; - case eError_CredentialCopy : text = tr( "CS The credentials file copy failed." ) ; break; - case eError_CredentialRemove: text = tr( "CS The credentials file remove failed." ) ; break; + case eError_CredentialPath : text = tr( "CS The credentials folder is incorrect." ) ; break; + case eError_CredentialCount : text = tr( "CS No credential file name provided." ) ; break; case eError_CredentialEmpty : text = tr( "CS The credentials folder is empty." ) ; break; case eError_TxCodeNoParam : text = tr( "CS No Treatment Code provided." ) ; break; case eError_TxCodeEmpty : text = tr( "CS The provided Treatment Code is empty." ) ; break; case eError_NotRegistered : text = tr( "CS Not Sent, Device not registered." ) ; break; + + case eError_Registration : text = tr( "CS The device registration failed." ) ; break; + case eError_TxReport : text = tr( "CS The treatment report delivery failed." ) ; break; } return text; } @@ -364,25 +404,44 @@ case eError_LogFolder : ; break; case eError_LogFileInp : ; break; case eError_CredentialFile : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; - case eError_CredentialMake : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; - case eError_CredentialCopy : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; - case eError_CredentialRemove: info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_CredentialPath : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_CredentialCount : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_CredentialEmpty : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_TxCodeNoParam : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_TxCodeEmpty : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_NotRegistered : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_Registration : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_TxReport : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; } return info; } /*! + * \brief CloudSyncController::errorOut + * \details the function to send out the error message to the cloudsync inp buffer. + * \param vErrorID - the error id from CloudSyncController::Errors_Enum in range of 950 to 999 + * \param vInfoItems - + */ +void CloudSyncController::errorOut(CloudSyncController::Errors_Enum vErrorID, const QVariantList &vInfoItems) { + QString msg = QString::number(UI2CS(eMessageID_Error)); + QString len = QString::number(1 + vInfoItems.count()); + QString prm = QString::number(vErrorID); + QStringList buf = { msg, len, prm }; + for (const auto &item: vInfoItems) { + buf += item.toString(); + } + sendUIBuff(buf); +} + +/*! * \brief CloudSyncController::toLog * \details logs the error in debug mode (service log) * \param vErrorID - error id * \param vInfoItems - extra information */ void CloudSyncController::toLog(CloudSyncController::Errors_Enum vErrorID, const QVariantList &vInfoItems) { + errorOut(vErrorID, vInfoItems); LOG_DEBUG(toText(vErrorID) + " " + toInfo(vErrorID, vInfoItems)); } @@ -436,16 +495,19 @@ * \param vMessage - The message id with destination added. 2k, 1K * \return the formatted buffer */ -QString CloudSyncController::makeUIBuff(const qint32 vMessageID) +QStringList CloudSyncController::makeUIBuff(const qint32 vMessageID, const QStringList &vPrm) { - QString msg = QString::number(vMessageID); - QString len = QString::number(_uiHistory[ vMessageID ].count()); - QString prm = _uiHistory[ vMessageID ].join(_separator); - return QString("%1,%2,%3") - .arg( msg ) - .arg( len ) - .arg( prm ) - ; + int count = vPrm.count(); + QStringList prm = count ? vPrm : _uiHistory[ vMessageID ]; + QString msg = QString::number(vMessageID); + QString len = QString::number(prm.count()); + + QStringList dataList; + dataList += msg; + dataList += len; + dataList += prm; + + return dataList; } /*! @@ -477,11 +539,17 @@ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // writing the message into the buffer. LOG_APPED_UI( vInpBuff ); + if(!QFile::exists(_date_inp_File)){ + Storage::FileHandler::makeFolder(_location); + } if ( ! Storage::FileHandler::write(_date_inp_File, vInpBuff + "\n") ) { error = eError_LogFileInp; args = { _date_inp_File }; ok = false; goto lErr; } return ok; lErr: - toLog(error, args); + // don't send the error back to the CS + // it calls this same function if the error is in this function + // and creates a loop + LOG_DEBUG(toText(error) + " " + toInfo(error, args)); return ok; } @@ -492,15 +560,16 @@ * \return true on successful writing to the file buffer. * \sa _inp_File */ -bool CloudSyncController::sendUIBuff(const QString &vData) +bool CloudSyncController::sendUIBuff(const QStringList &vDataList) { bool ok = true; QString inpBuff = "%1,%2,%3,%4"; + inpBuff = inpBuff .arg( _secSinceEpoch ) .arg( Types::safeIncrement(_seq)) - .arg( generateCRC() ) - .arg( vData ); + .arg( generateCRC(_secSinceEpoch, _seq, vDataList)) + .arg( vDataList.join(_separator)); ok = writeInpFile(inpBuff); return ok; @@ -608,19 +677,19 @@ } } -/*! - * \brief CloudSyncController::onTreatmentLogSave - * \details The slot being called when the TreatmentLogController notifies the CloudSyncController, about the treatment log being successfully saved. - * \param vDeviceID - Device ID - * \param vPatientID - Patient ID - * \param vFileName - The complete Treatment log path and file name. - */ -void CloudSyncController::onTreatmentLogSave(const QString &/*vDeviceID*/, const QString &/*vPatientID*/, const QString &vFileName) +void CloudSyncController::onTxPending(const QString &vFileName) { - QVariantList data { /*vDeviceID, vPatientID,*/ vFileName }; - sendCredentialsSend(); // TODO: This has to be removed later, it is a workaround for Cloud issue, during our talk with KBM. - saveUIHistory(eMessageID_TxReport, data ); - sendUIHistory(eMessageID_TxReport ); + bool ok = true; Q_UNUSED(ok); + QVariantList args ; + Errors_Enum error = eError_Unknown; + qint32 messageID = UI2CS(static_cast( eMessageID_TxReport )); + + if ( ! isRegistered() ) { error = eError_NotRegistered ; args = { vFileName }; ok = false; goto lErr; } + sendUIBuff(makeUIBuff( messageID , { vFileName } )); + + return; +lErr: + toLog(error, args); } /*! @@ -638,14 +707,15 @@ case eMessageID_DeviceInfo : ok = sendDeviceInfo ( ); break; case eMessageID_CredentialsSave : ok = sendCredentialsSave( vMessage ); break; - case eMessageID_CredentialsSend : ok = sendCredentialsSend( ); break; case eMessageID_ResetFactory : ok = sendResetFactory ( ); break; case eMessageID_DeviceState : ok = sendDeviceState ( ); break; case eMessageID_TxCodeDisplay : ok = sendTxCodeDisplay ( vMessage ); break; case eMessageID_TxReport : /* No Req/Rsp, it is event based */ break; // This message doesn't have the response since there is no request. UI will send when the data ready by HD. + case eMessageID_Error : ok = errorHandler ( vMessage ); break; + } return ok; } @@ -670,7 +740,6 @@ bool CloudSyncController::sendDeviceState() { bool ok = false; - sendCredentialsSend(); // TODO: This has to be removed later, it is a workaround for Cloud issue, during our talk with KBM. ok = sendUIHistory(eMessageID_DeviceState); return ok; } @@ -702,69 +771,38 @@ // if ( ! ok ) { } /* Not defined */ qint32 messageID = UI2CS(eMessageID_ResetFactory); // TODO : Store registration success in here - ok = sendUIBuff(QString("%1,1,%2").arg( messageID ).arg( ok ? eSucceed : eFailed )); + ok = sendUIBuff({ QString("%1").arg( messageID ) ,"1", QString("%1").arg( ok ? eSucceed : eFailed ) }); return ok; } /*! * \brief CloudSyncController::sendCredentialsSave - * \details saves the credentials files which their location have been sent. + * \details validates the credentials files which their sent location. * \param vMessage - the message received from CloudSync - * \return true on successfully saving the files. + * \return true on successfully validate the files. */ bool CloudSyncController::sendCredentialsSave( const Message &vMessage) { bool ok = true; QString destination = QString(Storage::CloudSync_Base_Path_Name) + Storage::CloudSync_Credentials_Folder_Name; - // create the destination folder if does not exist. - if ( ! Storage::FileHandler::makeFolder( destination ) ) { toLog(eError_CredentialMake , { }); ok = false; goto lOut; } - // file existence has been separated from copy remove to avoid partial copy. + if ( ! vMessage.params.count() ) { toLog(eError_CredentialCount , { }); ok = false; goto lOut; } for ( auto fileName : vMessage.params ) { - if ( ! QFileInfo::exists(fileName) ) { toLog(eError_CredentialFile , {fileName }); ok = false; goto lOut; } + QFileInfo fileinfo(fileName); + if ( ! fileinfo.exists() ) { toLog(eError_CredentialFile , {fileName }); ok = false; goto lOut; } + if ( fileinfo.absolutePath() != destination ) { toLog(eError_CredentialPath , {fileName }); ok = false; goto lOut; } } + // no need to call for isRegistered() function, we are testing for the count, location, and existence. - if ( ok ) { - for ( auto sourceFile : vMessage.params ) { - QString fileName = QFileInfo(sourceFile).fileName(); - // NOTE: If a file with the name newName already exists, copy() returns false (i.e., QFile will not overwrite it). - // For the current scenario it is ideal, since that folder will turn RO after the successful registration and copying the files and reboot. - if ( ! QFile::copy (sourceFile, destination + fileName) ) { toLog(eError_CredentialCopy , {fileName }); ok = false; goto lOut; } - } - // if all the copies are successful then remove them. - for ( auto sourceFile : vMessage.params ) { - QString fileName = QFileInfo(sourceFile).fileName(); - if ( ! QFile::remove(sourceFile ) ) { toLog(eError_CredentialRemove , {fileName }); ok = false; goto lOut; } - } - } - lOut: if ( ok ) sendCredentialsResponse(); + emit didRegisterDone(ok); + testReady(); return ok; } /*! - * \brief CloudSyncController::sendSaveCredentials - * \details saves the credentials files which their location have been sent. - * \param vMessage - the message received from CloudSync - * \return true on successfully saving the files. - */ -bool CloudSyncController::sendCredentialsSend() -{ - bool ok = true; - qint32 messageID = UI2CS(eMessageID_CredentialsSend); - QString source = QString(Storage::CloudSync_Base_Path_Name) + Storage::CloudSync_Credentials_Folder_Name; - QString destination = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + Storage::CloudSync_Credentials_Folder_Name; - - if ( Storage::FileHandler::copyFolder(source, destination) != 0 ) { toLog(eError_CredentialCopy , {destination }); ok = false; goto lOut; } - ok = sendUIBuff(QString("%1,1,%2").arg( messageID ).arg(destination)); - -lOut: - return ok; -} - -/*! * \brief CloudSyncController::sendCredentialsResponse * \return send the confirmation of the credential save on the device from UI to CS to let CS know when to delete them. */ @@ -773,7 +811,7 @@ enum { eSucceed, eFailed }; bool ok = false; qint32 messageID = UI2CS(eMessageID_CredentialsSave); - ok = sendUIBuff(QString("%1,1,%2").arg( messageID ).arg( ok ? eSucceed : eFailed )); + ok = sendUIBuff({ QString("%1").arg( messageID ) ,"1", QString("%1").arg( ok ? eSucceed : eFailed ) }); return ok; } @@ -795,13 +833,25 @@ if ( mTxCode.isEmpty() ) { toLog(eError_TxCodeEmpty , {}); ok = false; goto lOut; } emit didTxCodeReceive ( mTxCode ); - qDebug() << " ---------- " << mTxCode; + //DEBUG qDebug() << " ---------- " << mTxCode; lOut: return ok; } /*! + * \brief CloudSyncController::errorHandler + * \details nothing for now. + * \param vMessage - the received message + * \return always true for now. + */ +bool CloudSyncController::errorHandler(const Message &vMessage) +{ + qDebug() << "ERR: " << vMessage.params; + return true; +} + +/*! * \brief CloudSyncController::sendDeviceRegister * \details sends the device registration request * \return true on successful send. @@ -814,34 +864,28 @@ } /*! - * \brief CloudSyncController::testDeviceRegister - * \details checks if the device needs registration. - * \return true if registration required. - */ -bool CloudSyncController::testDeviceRegister() -{ - toLog(eError_CredentialEmpty,{}); // It is intentional that the vault folder path has not been sent to the log. - if( ! isRegistered() ) { - sendDeviceRegister(); - } - - return true; // for now always true. -} - -/*! * \brief CloudSyncController::isRegistered * \details checks if the device is registered by looking for existing credentials. * \return true if device has been registered. */ bool CloudSyncController::isRegistered() { QString source = QString(Storage::CloudSync_Base_Path_Name) + Storage::CloudSync_Credentials_Folder_Name; - qDebug() << source; QFileInfoList fileInfos = QDir(source).entryInfoList(QDir::NoDotAndDotDot|QDir::Files); return !fileInfos.isEmpty(); } /*! + * \brief CloudSyncController::testReady + * \details Checks if the CloudSync POST was passed and the device registration is complete. + */ +void CloudSyncController::testReady() +{ + bool ok = _postPass && isRegistered(); + emit didCloudSyncStatus( ok ); +} + +/*! * \brief CloudSyncController::saveDeviceInfo * \details keeps the received device information and set a flag to wait for the next message. * \param vAction - the action enum which identifies information source of HD or DG. @@ -892,7 +936,6 @@ saveUIHistory(eMessageID_DeviceInfo , { _deviceInfoHD, _deviceInfoDG, _deviceInfoUI } ); saveUIHistory(eMessageID_DeviceRegister , { _deviceInfoHD, _deviceInfoDG, _deviceInfoUI } ); // the device registration request format is the same as Device info with different message id. - testDeviceRegister (); // it is expected on the CloudSync App to get the sendDeviceInfo (); // this one may need to be removed and only will be sent on the request stopDeviceInfoWait(); @@ -943,11 +986,38 @@ /*! * \brief CloudSyncController::initDeviceInfoWait - * \details if currently not not in wait, then set the wait to the secs of wait. + * \details if currently not in wait, then set the wait to the secs of wait. */ void CloudSyncController::initDeviceInfoWait() { if ( _deviceInfoStop ) { // if timer is stopped _deviceInfoWait = _deviceInfoSecs; // enable the timer. _deviceInfoStop = false; } } + +/*! + * \brief CloudSyncController::generateCRC + * \details The crc algorithm is to get + * all the message sections including, epoch, seq, and the parameters, + * without any separator + * concatenate them together like a one long string + * and run the crc on them + * \example 1674193951,4,,1001,3,HD1234567890123,DG1234567890123,DEN-14517-UI-BN-S88.01192111.1 + * str: 1674193951410013HD1234567890123DG1234567890123DEN-14517-UI-BN-S88.01192111.1 + * out: 145 + * \note the parameters values will keep untouched, meaning, if a parameter has space it will not be removed. + * \param vSecSinceEpoch + * \param vSeq - the message sequence + * \param vData - the message payload in list of strings + * \return - quint8 crc code + */ +quint8 CloudSyncController::generateCRC(quint64 vSecSinceEpoch, quint64 vSeq, const QStringList &vDataList) +{ + quint8 crc = 0; + QByteArray buf; + buf += QByteArray::number(vSecSinceEpoch); + buf += QByteArray::number(vSeq); + buf += vDataList.join("").toLatin1(); + crc = crc8(buf); + return crc; +}