Index: sources/cloudsync/CloudSyncController.cpp =================================================================== diff -u -re2dc7bd9995a3bb410aa472a1f95c1cc9ba3136d -r80250cfdbe58a3df17950d342212f155d52d3971 --- sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision e2dc7bd9995a3bb410aa472a1f95c1cc9ba3136d) +++ sources/cloudsync/CloudSyncController.cpp (.../CloudSyncController.cpp) (revision 80250cfdbe58a3df17950d342212f155d52d3971) @@ -23,8 +23,8 @@ #include "GuiController.h" #include "DeviceController.h" #include "FileHandler.h" -//#include "GuiGlobals.h" + /*! * \brief CloudSyncController::CloudSyncController * \details Constructor @@ -151,41 +151,175 @@ if ( vFile != _date_out_File ) return; // ignore unwanted file updates. QString content; Storage::FileHandler::read(vFile, content); - interpreter(content); + sendUIResponse(content); } /*! * \brief CloudSyncController::addCSBuffWatch * \details Adds a watcher on the CloudSync Application output file. * \sa _out_File */ -void CloudSyncController::addCSBuffWatch() +bool CloudSyncController::addCSBuffWatch() { - bool ok = Storage::FileHandler::makeFolder(_location); - if ( ok ) { + bool ok = Storage::FileHandler::makeFolder(_location); + QVariantList args {}; + Errors_Enum error = eError_OK; + + 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_OK; + + QStringList lines = vContent.split('\n',QString::SkipEmptyParts); + QString buffer = lines.last(); + LOG_DEBUG(QString("CloudSync Message received [%1]").arg(buffer)); + + int index = -1; + Message message ; + + QStringList items = buffer.split(_separator); + int count = items.count(); + + // check the required message length + if ( count < eMessage_Count ) { error = eError_Count ; ok = false; goto lErr; } + index = eMessage_Timestamp ; message.timestamp = items[index].toInt (&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_MessageID ; message.id = items[index].toInt (&ok); if (!ok) { error = eError_MessageID ; ok = false; goto lErr; } + index = eMessage_ParamCount; message.paramCount = items[index].toInt (&ok); if (!ok) { error = eError_ParamCount ; ok = false; goto lErr; } + index = eMessage_CRC ; message.crc = items[index].toUInt(&ok); if (!ok) { error = eError_CRC ; ok = false; goto lErr; } + + // check the parameters count + if ( count - eMessage_Count < message.paramCount ) { error = eError_Parameter ; ok = false; goto lErr; } + + // getting the parameters + for ( int index = eMessage_Count; index < message.paramCount; index++ ) { + message.params.append( items[index] ); } - else { - LOG_DEBUG(tr("the CloudSync log folder cannot be created.")); + + vMessage = message; + return true; + +lErr: + // building the error info for each error + switch (error) { + case eError_Count : args = { count , eMessage_Count }; break; + case eError_Timestamp : args = { items[index].trimmed() }; break; + case eError_Sequence : args = { items[index].trimmed() }; break; + case eError_MessageID : args = { items[index].trimmed() }; break; + case eError_ParamCount : args = { items[index].trimmed() }; break; + case eError_CRC : args = { items[index].trimmed() }; break; + case eError_Parameter : args = { count - eMessage_Count , message.paramCount }; break; + default : break; } + LOG_DEBUG(toText(error) + " " + toInfo(error, args)); + return false; } /*! - * \brief CloudSyncController::interpreter + * \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; + if ( vErrorID == 0 ) return text; + switch (vErrorID) { + case eError_Count : text = tr( "E,CS,Incorrect header" ) ; break; + case eError_Timestamp : text = tr( "E,CS,Incorrect timestamp" ) ; break; + case eError_Sequence : text = tr( "E,CS,Incorrect sequence" ) ; break; + case eError_MessageID : text = tr( "E,CS,Incorrect ID" ) ; break; + case eError_ParamCount : text = tr( "E,CS,Incorrect parameter length" ) ; break; + case eError_CRC : text = tr( "E,CS,Incorrect CRC" ) ; break; + case eError_Parameter : text = tr( "E,CS,Incorrect parameter count" ) ; break; + case eError_NoHistory : text = tr( "E,CS,No history available for the request" ) ; break; + case eError_LogFolder : text = tr( "E,CS,The log folder cannot be created." ) ; break; + case eError_LogFileInp : text = tr( "E,CS,Error writing to the input file." ) ; break; + default : text = tr( "E,CS,Unknown Error" ) ; break; + } + return text; +} + +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 = ""; + switch (vErrorID) { + case eError_Count : 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_MessageID : 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_CRC : info = QString( "[%1:%2]" ).arg( vErrorID ).arg( item(0) ) ; break; + case eError_Parameter : 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; + default : info = QString( "[%1]" ).arg( vErrorID ) ; 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::interpreter(const QString &vContent) +bool CloudSyncController::sendUIResponse(const QString &vContent) { - LOG_DEBUG(QString("~~~CloudSync Message received [%1]").arg(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 - return true; + // qDebug() << vContent; + + bool ok = true; + Message message; + if ( ! interpret(vContent, message) ) return false; // The error handling internal to interpret method. + switch (message.id) { + case eMessageID_DeviceState : ok = sendUIHistory(message.id); break; // The device state is stored in history but the other might be different so the switch is used. + default : break; + } + return ok; } /*! @@ -195,12 +329,13 @@ void CloudSyncController::checkDate() { _datetime = QDateTime::currentDateTime(); + _secSinceEpoch = _datetime.toSecsSinceEpoch(); _timeFormatted = _datetime.toString(_timeFormat); QString dateFormatted = _datetime.toString(_dateFormat); if (_dateFormatted != dateFormatted) { _dateFormatted = dateFormatted; - // TODO: do we need to remove current watch? - addCSBuffWatch(); + // TODO: do we need to remove previous watch? + addCSBuffWatch(); // bool out is not used here and error handling is internal to the addCSBuffWatch } } @@ -213,24 +348,51 @@ */ bool CloudSyncController::sendUIBuff(const QString &vData) { + bool ok = true; + QVariantList args ; + Errors_Enum error = eError_OK; + QString inpBuff; _date_inp_File = _location + // The location _dateFormatted + _dateSeparator + _inp_File; // The file name - inpBuff = _timeFormatted; + inpBuff = QString::number(_secSinceEpoch); inpBuff += _separator + QString::number(_seq++); inpBuff += _separator + vData; inpBuff += '\n'; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // writing the message into the buffer. - if ( ! Storage::FileHandler::write(_date_inp_File, inpBuff) ) { - LOG_DEBUG(tr("Error writing to the CloudSync Input file.")); - return false; - } - return true; + if ( ! Storage::FileHandler::write(_date_inp_File, inpBuff) ) { error = eError_LogFileInp; args = { _date_inp_File }; ok = false; goto lErr; } + return ok; + +lErr: + toLog(error, args); + return ok; } /*! + * \brief CloudSyncController::sendUIHistory + * \details sends a response to the cloud sync upon request from the stored history. + * \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(qint32 vAction) +{ + bool ok = true; + QVariantList args ; + Errors_Enum error = eError_OK; + if ( ! _lastReceivedData.contains( vAction ) ) { error = eError_NoHistory; args = { vAction }; ok = false; goto lErr; } + + sendUIBuff(QString("%1,%2").arg(vAction).arg(_lastReceivedData[ vAction ].join(_separator))); + + 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 @@ -239,6 +401,12 @@ void CloudSyncController::onActionReceive(GuiActionType vAction, const QVariantList &vData) { QString inpBuff; + + // convert the data to the string list store/fetching it out from history. + // the original vData will be used if their actual values needed. + QStringList data; + for (auto datum : vData) { data += datum.toString(); } + switch (vAction) { case GuiActionType::ID_HDOperationModeData: case GuiActionType::ID_PreTreatmentStates : @@ -248,7 +416,12 @@ // TODO: This section is the translation/mapping section // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // preparing the message - inpBuff = Format::toHexString/*QString::number*//*enumString*/(vAction); + + // store the last message data + _lastReceivedData[eMessageID_DeviceState] = data; + + // prepare the buffer + inpBuff = QString::number(eMessageID_DeviceState); for (auto var : vData) { inpBuff += _separator + var.toString(); } if ( ! sendUIBuff(inpBuff) ) break;