/*! * * Copyright (c) 2021-2022 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) 18-Apr-2022 * \author (original) Behrouz NematiPour * \date (original) 14-Oct-2021 * */ #include "CloudSyncController.h" // Qt #include // Project #include "MainTimer.h" #include "MessageDispatcher.h" #include "ApplicationController.h" #include "GuiController.h" #include "DeviceController.h" #include "FileHandler.h" #include "crc.h" #include "TreatmentLog.h" SINGLETON_DISABLE(CloudSyncController) /*! * \brief CloudSyncController::CloudSyncController * \details Constructor * \param parent - QObject parent owner object. * Qt handles the children destruction by their parent objects life-cycle. */ CloudSyncController::CloudSyncController(QObject *parent) : QObject(parent) { testWatchBuffDate(); startTimer(_interval); } /*! * \brief CloudSyncController initializer */ bool CloudSyncController::init() { if ( _init ) return false; _init = true; initConnections(); LOG_DEBUG(tr("%1 Initialized").arg(metaObject()->className())); return true; } /*! * \brief CloudSyncController::init * \details Initialized the Class by calling the init() method first * And initializes the thread vThread by calling initThread * on success init(). * \param vThread - the thread * \return returns the return value of the init() method */ bool CloudSyncController::init(QThread &vThread) { if ( ! init() ) return false; initThread(vThread); return true; } /*! * \brief CloudSyncController::quit * \details quits the class * Calls quitThread */ void CloudSyncController::quit() { // disabled coco begin validated: CloudSync termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. quitThread(); // validated } // disabled coco end /*! * \brief CloudSyncController::initConnections * \details Initializes the required signal/slot connection between this class and other objects * to be able to communicate. */ void CloudSyncController::initConnections() { if ( ! gDisableCloudSyncFailStop ) { SINGLETON_DISABLE_CONNECT(didPOSTCloudSync) } 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(this , SIGNAL(didInitComplete ()), this , SLOT( onInitComplete ()),Qt::QueuedConnection); // it has to be queued connection, don't remove it. } /*! * \brief CloudSyncController::initThread * \details Moves this object into the thread vThread. * And checks that this method is called from main thread. * Also connects quitThread to CloudSync aboutToQuit. * \param vThread - the thread */ void CloudSyncController::initThread(QThread &vThread) { // runs in main thread Q_ASSERT_X(QThread::currentThread() == qApp->thread(), __func__, "The Class initialization must be done in Main Thread" ); _thread = &vThread; _thread->setObjectName(QString("%1_Thread").arg(metaObject()->className())); connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(quit())); _thread->start(); moveToThread(_thread); } /*! * \brief CloudSyncController::quitThread * \details Moves this object to main thread to be handled by QCloudSync * And to be destroyed there. */ void CloudSyncController::quitThread() { // disabled coco begin validated: CloudSync termination is not correctly done in coco!!! // it has been tested and works perfectly fine in normal run. if ( ! _thread ) return; // runs in thread moveToThread(qApp->thread()); // validated } // disabled coco end /*! * \brief CloudSyncController::timerEvent * \details The timer event handler which currently is triggered on each second to check for the date change, * Which caused the watched file change and needs to updated the watched list. * The check-in (watch dog) also needs to be here. */ void CloudSyncController::timerEvent(QTimerEvent *) { // TODO: touch the inp file to as a check-in for CloudSync to know we are up // a simple touch or a check-in message? testWatchBuffDate(); testDeviceInfoWait(); } /*! * \brief CloudSyncController::event * \details The override method of QObject event to handle the ThreadChange. * \param vEvent - the QObject event * \return true if the event e was recognized and processed */ bool CloudSyncController::event(QEvent *vEvent) { if (vEvent->type() == QEvent::ThreadChange) { emit didInitComplete(); } // Make sure the rest of events are handled return QObject::event(vEvent); } /*! * \brief CloudSyncController::onInitComplete * \details The slot to be called when the CloudSync initialization is complete, to call the testDeviceRegister. */ void CloudSyncController::onInitComplete() { testDeviceRegister(); } /*! * \brief CloudSyncController::onWatchFileChange * \details This slot will be called when the Device Controller identifies any changes in the watched files. * \param vFile - watched file * \note The DeviceController will emit the signal on any watched file update, it's up to the CloudSyncController to filter the result. */ void CloudSyncController::onWatchFileChange(const QString &vFile) { // TODO: It may improve the performance of the code to make it more specific. // Meaning, Device controller has only one file watcher and it will call all the slots of all the classes watching for a file change. // then ignore what is not important, but it may effect, the call queue and the threading performance. // Later investigation. if ( vFile != _date_out_File ) return; // ignore unwanted file updates. QString content; Storage::FileHandler::read(vFile, content); sendUIResponse(content); } /*! * \brief CloudSyncController::addCSBuffWatch * \details Adds a watcher on the CloudSync Application output file. * \sa _out_File */ bool CloudSyncController::addCSBuffWatch() { bool ok = Storage::FileHandler::makeFolder(_location); QVariantList args {}; Errors_Enum error = eError_Unknown; if ( ! ok ) { error = eError_LogFolder; args = {{ _location }}; ok = false; goto lErr; } _date_out_File = _location + // The location _dateFormatted + _dateSeparator + _out_File; // The file name // watching for the cloud sync output file buffer. _DeviceController.doAddWatch(_date_out_File); return ok; lErr: toLog(error, args); return ok; } /*! * \brief CloudSyncController::interpret * \details Checks the received buffer to make sure it has all the required parameters * \param vIndex * \return true if the buffer is correct. */ bool CloudSyncController::interpret(const QString &vContent, Message &vMessage) { bool ok = true; QVariantList args {}; Errors_Enum error = eError_Unknown; QStringList lines; QString buffer; int index = -1; quint32 count = 0; Message message; QStringList items; quint8 id; if ( vContent.isEmpty() ) { error = eError_OutFileEmpty; ok = false; goto lErr; } lines = vContent.split('\n',QString::SkipEmptyParts); buffer = lines.last(); // DEBUG: LOG_DEBUG(QString("CS [%1]").arg(buffer)); items = buffer.split(_separator); count = items.count(); // check the required message length if ( count < eMessage_Count ) { error = eError_HeaderCount ; ok = false; goto lErr; } index = eMessage_Timestamp ; message.timestamp = items[index].toUInt(&ok); if (!ok) { error = eError_Timestamp ; ok = false; goto lErr; } index = eMessage_Sequence ; message.sequence = items[index].toUInt(&ok); if (!ok) { error = eError_Sequence ; ok = false; goto lErr; } index = eMessage_CRC ; message.crc = items[index].toUInt(&ok); if (!ok) { error = eError_CRC ; ok = false; goto lErr; } index = eMessage_MessageID ; message.id = items[index].toInt (&ok); if (!ok) { error = eError_MessageID ; ok = false; goto lErr; } index = eMessage_ParamCount; message.paramCount = items[index].toUInt(&ok); if (!ok) { error = eError_ParamCount ; ok = false; goto lErr; } // check the parameters count if ( count - eMessage_Count != message.paramCount ) { error = eError_ParamMismatch ; ok = false; goto lErr; } // check missing parameters id = CS2UI(message.id); if ( ! paramCount.contains(id)) paramCount[id] = 0; if ( paramCount[id] != message.paramCount ) { error = eError_ParamMissing ; ok = false; goto lErr; } // convert the message id and check its validity message.id = CS2UI(static_cast(message.id)); if ( eMessageID_Start > message.id || message.id > eMessageID_Count ) { error = eError_InvalidID ; ok = false; goto lErr; } // getting the parameters for ( quint32 index = eMessage_Count; index < eMessage_Count + message.paramCount; index++ ) { message.params.append( items[index] ); } vMessage = message; return true; lErr: // building the error info for each error switch (error) { case eError_Unknown : args = { }; break; case eError_OutFileEmpty : args = { }; break; case eError_HeaderCount : args = { count , eMessage_Count }; break; case eError_Timestamp : args = { items[index].trimmed() }; break; case eError_Sequence : args = { items[index].trimmed() }; break; case eError_CRC : args = { items[index].trimmed() }; break; case eError_MessageID : args = { items[index].trimmed() }; break; case eError_InvalidID : args = { items[index].trimmed() }; break; case eError_ParamCount : args = { items[index].trimmed() }; break; case eError_ParamMismatch : args = { count - eMessage_Count , message.paramCount }; break; case eError_ParamMissing : args = { paramCount[message.id] , message.paramCount }; break; // Interpreter function only handles the message parsing errors. So default can be used. default : break; } toLog(error, args); return false; } /*! * \brief CloudSyncController::toText * \details returns the error message of the error id. * \param vErrorID - Error id * \param vMessage - error message * \return the error message or empty string if the error is 0 (no error), or Unknown Error if not found */ QString CloudSyncController::toText(CloudSyncController::Errors_Enum vErrorID) { QString text = tr( "CS Unknown Error" ) ; if ( vErrorID == 0 ) return text; switch (vErrorID) { case eError_Unknown : /* "CS Unknown Error" */ ; break; case eError_OutFileEmpty : text = tr( "CS Out buffer empty" ) ; break; case eError_HeaderCount : text = tr( "CS Incorrect header" ) ; break; case eError_Timestamp : text = tr( "CS Incorrect timestamp" ) ; break; case eError_Sequence : text = tr( "CS Incorrect sequence" ) ; break; case eError_CRC : text = tr( "CS Incorrect CRC" ) ; break; case eError_MessageID : text = tr( "CS Incorrect ID" ) ; break; case eError_ParamCount : text = tr( "CS Incorrect parameter count" ) ; break; case eError_InvalidID : text = tr( "CS Invalid ID" ) ; break; case eError_ParamMismatch : text = tr( "CS Mismatch parameter count" ) ; break; case eError_ParamMissing : text = tr( "CS Missing parameter" ) ; break; case eError_NoHistory : text = tr( "CS No history available" ) ; break; case eError_Duplicate : text = tr( "CS Duplicate data" ) ; break; 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_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; } return text; } /*! * \brief CloudSyncController::toInfo * \details Provides extra information, related to each error, to be concatenated later to the error description. * \param vErrorID - the error id * \param vInfoItems - the information items to be used in creating the info. * \return the extra info for the error as string */ QString CloudSyncController::toInfo(CloudSyncController::Errors_Enum vErrorID, const QVariantList &vInfoItems) { // IMPORTANT: Please be careful here used QVariantList::value to act as a loose list. // It means it is designed to not throw out of bound error and just use "~" as a missing info argument. auto item = [=](uint i) { return vInfoItems.value(i,"~").toString(); }; QString info = QString( "[%1]" ).arg( vErrorID ) ; switch (vErrorID) { case eError_Unknown : ; break; case eError_OutFileEmpty : ; break; case eError_HeaderCount : info = QString( "[%1:%2/%3]" ).arg( vErrorID ).arg( item(0) ).arg( item(1) ) ; break; case eError_Timestamp : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_Sequence : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_CRC : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_MessageID : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_InvalidID : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_ParamCount : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_ParamMismatch : info = QString( "[%1:%2/%3]" ).arg( vErrorID ).arg( item(0) ).arg( item(1) ) ; break; case eError_ParamMissing : info = QString( "[%1:%2/%3]" ).arg( vErrorID ).arg( item(0) ).arg( item(1) ) ; break; case eError_NoHistory : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; case eError_Duplicate : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; 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_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; } return info; } /*! * \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) { LOG_DEBUG(toText(vErrorID) + " " + toInfo(vErrorID, vInfoItems)); } /*! * \brief CloudSyncController::sendUIResponse * \details the function to be called after reading the CloudSync out file to interpret the content of the file. * \param vContent - the content to be interpreted. * \return true if the content has a meaning for the interpreter, false otherwise. */ bool CloudSyncController::sendUIResponse(const QString &vContent) { // IMPORTANT: // please note the following in development testing it has been found out that. // Do not use gedit. // if the out.buf is edited by the "gedit" application the watch will be broken. // The "nano" application works fine. // A simple python3 code to open file and write to file works as well and that's sufficient for UI<=>CS communication. // TODO: messages have to have a sequence. // if the seq is duplicate it will be ignored. // seq, id, payload // DEBUG: qDebug() << vContent; bool ok = true; Message message; if ( ! interpret(vContent, message) ) return false; // The error handling internal to interpret method. ok = sendMessage(message); return ok; } /*! * \brief CloudSyncController::testWatchBuffDate * \details Checks the date and updated the watched file in case the date changed. */ void CloudSyncController::testWatchBuffDate() { _datetime = QDateTime::currentDateTime().toUTC(); _secSinceEpoch = _datetime.toSecsSinceEpoch(); _timeFormatted = _datetime.toString(_timeFormat); QString dateFormatted = _datetime.toString(_dateFormat); if (_dateFormatted != dateFormatted) { _dateFormatted = dateFormatted; // TODO: do we need to remove previous watch? addCSBuffWatch(); // bool out is not used here and error handling is internal to the addCSBuffWatch } } /*! * \brief CloudSyncController::makeUIBuff * \details make formatted UI buffer to send out. * \param vMessage - The message id with destination added. 2k, 1K * \return the formatted buffer */ QString CloudSyncController::makeUIBuff(const qint32 vMessageID) { 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 ) ; } /*! * \brief CloudSyncController::isDuplicate * \param vMessage - current action/messageID received. * \param vData - current data to compare with the history * \return true if duplicate */ bool CloudSyncController::isDuplicate(const qint32 vMessageID, const QStringList &vData) { return _uiHistory.contains(vMessageID) && _uiHistory[vMessageID] == vData; } /*! * \brief CloudSyncController::writeInpFile * \details Writes to the CS Input buffer * \param vBuffer - the string out buffer. * \return true if successful */ bool CloudSyncController::writeInpFile(const QString &vInpBuff) { bool ok = true; QVariantList args ; Errors_Enum error = eError_Unknown; _date_inp_File = _location + // The location _dateFormatted + _dateSeparator + _inp_File; // The file name // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // writing the message into the buffer. LOG_EVENT_UI( vInpBuff ); 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); return ok; } /*! * \brief CloudSyncController::sendUIBuff * \details Sends the UI Buffer to the UI input file. * \param vData - the data to be sent out. * \return true on successful writing to the file buffer. * \sa _inp_File */ bool CloudSyncController::sendUIBuff(const QString &vData) { bool ok = true; QString inpBuff = "%1,%2,%3,%4"; inpBuff = inpBuff .arg( _secSinceEpoch ) .arg( Types::safeIncrement(_seq)) .arg( generateCRC() ) .arg( vData ); ok = writeInpFile(inpBuff); return ok; } /*! * \brief CloudSyncController::saveUIHistory * \details stores the action data in history, if vData is provided. * \param vAction - the request id, which in current design is same as the Message comes to the UI. * \param vData - The message data * \return true if a message history is available, false otherwise. */ bool CloudSyncController::saveUIHistory(const qint32 vAction, const QVariantList &vData) { bool ok = true; QVariantList args ; Errors_Enum error = eError_Unknown; qint32 messageID = UI2CS(static_cast(vAction)); QStringList data; if ( vData.isEmpty() ) { error = eError_NoHistory; args = { messageID }; ok = false; goto lErr; } data = Format::fromVariantList(vData); if ( isDuplicate(messageID, data) ) { error = eError_Duplicate; args = { messageID }; ok = false; return ok; } // TODO: goto lErr; } don't log it just ignore and return false. // store the last message data _uiHistory[messageID] = data; return ok; lErr: toLog(error, args); return ok; } /*! * \brief CloudSyncController::sendUIHistory * \details sends the saved history of the message as a response to the cloud sync upon request. * \param vAction - the request id, which in current design is same as the Message comes to the UI. * \return true if a message history is available, false otherwise. */ bool CloudSyncController::sendUIHistory(const qint32 vAction) { bool ok = true; QVariantList args ; Errors_Enum error = eError_Unknown; qint32 messageID = UI2CS(static_cast(vAction)); if ( ! _uiHistory.contains( messageID ) ) { error = eError_NoHistory; args = { messageID }; ok = false; goto lErr; } sendUIBuff(makeUIBuff(messageID)); return ok; lErr: toLog(error, args); return ok; } /*! * \brief CloudSyncController::onActionReceive * \details The slot which will be called when a CANBus message is received, and will be sent to CloudSync if related. * \param vAction - The message action * \param vData - The message data */ void CloudSyncController::onActionReceive(GuiActionType vAction, const QVariantList &vData) { // TODO: This section is the translation/mapping section // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // preparing the message // Same parameters will be sent for these messages for now, so data is not changed. switch (vAction) { case GuiActionType::ID_HDOperationModeData : { // ---------------------------------------------------------------------------------------- // DEBUG : disable the HD HW message on the device on CloudSync integration testing. // currently it is always on fault and resets CloudSync inp log QVariantList varData = vData; QStringList strData = Format::fromVariantList(vData); if ( strData.length() == 2 ) { if ( strData[0] == "0" ) { if ( strData[1] == "1") { varData = {0,0}; } // use Fault, wait4Tx instead of 0,0 to send fault. to able to simulate and bypass HD. else { return; } } } // ---------------------------------------------------------------------------------------- if (saveDeviceState(varData)) // if not empty, nor duplicate, and saved sendDeviceState(); } break; case GuiActionType::ID_AdjustSerialHDRsp : case GuiActionType::ID_AdjustSerialDGRsp : // this message is not complete in one step and saves only the received info and waits for the other if complete will send. saveDeviceInfo(vAction, vData); break; default: break; } } /*! * \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) { 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 ); } /*! * \brief CloudSyncController::sendMessage * \details Makes and Sends the appropriate message for the vAction. * Some messages are sent out upon request form the last received on the history and will not be asked from FW. * \return true if there is a history for that message and no error happened. */ bool CloudSyncController::sendMessage(const Message &vMessage) { bool ok = false; // this function is used in sendUIResponse, therefore the message IDs which are responses should be implemented here. switch (vMessage.id) { case eMessageID_DeviceRegister : /* No Request/Response */ break; 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. } return ok; } /*! * \brief CloudSyncController::saveDeviceState * \details Saves the device state in the history for later request * \param vData - the device information * \return */ bool CloudSyncController::saveDeviceState(const QVariantList &vData) { bool ok = false; ok = saveUIHistory(eMessageID_DeviceState, vData); return ok; } /*! * \brief CloudSyncController::sendDeviceState * \return Sends the last received device state from history to CloudSync */ 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; } /*! * \brief CloudSyncController::doResetFactory * \details does the reset factory * \return true on successful reset */ bool CloudSyncController::doResetFactory() { // reset factory has not been implemented yet. bool ok = true; // TODO: call the UI Software Reset Factory here when it has implemented. LOG_DEBUG("CloudSync Reset factory request has not been implemented yet."); return ok; } /*! * \brief CloudSyncController::sendResetFactory * \details sends the factory reset response to CloudSync * \return true on successful send. */ bool CloudSyncController::sendResetFactory() { enum { eSucceed, eFailed }; bool ok = false; ok = doResetFactory(); // 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 )); return ok; } /*! * \brief CloudSyncController::sendCredentialsSave * \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::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. for ( auto fileName : vMessage.params ) { if ( ! QFileInfo::exists(fileName) ) { toLog(eError_CredentialFile , {fileName }); ok = false; goto lOut; } } 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(); 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. */ bool CloudSyncController::sendCredentialsResponse() { enum { eSucceed, eFailed }; bool ok = false; qint32 messageID = UI2CS(eMessageID_CredentialsSave); ok = sendUIBuff(QString("%1,1,%2").arg( messageID ).arg( ok ? eSucceed : eFailed )); return ok; } /*! * \brief CloudSyncController::sendTxCodeDisplay * \details reads the received Tx Code from CloudSync app and notifies with a signal. * \param vMessage : message containing the Tx Code. * \return true on successful extracting the Tc Code. */ bool CloudSyncController::sendTxCodeDisplay(const CloudSyncController::Message &vMessage) { bool ok = true; QString mTxCode; // although it has been checked in the interpreter, we won't risk the crash and check the list empty. if ( vMessage.params.isEmpty() ) { toLog(eError_TxCodeNoParam , {}); ok = false; goto lOut; } mTxCode = vMessage.params[0].trimmed(); if ( mTxCode.isEmpty() ) { toLog(eError_TxCodeEmpty , {}); ok = false; goto lOut; } emit didTxCodeReceive ( mTxCode ); qDebug() << " ---------- " << mTxCode; lOut: return ok; } /*! * \brief CloudSyncController::sendDeviceRegister * \details sends the device registration request * \return true on successful send. */ bool CloudSyncController::sendDeviceRegister() { bool ok = false; ok = sendUIHistory(eMessageID_DeviceRegister); return ok; } /*! * \brief CloudSyncController::testDeviceRegister * \details checks if the device needs registration. * \return true if registration required. */ bool CloudSyncController::testDeviceRegister() { QString source = QString(Storage::CloudSync_Base_Path_Name) + Storage::CloudSync_Credentials_Folder_Name; qDebug() << source; QFileInfoList fileInfos = QDir(source).entryInfoList(QDir::NoDotAndDotDot|QDir::Files); if( fileInfos.isEmpty() ) { toLog(eError_CredentialEmpty,{}); // It is intentional that the vault folder path has not been sent to the log. sendDeviceRegister(); } return true; // for now always true. } /*! * \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. * \param vData - the device information * \param vTimedOut - if true the missing device information will be filled by zero and will be sent anyway. * \return */ bool CloudSyncController::saveDeviceInfo(GuiActionType vAction, const QVariantList &vData) { bool ok = false; // the messages are coming from different sources and the order of receiving could not be guessed. // so for each of these two we check and fill the designated variable // if both values received initDeviceInfoWait(); if ( vAction == GuiActionType::ID_AdjustSerialHDRsp ) { if ( vData.count() ) { _deviceInfoHD = vData[eDeviceInfo_Ix].toString(); } else { _deviceInfoHD = ""; } } if ( vAction == GuiActionType::ID_AdjustSerialDGRsp ) { if ( vData.count() ) { _deviceInfoDG = vData[eDeviceInfo_Ix].toString(); } else { _deviceInfoDG = ""; } } if ( !_deviceInfoHD.isEmpty() && !_deviceInfoDG.isEmpty() ) { saveDeviceInfoTimeOut(); ok = true; } return ok; } /*! * \brief CloudSyncController::saveDeviceInfoTimeOut * \details send the Device information regardless of the information filled or not. * \return true on successful save and send. */ bool CloudSyncController::saveDeviceInfoTimeOut() { bool ok = false; if ( _deviceInfoUI.isEmpty() ) { _deviceInfoUI = qApp->applicationVersion(); } 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(); return ok; } /*! * \brief CloudSyncController::sendDeviceInfo * \details sends the devices information if both the HD and DG information are ready otherwise will fill the missing with 0 if the vTimeout is set to true. * \return */ bool CloudSyncController::sendDeviceInfo() { bool ok = false; ok = sendUIHistory(eMessageID_DeviceInfo); return ok; } /*! * \brief CloudSyncController::testDeviceInfoWait * \details tests the wait conditions if there is a wait, decrements until times out and sends the device info. */ void CloudSyncController::testDeviceInfoWait() { if (_deviceInfoStop ) return; // DEBUG: qDebug() << _deviceInfoWait; if (_deviceInfoWait ){ _deviceInfoWait -- ; } else { saveDeviceInfoTimeOut(); } } /*! * \brief CloudSyncController::stopDeviceInfoWait * \details stops waiting for the device info. */ void CloudSyncController::stopDeviceInfoWait() { _deviceInfoStop = true; // NOTE: if it is accepted to use the last received info just comment these 3 cleanup lines. // so the last message will update the history only and it will be sent out. _deviceInfoHD = ""; _deviceInfoDG = ""; _deviceInfoUI = ""; } /*! * \brief CloudSyncController::initDeviceInfoWait * \details if currently not 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; } }