Index: sources/ApplicationPost.cpp =================================================================== diff -u -re38423dd3840e625ed0728d9b0fb3f9eb9292500 -rab28e427d67bdb054a0173efb719e820f97b20b0 --- sources/ApplicationPost.cpp (.../ApplicationPost.cpp) (revision e38423dd3840e625ed0728d9b0fb3f9eb9292500) +++ sources/ApplicationPost.cpp (.../ApplicationPost.cpp) (revision ab28e427d67bdb054a0173efb719e820f97b20b0) @@ -1,320 +1,320 @@ -/*! - * - * 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 ApplicationPost.cpp - * \author (last) Behrouz NematiPour - * \date (last) 29-Feb-2024 - * \author (original) Behrouz NematiPour - * \date (original) 26-Aug-2020 - * - */ -#include "ApplicationPost.h" -// Qt -#include -#include -#include - -// Project -#include "Logger.h" -#include "FileHandler.h" - -/*! - * \brief ApplicationPost::ApplicationPost - * \details Constructor - * \param parent - QObject parent owner object. - * Qt handles the children destruction by their parent objects life-cycle. - */ -ApplicationPost::ApplicationPost(QObject *parent) : QObject(parent) { } - -/*! - * \brief ApplicationPost::start - * \details Starting the post application initialization - * \return - */ -void ApplicationPost::start() -{ - QString postLogFileName = QDir::tempPath() + "/" + Storage::POST_LOG; - if (Storage::FileHandler::read(postLogFileName, _content)) { - - _isOSVersion = checkOSVersion (); - _isShaSum = checkShaSum (); - _isCANBus = checkCANBus (); - _isDisplay = checkDisplay (); - _isTouch = checkTouch (); - _isSDCard = checkSDCard (); - _isRtc = checkRtc (); - _isWiFi = checkWiFi (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. - _isBluetooth = checkBluetooth (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. - _isCloudSync = checkCloudSync (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. - _isEthernet = checkEthernet (); // this is not a Fault and not required the application to stop. No Alarm will be triggered. - _isSound = checkSound (); // this is not a Fault and not required the application to stop. No Alarm will be triggered. - _isYearCheck = checkYear (); - // WARNING: - // all of the checks have to be done, - // although some are not failing the final result, - // so they need to be assigned to a variable and then, AND(&&) them, - // otherwise on the first fail rest of the checks will not run by compiler optimization. - _isDone = - isOSVersion () && - isShaSum () && - isCANBus () && - isDisplay () && - isTouch () && - isSDCard () && - isRtc () && - isWiFi () && - isBluetooth () && - isCloudSync () && - // isEthernet () && // it is being executed to get the information but is not part of the POST failure. - // isSound () && - isYearCheck () - ; - } - else { - // TODO: Ignored for now but this could be a FileSystem check failure, and the post.log has to always exist. - // _done = false; - LOG_APPED(tr("The POST log file could not be read.")); - } - emit didDone(_isDone); -} - -/*! - * \brief ApplicationPost::checkOSVersion - * \details Checks the OS version - * \return false if the OS version is lower than 0.0.40 which is released for the Cybersecurity.. - */ -bool ApplicationPost::checkOSVersion() -{ - QString exrVer("%1\\s*\"\\d+\\.\\d+\\.\\d+\""); - QString exrBld("%1\\s*\"\\d+\""); - - QRegExp regVer(exrVer.arg(_postmsg_osversion)); - QRegExp regBld(exrBld.arg(_postmsg_osbuild )); - QString version; - QString build; - QStringList versions; - quint16 major; - quint16 minor; - quint16 micro; - - // check the statement exists in the long - int rowVer = _content.indexOf (regVer); - int rowBld = _content.indexOf (regBld); Q_UNUSED(rowBld); - bool ok = rowVer >= 0; // found - if ( ! ok ) goto lOut; - - // check the Os version is compatible - version = regVer.cap(0).replace(_postmsg_osversion,"").replace("\"",""); // 0 is the first captured and next if any are the subsets. - build = regBld.cap(0).replace(_postmsg_osbuild ,"").replace("\"",""); // 0 is the first captured and next if any are the subsets. - versions = version.split("."); - major = versions[0].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. - minor = versions[1].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. - micro = versions[2].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. - ok = major >= Storage::OS_VERSION_MAJOR && - minor >= Storage::OS_VERSION_MINOR && - micro >= Storage::OS_VERSION_MICRO ; - if ( ! ok ) goto lOut; - - _osVersion = version + "." + build; - -lOut: - if ( !ok ) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_OS_VERSION); - emit didOSVersion(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkOSInfo - * \details Checks if the - * \return it is false if the - */ -bool ApplicationPost::checkShaSum() -{ - bool ok = _content.contains(_postmsg_shasum + _postmsg_postfix_passed); - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SHASUM); - emit didShaSum(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkCANBus - * \details Checks the CANBus driver is loaded and the bus is functional - * \return false if there is an issue. - */ -bool ApplicationPost::checkCANBus() -{ - bool ok = _content.contains(_postmsg_canbus + _postmsg_postfix_passed); - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_CANBUS); - emit didCANBus(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkDisplay - * \details Checks the display driver is loaded - * \return false if there is an issue [No Implementation yet (always true)]. - */ -bool ApplicationPost::checkDisplay() -{ - bool ok = true; - //TODO: do the test : not a good test has been found yet. - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_DISPLAY); - emit didDisplay(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkTouchScreen - * \details Checks the touch driver is loaded - * \return false if there is an issue. - */ -bool ApplicationPost::checkTouch() -{ - bool ok = _content.contains(_postmsg_touch + _postmsg_postfix_passed); - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_TOUCH); - emit didTouch(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkSDCard - * \details Checks the SD-Card drive is loaded and functional - * \return false if there is an issue. - */ -bool ApplicationPost::checkSDCard() -{ - bool ok = _content.contains(_postmsg_sdcard + _postmsg_postfix_passed); - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SDCARD); - emit didSDCard(ok); - return ok; -} - -/*! - * \brief ApplicationPost::CRC - * \details Checks the RTC driver is loaded and functional - * \return false if there is an issue - */ -bool ApplicationPost::checkRtc() -{ - bool ok = _content.contains(_postmsg_rtc + _postmsg_postfix_passed); - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_RTC); - emit didRtc(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkEthernet - * \details Checks the Ethernet driver is loaded and functional. - * \return false if there is an issue [Removed from the PRS (always true)]. - */ -bool ApplicationPost::checkEthernet() -{ - bool ok = false; - int posDev = -1; - int posMac = -1; - QString section = ""; - posDev = _content.indexOf( _devEthernet ); if ( posDev < 0 ) goto lOut; - section = _content.mid(posDev, _macAppearLen); - posMac = section.indexOf(_macEthernetLabel); if ( posMac < 0 ) goto lOut; - _macEthernet = section.mid(posMac + _macEthernetLabel.length(), _macAddrssLen).toUpper(); if ( _macEthernet.isEmpty() ) goto lOut; - ok = true; - -lOut: - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_ETHERNET); - emit didEthernet(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkWiFi - * \details Checks the WiFi driver is loaded and functional - * \return false if there is an issue. - */ -bool ApplicationPost::checkWiFi() -{ - bool ok = false; - int posDev = -1; - int posMac = -1; - QString section = ""; - posDev = _content.indexOf( _devWireless ); if ( posDev < 0 ) goto lOut; - section = _content.mid(posDev, _macAppearLen); - posMac = section.indexOf(_macWirelessLabel); if ( posMac < 0 ) goto lOut; - _macWireless = section.mid(posMac + _macWirelessLabel.length(), _macAddrssLen).toUpper(); if ( _macWireless.isEmpty() ) goto lOut; - ok = _content.contains(_postmsg_wifi + _postmsg_postfix_passed); - -lOut: - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_WIFI); - emit didWiFi(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkBluetooth - * \details Checks the Bluetooth driver is loaded and functional - * \return false if there is an issue. - */ -bool ApplicationPost::checkBluetooth() -{ - bool ok = false; - int posDev = -1; - int posMac = -1; - QString section = ""; - posDev = _content.indexOf( _devBluetooth ); if ( posDev < 0 ) goto lOut; - section = _content.mid(posDev, _macAppearLen); - posMac = section.indexOf(_macBluetoothLabel); if ( posMac < 0 ) goto lOut; - _macBluetooth = section.mid(posMac + _macBluetoothLabel.length(), _macAddrssLen).toUpper(); if ( _macBluetooth.isEmpty() ) goto lOut; - ok = _content.contains(_postmsg_bluetooth + _postmsg_postfix_passed); - -lOut: - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_BLUETOOTH); - emit didBluetooth(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkCloudSync - * \details Checks the CloudSync service is loaded and functional - * \return false if there is an issue. - */ -bool ApplicationPost::checkCloudSync() -{ - bool ok = true; - if (! gDisableCloudSyncFailStop ) { - ok = _content.contains(_postmsg_cloudsync + _postmsg_postfix_passed); - } - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_CLOUDSYNC); - emit didCloudSync(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkSound - * \details Checks the sound driver is loaded. - * \return false if there is an issue [No Implementation yet (always true)]. - */ -bool ApplicationPost::checkSound() -{ - bool ok = true; - // do the test - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SOUND); - emit didSound(ok); - return ok; -} - -/*! - * \brief ApplicationPost::checkYear - * \details Checks if the year is greater than or equal to 2022. - * \return false if there is an issue. - */ -bool ApplicationPost::checkYear() -{ - QDate currentDate = QDate::currentDate(); - bool ok = currentDate.year() >= _yearMinimum; - if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_INVALID_YEAR); - emit didYearCheck(ok); - return ok; -} +/*! + * + * 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 ApplicationPost.cpp + * \author (last) Behrouz NematiPour + * \date (last) 29-Feb-2024 + * \author (original) Behrouz NematiPour + * \date (original) 26-Aug-2020 + * + */ +#include "ApplicationPost.h" +// Qt +#include +#include +#include + +// Project +#include "Logger.h" +#include "FileHandler.h" + +/*! + * \brief ApplicationPost::ApplicationPost + * \details Constructor + * \param parent - QObject parent owner object. + * Qt handles the children destruction by their parent objects life-cycle. + */ +ApplicationPost::ApplicationPost(QObject *parent) : QObject(parent) { } + +/*! + * \brief ApplicationPost::start + * \details Starting the post application initialization + * \return + */ +void ApplicationPost::start() +{ + QString postLogFileName = QDir::tempPath() + "/" + Storage::POST_LOG; + if (Storage::FileHandler::read(postLogFileName, _content)) { + + _isOSVersion = checkOSVersion (); + _isShaSum = checkShaSum (); + _isCANBus = checkCANBus (); + _isDisplay = checkDisplay (); + _isTouch = checkTouch (); + _isSDCard = checkSDCard (); + _isRtc = checkRtc (); + _isWiFi = checkWiFi (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. + _isBluetooth = checkBluetooth (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. + _isCloudSync = checkCloudSync (); // is not mandatory and the device can still be used without it. Alarm will be triggered to notify user in Active Alarm List. + _isEthernet = checkEthernet (); // this is not a Fault and not required the application to stop. No Alarm will be triggered. + _isSound = checkSound (); // this is not a Fault and not required the application to stop. No Alarm will be triggered. + _isYearCheck = checkYear (); + // WARNING: + // all of the checks have to be done, + // although some are not failing the final result, + // so they need to be assigned to a variable and then, AND(&&) them, + // otherwise on the first fail rest of the checks will not run by compiler optimization. + _isDone = + isOSVersion () && + isShaSum () && + isCANBus () && + isDisplay () && + isTouch () && + isSDCard () && + isRtc () && + isWiFi () && + isBluetooth () && + isCloudSync () && + // isEthernet () && // it is being executed to get the information but is not part of the POST failure. + // isSound () && + isYearCheck () + ; + } + else { + // TODO: Ignored for now but this could be a FileSystem check failure, and the post.log has to always exist. + // _done = false; + LOG_APPED_UI(tr("The POST log file could not be read.")); + } + emit didDone(_isDone); +} + +/*! + * \brief ApplicationPost::checkOSVersion + * \details Checks the OS version + * \return false if the OS version is lower than 0.0.40 which is released for the Cybersecurity.. + */ +bool ApplicationPost::checkOSVersion() +{ + QString exrVer("%1\\s*\"\\d+\\.\\d+\\.\\d+\""); + QString exrBld("%1\\s*\"\\d+\""); + + QRegExp regVer(exrVer.arg(_postmsg_osversion)); + QRegExp regBld(exrBld.arg(_postmsg_osbuild )); + QString version; + QString build; + QStringList versions; + quint16 major; + quint16 minor; + quint16 micro; + + // check the statement exists in the long + int rowVer = _content.indexOf (regVer); + int rowBld = _content.indexOf (regBld); Q_UNUSED(rowBld); + bool ok = rowVer >= 0; // found + if ( ! ok ) goto lOut; + + // check the Os version is compatible + version = regVer.cap(0).replace(_postmsg_osversion,"").replace("\"",""); // 0 is the first captured and next if any are the subsets. + build = regBld.cap(0).replace(_postmsg_osbuild ,"").replace("\"",""); // 0 is the first captured and next if any are the subsets. + versions = version.split("."); + major = versions[0].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. + minor = versions[1].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. + micro = versions[2].toUInt(); // type, existance, count has been already tested by regex, and was rejected in first check section. + ok = major >= Storage::OS_VERSION_MAJOR && + minor >= Storage::OS_VERSION_MINOR && + micro >= Storage::OS_VERSION_MICRO ; + if ( ! ok ) goto lOut; + + _osVersion = version + "." + build; + +lOut: + if ( !ok ) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_OS_VERSION); + emit didOSVersion(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkOSInfo + * \details Checks if the + * \return it is false if the + */ +bool ApplicationPost::checkShaSum() +{ + bool ok = _content.contains(_postmsg_shasum + _postmsg_postfix_passed); + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SHASUM); + emit didShaSum(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkCANBus + * \details Checks the CANBus driver is loaded and the bus is functional + * \return false if there is an issue. + */ +bool ApplicationPost::checkCANBus() +{ + bool ok = _content.contains(_postmsg_canbus + _postmsg_postfix_passed); + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_CANBUS); + emit didCANBus(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkDisplay + * \details Checks the display driver is loaded + * \return false if there is an issue [No Implementation yet (always true)]. + */ +bool ApplicationPost::checkDisplay() +{ + bool ok = true; + //TODO: do the test : not a good test has been found yet. + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_DISPLAY); + emit didDisplay(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkTouchScreen + * \details Checks the touch driver is loaded + * \return false if there is an issue. + */ +bool ApplicationPost::checkTouch() +{ + bool ok = _content.contains(_postmsg_touch + _postmsg_postfix_passed); + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_TOUCH); + emit didTouch(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkSDCard + * \details Checks the SD-Card drive is loaded and functional + * \return false if there is an issue. + */ +bool ApplicationPost::checkSDCard() +{ + bool ok = _content.contains(_postmsg_sdcard + _postmsg_postfix_passed); + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SDCARD); + emit didSDCard(ok); + return ok; +} + +/*! + * \brief ApplicationPost::CRC + * \details Checks the RTC driver is loaded and functional + * \return false if there is an issue + */ +bool ApplicationPost::checkRtc() +{ + bool ok = _content.contains(_postmsg_rtc + _postmsg_postfix_passed); + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_RTC); + emit didRtc(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkEthernet + * \details Checks the Ethernet driver is loaded and functional. + * \return false if there is an issue [Removed from the PRS (always true)]. + */ +bool ApplicationPost::checkEthernet() +{ + bool ok = false; + int posDev = -1; + int posMac = -1; + QString section = ""; + posDev = _content.indexOf( _devEthernet ); if ( posDev < 0 ) goto lOut; + section = _content.mid(posDev, _macAppearLen); + posMac = section.indexOf(_macEthernetLabel); if ( posMac < 0 ) goto lOut; + _macEthernet = section.mid(posMac + _macEthernetLabel.length(), _macAddrssLen).toUpper(); if ( _macEthernet.isEmpty() ) goto lOut; + ok = true; + +lOut: + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_ETHERNET); + emit didEthernet(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkWiFi + * \details Checks the WiFi driver is loaded and functional + * \return false if there is an issue. + */ +bool ApplicationPost::checkWiFi() +{ + bool ok = false; + int posDev = -1; + int posMac = -1; + QString section = ""; + posDev = _content.indexOf( _devWireless ); if ( posDev < 0 ) goto lOut; + section = _content.mid(posDev, _macAppearLen); + posMac = section.indexOf(_macWirelessLabel); if ( posMac < 0 ) goto lOut; + _macWireless = section.mid(posMac + _macWirelessLabel.length(), _macAddrssLen).toUpper(); if ( _macWireless.isEmpty() ) goto lOut; + ok = _content.contains(_postmsg_wifi + _postmsg_postfix_passed); + +lOut: + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_WIFI); + emit didWiFi(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkBluetooth + * \details Checks the Bluetooth driver is loaded and functional + * \return false if there is an issue. + */ +bool ApplicationPost::checkBluetooth() +{ + bool ok = false; + int posDev = -1; + int posMac = -1; + QString section = ""; + posDev = _content.indexOf( _devBluetooth ); if ( posDev < 0 ) goto lOut; + section = _content.mid(posDev, _macAppearLen); + posMac = section.indexOf(_macBluetoothLabel); if ( posMac < 0 ) goto lOut; + _macBluetooth = section.mid(posMac + _macBluetoothLabel.length(), _macAddrssLen).toUpper(); if ( _macBluetooth.isEmpty() ) goto lOut; + ok = _content.contains(_postmsg_bluetooth + _postmsg_postfix_passed); + +lOut: + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_BLUETOOTH); + emit didBluetooth(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkCloudSync + * \details Checks the CloudSync service is loaded and functional + * \return false if there is an issue. + */ +bool ApplicationPost::checkCloudSync() +{ + bool ok = true; + if (! gDisableCloudSyncFailStop ) { + ok = _content.contains(_postmsg_cloudsync + _postmsg_postfix_passed); + } + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_CLOUDSYNC); + emit didCloudSync(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkSound + * \details Checks the sound driver is loaded. + * \return false if there is an issue [No Implementation yet (always true)]. + */ +bool ApplicationPost::checkSound() +{ + bool ok = true; + // do the test + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_SOUND); + emit didSound(ok); + return ok; +} + +/*! + * \brief ApplicationPost::checkYear + * \details Checks if the year is greater than or equal to 2022. + * \return false if there is an issue. + */ +bool ApplicationPost::checkYear() +{ + QDate currentDate = QDate::currentDate(); + bool ok = currentDate.year() >= _yearMinimum; + if (! ok) emit didFail(Gui::GuiAlarmID::ALARM_ID_HD_UI_POST_FAILURE_INVALID_YEAR); + emit didYearCheck(ok); + return ok; +} Index: sources/canbus/MessageDispatcher.cpp =================================================================== diff -u -r26433c42f2efa2cb18af95f523581c7eeeff049d -rab28e427d67bdb054a0173efb719e820f97b20b0 --- sources/canbus/MessageDispatcher.cpp (.../MessageDispatcher.cpp) (revision 26433c42f2efa2cb18af95f523581c7eeeff049d) +++ sources/canbus/MessageDispatcher.cpp (.../MessageDispatcher.cpp) (revision ab28e427d67bdb054a0173efb719e820f97b20b0) @@ -1,1096 +1,1096 @@ -/*! - * - * 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 MessageDispatcher.cpp - * \author (last) Dara Navaei - * \date (last) 28-Feb-2024 - * \author (original) Behrouz NematiPour - * \date (original) 26-Aug-2020 - * - */ -#include "MessageDispatcher.h" - -// Qt -#include -#include - -// Project -#include "Logger.h" -#include "ApplicationController.h" -#include "FrameInterface.h" -#include "MessageAcknowModel.h" - -//#define DEBUG_ACKBACK_HD_TO_UI -//#define DEBUG_OUT_OF_SYNC - -using namespace Can; - -/*! - * \brief MessageDispatcher::MessageDispatcher - * \details Constructor - * \param parent - QObject parent owner object. - * Qt handles the children destruction by their parent objects life-cycle. - */ -MessageDispatcher::MessageDispatcher(QObject *parent) : QObject(parent) { } - -/*! - * \brief Message Handler initializer - */ -bool MessageDispatcher::init() -{ - if ( _init ) return false; - _init = true; - - // runs in the thread - initConnections(); - - LOG_DEBUG(tr("%1 Initialized").arg(metaObject()->className())); - - return true; -} - -/*! - * \brief MessageDispatcher::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 MessageDispatcher::init(QThread &vThread) -{ - if ( ! init() ) return false; - initThread(vThread); - return true; -} - -/*! - * \brief MessageDispatcher::quit - * \details quits the class - * Calls quitThread - */ -void MessageDispatcher::quit() -{ - // disabled coco begin validated: Application termination is not correctly done in coco!!! - // it has been tested and works perfectly fine in normal run. - quitThread(); // validated -} -// disabled coco end - -/*! - * \brief Message Handler connections definition - * \details Initializes the required signal/slot connection between this class and other objects - * to be able to communicate. - */ -void MessageDispatcher::initConnections() -{ - // From GUI - connect(&_ApplicationController, SIGNAL(didActionTransmit(GuiActionType , const QVariantList &)), - this , SLOT( onActionTransmit(GuiActionType , const QVariantList &))); - - // From HD - connect(&_FrameInterface , SIGNAL(didFrameReceive (Can_Id , const QByteArray &)), - this , SLOT( onFrameReceive (Can_Id , const QByteArray &))); - - // From Message Acknow Model timer timeout. - connect(&_MessageAcknowModel , SIGNAL(didFramesTransmit(Can_Id, Sequence, const FrameList &)), - this , SLOT( onFramesTransmit(Can_Id, Sequence, const FrameList &))); - - connect(&_MessageAcknowModel , SIGNAL(didFailedTransmit( Sequence )), - this , SLOT( onFailedTransmit( Sequence ))); - - // Application Settings are ready - connect(&_ApplicationController, SIGNAL(didSettingsDone ()), - this , SLOT( onSettingsDone ())); - - // ---- Signal/Slots - ADJUST_TRANSMT_MODEL_BRIDGE_CONNECTIONS(_ApplicationController) - ACTION_RECEIVE_MODEL_BRIDGE_CONNECTIONS(_interpreter ) -} - -/*! - * \brief ApplicationController::initThread - * \details Moves this object into the thread vThread. - * And checks that this method is called from main thread. - * Also connects quitThread to application aboutToQuit. - * \param vThread - the thread - */ -void MessageDispatcher::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 MessageDispatcher::quitThread - * \details Moves this object to main thread to be handled by QApplication - * And to be destroyed there. - */ -void MessageDispatcher::quitThread() -{ - // disabled coco begin validated: Application 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 MessageDispatcher::onFrameReceive - * \details Upon message has been received over CANBus this slot will be called - * by FrameInterface::didFrameReceive signal to process the frame - * Upon completion of collected all the required frames - * on successful interpretation of the message, emits didActionReceived signal. - * The message will be removed from list of the channel vCan_Id messages. - * \param vCan_Id - CANBus channel of the frame - * \param vPayload - Payload of the frame - */ -void MessageDispatcher::onFrameReceive(Can_Id vCan_Id, const QByteArray &vPayload) -{ - // Append a message to the list - // disabled coco begin validated: if empty (first condition) is true, it must never check for the complete (second condition) - // because if the list is empty there is no last() item - if (_messageList[vCan_Id].isEmpty() || _messageList[vCan_Id].last().isComplete()) { - // disabled coco end - _messageList[vCan_Id].append(Message()); - } - - // build the message and check. - if (! buildMessage(vCan_Id, vPayload)) { - return; - } - Message mMessage = _messageList[vCan_Id].last(); - - // TODO : must be moved to a MessageModel class - if (mMessage.isComplete()) { - rxCount(); - #ifdef DEBUG_OUT_OF_SYNC - if (_rxSequence != mMessage.sequence) { - qDebug() << tr("Out of Sync : %1 , %2").arg(_rxSequence).arg(mMessage.sequence); - } - #endif - interpretMessage(mMessage); - } -} - -/*! - * \brief MessageDispatcher::onFramesTransmit - * \details this slots calls the framesTransmit to emit the didFrameTransmit signal - * to queue the frame(s) to be sent - * \param vSequence - sequence of the message which is going to be resent. (not used) - * \param vFrameList - frame(s) to be sent - */ -void MessageDispatcher::onFramesTransmit(Can_Id vCan_Id, Sequence vSequence, const FrameList &vFrameList) -{ - Q_UNUSED(vSequence) - framesTransmit(vCan_Id, vFrameList); -} - -/*! - * \brief MessageDispatcher::onFailedTransmit - * \details the slot which will be called on transmit failed on MessageAcknowModel - * \param vSequence - */ -void MessageDispatcher::onFailedTransmit(Sequence vSequence) -{ - // disabled coco begin validated: Is a placeholder and has not been implemented yet - emit didFailedTransmit(vSequence); -} -// disabled coco end - -/*! - * \brief MessageDispatcher::onActionTransmit - * \details This slot will be called by ApplicationController::didActionTransmit - * upon UI message transmit request and calls MessageDispatcher::actionTransmit method. - * \param vActionId - The ActionID of the message - * \param vData - The data of the Message - */ -void MessageDispatcher::onActionTransmit(GuiActionType vActionId, const QVariantList &vData) -{ - actionTransmit(vActionId, vData); -} - -/*! - * \brief MessageDispatcher::onSettingsDone - * \details The slot to handle didSettingsDone signal of the ApplicationController - */ -void MessageDispatcher::onSettingsDone() -{ - _interpreter.updateUnhandledMessages(); -} - -// ---------------------------------------------------------------------------------------------------- - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Blood/Dialysate Adjustment Denali message. - * \param vData - Data model contains Blood Flow Rate - and Dialysate Flow Rate - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustBloodDialysateRequestData &vData) -{ - QVariantList mData; - mData += vData.bloodFlow; - mData += vData.dialysateFlow; - onActionTransmit(GuiActionType::ID_AdjustBloodDialysateReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the treatment duration Adjustment Denali message. - * \param vData - Data model contains treatment duration adjustment value in minutes - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDurationRequestData &vData) -{ - QVariantList mData; - mData += vData.duration; - onActionTransmit(GuiActionType::ID_AdjustDurationReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Ultrafiltration State Adjustment Denali message. - * \param vData - Data model contains treatment ultrafiltration state adjustment - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationStateRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - actionTransmit(GuiActionType::ID_AdjustUltrafiltrationStateReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Ultrafiltration Adjustment Denali message. - * \param vData - Data model contains treatment ultrafiltration adjustment volume - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationEditRequestData &vData) -{ - QVariantList mData; - mData += vData.volume; - onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationEditReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Ultrafiltration Adjustment User Selected Option Denali message. - * \param vData - Data model contains treatment ultrafiltration adjustment volume and user selected option. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationConfirmRequestData &vData) -{ - QVariantList mData; - mData += vData.volume; - mData += vData.option; - onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Alarm Request Silence message. - * \param vData - Data model contains the silence request information. - * \return void - */ -void MessageDispatcher::onAdjustment(const AlarmSilenceRequestData &vData) -{ - QVariantList mData; - mData += vData.silence; - onActionTransmit(GuiActionType::ID_AlarmSilenceReq, mData); -} - -/** - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the User Action Denali message. - * \param vData - Data model contains User Action on the alarm dialog. - * \return void - */ -void MessageDispatcher::onAdjustment(const AlarmUserActionRequestData &vData) -{ - QVariantList mData; - mData += vData.action; - onActionTransmit(GuiActionType::ID_AlarmUserActionReq, mData); -} - -/** - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Alarm Active List Request denali message. - * \param vData - Data model contains Alarm Active List payload. - * \return void - */ -void MessageDispatcher::onAdjustment(const AlarmActiveListRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AlarmActiveListReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the DG Set Date Time Denali message. - * \details This method transmits the request to set the alarm volume. - * \param vData - Data model contains the alarm volume - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustHDAlarmVolumeRequestData &vData) -{ - QVariantList mData; - mData += vData.volume2HD(); - onActionTransmit(GuiActionType::ID_AlarmVolumeSetReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Alarm Acknowledge Request message. - * \param vData - Data model contains the user acknowledge request information. - * \return void - */ -/* // disabled coco begin validated: Manually tested. This model class is a placeholder and there is no use case for this now. -void MessageDispatcher::onAdjustment(const AlarmClearedConditionRequestData &vData) -{ - QVariantList mData; - mData += vData.alarmID; - onActionTransmit(GuiActionType::ID_AlarmClearedConditionReq, mData); -} -// disabled coco end */ - -/** - * \details This method transmits the Saline Bolus Adjustment Denali message. - * \param vData - Data model contains treatment Saline Bolus adjustment state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustSalineRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustSalineReq, mData); -} - -/** - * \details This method transmits the Heparin Adjustment Denali message. - * \param vData - Data model contains treatment Heparin adjustment state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustHeparinRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustHeparinReq, mData); -} - -/** - * \details This method transmits the Rinseback Adjustment Denali message. - * \param vData - Data model contains treatment Rinseback adjustment state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustRinsebackRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustRinsebackReq, mData); -} - -/** - * \details This method transmits the Recirculate Adjustment Denali message. - * \param vData - Data model contains treatment Recirculate adjustment state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustRecirculateRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustRecirculateReq, mData); -} - -/** - * \details This method transmits the Treatment End Adjustment Denali message. - * \param vData - Data model contains treatment end adjustment state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustTreatmentEndRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustTreatmentEndReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the HD Version Denali message. - * \param vData - Data model contains HD Version state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustVersionsRequestData &vData) -{ - QVariantList mData; - mData += QVariant::fromValue(vData.ver_major); - mData += QVariant::fromValue(vData.ver_minor); - mData += QVariant::fromValue(vData.ver_micro); - mData += QVariant::fromValue(vData.ver_revis); - mData += QVariant::fromValue(vData.ver_comp ); - onActionTransmit(GuiActionType::ID_AdjustVersionsUIReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the HD Version Denali message. - * \param vData - Data model contains HD Version state. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustVersionsResponseData &vData) -{ - QVariantList mData; - mData += QVariant::fromValue(vData.ver_major); - mData += QVariant::fromValue(vData.ver_minor); - mData += QVariant::fromValue(vData.ver_micro); - mData += QVariant::fromValue(vData.ver_revis); - mData += QVariant::fromValue(vData.ver_comp ); - onActionTransmit(GuiActionType::ID_AdjustVersionsUIRsp, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the DG Set Date Time Denali message. - * \param vData - Data model contains the epoch time - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustServiceDatesRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustServiceDatesReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disposables Removal Confirm Adjustment Denali message. - * \param vData - Data model contains Service Mode adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustServiceModeRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustServiceModeReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Pressures Limits Adjustment Denali message. - * \param vData - Data model contains treatment Pressures Limits adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustPressuresLimitsRequestData &vData) -{ - QVariantList mData; - mData += vData.mArterialPressureLimitWindow ; - mData += vData.mVenousPressureLimitWindow ; - mData += vData.mVenousPressureLimitAsymtrc ; - onActionTransmit(GuiActionType::ID_AdjustPressuresLimitsReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the InitTreatment request message. - * \param vData - Data model contains the init treatment request information. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustInitTreatmentRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustInitTreatmentReq, mData); -} - -/** - * \details This method transmits the Treatment Parameter request message. - * \param vData - Data model contains treatment parameter adjustment data structure - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustParametersValidationRequestData &vData) -{ - QVariantList mData; - mData += vData.mBloodFlowRate ; - mData += vData.mDialysateFlowRate ; - mData += vData.mTreatmentDuration ; - mData += vData.mHeparinStopTime ; - mData += vData.mSalineBolus ; - mData += vData.mAcidConcentrate ; - mData += vData.mBicarbonateConcentrate ; - mData += vData.mDialyzerType ; - mData += vData.mHeparinType ; - mData += vData.mBloodPressureMeasureInterval ; - mData += vData.mRinsebackFlowRate ; - mData += vData.mArterialPressureLimitWindow ; - mData += vData.mVenousPressureLimitWindow ; - mData += vData.mVenousPressureLimitAsymtrc ; - mData += vData.mHeparinDispensingRate ; - mData += vData.mHeparinBolusVolume ; - mData += vData.mDialysateTemp ; - - onActionTransmit(GuiActionType::ID_AdjustParametersValidationReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Confirm Treatment request message. - * \param vData - Data model contains the confirm treatment request - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustParametersConfirmRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustParametersConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Water Sample Adjustment Denali message. - * \param vData - Data model contains Water Sample adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustWaterSampleRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustWaterSampleReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Water Sample Result Adjustment Denali message. - * \param vData - Data model contains Water Sample Result adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustWaterSampleResultRequestData &vData) -{ - QVariantList mData; - mData += vData.requestedState; - onActionTransmit(GuiActionType::ID_AdjustWaterSampleResultReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Consumables Confirm Adjustment Denali message. - * \param vData - Data model contains Consumables Confirm adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustConsumablesConfirmRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustConsumablesConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disposables Confirm Adjustment Denali message. - * \param vData - Data model contains Disposables Confirm adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDisposablesConfirmRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustDisposablesConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disposables Prime Adjustment Denali message. - * \param vData - Data model contains Disposables Prime adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDisposablesPrimeRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustDisposablesPrimeReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Patient Connection Begin Adjustment Denali message. - * \param vData - Data model contains Patient Connection Begin adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustPatientConnectionBeginRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustPatientConnectionBeginReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Ultrafiltration Adjustment Denali message. - * \param vData - Data model contains treatment ultrafiltration adjustment volume - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationInitRequestData &vData) -{ - QVariantList mData; - mData += vData.volume; - onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationInitReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Patient Connection Confirm Adjustment Denali message. - * \param vData - Data model contains Patient Connection Confirm adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustPatientConnectionConfirmRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustPatientConnectionConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Start Treatment Adjustment Denali message. - * \param vData - Data model contains Start Treatment adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustStartTreatmentRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustStartTreatmentReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Patient Disconnection Notify Adjustment Denali message. - * \param vData - Data model contains Patient Disconnection Notify adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustPatientDisconnectionNotifyRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustPatientDisconnectNotifyReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Patient Disconnection Confirm Adjustment Denali message. - * \param vData - Data model contains Patient Disconnection Confirm adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustPatientDisconnectionConfirmRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustPatientDisconnectConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disposables Removal Confirm Adjustment Denali message. - * \param vData - Data model contains Disposables Removal Confirm adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDisposablesRemovalConfirmRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustDisposablesRemovalConfirmReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Treatment Log Adjustment Denali message. - * \param vData - Data model contains Treatment Log adjustment. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustTreatmentLogRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustTreatmentLogReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the HD Set Date Time Denali message. - * \param vData - Data model contains the epoch time - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustHDDateTimeRequestData &vData) -{ - QVariantList mData; - mData += vData.mEpoch ; - onActionTransmit(GuiActionType::ID_AdjustHDDateTimeReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the DG Set Date Time Denali message. - * \param vData - Data model contains the epoch time - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDGDateTimeRequestData &vData) -{ - QVariantList mData; - mData += vData.mEpoch ; - onActionTransmit(GuiActionType::ID_AdjustDGDateTimeReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the DG Cleaning Usage request message. - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDGCleaningUsageRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustDGCleaningUsageReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disinfect request Denali message. - * \param vData - Data model contains the epoch time - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDisinfectStartRequestData &vData) -{ - QVariantList mData; - mData += vData.mState; - onActionTransmit(GuiActionType::ID_AdjustDisinfectStartReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the Disinfect mode request Denali message. - * \param vData - Data model contains the initiate/cancel boolean disinfection mode - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustDisinfectModeRequestData &vData) -{ - QVariantList mData; - mData += vData.mInititate; - onActionTransmit(GuiActionType::ID_AdjustDisinfectModeReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the UI power on self test final result - * \param vData - Data model contains the final result of the UI POST - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustUIPostFinalResultRequestData &vData) -{ - QVariantList mData; - mData += vData.mResult; - onActionTransmit(GuiActionType::ID_UIPostFinalResultData, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the UI Generic User Confirmation result. - * \param vData - Data model contains the Generic User Confirmation result. - * \return void - */ -void MessageDispatcher::onAdjustment(const DuetConfirmUIrData &vData) -{ - QVariantList mData; - mData += vData.mId; - mData += vData.mConfirm; - onActionTransmit(GuiActionType::ID_DuetConfirmUIr, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the UI RO Water Mode adjustment request. - * \param vData - Data model contains the UI RO Water Mode adjustment request data. - * \return void - */ -void MessageDispatcher::onAdjustment(const DuetRoWaterModeUIiData &vData) -{ - QVariantList mData; - mData += vData.mStatus; - onActionTransmit(GuiActionType::ID_DuetRoWaterModeUIi, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the HD Usage Info Denali message. - * \param vData - - * \return void - */ -void MessageDispatcher::onAdjustment(const HDUsageInfoRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_HDUsageInfoReq, mData); -} - -/*! - * \brief MessageDispatcher::onAdjustment - * \details This method transmits the HD institutional record request to HD. - * \param vData - - * \return void - */ -void MessageDispatcher::onAdjustment(const AdjustInstitutionalRequestData &) -{ - QVariantList mData; - onActionTransmit(GuiActionType::ID_AdjustInstitutionalRecordReq, mData); -} -// ---------------------------------------------------------------------------------------------------- - -/*! - * \brief MessageDispatcher::actionTransmit - * \details This method is called by slot MessageDispatcher::onActionTransmit - * which emits didFrameTransmit on successful interpretation of the requested message - * and successfully creating of frame(s). - * \param vActionId - The ActionID of the message - * \param vData - The data of the Message - */ -void MessageDispatcher::actionTransmit(GuiActionType vActionId, const QVariantList &vData, Sequence vSequence, Can_Id vCanId) -{ - txCount(); - if (vSequence == 0) { // initialize - // it's obvious that this assignment does not effect outside of the function. - // but is make it easier to just assume the correct value has been passed - // and still using the same variable (function parameter) as a local variable. - vSequence = _txSequence; - } - - QByteArray mData; - Can_Id canid = vCanId; - if (! _interpreter.interpretMessage(vActionId, vData, mData, canid)) { - LOG_DEBUG(QString("Incorrect Message, cannot be interpreted, %1").arg(Format::toHexString(vActionId))); // TODO : LogInfo Improvement - return; - } - - // TODO : Create a buildFrames method - FrameList frameList; - Sequence mSequence = vSequence; - bool mNeedsAcknow = needsAcknow(vActionId); - if (mNeedsAcknow) { - mSequence = -mSequence; - if ( ! gDisableAcknowLog ) { - LOG_APPED(tr("UI,Ack Req, Sq:%1, ID:%2").arg(mSequence).arg(Format::toHexString(vActionId))); - } -#ifdef DEBUG_ACKBACK_HD_TO_UI - qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckReq : %1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence); -#endif - } - - // disabled coco begin validated: Has been tested manually but in this function this cannot be false because the message interpreter is doing the same validation. - // still checking here in case the logic has changed therefore buildFrame should still validate the message for developer safety. - if ( ! _builder.buildFrames(vActionId, mData, frameList, mSequence) ) { - LOG_DEBUG(QString("Incorrect Message cannot be built")); // TODO : LogInfo Improvement - return; - } - // disabled coco end - if (mNeedsAcknow) { - // NOTE : here vSequence should be used which is not negative - // because when we get the Acknow it is not the negative - // since it does not need Re-Acknow - // and this is the sequence number which will be used - // to remove the message from the Acknow list. - emit didAcknowTransmit(canid, vSequence, frameList); - } - framesTransmit(canid, frameList); -} - -/*! - * \brief MessageDispatcher::framesTransmit - * \details iterates through all the frames and emits to send the frames - * \param vCan_Id - The channel to send the frames to - * \param vFrameList - List of the frames to be sent - */ -void MessageDispatcher::framesTransmit(Can_Id vCan_Id, const FrameList &vFrameList) -{ - for (const auto &frame : vFrameList) { - emit didFrameTransmit(vCan_Id, frame); - } -} - -/*! - * \brief MessageDispatcher::buildMessage - * \details Calls the messageBuilder buildMessage method. - * \param vCan_Id - CANBus channel of the frame - * \param vPayload - Payload of the frame - * \return false on error - */ -bool MessageDispatcher::buildMessage(Can_Id vCan_Id, const QByteArray &vPayload) -{ - if (vPayload.length() < eLenCanFrame) { - // Each frame has to have exactly 8 (eLenCanFrame) bytes of data and unused bytes should be passed as 00. - LOG_DEBUG(QString("Incorrect frame length. Exp:%1,got:%2").arg(eLenCanFrame).arg(vPayload.length())); - return false; - } - if (! _builder.buildMessage(vPayload, _messageList[vCan_Id].last(), vCan_Id)) { - _messageList[vCan_Id].removeLast(); - return false; - } - return true; -} - -/*! - * \brief MessageDispatcher::checkAcknowReceived - * \details check if the message was an acknowledge. - * \param vMessage - The received message - * \param vSrcText - The source entity identifier text - * \return true if the message is Acknow - */ -bool MessageDispatcher::checkAcknowReceived(const Message &vMessage, const QString &vSrcText) -{ - GuiActionType mActionId = vMessage.actionId; - Sequence mSequence = vMessage.sequence; - bool ok = false; - if ( mActionId == GuiActionType::ID_Acknow ) { - ok = true; - if ( ! gDisableAcknowLog ) { - LOG_APPED(tr("%1,Ack Bak, Sq:%2").arg(vSrcText).arg(mSequence)); - } - #ifdef DEBUG_ACKBACK_HD_TO_UI - qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HD AckBak : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence).arg(vMessage.actionId); - #endif - emit didAcknowReceive(mSequence); - } - return ok; -} - -/*! - * \brief MessageDispatcher::checkAcknowTransmit - * \details Sends the Ack message. - * \param vMessage - the received message which to check if it may need Ack. - * \param vSrcText - the source of the message which needs Ack. - * \return returns true if the message needs an Ack (Negative Sequence) - */ -bool MessageDispatcher::checkAcknowTransmit(const Message &vMessage, const QString &vSrcText) -{ - bool ok = false; - GuiActionType mActionId = vMessage.actionId; - Sequence mSequence = vMessage.sequence; - - // UI shall acknowledge the messages is intended for UI. - if ( ! needsAcknow(vMessage.can_id)) return ok; // false - - if (mSequence < 0) { - ok = true; - if ( ! gDisableAcknowLog ) { - LOG_APPED(tr("%1,Ack Req, Sq:%2, ID:%3").arg(vSrcText).arg(mSequence).arg(Format::toHexString(mActionId))); - } - #ifdef DEBUG_ACKBACK_HD_TO_UI - qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HD AckReq : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence).arg(vMessage.actionId); - #endif - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckBak is immediately handled at the same place. - QString dstText; - Can_Id dstID = MessageInterpreter::identifyDestination(vMessage.can_id, &dstText); - actionTransmit(GuiActionType::ID_Acknow, {}, -mSequence, dstID); - if ( ! gDisableAcknowLog ) { - LOG_APPED(tr("UI,Ack Bak, Sq:%1, Dst:%2").arg(-mSequence).arg(dstText)); - } - #ifdef DEBUG_ACKBACK_HD_TO_UI - qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckBak : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(-mSequence).arg(vMessage.actionId); - #endif - } - return ok; -} - -/*! - * \brief MessageDispatcher::interpretMessage - * \details Calls the MessageInterpreter interpretMessage method - * Regarding the Message Id and the sequence emit different signals - * to handle the normal or acknowledge messages. - * \param vMessage - The Message - * \return false on error - */ -bool MessageDispatcher::interpretMessage(const Message &vMessage) -{ - bool ok = false; - QVariantList mData; - QString srcText; - MessageInterpreter::identifySource(vMessage.can_id, &srcText); - if ( ! checkAcknowReceived(vMessage, srcText) ) { // check if the message was an acknowledge. - checkAcknowTransmit(vMessage, srcText); // then if needs acknow send it immediately. - } - - // disabled coco begin validated: Has been validated manually. If MessageInterpreter::interpretMessage fails, this also returns false - if ( _interpreter.interpretMessage( vMessage, mData ) ) { - ok = true; - emit didActionReceive(vMessage.actionId, mData); - } - // disabled coco end - - _messageList[vMessage.can_id].removeLast(); - return ok; -} - -/*! - * \brief MessageDispatcher::rxCount - * \details count received messages up the size of the Sequence type size - * \return message count - */ -Sequence MessageDispatcher::rxCount() -{ - // disabled coco begin validated: has been manually validated since it requires so many received messages to reset the seq - if ( _rxSequence < SEQUENCE_MAX ) { - ++_rxSequence; - } else { - _rxSequence = 1; - } - return _rxSequence; -} -// disabled coco end - -/*! - * \brief MessageDispatcher::txCount - * \details count transmitted messages up the size of the Sequence type size - * \return message count - */ -Sequence MessageDispatcher::txCount() -{ - // disabled coco begin validated: has been manually validated since it requires so many received messages to reset the seq - if ( _txSequence < SEQUENCE_MAX ) { - ++_txSequence; - } else { - _txSequence = 1; - } - return _txSequence; -} -// disabled coco end - -/*! - * \brief MessageDispatcher::needsAcknow - * \details List of the Action types which need Acknow - * \param vActionId - Action Type id - * \return true if needs an Acknow - */ -bool MessageDispatcher::needsAcknow(GuiActionType vActionId) -{ - return _needsAcknow.contains(vActionId); -} - -/*! - * \brief MessageDispatcher::needsAcknow - * \details List of the CAN channels which need Acknow - * \param vCan_Id - The message CANBus id - * \return true if needs an Acknow - */ -bool MessageDispatcher::needsAcknow(Can_Id vCan_Id) -{ - bool ok = true; - switch(vCan_Id) { // list if the channels UI shall not Ack - case eChlid_HD_DG : // 0x008, ///< HD => DG - case eChlid_DG_HD : // 0x010, ///< DG => HD - case eDialin_HD : // 0x400, ///< dialin => HD - case eHD_Dialin : // 0x401, ///< HD => dialin - case eDialin_DG : // 0x402, ///< dialin => DG - case eDG_Dialin : // 0x403, ///< DG => dialin - ok = false; break; - - default: break; - } - - return ok; -} +/*! + * + * 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 MessageDispatcher.cpp + * \author (last) Dara Navaei + * \date (last) 28-Feb-2024 + * \author (original) Behrouz NematiPour + * \date (original) 26-Aug-2020 + * + */ +#include "MessageDispatcher.h" + +// Qt +#include +#include + +// Project +#include "Logger.h" +#include "ApplicationController.h" +#include "FrameInterface.h" +#include "MessageAcknowModel.h" + +//#define DEBUG_ACKBACK_HD_TO_UI +//#define DEBUG_OUT_OF_SYNC + +using namespace Can; + +/*! + * \brief MessageDispatcher::MessageDispatcher + * \details Constructor + * \param parent - QObject parent owner object. + * Qt handles the children destruction by their parent objects life-cycle. + */ +MessageDispatcher::MessageDispatcher(QObject *parent) : QObject(parent) { } + +/*! + * \brief Message Handler initializer + */ +bool MessageDispatcher::init() +{ + if ( _init ) return false; + _init = true; + + // runs in the thread + initConnections(); + + LOG_DEBUG(tr("%1 Initialized").arg(metaObject()->className())); + + return true; +} + +/*! + * \brief MessageDispatcher::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 MessageDispatcher::init(QThread &vThread) +{ + if ( ! init() ) return false; + initThread(vThread); + return true; +} + +/*! + * \brief MessageDispatcher::quit + * \details quits the class + * Calls quitThread + */ +void MessageDispatcher::quit() +{ + // disabled coco begin validated: Application termination is not correctly done in coco!!! + // it has been tested and works perfectly fine in normal run. + quitThread(); // validated +} +// disabled coco end + +/*! + * \brief Message Handler connections definition + * \details Initializes the required signal/slot connection between this class and other objects + * to be able to communicate. + */ +void MessageDispatcher::initConnections() +{ + // From GUI + connect(&_ApplicationController, SIGNAL(didActionTransmit(GuiActionType , const QVariantList &)), + this , SLOT( onActionTransmit(GuiActionType , const QVariantList &))); + + // From HD + connect(&_FrameInterface , SIGNAL(didFrameReceive (Can_Id , const QByteArray &)), + this , SLOT( onFrameReceive (Can_Id , const QByteArray &))); + + // From Message Acknow Model timer timeout. + connect(&_MessageAcknowModel , SIGNAL(didFramesTransmit(Can_Id, Sequence, const FrameList &)), + this , SLOT( onFramesTransmit(Can_Id, Sequence, const FrameList &))); + + connect(&_MessageAcknowModel , SIGNAL(didFailedTransmit( Sequence )), + this , SLOT( onFailedTransmit( Sequence ))); + + // Application Settings are ready + connect(&_ApplicationController, SIGNAL(didSettingsDone ()), + this , SLOT( onSettingsDone ())); + + // ---- Signal/Slots + ADJUST_TRANSMT_MODEL_BRIDGE_CONNECTIONS(_ApplicationController) + ACTION_RECEIVE_MODEL_BRIDGE_CONNECTIONS(_interpreter ) +} + +/*! + * \brief ApplicationController::initThread + * \details Moves this object into the thread vThread. + * And checks that this method is called from main thread. + * Also connects quitThread to application aboutToQuit. + * \param vThread - the thread + */ +void MessageDispatcher::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 MessageDispatcher::quitThread + * \details Moves this object to main thread to be handled by QApplication + * And to be destroyed there. + */ +void MessageDispatcher::quitThread() +{ + // disabled coco begin validated: Application 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 MessageDispatcher::onFrameReceive + * \details Upon message has been received over CANBus this slot will be called + * by FrameInterface::didFrameReceive signal to process the frame + * Upon completion of collected all the required frames + * on successful interpretation of the message, emits didActionReceived signal. + * The message will be removed from list of the channel vCan_Id messages. + * \param vCan_Id - CANBus channel of the frame + * \param vPayload - Payload of the frame + */ +void MessageDispatcher::onFrameReceive(Can_Id vCan_Id, const QByteArray &vPayload) +{ + // Append a message to the list + // disabled coco begin validated: if empty (first condition) is true, it must never check for the complete (second condition) + // because if the list is empty there is no last() item + if (_messageList[vCan_Id].isEmpty() || _messageList[vCan_Id].last().isComplete()) { + // disabled coco end + _messageList[vCan_Id].append(Message()); + } + + // build the message and check. + if (! buildMessage(vCan_Id, vPayload)) { + return; + } + Message mMessage = _messageList[vCan_Id].last(); + + // TODO : must be moved to a MessageModel class + if (mMessage.isComplete()) { + rxCount(); + #ifdef DEBUG_OUT_OF_SYNC + if (_rxSequence != mMessage.sequence) { + qDebug() << tr("Out of Sync : %1 , %2").arg(_rxSequence).arg(mMessage.sequence); + } + #endif + interpretMessage(mMessage); + } +} + +/*! + * \brief MessageDispatcher::onFramesTransmit + * \details this slots calls the framesTransmit to emit the didFrameTransmit signal + * to queue the frame(s) to be sent + * \param vSequence - sequence of the message which is going to be resent. (not used) + * \param vFrameList - frame(s) to be sent + */ +void MessageDispatcher::onFramesTransmit(Can_Id vCan_Id, Sequence vSequence, const FrameList &vFrameList) +{ + Q_UNUSED(vSequence) + framesTransmit(vCan_Id, vFrameList); +} + +/*! + * \brief MessageDispatcher::onFailedTransmit + * \details the slot which will be called on transmit failed on MessageAcknowModel + * \param vSequence + */ +void MessageDispatcher::onFailedTransmit(Sequence vSequence) +{ + // disabled coco begin validated: Is a placeholder and has not been implemented yet + emit didFailedTransmit(vSequence); +} +// disabled coco end + +/*! + * \brief MessageDispatcher::onActionTransmit + * \details This slot will be called by ApplicationController::didActionTransmit + * upon UI message transmit request and calls MessageDispatcher::actionTransmit method. + * \param vActionId - The ActionID of the message + * \param vData - The data of the Message + */ +void MessageDispatcher::onActionTransmit(GuiActionType vActionId, const QVariantList &vData) +{ + actionTransmit(vActionId, vData); +} + +/*! + * \brief MessageDispatcher::onSettingsDone + * \details The slot to handle didSettingsDone signal of the ApplicationController + */ +void MessageDispatcher::onSettingsDone() +{ + _interpreter.updateUnhandledMessages(); +} + +// ---------------------------------------------------------------------------------------------------- + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Blood/Dialysate Adjustment Denali message. + * \param vData - Data model contains Blood Flow Rate + and Dialysate Flow Rate + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustBloodDialysateRequestData &vData) +{ + QVariantList mData; + mData += vData.bloodFlow; + mData += vData.dialysateFlow; + onActionTransmit(GuiActionType::ID_AdjustBloodDialysateReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the treatment duration Adjustment Denali message. + * \param vData - Data model contains treatment duration adjustment value in minutes + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDurationRequestData &vData) +{ + QVariantList mData; + mData += vData.duration; + onActionTransmit(GuiActionType::ID_AdjustDurationReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Ultrafiltration State Adjustment Denali message. + * \param vData - Data model contains treatment ultrafiltration state adjustment + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationStateRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + actionTransmit(GuiActionType::ID_AdjustUltrafiltrationStateReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Ultrafiltration Adjustment Denali message. + * \param vData - Data model contains treatment ultrafiltration adjustment volume + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationEditRequestData &vData) +{ + QVariantList mData; + mData += vData.volume; + onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationEditReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Ultrafiltration Adjustment User Selected Option Denali message. + * \param vData - Data model contains treatment ultrafiltration adjustment volume and user selected option. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationConfirmRequestData &vData) +{ + QVariantList mData; + mData += vData.volume; + mData += vData.option; + onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Alarm Request Silence message. + * \param vData - Data model contains the silence request information. + * \return void + */ +void MessageDispatcher::onAdjustment(const AlarmSilenceRequestData &vData) +{ + QVariantList mData; + mData += vData.silence; + onActionTransmit(GuiActionType::ID_AlarmSilenceReq, mData); +} + +/** + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the User Action Denali message. + * \param vData - Data model contains User Action on the alarm dialog. + * \return void + */ +void MessageDispatcher::onAdjustment(const AlarmUserActionRequestData &vData) +{ + QVariantList mData; + mData += vData.action; + onActionTransmit(GuiActionType::ID_AlarmUserActionReq, mData); +} + +/** + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Alarm Active List Request denali message. + * \param vData - Data model contains Alarm Active List payload. + * \return void + */ +void MessageDispatcher::onAdjustment(const AlarmActiveListRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AlarmActiveListReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the DG Set Date Time Denali message. + * \details This method transmits the request to set the alarm volume. + * \param vData - Data model contains the alarm volume + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustHDAlarmVolumeRequestData &vData) +{ + QVariantList mData; + mData += vData.volume2HD(); + onActionTransmit(GuiActionType::ID_AlarmVolumeSetReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Alarm Acknowledge Request message. + * \param vData - Data model contains the user acknowledge request information. + * \return void + */ +/* // disabled coco begin validated: Manually tested. This model class is a placeholder and there is no use case for this now. +void MessageDispatcher::onAdjustment(const AlarmClearedConditionRequestData &vData) +{ + QVariantList mData; + mData += vData.alarmID; + onActionTransmit(GuiActionType::ID_AlarmClearedConditionReq, mData); +} +// disabled coco end */ + +/** + * \details This method transmits the Saline Bolus Adjustment Denali message. + * \param vData - Data model contains treatment Saline Bolus adjustment state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustSalineRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustSalineReq, mData); +} + +/** + * \details This method transmits the Heparin Adjustment Denali message. + * \param vData - Data model contains treatment Heparin adjustment state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustHeparinRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustHeparinReq, mData); +} + +/** + * \details This method transmits the Rinseback Adjustment Denali message. + * \param vData - Data model contains treatment Rinseback adjustment state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustRinsebackRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustRinsebackReq, mData); +} + +/** + * \details This method transmits the Recirculate Adjustment Denali message. + * \param vData - Data model contains treatment Recirculate adjustment state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustRecirculateRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustRecirculateReq, mData); +} + +/** + * \details This method transmits the Treatment End Adjustment Denali message. + * \param vData - Data model contains treatment end adjustment state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustTreatmentEndRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustTreatmentEndReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the HD Version Denali message. + * \param vData - Data model contains HD Version state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustVersionsRequestData &vData) +{ + QVariantList mData; + mData += QVariant::fromValue(vData.ver_major); + mData += QVariant::fromValue(vData.ver_minor); + mData += QVariant::fromValue(vData.ver_micro); + mData += QVariant::fromValue(vData.ver_revis); + mData += QVariant::fromValue(vData.ver_comp ); + onActionTransmit(GuiActionType::ID_AdjustVersionsUIReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the HD Version Denali message. + * \param vData - Data model contains HD Version state. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustVersionsResponseData &vData) +{ + QVariantList mData; + mData += QVariant::fromValue(vData.ver_major); + mData += QVariant::fromValue(vData.ver_minor); + mData += QVariant::fromValue(vData.ver_micro); + mData += QVariant::fromValue(vData.ver_revis); + mData += QVariant::fromValue(vData.ver_comp ); + onActionTransmit(GuiActionType::ID_AdjustVersionsUIRsp, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the DG Set Date Time Denali message. + * \param vData - Data model contains the epoch time + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustServiceDatesRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustServiceDatesReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disposables Removal Confirm Adjustment Denali message. + * \param vData - Data model contains Service Mode adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustServiceModeRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustServiceModeReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Pressures Limits Adjustment Denali message. + * \param vData - Data model contains treatment Pressures Limits adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustPressuresLimitsRequestData &vData) +{ + QVariantList mData; + mData += vData.mArterialPressureLimitWindow ; + mData += vData.mVenousPressureLimitWindow ; + mData += vData.mVenousPressureLimitAsymtrc ; + onActionTransmit(GuiActionType::ID_AdjustPressuresLimitsReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the InitTreatment request message. + * \param vData - Data model contains the init treatment request information. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustInitTreatmentRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustInitTreatmentReq, mData); +} + +/** + * \details This method transmits the Treatment Parameter request message. + * \param vData - Data model contains treatment parameter adjustment data structure + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustParametersValidationRequestData &vData) +{ + QVariantList mData; + mData += vData.mBloodFlowRate ; + mData += vData.mDialysateFlowRate ; + mData += vData.mTreatmentDuration ; + mData += vData.mHeparinStopTime ; + mData += vData.mSalineBolus ; + mData += vData.mAcidConcentrate ; + mData += vData.mBicarbonateConcentrate ; + mData += vData.mDialyzerType ; + mData += vData.mHeparinType ; + mData += vData.mBloodPressureMeasureInterval ; + mData += vData.mRinsebackFlowRate ; + mData += vData.mArterialPressureLimitWindow ; + mData += vData.mVenousPressureLimitWindow ; + mData += vData.mVenousPressureLimitAsymtrc ; + mData += vData.mHeparinDispensingRate ; + mData += vData.mHeparinBolusVolume ; + mData += vData.mDialysateTemp ; + + onActionTransmit(GuiActionType::ID_AdjustParametersValidationReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Confirm Treatment request message. + * \param vData - Data model contains the confirm treatment request + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustParametersConfirmRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustParametersConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Water Sample Adjustment Denali message. + * \param vData - Data model contains Water Sample adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustWaterSampleRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustWaterSampleReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Water Sample Result Adjustment Denali message. + * \param vData - Data model contains Water Sample Result adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustWaterSampleResultRequestData &vData) +{ + QVariantList mData; + mData += vData.requestedState; + onActionTransmit(GuiActionType::ID_AdjustWaterSampleResultReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Consumables Confirm Adjustment Denali message. + * \param vData - Data model contains Consumables Confirm adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustConsumablesConfirmRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustConsumablesConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disposables Confirm Adjustment Denali message. + * \param vData - Data model contains Disposables Confirm adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDisposablesConfirmRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustDisposablesConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disposables Prime Adjustment Denali message. + * \param vData - Data model contains Disposables Prime adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDisposablesPrimeRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustDisposablesPrimeReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Patient Connection Begin Adjustment Denali message. + * \param vData - Data model contains Patient Connection Begin adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustPatientConnectionBeginRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustPatientConnectionBeginReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Ultrafiltration Adjustment Denali message. + * \param vData - Data model contains treatment ultrafiltration adjustment volume + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustUltrafiltrationInitRequestData &vData) +{ + QVariantList mData; + mData += vData.volume; + onActionTransmit(GuiActionType::ID_AdjustUltrafiltrationInitReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Patient Connection Confirm Adjustment Denali message. + * \param vData - Data model contains Patient Connection Confirm adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustPatientConnectionConfirmRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustPatientConnectionConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Start Treatment Adjustment Denali message. + * \param vData - Data model contains Start Treatment adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustStartTreatmentRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustStartTreatmentReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Patient Disconnection Notify Adjustment Denali message. + * \param vData - Data model contains Patient Disconnection Notify adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustPatientDisconnectionNotifyRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustPatientDisconnectNotifyReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Patient Disconnection Confirm Adjustment Denali message. + * \param vData - Data model contains Patient Disconnection Confirm adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustPatientDisconnectionConfirmRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustPatientDisconnectConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disposables Removal Confirm Adjustment Denali message. + * \param vData - Data model contains Disposables Removal Confirm adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDisposablesRemovalConfirmRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustDisposablesRemovalConfirmReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Treatment Log Adjustment Denali message. + * \param vData - Data model contains Treatment Log adjustment. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustTreatmentLogRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustTreatmentLogReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the HD Set Date Time Denali message. + * \param vData - Data model contains the epoch time + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustHDDateTimeRequestData &vData) +{ + QVariantList mData; + mData += vData.mEpoch ; + onActionTransmit(GuiActionType::ID_AdjustHDDateTimeReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the DG Set Date Time Denali message. + * \param vData - Data model contains the epoch time + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDGDateTimeRequestData &vData) +{ + QVariantList mData; + mData += vData.mEpoch ; + onActionTransmit(GuiActionType::ID_AdjustDGDateTimeReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the DG Cleaning Usage request message. + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDGCleaningUsageRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustDGCleaningUsageReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disinfect request Denali message. + * \param vData - Data model contains the epoch time + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDisinfectStartRequestData &vData) +{ + QVariantList mData; + mData += vData.mState; + onActionTransmit(GuiActionType::ID_AdjustDisinfectStartReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the Disinfect mode request Denali message. + * \param vData - Data model contains the initiate/cancel boolean disinfection mode + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustDisinfectModeRequestData &vData) +{ + QVariantList mData; + mData += vData.mInititate; + onActionTransmit(GuiActionType::ID_AdjustDisinfectModeReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the UI power on self test final result + * \param vData - Data model contains the final result of the UI POST + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustUIPostFinalResultRequestData &vData) +{ + QVariantList mData; + mData += vData.mResult; + onActionTransmit(GuiActionType::ID_UIPostFinalResultData, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the UI Generic User Confirmation result. + * \param vData - Data model contains the Generic User Confirmation result. + * \return void + */ +void MessageDispatcher::onAdjustment(const DuetConfirmUIrData &vData) +{ + QVariantList mData; + mData += vData.mId; + mData += vData.mConfirm; + onActionTransmit(GuiActionType::ID_DuetConfirmUIr, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the UI RO Water Mode adjustment request. + * \param vData - Data model contains the UI RO Water Mode adjustment request data. + * \return void + */ +void MessageDispatcher::onAdjustment(const DuetRoWaterModeUIiData &vData) +{ + QVariantList mData; + mData += vData.mStatus; + onActionTransmit(GuiActionType::ID_DuetRoWaterModeUIi, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the HD Usage Info Denali message. + * \param vData - + * \return void + */ +void MessageDispatcher::onAdjustment(const HDUsageInfoRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_HDUsageInfoReq, mData); +} + +/*! + * \brief MessageDispatcher::onAdjustment + * \details This method transmits the HD institutional record request to HD. + * \param vData - + * \return void + */ +void MessageDispatcher::onAdjustment(const AdjustInstitutionalRequestData &) +{ + QVariantList mData; + onActionTransmit(GuiActionType::ID_AdjustInstitutionalRecordReq, mData); +} +// ---------------------------------------------------------------------------------------------------- + +/*! + * \brief MessageDispatcher::actionTransmit + * \details This method is called by slot MessageDispatcher::onActionTransmit + * which emits didFrameTransmit on successful interpretation of the requested message + * and successfully creating of frame(s). + * \param vActionId - The ActionID of the message + * \param vData - The data of the Message + */ +void MessageDispatcher::actionTransmit(GuiActionType vActionId, const QVariantList &vData, Sequence vSequence, Can_Id vCanId) +{ + txCount(); + if (vSequence == 0) { // initialize + // it's obvious that this assignment does not effect outside of the function. + // but is make it easier to just assume the correct value has been passed + // and still using the same variable (function parameter) as a local variable. + vSequence = _txSequence; + } + + QByteArray mData; + Can_Id canid = vCanId; + if (! _interpreter.interpretMessage(vActionId, vData, mData, canid)) { + LOG_DEBUG(QString("Incorrect Message, cannot be interpreted, %1").arg(Format::toHexString(vActionId))); // TODO : LogInfo Improvement + return; + } + + // TODO : Create a buildFrames method + FrameList frameList; + Sequence mSequence = vSequence; + bool mNeedsAcknow = needsAcknow(vActionId); + if (mNeedsAcknow) { + mSequence = -mSequence; + if ( ! gDisableAcknowLog ) { + LOG_APPED_UI(tr("Ack Req, Sq:%1, ID:%2").arg(mSequence).arg(Format::toHexString(vActionId))); + } +#ifdef DEBUG_ACKBACK_HD_TO_UI + qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckReq : %1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence); +#endif + } + + // disabled coco begin validated: Has been tested manually but in this function this cannot be false because the message interpreter is doing the same validation. + // still checking here in case the logic has changed therefore buildFrame should still validate the message for developer safety. + if ( ! _builder.buildFrames(vActionId, mData, frameList, mSequence) ) { + LOG_DEBUG(QString("Incorrect Message cannot be built")); // TODO : LogInfo Improvement + return; + } + // disabled coco end + if (mNeedsAcknow) { + // NOTE : here vSequence should be used which is not negative + // because when we get the Acknow it is not the negative + // since it does not need Re-Acknow + // and this is the sequence number which will be used + // to remove the message from the Acknow list. + emit didAcknowTransmit(canid, vSequence, frameList); + } + framesTransmit(canid, frameList); +} + +/*! + * \brief MessageDispatcher::framesTransmit + * \details iterates through all the frames and emits to send the frames + * \param vCan_Id - The channel to send the frames to + * \param vFrameList - List of the frames to be sent + */ +void MessageDispatcher::framesTransmit(Can_Id vCan_Id, const FrameList &vFrameList) +{ + for (const auto &frame : vFrameList) { + emit didFrameTransmit(vCan_Id, frame); + } +} + +/*! + * \brief MessageDispatcher::buildMessage + * \details Calls the messageBuilder buildMessage method. + * \param vCan_Id - CANBus channel of the frame + * \param vPayload - Payload of the frame + * \return false on error + */ +bool MessageDispatcher::buildMessage(Can_Id vCan_Id, const QByteArray &vPayload) +{ + if (vPayload.length() < eLenCanFrame) { + // Each frame has to have exactly 8 (eLenCanFrame) bytes of data and unused bytes should be passed as 00. + LOG_DEBUG(QString("Incorrect frame length. Exp:%1,got:%2").arg(eLenCanFrame).arg(vPayload.length())); + return false; + } + if (! _builder.buildMessage(vPayload, _messageList[vCan_Id].last(), vCan_Id)) { + _messageList[vCan_Id].removeLast(); + return false; + } + return true; +} + +/*! + * \brief MessageDispatcher::checkAcknowReceived + * \details check if the message was an acknowledge. + * \param vMessage - The received message + * \param vSrcText - The source entity identifier text + * \return true if the message is Acknow + */ +bool MessageDispatcher::checkAcknowReceived(const Message &vMessage, const QString &vSrcText) +{ + GuiActionType mActionId = vMessage.actionId; + Sequence mSequence = vMessage.sequence; + bool ok = false; + if ( mActionId == GuiActionType::ID_Acknow ) { + ok = true; + if ( ! gDisableAcknowLog ) { + LOG_APPED(tr(" ,%1,Ack Bak, Sq:%2").arg(vSrcText).arg(mSequence)); + } + #ifdef DEBUG_ACKBACK_HD_TO_UI + qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HD AckBak : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence).arg(vMessage.actionId); + #endif + emit didAcknowReceive(mSequence); + } + return ok; +} + +/*! + * \brief MessageDispatcher::checkAcknowTransmit + * \details Sends the Ack message. + * \param vMessage - the received message which to check if it may need Ack. + * \param vSrcText - the source of the message which needs Ack. + * \return returns true if the message needs an Ack (Negative Sequence) + */ +bool MessageDispatcher::checkAcknowTransmit(const Message &vMessage, const QString &vSrcText) +{ + bool ok = false; + GuiActionType mActionId = vMessage.actionId; + Sequence mSequence = vMessage.sequence; + + // UI shall acknowledge the messages is intended for UI. + if ( ! needsAcknow(vMessage.can_id)) return ok; // false + + if (mSequence < 0) { + ok = true; + if ( ! gDisableAcknowLog ) { + LOG_APPED(tr(" ,%1,Ack Req, Sq:%2, ID:%3").arg(vSrcText).arg(mSequence).arg(Format::toHexString(mActionId))); + } + #ifdef DEBUG_ACKBACK_HD_TO_UI + qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HD AckReq : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(mSequence).arg(vMessage.actionId); + #endif + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckBak is immediately handled at the same place. + QString dstText; + Can_Id dstID = MessageInterpreter::identifyDestination(vMessage.can_id, &dstText); + actionTransmit(GuiActionType::ID_Acknow, {}, -mSequence, dstID); + if ( ! gDisableAcknowLog ) { + LOG_APPED_UI(tr("Ack Bak, Sq:%1, Dst:%2").arg(-mSequence).arg(dstText)); + } + #ifdef DEBUG_ACKBACK_HD_TO_UI + qDebug() << tr(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UI AckBak : %1 %2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ").arg(-mSequence).arg(vMessage.actionId); + #endif + } + return ok; +} + +/*! + * \brief MessageDispatcher::interpretMessage + * \details Calls the MessageInterpreter interpretMessage method + * Regarding the Message Id and the sequence emit different signals + * to handle the normal or acknowledge messages. + * \param vMessage - The Message + * \return false on error + */ +bool MessageDispatcher::interpretMessage(const Message &vMessage) +{ + bool ok = false; + QVariantList mData; + QString srcText; + MessageInterpreter::identifySource(vMessage.can_id, &srcText); + if ( ! checkAcknowReceived(vMessage, srcText) ) { // check if the message was an acknowledge. + checkAcknowTransmit(vMessage, srcText); // then if needs acknow send it immediately. + } + + // disabled coco begin validated: Has been validated manually. If MessageInterpreter::interpretMessage fails, this also returns false + if ( _interpreter.interpretMessage( vMessage, mData ) ) { + ok = true; + emit didActionReceive(vMessage.actionId, mData); + } + // disabled coco end + + _messageList[vMessage.can_id].removeLast(); + return ok; +} + +/*! + * \brief MessageDispatcher::rxCount + * \details count received messages up the size of the Sequence type size + * \return message count + */ +Sequence MessageDispatcher::rxCount() +{ + // disabled coco begin validated: has been manually validated since it requires so many received messages to reset the seq + if ( _rxSequence < SEQUENCE_MAX ) { + ++_rxSequence; + } else { + _rxSequence = 1; + } + return _rxSequence; +} +// disabled coco end + +/*! + * \brief MessageDispatcher::txCount + * \details count transmitted messages up the size of the Sequence type size + * \return message count + */ +Sequence MessageDispatcher::txCount() +{ + // disabled coco begin validated: has been manually validated since it requires so many received messages to reset the seq + if ( _txSequence < SEQUENCE_MAX ) { + ++_txSequence; + } else { + _txSequence = 1; + } + return _txSequence; +} +// disabled coco end + +/*! + * \brief MessageDispatcher::needsAcknow + * \details List of the Action types which need Acknow + * \param vActionId - Action Type id + * \return true if needs an Acknow + */ +bool MessageDispatcher::needsAcknow(GuiActionType vActionId) +{ + return _needsAcknow.contains(vActionId); +} + +/*! + * \brief MessageDispatcher::needsAcknow + * \details List of the CAN channels which need Acknow + * \param vCan_Id - The message CANBus id + * \return true if needs an Acknow + */ +bool MessageDispatcher::needsAcknow(Can_Id vCan_Id) +{ + bool ok = true; + switch(vCan_Id) { // list if the channels UI shall not Ack + case eChlid_HD_DG : // 0x008, ///< HD => DG + case eChlid_DG_HD : // 0x010, ///< DG => HD + case eDialin_HD : // 0x400, ///< dialin => HD + case eHD_Dialin : // 0x401, ///< HD => dialin + case eDialin_DG : // 0x402, ///< dialin => DG + case eDG_Dialin : // 0x403, ///< DG => dialin + ok = false; break; + + default: break; + } + + return ok; +} Index: sources/device/DeviceController.cpp =================================================================== diff -u -rd173768dbbf996543a5d313e7b45bbace90df1c3 -rab28e427d67bdb054a0173efb719e820f97b20b0 --- sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision d173768dbbf996543a5d313e7b45bbace90df1c3) +++ sources/device/DeviceController.cpp (.../DeviceController.cpp) (revision ab28e427d67bdb054a0173efb719e820f97b20b0) @@ -1,1311 +1,1315 @@ -/*! - * - * 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 DeviceController.cpp - * \author (last) Behrouz NematiPour - * \date (last) 11-Mar-2024 - * \author (original) Behrouz NematiPour - * \date (original) 03-Jun-2021 - * - */ -#include "DeviceController.h" - -// Linux -#include -#include - -// Qt -#include -#include -#include - -// Project -#include "Threads.h" -#include "StorageGlobals.h" -#include "Logger.h" -#include "CloudSyncController.h" -#include "ApplicationController.h" -#include "FileHandler.h" -#include "DeviceModels.h" -#include "Settings.h" -#include "encryption.h" - -// namespace -using namespace Model; -using namespace Device; -using namespace Storage; - -/*! - * \brief DeviceController::DeviceController - * \details Constructor - * \param parent - QObject parent owner object. - * Qt handles the children destruction by their parent objects life-cycle. - */ -DeviceController::DeviceController(QObject *parent) : QObject(parent) { - _fileSystemWatcher.setParent(this); - DEVICE_DEV_PARENT_LIST -} - -/*! - * \brief DeviceController::init - * \details Initializes the class by setting the connections and starting the timer - * \return False if it has been called before. - */ -bool DeviceController::init() -{ - if ( _init ) return false; - _init = true; - - // runs in DeviceController thread - initConnections(); - startTimer(_interval); - - return true; -} - -/*! - * \brief DeviceController::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 DeviceController::init(QThread &vThread) -{ - if ( ! init() ) return false; - initThread(vThread); - return true; -} - -/*! - * \brief DeviceController::quit - * \details quits the class - * Calls quitThread - */ -void DeviceController::quit() -{ - quitThread(); -} - - -/*! - * \brief DeviceController::initConnections - * \details Initializes the required signal/slot connection between this class and other objects - * to be able to communicate. - */ -void DeviceController::initConnections() -{ - connect(&_ApplicationController , SIGNAL(didUSBDriveUmount()), - this , SLOT( onUSBDriveUmount())); - - connect(this , SIGNAL(didScreenshot(const QImage &, const QString &)), - this , SLOT( onScreenshot(const QImage &, const QString &))); - - connect(&_fileSystemWatcher , SIGNAL( fileChanged(const QString &)), - this , SLOT( onWatchFileChanged(const QString &))); - - connect(&_ApplicationController , SIGNAL(didPOSTOSVersionData (const QString &)), - this , SLOT( onPOSTOSVersionData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTEthernetData (const QString &)), - this , SLOT( onPOSTEthernetData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTWirelessData (const QString &)), - this , SLOT( onPOSTWirelessData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTBluetoothData (const QString &)), - this , SLOT( onPOSTBluetoothData (const QString &))); - connect(&_ApplicationController , SIGNAL(didPOSTCloudSyncData (const QString &)), - this , SLOT( onPOSTCloudSyncData (const QString &))); - - connect(&_Logger , SIGNAL(didLogBackup (const QString &)), - this , SLOT( onLogBackup (const QString &))); - connect(&_CloudSyncController , SIGNAL(didLogUpload (const QString &)), - this , SLOT( onLogUpload (const QString &))); - - DEVICE_DEV_INIT_CONNECTIONS_LIST - - connect(this, SIGNAL(didEventThreadChange()), - this, SLOT( onEventThreadChange())); - -} - -/*! - * \brief ApplicationController::initThread - * \details Moves this object into the thread vThread. - * And checks that this method is called from main thread. - * Also connects quitThread to application aboutToQuit. - * \param vThread - the thread - */ -void DeviceController::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); - - emit didEventThreadChange( QPrivateSignal() ); -} - -/*! - * \brief DeviceController::quitThread - * \details Moves this object to main thread to be handled by QApplication - * And to be destroyed there. - */ -void DeviceController::quitThread() -{ - // disabled coco begin validated: Application 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 DeviceController::usbSeek - * \details Tries to look for the available USB devices - * Starts from sda1 to sdz1. - * \note will only look for the first partition if there is any - * \param vDevice - Found device (/dev/sda1) - * \return true if a device found - */ -bool DeviceController::usbSeek(QString &vDevice) -{ - // disabled coco begin validated: Needed User Interaction so tested manually - // This function cannot be tested on any device other than target - // because only on device the usb is /dev/sdX# and others are mmcblk___ - // but on VM and normal Linuxes all drives are /dev/sdX# and cannot be identified as USB. - // And the difference between how USB is detected and sd-card is that - // for the USB we need to look into /dev/sdX# - // but for the sd-card we look for the mounted path which is much easier. - // That's why indication of the sd-card is more accurate than USB at least on VM. - QString dev = "/dev/sd"; - QString device = ""; - for (char a = 'a'; a <= 'z'; a++) { - device = dev + a + '1'; - if (QFileInfo::exists(device)) { - vDevice = device; - return true; // application is deciding on the first existing drive - } - } - vDevice = device; - return false; - // disabled coco end -} - -/*! - * \brief DeviceController::driveSpaceCheck - * \param vPath - Device mount point - * \param vTotalBytes - Total volume size in bytes - * \param vAvailableBytes - Size (in bytes) available for the current user - * \param vPercent - The percentage of available space - * \return - The drive mounted and ready - * \note if device ejected manually system assumes it's still ready. - */ -bool DeviceController::driveSpaceCheck(const QString &vPath, qint64 &vTotalBytes, qint64 &vAvailableBytes, bool *vIsReadOnly) -{ - QStorageInfo storage(vPath); - bool isReady = storage.isReady (); - bool isReadOnly = storage.isReadOnly(); - bool isMounted = FileHandler::isMounted(vPath); - - if ( vIsReadOnly ) *vIsReadOnly = isReadOnly; - - if ( isReady ) { - vTotalBytes = storage.bytesTotal(); - vAvailableBytes = storage.bytesAvailable(); - } - - if ( ! isMounted || ! isReady ) { - isReady = false; - vTotalBytes = 0; - vAvailableBytes = 0; - } - return isReady; -} - -/*! - * \brief DeviceController::timerEvent - * \details This event handler has been re-implemented in here - * to receive timer events for the object - * for the timer which has been set to _checkInterval - * Runs the usbCheck on interval - */ -void DeviceController::timerEvent(QTimerEvent *) -{ -#ifdef BUILD_FOR_TARGET - usbCheck(); - sdcardSpaceCheck(); - - // The treatment logs are held in a separate partition from the unencrypted - // logs and need a separate disk space usage check - settingsPartitionSpaceCheck(); -#endif - findPendingLogs(); -} - -/*! - * \brief DeviceController::usbCheck - * \details Runs usbSeek to mount or umount or remove it - * regarding the state it's in. - */ -void DeviceController::usbCheck() -{ - QString device = ""; - usbSpaceCheck(); - if (usbSeek(device)) { - if (! _umounted ) { // avoid to mount the USB which has just been unmounted - usbMount(device); - } else { // the umount is requested? - usbUmount(USB_Mount_Point); - } - } else { - if ( ! _removed ) { - usbRemove(); - } - } -} - -/*! - * \brief DeviceController::sdcardSpaceCheck - * \details Checks for the SD-Card drive space. - */ -void DeviceController::sdcardSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - static quint8 mOPercent = 0; quint8 mCPercent = 0; - - qint64 mCTotal = 0; - - bool isMounted = FileHandler::isMounted(Storage::SDCard_Base_Path_Name); - QString pathToCheckSpace = isMounted ? Storage::SDCard_Base_Path_Name : gStandard_tmp; - mCIsReady = driveSpaceCheck(pathToCheckSpace, mCTotal, mCAvailable, &mCIsReadOnly); - - //DEBUG: qDebug()<< "Checking space for path of : " << pathToCheckSpace << " mCTotal " << mCTotal << " available " << mCAvailable; - - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; - emit didSDCardStateChange(mCIsReady, mCIsReadOnly); - } - //NOTE: this if block has to be independent of the mOIsReady != mCIsReady - // because current and old may be the same all the time and then this if block will not execute - // and reaches to the log and fills the log unnecessarily. - if (! mCIsReady ) { - mOPercent = 0; - mOAvailable = 0; - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - return; - } - - mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mCPercent < _minRequiredAvailableSpacePercent) { - LOG_DEBUG(QString("SD-CARD space lower than %1%").arg(_minRequiredAvailableSpacePercent)); - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); - } - - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; - if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { - mOPercent = mCPercent ; - mOAvailable = mCAvailable ; - emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; - } -} - -/*! - * \brief DeviceController::usbSpaceCheck - * \details Checks for the USB drive space. - */ -void DeviceController::usbSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOTotal = 0; qint64 mCTotal = 0; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - - quint8 mPercent = 0; - - mCIsReady = driveSpaceCheck(Storage::USB_Mount_Point, mCTotal, mCAvailable, &mCIsReadOnly); -#if BUILD_FOR_DESKTOP - mCIsReady = true; // it is set to always true since on desktop a local folder is used for the USB folder which doesn't need (un)mount. -#endif - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - emit didUSBStateChange(mCIsReady, mCIsReadOnly); - - if (! mCIsReady ) { - mOTotal = 0; - mOAvailable = 0; - emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); - return; - } - } - - mPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mOTotal != mCTotal || mOAvailable != mCAvailable) { - mOTotal = mCTotal ; - mOAvailable = mCAvailable ; - emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); - } - - // DEBUG: qDebug() << "DeviceController::usbSpaceCheck" - // << mCIsReady - // << mCIsReadOnly - // << mCTotal - // << mCAvailable ; -} - -/*! - * \brief DeviceController::settingsPartitionSpaceCheck - * \details Checks the disk space of the encrypted partition - */ -void DeviceController::settingsPartitionSpaceCheck() -{ - static bool mInitialized = false; - // Old Info ; // Current info - static bool mOIsReady = false; bool mCIsReady = false; - static bool mOIsReadOnly = false; bool mCIsReadOnly = false; - static qint64 mOAvailable = 0; qint64 mCAvailable = 0; - static quint8 mOPercent = 0; quint8 mCPercent = 0; - - qint64 mCTotal = 0; - mCIsReady = driveSpaceCheck(Storage::Settings_Path(), mCTotal, mCAvailable, &mCIsReadOnly); - - //DEBUG: qDebug()<< "Checking space for path of : " << Storage::Settings_Path() << " mCTotal " << mCTotal << " available " << mCAvailable; - - if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { - mOIsReadOnly = mCIsReadOnly; - mOIsReady = mCIsReady; - mInitialized = true; - //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; - emit didSettingsPartitionStateChange(mCIsReady, mCIsReadOnly); - } - //NOTE: this if block has to be independent of the mOIsReady != mCIsReady - // because current and old may be the same all the time and then this if block will not execute - // and reaches to the log and fills the log unnecessarily. - if (! mCIsReady ) { - mOPercent = 0; - mOAvailable = 0; - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - return; - } - - mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; - if (mCPercent < _minRequiredAvailableSpacePercent) { - LOG_DEBUG(QString("Settings partition space lower than %1%").arg(_minRequiredAvailableSpacePercent)); - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); - } - - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; - if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { - mOPercent = mCPercent ; - mOAvailable = mCAvailable ; - emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); - /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; - } -} - - -/*! - * \brief DeviceController::usbError - * \details Logs any error which has been happened - * On USB device vDevice - * \note When this method has been called error number will be read from errno variable, - * Which has been set by umount or mount. - * \param vDevice - */ -void DeviceController::usbError(const QString &vDevice) -{ - // disabled coco begin validated: This needs user interaction to plug-in/out the USB device - // has been tested manually - QString error; - static QString lastError; - switch (errno) { - case EBUSY: - error = tr("%1 - Device or resource busy (%2)").arg(errno).arg(vDevice); - _mounted = true; - break; - - default: - error = tr("%1 - %2 (%3 , %4)").arg(errno).arg(strerror(errno)).arg(vDevice).arg(USB_Mount_Point); - break; - - } - if (error != lastError) { - LOG_DEBUG("USB: " + error); - lastError = error; - } -} -// disabled coco end - -/*! - * \brief DeviceController::onUSBDriveUmount - * \details This is the slot connected to the _ApplicationController's didUSBDriveUmount SIGNAL, - * To notify the USB drive detach. - */ -void DeviceController::onUSBDriveUmount() -{ - // disabled coco begin validated: This needs user interaction to plug-in/out the USB device - // has been tested manually - _umounted = true; -} -// disabled coco end - -/*! - * \brief DeviceController::usbMount - * \details Mounts the USB device vDevice - * \note Emits didUSBDriveMount signal - * \param vDevice - USB device to be mounted (e.g. /dev/sda1) - * \return true on successful mount - */ -void DeviceController::usbMount(const QString &vDevice) -{ - usbMountReq(vDevice); -} - -/*! - * \brief DeviceController::usbUmount - * \details Unmounts the USB device vDevice - * \note Emits didUSBDriveUmount signal - * \param vDevice - USB device to be unmounted (e.g. /dev/sda1) - * \return true on successful unmount - */ -void DeviceController::usbUmount(const QString &vDevice) -{ - usbMountReq(vDevice, false); -} - -/*! - * \brief DeviceController::usbRemove - * \details Removed the USB mount point - * So next time it is not mounted as next device. - * \note Emits didUSBDriveRemove signal - */ -void DeviceController::usbRemove() -{ - // disabled coco begin validated: This needs user interaction to plug-out the USB device - // has been tested manually - usbUmount(USB_Mount_Point); - _umounted = false; - _removed = true; - LOG_DEBUG("USB drive removed"); - emit didUSBDriveRemove(); -} -// disabled coco end - -/*! - * \brief DeviceController::checkError - * \details check if has error, then sets the base model accept to false and the reason to the error. - * in that case logs the error message and emit the didAttributeResponse to notify the GUI. - * \param vError - the error code, this can be the Gui enum or system number - * \param vExtraLogInfo - any extra information to be logged. Not display to user. - * \return - */ -template -bool DeviceController::checkError(DeviceError::Scripts_Error_Enum vError, TModel &vModel, QString vExtraLogInfo) -{ - if ( vError ) { - QString src = (vError > DeviceError::eDevice_Scripts_Error_Start ? MAbstract::unitText(MAbstract::Unit_Enum::eUI) : MAbstract::unitText(MAbstract::Unit_Enum::eDV)) + ","; - vModel._data.mAccepted = false ; - vModel._data.mReason = vError ; - vModel._data.mMessage = DeviceError::deviceErrorText(vError, vError); - LOG_APPED(" ," + src + vModel._data.mMessage + " " + vExtraLogInfo); - emit didAttributeResponse(vModel._data); - return true; - } - return false; -} - -///////////////////////////////////////////// DeviceBrightness -/*! - * \brief DeviceController::onAttributeRequest - * \details Sets the brightness level - * \param vBrightness - */ -void DeviceController::onAttributeRequest(const DeviceBrightnessRequestData &vData) -{ - // ----- initializing the member variable models - _deviceBrightnessRequest ._data = vData; - - - // ----- extract the required data - _deviceBrightnessRequest.setBrightnessSysVal(); - LOG_APPED( _deviceBrightnessRequest.toString()); - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, vData.mRead ? Brightness_Get : Brightness_Set), _deviceBrightnessResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBrightness.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBrightnessResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - QStringList params; - params << QString::number(_deviceBrightnessRequest._data.mBrightness_val); - TimedProcess *timedProcess = new TimedProcess(&_processBrightness, script, timeout_ms, params); - timedProcess->start(); - -} - -/*! - * \brief DeviceController::onProcessBrightnessFinished - * \details Called when the process to set the brightness has finished - * \param vExitCode (int) the exit code - * \note exit code -> 0 : set Accept [MBase] -> Log -> emit - * !0 : set Attrib [MBrgh] -> Log -> emit - * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) - * 2 - get an error when in onProcessBrightnessExitCode : scriptErrorText([vExitCode]) - * 3 - get no error when in onProcessBrightnessExitCode : MDeviceResponse.toString() - * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. - */ -void DeviceController::onProcessBrightnessExitCode(int vExitCode, QProcess::ExitStatus) -{ - if ( ! checkError(static_cast(vExitCode), _deviceBrightnessResponse, _deviceBrightnessResponse.toString()) ) { // has no error - if (_deviceBrightnessRequest._data.mRead) { - bool ok = false; - int brightness = _processBrightness.readLine().toInt(&ok); - if (ok) { - _deviceBrightnessResponse.setBrightnessPercent(brightness); - } - else { - checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp,_deviceBrightnessResponse, _deviceBrightnessResponse.toString()); - return; - } - } - else { - _deviceBrightnessResponse.setBrightnessPercent(_deviceBrightnessRequest._data.mBrightness_val); - _deviceBrightnessResponse._data.mMessage = _deviceBrightnessResponse.toString(); - } - LOG_APPED(_deviceBrightnessResponse._data.mMessage); - emit didAttributeResponse(_deviceBrightnessResponse._data); - } -} - - -///////////////////////////////////////////// DeviceRootSSHAccess -/*! - * \brief DeviceController::onAttributeRequest - * \details Sets the RootSSHAccess - * \param vRootSSHAccess - */ -void DeviceController::onAttributeRequest(const DeviceRootSSHAccessRequestData &vData) -{ - // ----- initializing the member variable models - _deviceRootSSHAccessRequest._data = vData; - LOG_APPED( _deviceRootSSHAccessRequest.toString()); - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, RootSSHAccess), _deviceRootSSHAccessResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processRootSSHAccess.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceRootSSHAccessResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - QStringList params; - if ( ! _deviceRootSSHAccessRequest._data.mIsGet ) params << FSN(_deviceRootSSHAccessRequest._data.mRootSSHAccess); - TimedProcess *timedProcess = new TimedProcess(&_processRootSSHAccess, script, timeout_ms, params); - timedProcess->start(); -} - -/*! - * \brief DeviceController::onProcessRootSSHAccessExitCode - * \details Called when the process to set the RootSSHAccess has finished - * \param vExitCode (int) the exit code - * \note exit code -> 0 : set Accept [MBase] -> Log -> emit - * !0 : set Attrib [MBrgh] -> Log -> emit - * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) - * 2 - get an error when in onProcessRootSSHAccessExitCode : scriptErrorText([vExitCode]) - * 3 - get no error when in onProcessRootSSHAccessExitCode : MDeviceResponse.toString() - * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. - */ -void DeviceController::onProcessRootSSHAccessExitCode(int vExitCode, QProcess::ExitStatus) -{ - if ( ! checkError(static_cast(vExitCode), _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()) ) { // has no error - if (_deviceRootSSHAccessRequest._data.mIsGet) { - bool ok = true; - Qt::CheckState rootSSHAccess = Qt:: Unchecked; - uint value = _processRootSSHAccess.readLine().toUInt(&ok); - if ( ! ok ) goto lError; - - switch (value) { - case 0 : rootSSHAccess = Qt:: Unchecked; break; - case 1 : rootSSHAccess = Qt::PartiallyChecked; break; - case 2 : rootSSHAccess = Qt:: Checked; break; - default : ok = false; - } - - if ( ! ok ) goto lError; - - _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); - _deviceRootSSHAccessResponse._data.mRootSSHAccess = rootSSHAccess; - } - else { - _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); - _deviceRootSSHAccessResponse._data.mRootSSHAccess = _deviceRootSSHAccessRequest._data.mRootSSHAccess; - } - - LOG_APPED(_deviceRootSSHAccessResponse._data.mMessage); - emit didAttributeResponse(_deviceRootSSHAccessResponse._data); - } - else { - // the error in this case is handled in the checkError - } - return; - -lError: - checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp, _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()); -} - -///////////////////////////////////////////// DeviceCryptSetup -/*! - * \brief DeviceController::onAttributeRequest - * \details Calls the CryptSetup script with the model data DeviceCryptSetupRequestData - * \param vData - the model data - */ -void DeviceController::onAttributeRequest(const DeviceCryptSetupRequestData &vData) -{ - //DEBUG qDebug() << " ---------- " << vData.mCommand << vData.mPassword; - - _deviceCryptSetupRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Crypt_Setup), _deviceCryptSetupResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processCryptSetup.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceCryptSetupResponse); - return; - } - - - // ----- run the process - int timeout = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processCryptSetup, script, timeout, { _deviceCryptSetupRequest._data.mCommand }); - _processCryptSetup.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceCryptSetupRequest._data.mPassword)); - timedProcess->start(); - - // Update UI with a response - MDeviceCryptSetupResponse model; - model._data.mAccepted = false; - model._data.mMessage = tr("Encrypted Partition %1 started.").arg(_deviceCryptSetupRequest._data.mCommand); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessCryptSetupExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessCryptSetupExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - const int ERR_CRYPTSETUP_MOUNT_ISMOUNT=134; // is used in crypt_setup.sh do not modify - - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the pared device info in text and it will be empty string if error happens. - MDeviceCryptSetupResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processCryptSetup.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mEchoInfo; - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); - - bool isSetup = _deviceCryptSetupRequest._data.mCommand == "setup"; - bool isMount = _deviceCryptSetupRequest._data.mCommand == "mount"; - bool isMounted = isMount && ( - vExitCode == ERR_CRYPTSETUP_MOUNT_ISMOUNT || // is already mounted - vExitCode == 0 // successful mount - ); - bool isUpdate = gEnableUpdating && isMounted; - - if ( isMount ) emit didCryptSetupMount(model._data.mAccepted); - - QString msg = ""; - int err = 0 ; - //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. - Storage::Settings settings; - // moving the configuration files if the encrypted partition creation was successful. - if ( isUpdate ) goto lMove; // if it is gEnableUpdating, bypass the mAccepted for already mounted. - if ( ! model._data.mAccepted ) goto lErr ; // any other case goto error - if ( ! isSetup ) goto lOut ; // if not setup do NOT continue to move configurations - -lMove: - err = settings.configurationsMove(&msg, isUpdate); - if ( err ) { - model._data.mAccepted = false ; - model._data.mReason = err ; - model._data.mMessage = msg ; - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); - } - -lOut: - return; - -lErr: - LOG_DEBUG(QString("Encrypted Partition %1 failed").arg(_deviceCryptSetupRequest._data.mCommand)); -} - -///////////////////////////////////////////// DeviceBluetoothPaired -void DeviceController::onAttributeRequest(const DeviceBluetoothPairedResetRequestData &) -{ - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Reset), _deviceBluetoothPairedResetResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBluetoothPairedReset.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedResetResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedReset, script, timeout_ms); - timedProcess->start(); -} - -void DeviceController::onProcessBluetoothPairedResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // TODO: review the usage and definition of this object _deviceBluetoothPairedResetResponse. do we need it any more? - MDeviceBluetoothPairedResetResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processBluetoothPairedReset.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - LOG_APPED_UI(model.data().mMessage); - emit didAttributeResponse(model.data()); -} - -///////////////////////////////////////////// DeviceBluetoothPairedQuery -void DeviceController::onAttributeRequest(const DeviceBluetoothPairedQueryRequestData &) -{ - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Query), _deviceBluetoothPairedQueryResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processBluetoothPairedQuery.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedQueryResponse); - return; - } - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedQuery, script, timeout_ms); - timedProcess->start(); -} - -void DeviceController::onProcessBluetoothPairedQueryExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the pared device info in text and it will be empty string if error happens. - MDeviceBluetoothPairedQueryResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processBluetoothPairedQuery.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - emit didAttributeResponse(model.data()); - LOG_APPED_UI(model.data().mMessage); -} - -/*! - * \brief DeviceController::doScreenshot - * \details emit the screenshot signal to run that in Device controller thread - * \param vCurrentDateTime - */ -void DeviceController::doScreenshot(const QImage &vImage, const QString &vFileName) { - emit didScreenshot(vImage, vFileName); -} - -/*! - * \brief DeviceController::onScreenshot - * \details The function to save the image vImage in the file vFileName - * \param vImage - The image source object - * \param vFileName - The filename to same the image to - */ -void DeviceController::onScreenshot(const QImage &vImage, const QString &vFileName) { - vImage.save(vFileName); - LOG_DEBUG("Screenshot saved in " + vFileName); -} - -/*! - * \brief DeviceController::ondoAddWatch - * \details The thread safe add file watch method - * \param vFile - The file to add to watch. - */ -void DeviceController::ondoAddWatch(const QString &vFile, bool vCreate) -{ - DeviceError::Scripts_Error_Enum err = DeviceError::eDevice_OK; - if ( vCreate ) { - if ( ! FileHandler::write( vFile, "", false) ) { err = DeviceError::eDevice_Watch_Error_NotCreate; goto lErr; } - } else { - if ( ! QFileInfo::exists ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotFound ; goto lErr; } - } - if ( ! _fileSystemWatcher.addPath( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotAdded ; goto lErr; } - return; -lErr: - LOG_DEBUG(DeviceError::deviceErrorText(err, 0)); -} - -/*! - * \brief DeviceController::onWatchFileChanged - * \details This slot is called once the files being watched is updated. - * \param vFile - the file name. - */ -void DeviceController::onWatchFileChanged(const QString &vFile) -{ - emit didWatchFileChange(vFile); -} - -/*! - * \brief DeviceController::onEventThreadChange - * \details The signal handler for the DeviceController(this)::didEventThreadChange - * to start checking for the Encrypted partition readiness. - */ -void DeviceController::onEventThreadChange() -{ - //DEBUG qDebug() << " ---------- " << __FUNCTION__ << QThread::currentThread()->objectName() << QThread::currentThread() << qApp->thread(); - if ( QThread::currentThread() != &Threads::_DeviceController_Thread ) { - qCritical() << " ***** Device controller thread not initialized correctly ***** "; - return; - } - _hasThread = true; - checkConfugurationMountReady(); -} - -/*! - * \brief DeviceController::onPOSTOSVersionData - * \details Collects the OS Version - * when it is ready after the POST is done reading OS Version - * \param vMacAddress - The Ethernet MAC address - */ -void DeviceController::onPOSTOSVersionData(const QString &vOSVersion) { - _osVersion = vOSVersion; - emit didPOSTOSVersionData (vOSVersion); -} - -/*! - * \brief DeviceController::onPOSTEthernetData - * \details Collects the ethernet mac address - * when it is ready after the POST is done for the Ethernet - * \param vMacAddress - The Ethernet MAC address - */ -void DeviceController::onPOSTEthernetData(const QString &vMacAddress) { - _macEthernet = vMacAddress; - emit didPOSTEthernetData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTWirelessData - * \details Collects the wireless mac address - * when it is ready after the POST is done for the Wireless connection - * \param vMacAddress - The Wireless MAC address - */ -void DeviceController::onPOSTWirelessData(const QString &vMacAddress) { - _macWireless = vMacAddress; - - encryption::varSalt(vMacAddress); - _hasSalt = ! vMacAddress.trimmed().isEmpty(); - checkConfugurationMountReady(); - - emit didPOSTWirelessData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTBluetoothData - * \details Collects the bluetooth mac address - * when it is ready after the POST is done for the Bluetooth - * \param vMacAddress - The Bluetooth MAC address - */ -void DeviceController::onPOSTBluetoothData(const QString &vMacAddress) { - _macBluetooth = vMacAddress; - emit didPOSTBluetoothData (vMacAddress); -} - -/*! - * \brief DeviceController::onPOSTCloudSyncData - * \details Collects the CloudSync Network Address - * when it is ready after the POST is done for the CloudSync - * \param vNetAddress - *** Not defined yet and is a placeholder for later use *** - */ -void DeviceController::onPOSTCloudSyncData(const QString &vNetAddress) { - _netCloudSync = vNetAddress; - emit didPOSTCloudSyncData (vNetAddress); -} - -bool DeviceController::logBackup(const QString &vFileName) -{ - if ( ! gLogUpload ) return false; // no log backup - - bool ok = true; - QString fileSrc = vFileName; - QFileInfo fileInfo(vFileName); - QString filePath(fileInfo.absolutePath()); - QString fileBase(fileInfo.baseName()); - QString fileSufx(fileInfo.completeSuffix().prepend(_Logger.logFileNamePendingSubExt())); - QString fileDest(QString("%1/%2.%3").arg(filePath, fileBase, fileSufx)); - // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; - if ( gLogCompress ) { - fileSrc += _Logger.logFileNameCompressExt(); - fileDest += _Logger.logFileNameCompressExt(); - ok = FileHandler::backupFile(vFileName); - } - ok = QFile::rename(fileSrc, fileDest); - return ok; -} - -void DeviceController::onLogBackup(const QString &vFileName) -{ - if ( ! gLogUpload ) return; // no log backup ( slot ) - - bool ok = true; - ok = logBackup( vFileName); - emit didLogBackup(ok, vFileName); -} - -bool DeviceController::logUpload(const QString &vFileName) -{ - if ( ! gLogUpload ) return false; // no log Uploaded rename - - bool ok = true; - QFileInfo fileInfo(vFileName); - QString filePath; - QString fileBase(fileInfo.baseName()); - QString ext = _Logger.logFileNamePendingSubExt(); - QString fileSufx(fileInfo.completeSuffix().remove(ext)); - QString fileDest = QString("%1.%2").arg(fileBase, fileSufx); - - Logger::LogType logType = _Logger.logFileLogType(vFileName, filePath); - ok = logType != Logger::eLogNone; - if ( ! ok ) { LOG_APPED_UI(QString("CS Incorrect log upload type [%1]").arg(fileSufx)); goto lOut; } - // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; - - ok = QFile::rename(filePath + vFileName, filePath + fileDest); -lOut: - return ok; -} - -void DeviceController::onLogUpload(const QString &vFileName) -{ - if ( ! gLogUpload ) return; // no log uploaded rename ( slot ) - - bool ok = true; - ok = logUpload( vFileName); - emit didLogUpload(ok, vFileName); -} - -/*! - * \brief DeviceController::checkConfugurationMountReady - * \details Cheks if the system is ready to mount the encrypted partition. - * The object has to be on its own thread and the salt for the decryption has to be ready. - */ -void DeviceController::checkConfugurationMountReady() -{ - /// in manufacturing or updating the system is logged with root and configurations are in /home/root - /// therefore no need to mount the cononfiguraiton partition. - /// also for manufacturing the partition is being set up - /// and is less steps for setup if the partition is not mounted. - if ( gEnableManufacturing ) return; // it should do the mount when gEnableUpdating, therefore here should not add gEnableUpdating. - if ( ! ( _hasThread && _hasSalt ) ) return; - - DeviceCryptSetupRequestData data; - data.mCommand = "mount"; - bool ok = false; - data.mPassword = encryption::configurationsPassword( ok ); - if ( ! ok ) { // not enough infromation to create a secure passowrd - // status(tr("Not enough secure information provided")); - } - else { - onAttributeRequest(data); - } - - // I_AM_HERE - // HERE move the settings read from ApplicationController to here - // here is when the DeviceController is moved to its thread - // and now can start mounting the encrypted partition - // and then read the settings. - // TODO don't forget to check for the required configurations files and parameters in the settings class. - // and take care of the security flag in the Storage class. -} - -/*! - * \brief DeviceController::findPendingLogs - * \details this function counts downs for the _pendingInterval - * when the _pendingCounter reaches 0 will search for the files - * and if there is any will get the recent file in the list - */ -void DeviceController::findPendingLogs() -{ - if ( ! gLogUpload ) return; // no log upload pending detection - - static QString pendingLog = ""; - if( _pendingCounter ) { - _pendingCounter -- ; - return; - } - else { - _pendingCounter = _pendingInterval; // every minute - } - - QFileInfoList pendingFiles; - QString logLoc = Log_Folder_Base; - QString logExt = QString("*.%1*").arg(_Logger.logFileNamePendingSubExt()); // "*.u.*"; - for( auto logFolder : { Log_Folder_Application, Log_Folder_Service/*, Log_Folder_CloudSync*/ } ) { - pendingFiles = Storage::FileHandler::find( logLoc + logFolder, { logExt } ); - // look into the list. - // if there are pending files, - // send a request only for the top on the list - /// Note I thought it makes sense to send the oldest on the application and service logs - /// but there are some conversation about the situation if something happens on the device, - /// and it would be a critical situation to get the recent/top log and won't wait for the old ones to upload. - // * When gets uploaded, moves from pending then next one comes to top - // the process repeats until there is no file in pending - - bool uploadOldestFirst = true; //TODO: make if configurable(cfg, or cli) - - if ( pendingFiles.count() ) { - // the most recent/first log file, to first ask for the current log which has just been saved as pending - if ( uploadOldestFirst ) { - _pendingLog = pendingFiles.last().absoluteFilePath(); - } else { - _pendingLog = pendingFiles.first().absoluteFilePath(); - } - QString message = pendingLog; - LOG_DEBUG(message); - emit didPendingLog( _pendingLog, FileHandler::sha256sum( _pendingLog ) ); - // when a pending file found in the first log folder stop - // until there is none in first one (log) - // then continue to the next log folder (service) ... - goto lOut; - } - } -lOut: - return; -} - -///////////////////////////////////////////// DeviceFactoryReset -void DeviceController::onAttributeRequest(const DeviceFactoryResetRequestData &vData) -{ - _deviceFactoryResetRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Factory_Reset), _deviceFactoryResetResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processFactoryReset.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceFactoryResetResponse); - return; - } - - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processFactoryReset, script, timeout_ms); - timedProcess->start(); - - MDeviceFactoryResetResponse model; - model._data.mAccepted = false; // will indirectly set the property factoryResetEnabled - model._data.mMessage = tr("Factory Reset started."); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessFactoryResetExitCode - * \param vExitCode - the script exit code. - * \param vStatus - the script echoed message. - */ -void DeviceController::onProcessFactoryResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the paired device info in text and it will be empty string if error happens. - MDeviceFactoryResetResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processFactoryReset.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mMessage << deviceInfo; - emit didAttributeResponse (model.data()); - emit didFactoryReset (model._data.mAccepted); - - LOG_APPED_UI(model.data().mMessage); -} - -///////////////////////////////////////////// DeviceDecommission -void DeviceController::onAttributeRequest(const DeviceDecommissionRequestData &vData) -{ - _deviceDecommissionRequest._data = vData; - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, Device_Decommission), _deviceDecommissionResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processDecommission.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceDecommissionResponse); - return; - } - - - // ----- run the process - int timeout_ms = 10000; - TimedProcess *timedProcess = new TimedProcess(&_processDecommission, script, timeout_ms, { CloudSyncPath }); - _processDecommission.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceDecommissionRequest._data.mPassword)); - timedProcess->start(); - - MDeviceDecommissionResponse model; - model._data.mAccepted = false; // will indirectly set the property decommissionEnabled - model._data.mMessage = tr("Decommissioning started."); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessDecommissionExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessDecommissionExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - // The Exit code in this script is not used. - // any other checking is done by UI Software at the moment this script is called. - // The only thing matters is the paired device info in text and it will be empty string if error happens. - MDeviceDecommissionResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processDecommission.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - // DEBUG: qDebug() << model._data.mMessage << deviceInfo; - emit didAttributeResponse (model.data()); - emit didDecommissioning (model._data.mAccepted); - - LOG_APPED_UI(model.data().mMessage); -} - -///////////////////////////////////////////// DeviceUSBMounting -void DeviceController::onAttributeRequest(const DeviceUSBMountRequestData &vData) -{ - Q_UNUSED(vData) - usbMountReq(vData.usbDevice, vData.isMountRequest); -} - -/*! - * \brief DeviceController::usbMountReq - * \details Calls the Usb unmount/mount script - * \param vIsMount - indicate if the request is for mounting or unmounting - * \param vDevice - the path to the USB device - */ -void DeviceController::usbMountReq(const QString &vDevice, bool vIsMount) -{ - qDebug() << __FUNCTION__ << vDevice << vIsMount; - _deviceUSBMountRequest._data.isMountRequest = vIsMount ; - _deviceUSBMountRequest._data.usbDevice = vDevice ; // not necessary, but to be consistent - - // ----- check that script exists. - QString script; - if ( checkError( DeviceError::checkScript(script, (vIsMount ? USB_Mount : USB_Unmount )), _deviceUSBMountResponse, script) ) - return; - - // ----- check if the process is not running - if ( _processUSBMount.state() != QProcess::NotRunning ) { - checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceUSBMountResponse); - return; - } - - // ----- run the process - int timeout_ms = 5000; - TimedProcess *timedProcess = new TimedProcess(&_processUSBMount, script, timeout_ms, {vDevice, USB_Mount_Point, USB_File_System}); - timedProcess->start(); - - MDeviceUSBMountResponse model; - model._data.mAccepted = false; - model._data.mMessage = vIsMount ? tr("USB unmount started.") : tr("USB mount started"); - emit didAttributeResponse(model.data()); -} - -/*! - * \brief DeviceController::onProcessUSBMountExitCode - * \param vExitCode - * \param vStatus - */ -void DeviceController::onProcessUSBMountExitCode(int vExitCode, QProcess::ExitStatus vStatus) -{ - MDeviceUSBMountResponse model; - QByteArray deviceInfo; - if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; - else deviceInfo = _processUSBMount.readAll(); - model.fromByteArray( deviceInfo, &vExitCode ); - emit didAttributeResponse(model.data()); - - // Re-evaluate the USB space available - need to call this here to avoid - // visual lag caused by waiting to call this function on the timer timeout - usbSpaceCheck(); - - bool ok = ! vStatus; - QString usbDevice = _deviceUSBMountRequest._data.usbDevice; - if(_deviceUSBMountRequest._data.isMountRequest) { // *** USB Mount - if ( ok ) { - _mounted = true; - _removed = false; - LOG_DEBUG(QString("USB flash drive %1 has been mounted on %2").arg(usbDevice).arg(USB_Mount_Point)); - emit didUSBDriveMount(); - } else { - usbError(usbDevice); - } - } else { // *** USB Unmount - if ( ok ) { - _mounted = false; - // _umounted = true; // I think it might be needed, but needs more testing. - LOG_DEBUG(QString("USB drive %2 unmounted").arg(usbDevice)); - emit didUSBDriveUmount(); - } else { - // the error is irrelevant, commented out for now - // usbError(usbDrive); - } - } - - // log error and exit - return; -} +/*! + * + * 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 DeviceController.cpp + * \author (last) Behrouz NematiPour + * \date (last) 11-Mar-2024 + * \author (original) Behrouz NematiPour + * \date (original) 03-Jun-2021 + * + */ +#include "DeviceController.h" + +// Linux +#include +#include + +// Qt +#include +#include +#include + +// Project +#include "Threads.h" +#include "StorageGlobals.h" +#include "Logger.h" +#include "CloudSyncController.h" +#include "ApplicationController.h" +#include "FileHandler.h" +#include "DeviceModels.h" +#include "Settings.h" +#include "encryption.h" + +// namespace +using namespace Model; +using namespace Device; +using namespace Storage; + +/*! + * \brief DeviceController::DeviceController + * \details Constructor + * \param parent - QObject parent owner object. + * Qt handles the children destruction by their parent objects life-cycle. + */ +DeviceController::DeviceController(QObject *parent) : QObject(parent) { + _fileSystemWatcher.setParent(this); + DEVICE_DEV_PARENT_LIST +} + +/*! + * \brief DeviceController::init + * \details Initializes the class by setting the connections and starting the timer + * \return False if it has been called before. + */ +bool DeviceController::init() +{ + if ( _init ) return false; + _init = true; + + // runs in DeviceController thread + initConnections(); + startTimer(_interval); + + return true; +} + +/*! + * \brief DeviceController::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 DeviceController::init(QThread &vThread) +{ + if ( ! init() ) return false; + initThread(vThread); + return true; +} + +/*! + * \brief DeviceController::quit + * \details quits the class + * Calls quitThread + */ +void DeviceController::quit() +{ + quitThread(); +} + + +/*! + * \brief DeviceController::initConnections + * \details Initializes the required signal/slot connection between this class and other objects + * to be able to communicate. + */ +void DeviceController::initConnections() +{ + connect(&_ApplicationController , SIGNAL(didUSBDriveUmount()), + this , SLOT( onUSBDriveUmount())); + + connect(this , SIGNAL(didScreenshot(const QImage &, const QString &)), + this , SLOT( onScreenshot(const QImage &, const QString &))); + + connect(&_fileSystemWatcher , SIGNAL( fileChanged(const QString &)), + this , SLOT( onWatchFileChanged(const QString &))); + + connect(&_ApplicationController , SIGNAL(didPOSTOSVersionData (const QString &)), + this , SLOT( onPOSTOSVersionData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTEthernetData (const QString &)), + this , SLOT( onPOSTEthernetData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTWirelessData (const QString &)), + this , SLOT( onPOSTWirelessData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTBluetoothData (const QString &)), + this , SLOT( onPOSTBluetoothData (const QString &))); + connect(&_ApplicationController , SIGNAL(didPOSTCloudSyncData (const QString &)), + this , SLOT( onPOSTCloudSyncData (const QString &))); + + connect(&_Logger , SIGNAL(didLogBackup (const QString &)), + this , SLOT( onLogBackup (const QString &))); + connect(&_CloudSyncController , SIGNAL(didLogUpload (const QString &)), + this , SLOT( onLogUpload (const QString &))); + + DEVICE_DEV_INIT_CONNECTIONS_LIST + + connect(this, SIGNAL(didEventThreadChange()), + this, SLOT( onEventThreadChange())); + +} + +/*! + * \brief ApplicationController::initThread + * \details Moves this object into the thread vThread. + * And checks that this method is called from main thread. + * Also connects quitThread to application aboutToQuit. + * \param vThread - the thread + */ +void DeviceController::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); + + emit didEventThreadChange( QPrivateSignal() ); +} + +/*! + * \brief DeviceController::quitThread + * \details Moves this object to main thread to be handled by QApplication + * And to be destroyed there. + */ +void DeviceController::quitThread() +{ + // disabled coco begin validated: Application 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 DeviceController::usbSeek + * \details Tries to look for the available USB devices + * Starts from sda1 to sdz1. + * \note will only look for the first partition if there is any + * \param vDevice - Found device (/dev/sda1) + * \return true if a device found + */ +bool DeviceController::usbSeek(QString &vDevice) +{ + // disabled coco begin validated: Needed User Interaction so tested manually + // This function cannot be tested on any device other than target + // because only on device the usb is /dev/sdX# and others are mmcblk___ + // but on VM and normal Linuxes all drives are /dev/sdX# and cannot be identified as USB. + // And the difference between how USB is detected and sd-card is that + // for the USB we need to look into /dev/sdX# + // but for the sd-card we look for the mounted path which is much easier. + // That's why indication of the sd-card is more accurate than USB at least on VM. + QString dev = "/dev/sd"; + QString device = ""; + for (char a = 'a'; a <= 'z'; a++) { + device = dev + a + '1'; + if (QFileInfo::exists(device)) { + vDevice = device; + return true; // application is deciding on the first existing drive + } + } + vDevice = device; + return false; + // disabled coco end +} + +/*! + * \brief DeviceController::driveSpaceCheck + * \param vPath - Device mount point + * \param vTotalBytes - Total volume size in bytes + * \param vAvailableBytes - Size (in bytes) available for the current user + * \param vPercent - The percentage of available space + * \return - The drive mounted and ready + * \note if device ejected manually system assumes it's still ready. + */ +bool DeviceController::driveSpaceCheck(const QString &vPath, qint64 &vTotalBytes, qint64 &vAvailableBytes, bool *vIsReadOnly) +{ + QStorageInfo storage(vPath); + bool isReady = storage.isReady (); + bool isReadOnly = storage.isReadOnly(); + bool isMounted = FileHandler::isMounted(vPath); + + if ( vIsReadOnly ) *vIsReadOnly = isReadOnly; + + if ( isReady ) { + vTotalBytes = storage.bytesTotal(); + vAvailableBytes = storage.bytesAvailable(); + } + + if ( ! isMounted || ! isReady ) { + isReady = false; + vTotalBytes = 0; + vAvailableBytes = 0; + } + return isReady; +} + +/*! + * \brief DeviceController::timerEvent + * \details This event handler has been re-implemented in here + * to receive timer events for the object + * for the timer which has been set to _checkInterval + * Runs the usbCheck on interval + */ +void DeviceController::timerEvent(QTimerEvent *) +{ +#ifdef BUILD_FOR_TARGET + usbCheck(); + sdcardSpaceCheck(); + + // The treatment logs are held in a separate partition from the unencrypted + // logs and need a separate disk space usage check + settingsPartitionSpaceCheck(); +#endif + findPendingLogs(); +} + +/*! + * \brief DeviceController::usbCheck + * \details Runs usbSeek to mount or umount or remove it + * regarding the state it's in. + */ +void DeviceController::usbCheck() +{ + QString device = ""; + usbSpaceCheck(); + if (usbSeek(device)) { + if (! _umounted ) { // avoid to mount the USB which has just been unmounted + if (! _mounted ) { // avoid to mount the USB which has just been mounted + usbMount(device); + } + } else { // the umount is requested? + usbUmount(USB_Mount_Point); + } + } else { + if ( ! _removed ) { + usbRemove(); + } + } +} + +/*! + * \brief DeviceController::sdcardSpaceCheck + * \details Checks for the SD-Card drive space. + */ +void DeviceController::sdcardSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + static quint8 mOPercent = 0; quint8 mCPercent = 0; + + qint64 mCTotal = 0; + + bool isMounted = FileHandler::isMounted(Storage::SDCard_Base_Path_Name); + QString pathToCheckSpace = isMounted ? Storage::SDCard_Base_Path_Name : gStandard_tmp; + mCIsReady = driveSpaceCheck(pathToCheckSpace, mCTotal, mCAvailable, &mCIsReadOnly); + + //DEBUG: qDebug()<< "Checking space for path of : " << pathToCheckSpace << " mCTotal " << mCTotal << " available " << mCAvailable; + + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; + emit didSDCardStateChange(mCIsReady, mCIsReadOnly); + } + //NOTE: this if block has to be independent of the mOIsReady != mCIsReady + // because current and old may be the same all the time and then this if block will not execute + // and reaches to the log and fills the log unnecessarily. + if (! mCIsReady ) { + mOPercent = 0; + mOAvailable = 0; + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + return; + } + + mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mCPercent < _minRequiredAvailableSpacePercent) { + LOG_DEBUG(QString("SD-CARD space lower than %1%").arg(_minRequiredAvailableSpacePercent)); + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); + } + + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; + if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { + mOPercent = mCPercent ; + mOAvailable = mCAvailable ; + emit didSDCardSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; + } +} + +/*! + * \brief DeviceController::usbSpaceCheck + * \details Checks for the USB drive space. + */ +void DeviceController::usbSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOTotal = 0; qint64 mCTotal = 0; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + + quint8 mPercent = 0; + + mCIsReady = driveSpaceCheck(Storage::USB_Mount_Point, mCTotal, mCAvailable, &mCIsReadOnly); +#if BUILD_FOR_DESKTOP + mCIsReady = true; // it is set to always true since on desktop a local folder is used for the USB folder which doesn't need (un)mount. +#endif + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + emit didUSBStateChange(mCIsReady, mCIsReadOnly); + + if (! mCIsReady ) { + mOTotal = 0; + mOAvailable = 0; + emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); + return; + } + } + + mPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mOTotal != mCTotal || mOAvailable != mCAvailable) { + mOTotal = mCTotal ; + mOAvailable = mCAvailable ; + emit didUSBSpaceChange(mCIsReady, mCTotal, mCAvailable, mPercent); + } + + // DEBUG: qDebug() << "DeviceController::usbSpaceCheck" + // << mCIsReady + // << mCIsReadOnly + // << mCTotal + // << mCAvailable ; +} + +/*! + * \brief DeviceController::settingsPartitionSpaceCheck + * \details Checks the disk space of the encrypted partition + */ +void DeviceController::settingsPartitionSpaceCheck() +{ + static bool mInitialized = false; + // Old Info ; // Current info + static bool mOIsReady = false; bool mCIsReady = false; + static bool mOIsReadOnly = false; bool mCIsReadOnly = false; + static qint64 mOAvailable = 0; qint64 mCAvailable = 0; + static quint8 mOPercent = 0; quint8 mCPercent = 0; + + qint64 mCTotal = 0; + mCIsReady = driveSpaceCheck(Storage::Settings_Path(), mCTotal, mCAvailable, &mCIsReadOnly); + + //DEBUG: qDebug()<< "Checking space for path of : " << Storage::Settings_Path() << " mCTotal " << mCTotal << " available " << mCAvailable; + + if (mOIsReadOnly != mCIsReadOnly || mOIsReady != mCIsReady || ! mInitialized ) { + mOIsReadOnly = mCIsReadOnly; + mOIsReady = mCIsReady; + mInitialized = true; + //DEBUG:0: qDebug() << " ~~~~~~~~~~ " << __FUNCTION__ << mInitialized << mCIsReady << mOIsReady << mCIsReadOnly << mOIsReadOnly; + emit didSettingsPartitionStateChange(mCIsReady, mCIsReadOnly); + } + //NOTE: this if block has to be independent of the mOIsReady != mCIsReady + // because current and old may be the same all the time and then this if block will not execute + // and reaches to the log and fills the log unnecessarily. + if (! mCIsReady ) { + mOPercent = 0; + mOAvailable = 0; + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + return; + } + + mCPercent = mCTotal ? ((100 * mCAvailable) / mCTotal) : 0; + if (mCPercent < _minRequiredAvailableSpacePercent) { + LOG_DEBUG(QString("Settings partition space lower than %1%").arg(_minRequiredAvailableSpacePercent)); + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + emit didSDCardSpaceTooLow(_minRequiredAvailableSpacePercent); + } + + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mOTotal << mCTotal << (mOTotal == mCTotal) << mOAvailable << mCAvailable << (mOAvailable == mCAvailable) << mPercent << mCIsReadOnly; + if (mOPercent != mCPercent && mOAvailable != mCAvailable ) { + mOPercent = mCPercent ; + mOAvailable = mCAvailable ; + emit didSettingsPartitionSpaceChange(mCIsReady, mCTotal, mCAvailable, mCPercent); + /// DEBUG: qDebug() << Storage::SDCard_Base_Path_Name << mCIsReady << mCTotal << mCAvailable << mPercent ; + } +} + + +/*! + * \brief DeviceController::usbError + * \details Logs any error which has been happened + * On USB device vDevice + * \note When this method has been called error number will be read from errno variable, + * Which has been set by umount or mount. + * \param vDevice + */ +void DeviceController::usbError(const QString &vDevice) +{ + // disabled coco begin validated: This needs user interaction to plug-in/out the USB device + // has been tested manually + QString error; + static QString lastError; + switch (errno) { + case EBUSY: + error = tr("%1 - Device or resource busy (%2)").arg(errno).arg(vDevice); + _mounted = true; + break; + + default: + error = tr("%1 - %2 (%3 , %4)").arg(errno).arg(strerror(errno)).arg(vDevice).arg(USB_Mount_Point); + break; + + } + if (error != lastError) { + LOG_DEBUG("USB: " + error); + lastError = error; + } +} +// disabled coco end + +/*! + * \brief DeviceController::onUSBDriveUmount + * \details This is the slot connected to the _ApplicationController's didUSBDriveUmount SIGNAL, + * To notify the USB drive detach. + */ +void DeviceController::onUSBDriveUmount() +{ + // disabled coco begin validated: This needs user interaction to plug-in/out the USB device + // has been tested manually + _umounted = true; +} +// disabled coco end + +/*! + * \brief DeviceController::usbMount + * \details Mounts the USB device vDevice + * \note Emits didUSBDriveMount signal + * \param vDevice - USB device to be mounted (e.g. /dev/sda1) + * \return true on successful mount + */ +void DeviceController::usbMount(const QString &vDevice) +{ + usbMountReq(vDevice); +} + +/*! + * \brief DeviceController::usbUmount + * \details Unmounts the USB device vDevice + * \note Emits didUSBDriveUmount signal + * \param vDevice - USB device to be unmounted (e.g. /dev/sda1) + * \return true on successful unmount + */ +void DeviceController::usbUmount(const QString &vDevice) +{ + usbMountReq(vDevice, false); +} + +/*! + * \brief DeviceController::usbRemove + * \details Removed the USB mount point + * So next time it is not mounted as next device. + * \note Emits didUSBDriveRemove signal + */ +void DeviceController::usbRemove() +{ + // disabled coco begin validated: This needs user interaction to plug-out the USB device + // has been tested manually + usbUmount(USB_Mount_Point); + _umounted = false; + _removed = true; + LOG_DEBUG("USB drive removed"); + emit didUSBDriveRemove(); +} +// disabled coco end + +/*! + * \brief DeviceController::checkError + * \details check if has error, then sets the base model accept to false and the reason to the error. + * in that case logs the error message and emit the didAttributeResponse to notify the GUI. + * \param vError - the error code, this can be the Gui enum or system number + * \param vExtraLogInfo - any extra information to be logged. Not display to user. + * \return + */ +template +bool DeviceController::checkError(DeviceError::Scripts_Error_Enum vError, TModel &vModel, QString vExtraLogInfo) +{ + if ( vError ) { + QString src = (vError > DeviceError::eDevice_Scripts_Error_Start ? MAbstract::unitText(MAbstract::Unit_Enum::eUI) : MAbstract::unitText(MAbstract::Unit_Enum::eDV)) + ","; + vModel._data.mAccepted = false ; + vModel._data.mReason = vError ; + vModel._data.mMessage = DeviceError::deviceErrorText(vError, vError); + LOG_APPED(" ," + src + vModel._data.mMessage + " " + vExtraLogInfo); + emit didAttributeResponse(vModel._data); + return true; + } + return false; +} + +///////////////////////////////////////////// DeviceBrightness +/*! + * \brief DeviceController::onAttributeRequest + * \details Sets the brightness level + * \param vBrightness + */ +void DeviceController::onAttributeRequest(const DeviceBrightnessRequestData &vData) +{ + // ----- initializing the member variable models + _deviceBrightnessRequest ._data = vData; + + + // ----- extract the required data + _deviceBrightnessRequest.setBrightnessSysVal(); + LOG_APPED( _deviceBrightnessRequest.toString()); + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, vData.mRead ? Brightness_Get : Brightness_Set), _deviceBrightnessResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBrightness.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBrightnessResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + QStringList params; + params << QString::number(_deviceBrightnessRequest._data.mBrightness_val); + TimedProcess *timedProcess = new TimedProcess(&_processBrightness, script, timeout_ms, params); + timedProcess->start(); + +} + +/*! + * \brief DeviceController::onProcessBrightnessFinished + * \details Called when the process to set the brightness has finished + * \param vExitCode (int) the exit code + * \note exit code -> 0 : set Accept [MBase] -> Log -> emit + * !0 : set Attrib [MBrgh] -> Log -> emit + * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) + * 2 - get an error when in onProcessBrightnessExitCode : scriptErrorText([vExitCode]) + * 3 - get no error when in onProcessBrightnessExitCode : MDeviceResponse.toString() + * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. + */ +void DeviceController::onProcessBrightnessExitCode(int vExitCode, QProcess::ExitStatus) +{ + if ( ! checkError(static_cast(vExitCode), _deviceBrightnessResponse, _deviceBrightnessResponse.toString()) ) { // has no error + if (_deviceBrightnessRequest._data.mRead) { + bool ok = false; + int brightness = _processBrightness.readLine().toInt(&ok); + if (ok) { + _deviceBrightnessResponse.setBrightnessPercent(brightness); + } + else { + checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp,_deviceBrightnessResponse, _deviceBrightnessResponse.toString()); + return; + } + } + else { + _deviceBrightnessResponse.setBrightnessPercent(_deviceBrightnessRequest._data.mBrightness_val); + _deviceBrightnessResponse._data.mMessage = _deviceBrightnessResponse.toString(); + } + LOG_APPED(_deviceBrightnessResponse._data.mMessage); + emit didAttributeResponse(_deviceBrightnessResponse._data); + } +} + + +///////////////////////////////////////////// DeviceRootSSHAccess +/*! + * \brief DeviceController::onAttributeRequest + * \details Sets the RootSSHAccess + * \param vRootSSHAccess + */ +void DeviceController::onAttributeRequest(const DeviceRootSSHAccessRequestData &vData) +{ + // ----- initializing the member variable models + _deviceRootSSHAccessRequest._data = vData; + LOG_APPED( _deviceRootSSHAccessRequest.toString()); + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, RootSSHAccess), _deviceRootSSHAccessResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processRootSSHAccess.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceRootSSHAccessResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + QStringList params; + if ( ! _deviceRootSSHAccessRequest._data.mIsGet ) params << FSN(_deviceRootSSHAccessRequest._data.mRootSSHAccess); + TimedProcess *timedProcess = new TimedProcess(&_processRootSSHAccess, script, timeout_ms, params); + timedProcess->start(); +} + +/*! + * \brief DeviceController::onProcessRootSSHAccessExitCode + * \details Called when the process to set the RootSSHAccess has finished + * \param vExitCode (int) the exit code + * \note exit code -> 0 : set Accept [MBase] -> Log -> emit + * !0 : set Attrib [MBrgh] -> Log -> emit + * 1 - get an error when in onAttributeRequest : scriptErrorText([Gui Enum ]) + * 2 - get an error when in onProcessRootSSHAccessExitCode : scriptErrorText([vExitCode]) + * 3 - get no error when in onProcessRootSSHAccessExitCode : MDeviceResponse.toString() + * - in case 3 the specific model _data has to be filled prior to the toString to have it in the log. + */ +void DeviceController::onProcessRootSSHAccessExitCode(int vExitCode, QProcess::ExitStatus) +{ + if ( ! checkError(static_cast(vExitCode), _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()) ) { // has no error + if (_deviceRootSSHAccessRequest._data.mIsGet) { + bool ok = true; + Qt::CheckState rootSSHAccess = Qt:: Unchecked; + uint value = _processRootSSHAccess.readLine().toUInt(&ok); + if ( ! ok ) goto lError; + + switch (value) { + case 0 : rootSSHAccess = Qt:: Unchecked; break; + case 1 : rootSSHAccess = Qt::PartiallyChecked; break; + case 2 : rootSSHAccess = Qt:: Checked; break; + default : ok = false; + } + + if ( ! ok ) goto lError; + + _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); + _deviceRootSSHAccessResponse._data.mRootSSHAccess = rootSSHAccess; + } + else { + _deviceRootSSHAccessResponse._data.mMessage = _deviceRootSSHAccessResponse.toString(); + _deviceRootSSHAccessResponse._data.mRootSSHAccess = _deviceRootSSHAccessRequest._data.mRootSSHAccess; + } + + LOG_APPED(_deviceRootSSHAccessResponse._data.mMessage); + emit didAttributeResponse(_deviceRootSSHAccessResponse._data); + } + else { + // the error in this case is handled in the checkError + } + return; + +lError: + checkError(DeviceError::eDevice_Scripts_Error_Incorrect_Rsp, _deviceRootSSHAccessResponse, _deviceRootSSHAccessResponse.toString()); +} + +///////////////////////////////////////////// DeviceCryptSetup +/*! + * \brief DeviceController::onAttributeRequest + * \details Calls the CryptSetup script with the model data DeviceCryptSetupRequestData + * \param vData - the model data + */ +void DeviceController::onAttributeRequest(const DeviceCryptSetupRequestData &vData) +{ + //DEBUG qDebug() << " ---------- " << vData.mCommand << vData.mPassword; + + _deviceCryptSetupRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Crypt_Setup), _deviceCryptSetupResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processCryptSetup.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceCryptSetupResponse); + return; + } + + + // ----- run the process + int timeout = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processCryptSetup, script, timeout, { _deviceCryptSetupRequest._data.mCommand }); + _processCryptSetup.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceCryptSetupRequest._data.mPassword)); + timedProcess->start(); + + // Update UI with a response + MDeviceCryptSetupResponse model; + model._data.mAccepted = false; + model._data.mMessage = tr("Encrypted Partition %1 started.").arg(_deviceCryptSetupRequest._data.mCommand); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessCryptSetupExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessCryptSetupExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + const int ERR_CRYPTSETUP_MOUNT_ISMOUNT=134; // is used in crypt_setup.sh do not modify + + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the pared device info in text and it will be empty string if error happens. + MDeviceCryptSetupResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processCryptSetup.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mEchoInfo; + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); + + bool isSetup = _deviceCryptSetupRequest._data.mCommand == "setup"; + bool isMount = _deviceCryptSetupRequest._data.mCommand == "mount"; + bool isMounted = isMount && ( + vExitCode == ERR_CRYPTSETUP_MOUNT_ISMOUNT || // is already mounted + vExitCode == 0 // successful mount + ); + bool isUpdate = gEnableUpdating && isMounted; + + if ( isMount ) emit didCryptSetupMount(model._data.mAccepted); + + QString msg = ""; + int err = 0 ; + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + // moving the configuration files if the encrypted partition creation was successful. + if ( isUpdate ) goto lMove; // if it is gEnableUpdating, bypass the mAccepted for already mounted. + if ( ! model._data.mAccepted ) goto lErr ; // any other case goto error + if ( ! isSetup ) goto lOut ; // if not setup do NOT continue to move configurations + +lMove: + err = settings.configurationsMove(&msg, isUpdate); + if ( err ) { + model._data.mAccepted = false ; + model._data.mReason = err ; + model._data.mMessage = msg ; + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); + } + +lOut: + return; + +lErr: + LOG_DEBUG(QString("Encrypted Partition %1 failed").arg(_deviceCryptSetupRequest._data.mCommand)); +} + +///////////////////////////////////////////// DeviceBluetoothPaired +void DeviceController::onAttributeRequest(const DeviceBluetoothPairedResetRequestData &) +{ + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Reset), _deviceBluetoothPairedResetResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBluetoothPairedReset.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedResetResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedReset, script, timeout_ms); + timedProcess->start(); +} + +void DeviceController::onProcessBluetoothPairedResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // TODO: review the usage and definition of this object _deviceBluetoothPairedResetResponse. do we need it any more? + MDeviceBluetoothPairedResetResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processBluetoothPairedReset.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + LOG_APPED_UI(model.data().mMessage); + emit didAttributeResponse(model.data()); +} + +///////////////////////////////////////////// DeviceBluetoothPairedQuery +void DeviceController::onAttributeRequest(const DeviceBluetoothPairedQueryRequestData &) +{ + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Bluetooth_Paired_Query), _deviceBluetoothPairedQueryResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processBluetoothPairedQuery.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceBluetoothPairedQueryResponse); + return; + } + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processBluetoothPairedQuery, script, timeout_ms); + timedProcess->start(); +} + +void DeviceController::onProcessBluetoothPairedQueryExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the pared device info in text and it will be empty string if error happens. + MDeviceBluetoothPairedQueryResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processBluetoothPairedQuery.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + emit didAttributeResponse(model.data()); + LOG_APPED_UI(model.data().mMessage); +} + +/*! + * \brief DeviceController::doScreenshot + * \details emit the screenshot signal to run that in Device controller thread + * \param vCurrentDateTime + */ +void DeviceController::doScreenshot(const QImage &vImage, const QString &vFileName) { + emit didScreenshot(vImage, vFileName); +} + +/*! + * \brief DeviceController::onScreenshot + * \details The function to save the image vImage in the file vFileName + * \param vImage - The image source object + * \param vFileName - The filename to same the image to + */ +void DeviceController::onScreenshot(const QImage &vImage, const QString &vFileName) { + vImage.save(vFileName); + LOG_DEBUG("Screenshot saved in " + vFileName); +} + +/*! + * \brief DeviceController::ondoAddWatch + * \details The thread safe add file watch method + * \param vFile - The file to add to watch. + */ +void DeviceController::ondoAddWatch(const QString &vFile, bool vCreate) +{ + DeviceError::Scripts_Error_Enum err = DeviceError::eDevice_OK; + if ( vCreate ) { + if ( ! FileHandler::write( vFile, "", false) ) { err = DeviceError::eDevice_Watch_Error_NotCreate; goto lErr; } + } else { + if ( ! QFileInfo::exists ( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotFound ; goto lErr; } + } + if ( ! _fileSystemWatcher.addPath( vFile ) ) { err = DeviceError::eDevice_Watch_Error_NotAdded ; goto lErr; } + + LOG_APPED_UI(QString("Device watch %1").arg(vFile)); + return; +lErr: + LOG_DEBUG(DeviceError::deviceErrorText(err, 0)); +} + +/*! + * \brief DeviceController::onWatchFileChanged + * \details This slot is called once the files being watched is updated. + * \param vFile - the file name. + */ +void DeviceController::onWatchFileChanged(const QString &vFile) +{ + emit didWatchFileChange(vFile); +} + +/*! + * \brief DeviceController::onEventThreadChange + * \details The signal handler for the DeviceController(this)::didEventThreadChange + * to start checking for the Encrypted partition readiness. + */ +void DeviceController::onEventThreadChange() +{ + //DEBUG qDebug() << " ---------- " << __FUNCTION__ << QThread::currentThread()->objectName() << QThread::currentThread() << qApp->thread(); + if ( QThread::currentThread() != &Threads::_DeviceController_Thread ) { + qCritical() << " ***** Device controller thread not initialized correctly ***** "; + return; + } + _hasThread = true; + checkConfugurationMountReady(); +} + +/*! + * \brief DeviceController::onPOSTOSVersionData + * \details Collects the OS Version + * when it is ready after the POST is done reading OS Version + * \param vMacAddress - The Ethernet MAC address + */ +void DeviceController::onPOSTOSVersionData(const QString &vOSVersion) { + _osVersion = vOSVersion; + emit didPOSTOSVersionData (vOSVersion); +} + +/*! + * \brief DeviceController::onPOSTEthernetData + * \details Collects the ethernet mac address + * when it is ready after the POST is done for the Ethernet + * \param vMacAddress - The Ethernet MAC address + */ +void DeviceController::onPOSTEthernetData(const QString &vMacAddress) { + _macEthernet = vMacAddress; + emit didPOSTEthernetData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTWirelessData + * \details Collects the wireless mac address + * when it is ready after the POST is done for the Wireless connection + * \param vMacAddress - The Wireless MAC address + */ +void DeviceController::onPOSTWirelessData(const QString &vMacAddress) { + _macWireless = vMacAddress; + + encryption::varSalt(vMacAddress); + _hasSalt = ! vMacAddress.trimmed().isEmpty(); + checkConfugurationMountReady(); + + emit didPOSTWirelessData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTBluetoothData + * \details Collects the bluetooth mac address + * when it is ready after the POST is done for the Bluetooth + * \param vMacAddress - The Bluetooth MAC address + */ +void DeviceController::onPOSTBluetoothData(const QString &vMacAddress) { + _macBluetooth = vMacAddress; + emit didPOSTBluetoothData (vMacAddress); +} + +/*! + * \brief DeviceController::onPOSTCloudSyncData + * \details Collects the CloudSync Network Address + * when it is ready after the POST is done for the CloudSync + * \param vNetAddress - *** Not defined yet and is a placeholder for later use *** + */ +void DeviceController::onPOSTCloudSyncData(const QString &vNetAddress) { + _netCloudSync = vNetAddress; + emit didPOSTCloudSyncData (vNetAddress); +} + +bool DeviceController::logBackup(const QString &vFileName) +{ + if ( ! gLogUpload ) return false; // no log backup + + bool ok = true; + QString fileSrc = vFileName; + QFileInfo fileInfo(vFileName); + QString filePath(fileInfo.absolutePath()); + QString fileBase(fileInfo.baseName()); + QString fileSufx(fileInfo.completeSuffix().prepend(_Logger.logFileNamePendingSubExt())); + QString fileDest(QString("%1/%2.%3").arg(filePath, fileBase, fileSufx)); + // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; + if ( gLogCompress ) { + fileSrc += _Logger.logFileNameCompressExt(); + fileDest += _Logger.logFileNameCompressExt(); + ok = FileHandler::backupFile(vFileName); + } + ok = QFile::rename(fileSrc, fileDest); + return ok; +} + +void DeviceController::onLogBackup(const QString &vFileName) +{ + if ( ! gLogUpload ) return; // no log backup ( slot ) + + bool ok = true; + ok = logBackup( vFileName); + emit didLogBackup(ok, vFileName); +} + +bool DeviceController::logUpload(const QString &vFileName) +{ + if ( ! gLogUpload ) return false; // no log Uploaded rename + + bool ok = true; + QFileInfo fileInfo(vFileName); + QString filePath; + QString fileBase(fileInfo.baseName()); + QString ext = _Logger.logFileNamePendingSubExt(); + QString fileSufx(fileInfo.completeSuffix().remove(ext)); + QString fileDest = QString("%1.%2").arg(fileBase, fileSufx); + + Logger::LogType logType = _Logger.logFileLogType(vFileName, filePath); + ok = logType != Logger::eLogNone; + if ( ! ok ) { LOG_APPED_UI(QString("CS Incorrect log upload type [%1]").arg(fileSufx)); goto lOut; } + // DEBUG qDebug() << __FUNCTION__ << "\n" << vFileName << "\n" << fileDest; + + ok = QFile::rename(filePath + vFileName, filePath + fileDest); +lOut: + return ok; +} + +void DeviceController::onLogUpload(const QString &vFileName) +{ + if ( ! gLogUpload ) return; // no log uploaded rename ( slot ) + + bool ok = true; + ok = logUpload( vFileName); + emit didLogUpload(ok, vFileName); +} + +/*! + * \brief DeviceController::checkConfugurationMountReady + * \details Cheks if the system is ready to mount the encrypted partition. + * The object has to be on its own thread and the salt for the decryption has to be ready. + */ +void DeviceController::checkConfugurationMountReady() +{ + /// in manufacturing or updating the system is logged with root and configurations are in /home/root + /// therefore no need to mount the cononfiguraiton partition. + /// also for manufacturing the partition is being set up + /// and is less steps for setup if the partition is not mounted. + if ( gEnableManufacturing ) return; // it should do the mount when gEnableUpdating, therefore here should not add gEnableUpdating. + if ( ! ( _hasThread && _hasSalt ) ) return; + + DeviceCryptSetupRequestData data; + data.mCommand = "mount"; + bool ok = false; + data.mPassword = encryption::configurationsPassword( ok ); + if ( ! ok ) { // not enough infromation to create a secure passowrd + // status(tr("Not enough secure information provided")); + } + else { + onAttributeRequest(data); + } + + // I_AM_HERE + // HERE move the settings read from ApplicationController to here + // here is when the DeviceController is moved to its thread + // and now can start mounting the encrypted partition + // and then read the settings. + // TODO don't forget to check for the required configurations files and parameters in the settings class. + // and take care of the security flag in the Storage class. +} + +/*! + * \brief DeviceController::findPendingLogs + * \details this function counts downs for the _pendingInterval + * when the _pendingCounter reaches 0 will search for the files + * and if there is any will get the recent file in the list + */ +void DeviceController::findPendingLogs() +{ + if ( ! gLogUpload ) return; // no log upload pending detection + + static QString pendingLog = ""; + if( _pendingCounter ) { + _pendingCounter -- ; + return; + } + else { + _pendingCounter = _pendingInterval; // every minute + } + + QFileInfoList pendingFiles; + QString logLoc = Log_Folder_Base; + QString logExt = QString("*.%1*").arg(_Logger.logFileNamePendingSubExt()); // "*.u.*"; + for( auto logFolder : { Log_Folder_Application, Log_Folder_Service/*, Log_Folder_CloudSync*/ } ) { + pendingFiles = Storage::FileHandler::find( logLoc + logFolder, { logExt } ); + // look into the list. + // if there are pending files, + // send a request only for the top on the list + /// Note I thought it makes sense to send the oldest on the application and service logs + /// but there are some conversation about the situation if something happens on the device, + /// and it would be a critical situation to get the recent/top log and won't wait for the old ones to upload. + // * When gets uploaded, moves from pending then next one comes to top + // the process repeats until there is no file in pending + + bool uploadOldestFirst = true; //TODO: make if configurable(cfg, or cli) + + if ( pendingFiles.count() ) { + // the most recent/first log file, to first ask for the current log which has just been saved as pending + if ( uploadOldestFirst ) { + _pendingLog = pendingFiles.last().absoluteFilePath(); + } else { + _pendingLog = pendingFiles.first().absoluteFilePath(); + } + QString message = pendingLog; + LOG_DEBUG(message); + emit didPendingLog( _pendingLog, FileHandler::sha256sum( _pendingLog ) ); + // when a pending file found in the first log folder stop + // until there is none in first one (log) + // then continue to the next log folder (service) ... + goto lOut; + } + } +lOut: + return; +} + +///////////////////////////////////////////// DeviceFactoryReset +void DeviceController::onAttributeRequest(const DeviceFactoryResetRequestData &vData) +{ + _deviceFactoryResetRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Factory_Reset), _deviceFactoryResetResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processFactoryReset.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceFactoryResetResponse); + return; + } + + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processFactoryReset, script, timeout_ms); + timedProcess->start(); + + MDeviceFactoryResetResponse model; + model._data.mAccepted = false; // will indirectly set the property factoryResetEnabled + model._data.mMessage = tr("Factory Reset started."); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessFactoryResetExitCode + * \param vExitCode - the script exit code. + * \param vStatus - the script echoed message. + */ +void DeviceController::onProcessFactoryResetExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the paired device info in text and it will be empty string if error happens. + MDeviceFactoryResetResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processFactoryReset.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + emit didAttributeResponse (model.data()); + emit didFactoryReset (model._data.mAccepted); + + LOG_APPED_UI(model.data().mMessage); +} + +///////////////////////////////////////////// DeviceDecommission +void DeviceController::onAttributeRequest(const DeviceDecommissionRequestData &vData) +{ + _deviceDecommissionRequest._data = vData; + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, Device_Decommission), _deviceDecommissionResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processDecommission.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceDecommissionResponse); + return; + } + + + // ----- run the process + int timeout_ms = 10000; + TimedProcess *timedProcess = new TimedProcess(&_processDecommission, script, timeout_ms, { CloudSyncPath }); + _processDecommission.setEnvironment(QProcess::systemEnvironment() << QString("PASSWORD=%1").arg(_deviceDecommissionRequest._data.mPassword)); + timedProcess->start(); + + MDeviceDecommissionResponse model; + model._data.mAccepted = false; // will indirectly set the property decommissionEnabled + model._data.mMessage = tr("Decommissioning started."); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessDecommissionExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessDecommissionExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + // The Exit code in this script is not used. + // any other checking is done by UI Software at the moment this script is called. + // The only thing matters is the paired device info in text and it will be empty string if error happens. + MDeviceDecommissionResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processDecommission.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + // DEBUG: qDebug() << model._data.mMessage << deviceInfo; + emit didAttributeResponse (model.data()); + emit didDecommissioning (model._data.mAccepted); + + LOG_APPED_UI(model.data().mMessage); +} + +///////////////////////////////////////////// DeviceUSBMounting +void DeviceController::onAttributeRequest(const DeviceUSBMountRequestData &vData) +{ + Q_UNUSED(vData) + usbMountReq(vData.usbDevice, vData.isMountRequest); +} + +/*! + * \brief DeviceController::usbMountReq + * \details Calls the Usb unmount/mount script + * \param vIsMount - indicate if the request is for mounting or unmounting + * \param vDevice - the path to the USB device + */ +void DeviceController::usbMountReq(const QString &vDevice, bool vIsMount) +{ + qDebug() << __FUNCTION__ << vDevice << vIsMount; + _deviceUSBMountRequest._data.isMountRequest = vIsMount ; + _deviceUSBMountRequest._data.usbDevice = vDevice ; // not necessary, but to be consistent + + // ----- check that script exists. + QString script; + if ( checkError( DeviceError::checkScript(script, (vIsMount ? USB_Mount : USB_Unmount )), _deviceUSBMountResponse, script) ) + return; + + // ----- check if the process is not running + if ( _processUSBMount.state() != QProcess::NotRunning ) { + checkError(DeviceError::eDevice_Scripts_Error_IsRunning, _deviceUSBMountResponse); + return; + } + + // ----- run the process + int timeout_ms = 5000; + TimedProcess *timedProcess = new TimedProcess(&_processUSBMount, script, timeout_ms, {vDevice, USB_Mount_Point, USB_File_System}); + timedProcess->start(); + + MDeviceUSBMountResponse model; + model._data.mAccepted = false; + model._data.mMessage = vIsMount ? tr("USB unmount started.") : tr("USB mount started"); + emit didAttributeResponse(model.data()); +} + +/*! + * \brief DeviceController::onProcessUSBMountExitCode + * \param vExitCode + * \param vStatus + */ +void DeviceController::onProcessUSBMountExitCode(int vExitCode, QProcess::ExitStatus vStatus) +{ + MDeviceUSBMountResponse model; + QByteArray deviceInfo; + if ( vStatus ) vExitCode = Device::DeviceError::eDevice_Scripts_Error_Status; + else deviceInfo = _processUSBMount.readAll(); + model.fromByteArray( deviceInfo, &vExitCode ); + emit didAttributeResponse(model.data()); + + // Re-evaluate the USB space available - need to call this here to avoid + // visual lag caused by waiting to call this function on the timer timeout + usbSpaceCheck(); + + bool ok = ! vStatus; + QString usbDevice = _deviceUSBMountRequest._data.usbDevice; + if(_deviceUSBMountRequest._data.isMountRequest) { // *** USB Mount + if ( ok && ! _mounted ) { + _mounted = true; + _removed = false; + LOG_DEBUG(QString("USB flash drive %1 has been mounted on %2").arg(usbDevice).arg(USB_Mount_Point)); + emit didUSBDriveMount(); + } else { + usbError(usbDevice); + } + } else { // *** USB Unmount + if ( ok && _mounted ) { + _mounted = false; + // _umounted = true; // I think it might be needed, but needs more testing. + LOG_DEBUG(QString("USB drive %2 unmounted").arg(usbDevice)); + emit didUSBDriveUmount(); + } else { + // the error is irrelevant, commented out for now + // usbError(usbDrive); + } + } + + // log error and exit + return; +}