Index: sources/view/settings/VSettings.cpp =================================================================== diff -u -r56ad953ae404fcf6956bd4f76b7a54b12d0285d3 -r6c6f1f5d466badd9b4fd67be7c907234c342b2a2 --- sources/view/settings/VSettings.cpp (.../VSettings.cpp) (revision 56ad953ae404fcf6956bd4f76b7a54b12d0285d3) +++ sources/view/settings/VSettings.cpp (.../VSettings.cpp) (revision 6c6f1f5d466badd9b4fd67be7c907234c342b2a2) @@ -1,58 +1,323 @@ /*! * - * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. + * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file VSettings.cpp * \author (last) Behrouz NematiPour - * \date (last) 29-Mar-2021 + * \date (last) 02-Apr-2023 * \author (original) Behrouz NematiPour * \date (original) 29-Mar-2021 * */ #include "VSettings.h" // Qt +#include +#include // Project #include "GuiController.h" #include "MSettings.h" +#include "Settings.h" +#include -VIEW_DEF(VSettings, SettingsData) +VIEW_DEF_CLASS(VSettings) +void VSettings::initConnections() { + ACTION_VIEW_CONNECTION (SettingsData ); + connect(&_GuiController, SIGNAL(didActionReceive (GuiActionType, const QVariantList &)), + this , SLOT( onActionReceive (GuiActionType, const QVariantList &))); + +} + +void VSettings::initConnectionsSettings() +{ + static bool init = false; if ( init ) return; + + PROPERTY_POST_CONNECTION(VSettings, servicePass ); + PROPERTY_POST_CONNECTION(VSettings, alarmVolume ); + PROPERTY_POST_CONNECTION(VSettings, roWaterMode ); + PROPERTY_POST_CONNECTION(VSettings, noCANBus ); + + init = true; +} + +void VSettings::servicePass_post(const QString &vservicePass) { + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + if ( settings.save(servicePassCategory(), servicePassGroup(), servicePassKey(), vservicePass) != 0 ) { + servicePass(""); + // FIXME: Notify UI with a message + } +} + +void VSettings::roWaterMode_post(const bool &vroWaterMode_post) { + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + if ( settings.save(roWaterModeCategory(), roWaterModeGroup(), roWaterModeKey(), QString::number(vroWaterMode_post)) != 0 ) { + roWaterMode(false); + // FIXME: Notify UI with a message + } +} + +void VSettings::alarmVolume_post(const quint8 &valarmVolume) { + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + settings.save(alarmVolumeCategory(), alarmVolumeGroup(), alarmVolumeKey(), QString::number(valarmVolume)); +} + +void VSettings::noCANBus_post(const bool &vnoCANBus) { + //TODO The Settings shall be the Singleton SettingsController and modify the MSettings like the others. + Storage::Settings settings; + settings.save(noCANBusCategory(), noCANBusGroup(), noCANBusKey(), QString::number(vnoCANBus)); +} + void VSettings::onActionReceive(const SettingsData &) { - QVariantMap mCategorys; - for (const auto &category : _Settings.categorys()) { - QVariantMap details; + // TODO: this function needs to be moved to the controller, to execute in settings thread not the main thread. + // it should then send the output here to update the specific properties defined. + QStringList mCategorys = _Settings.categorys(); + for (const auto &category : mCategorys) { QStringList groups = _Settings.groups(category); - // DEBUG: - // qDebug() << " ----- " << category << groups; - details["groups"] = groups; - mCategorys[category] = details; + for (const auto &group : groups) { + QStringList keys = _Settings.keys (category, group); + QVariantList values = _Settings.values (category, group); + if ( Storage::Settings::isCategoryInstructions( category ) ) { + TKeysList keysList = updateReplacements(group, keys ); + updateInstructions(group, keysList, values ); + updateInstructions(group ); + } + else { //TODO: Since it is global system settings, move this to the settings controller so the C++ backend can also use it. like Date/Time formats. + for (const auto &key : qAsConst(keys)) { + // DEBUG: qDebug() << " ~~~~~~~~~~ " << category << group << key << _Settings.value(category, group, key).toString (); + QVariantMap keyValue; + if ( isservicePass (category, group, key) ) { + QString mServicePass; + mServicePass = _Settings.value(category, group, key).toString (); + keyValue[key] = mServicePass ; + servicePass ( mServicePass); + } + else if ( isroWaterMode (category, group, key) ) { + bool mRoWaterMode; + mRoWaterMode = _Settings.value(category, group, key).toBool (); + keyValue[key] = mRoWaterMode ; + roWaterMode ( mRoWaterMode); + } + else if ( isalarmVolume (category, group, key) ) { + quint8 mAlarmVolume; + mAlarmVolume = _Settings.value(category, group, key).toInt (); // returns 0 if fails, so no error checking needed. + keyValue[key] = mAlarmVolume ; + alarmVolume ( mAlarmVolume); + } + else if ( isnoCANBus (category, group, key) ) { + bool mNoCANBus; + mNoCANBus = _Settings.value(category, group, key).toBool (); // returns 0/false if fails, so no error checking needed. + keyValue[key] = mNoCANBus ; + noCANBus ( mNoCANBus); + if (_noCANBus ) + LOG_APPED_UI(QString("System is working on NoCANBus set")); + } + else { + keyValue[key] = _Settings.value(category, group, key); + } + } + } + } } - category(mCategorys); - QVariantMap mSettings; - for (const auto &group : _Settings.groups()) { - QVariantMap details; - // DEBUG : - // qDebug() << " ##### " - // << _Settings.keys (group) - // << _Settings.values (group) - // << _Settings.location (group); - QStringList keys = _Settings.keys (group); - QVariantList values = _Settings.values (group); - QString location = _Settings.location (group); + categorys (mCategorys); + emit instructionsChanged(_instructions); - details["location"] = location; - details["keys" ] = keys ; - details["values" ] = values ; + // If the configuration exits, then it has been set, and this call internally will be neutral, + // otherwise will use the default value and will notify the update. + servicePass ( _servicePass ); + alarmVolume ( _alarmVolume ); + roWaterMode ( _roWaterMode ); + // noCANBus ( _noCANBus ); // This line has been put here to remind developers that it is intentionally removed, to not to add a default value. - mSettings[group] = details; + //DEBUG qDebug() << servicePass() << roWaterMode() << alarmVolume() << noCANBus(); + adjustment(true); + + initConnectionsSettings(); +} + +VSettings::TKeysList VSettings::updateReplacements(const QString &vGroup, const QStringList &vKeys) +{ + TKeysList keysList; + for ( quint16 keyIndex = 0; keyIndex < vKeys.count(); keyIndex++ ) { + QRegExp regx("\\{\\s*\\d+\\s*:\\s*\\d+\\s*:\\s*\\w*\\s*:\\s*\\d*\\s*\\}"); + QString key = vKeys[keyIndex]; + int replacementCount = key.count(regx); + QStringList keyList; + for ( int j = 0; j < replacementCount; j++ ) { + int pos = key.indexOf(regx); + int len = regx.matchedLength(); + QString blk = key.mid(pos+1, len-2); + keyList += key.mid(0, pos); + keyList += ""; // value placeholder + key.remove(0, pos + regx.matchedLength()); + QStringList lst = blk.split(":"); + quint16 id = lst[0].toUInt(); + quint16 ix = lst[1].toUInt(); + QString ex = lst[2] ; + quint16 rd = lst[3].toUInt(); + TLocation loc; + loc.group = vGroup ; + loc.keyIndex = keyIndex ; + loc.locIndex = j * 2 + 1 ; + loc.prmIndex = ix ; + loc.prmExtra = ex ; + loc.prmRound = rd ; + _replacements[id] += loc; + //DEBUG: qDebug() << "#" << replacementCount << id << loc.group << loc.keyIndex << loc.locIndex << loc.prmIndex; + } + keyList += key; + keysList += keyList; } - settings(mSettings); + return keysList; } +void VSettings::updateInstructions(const QString &vGroup, const TKeysList &vKeysList, const QVariantList &vValues) +{ + TInstruction mInstruction; + mInstruction.location = _location; + mInstruction.keys = vKeysList; + mInstruction.values = vValues ; + _instructionMap[vGroup] = mInstruction; +} + +void VSettings::updateInstructions(const QString &vGroup) +{ + //TODO: this function or even the structure may need to update to send less data to qml and only the updated part. + TKeysList keysList = _instructionMap[vGroup].keys ; + QVariantList values = _instructionMap[vGroup].values; + QStringList keyList; + for ( const auto &keys : keysList) { + keyList += keys.join(""); + } + QVariantMap details; + details["location"] = _location ; + details["keys" ] = keyList ; + details["values" ] = values ; + _instructions[vGroup] = details ; +} + +/*! + * \brief VSettings::onActionReceive + * \details emits didActionReceive signal to notify other classes (Gui) + * , an action has been received. + * \param vAction - the action + * \param vData - the action data + */ +void VSettings::onActionReceive (GuiActionType vAction, const QVariantList &vData) +{ + bool isChanged = false; + quint16 id = qFromBigEndian((quint16)vAction); + if (_replacements.contains(id)) { + isChanged = true; + //DEBUG: qDebug() << _replacements[id].count(); + for ( const auto &item : _replacements[id]) { + //DEBUG: qDebug() << id << item.group << item.keyIndex << item.locIndex << item.prmIndex; + QString value; + quint16 len = vData.length(); + if ( 0 < item.prmIndex && item.prmIndex <= len ) { + if ( item.prmRound > 0 ) { + value = QString::number(vData[item.prmIndex - 1].toFloat(), 'f', item.prmRound) + item.prmExtra; + } + else { + value = vData[item.prmIndex - 1].toString() + item.prmExtra; + } + } + else { + LOG_DEBUG(QString("Given prmIndex %1 is out of message %2 parameters count %3 on instruction group '%4' and key index %5") + .arg(item.prmIndex).arg(id).arg(len).arg(item.group).arg(item.keyIndex)); + continue; + } + _instructionMap[item.group].keys[item.keyIndex][item.locIndex] = value; + updateInstructions(item.group); + } + } + if ( isChanged ) emit instructionsChanged(_instructions); +} + +/*! + * \brief VSettings::isPasswordValid + * \details Validates the passed string for conforming to the + * - at least one capital letter, + * - at least one digit, + * - at least one symbol + * rule + * \param vPassword - the string to check + * \returns true if string pass conforms, false otherwise + */ +bool View::VSettings::isPasswordValid(const QString &vPassword) { + int minLen = 8 ; + QString pla = "(?=.*[%1])" ; // positive look ahead + QString regSntnc = "^%1$" ; // regular expression sentence with has a start/end + QString regUpper = "A-Z" ; + QString regLower = "a-z" ; + QString regDigit = "0-9" ; + QString regSymbl = "@$!%*?&.,_-" ; + QString rln = "[%1]{%2,}" ; + + // "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&])[A-Za-z0-9@$!%*?&]{8,}$" + QString regStr = regSntnc.arg( + pla.arg(regUpper) + + pla.arg(regLower) + + pla.arg(regDigit) + + pla.arg(regSymbl) + + rln.arg(regUpper + + regLower + + regDigit + + regSymbl) + .arg(minLen ) + ); + QRegularExpression passwordRegex(regStr); + bool ok; + ok = passwordRegex.match(vPassword).hasMatch(); + return ok; +} + +QString View::VSettings::hashedPassword(const QString &vPassword, bool vIsService) +{ + bool ok; + QString hashed = encryption::hashedString(vPassword, ok, ! vIsService); + encryptionPass(ok); + return ok ? hashed : ""; +} + +/*! + * \brief View::VSettings::isServicePasswordMatch + * \details matches the given password string, vPassword, with the service password. + * \param vPassword - the given password string + * \return true if it matches. + */ +bool View::VSettings::isServicePasswordMatch(const QString &vPassword) +{ + //DEBUG qDebug() << __FUNCTION__ << _servicePass << hashedPassword(vPassword, true); + bool isNotEmpty = ( ! vPassword.trimmed().isEmpty() ); + bool isMatch = ( _servicePass == hashedPassword(vPassword, true) ); + return isMatch && isNotEmpty; +} + +/*! + * \brief View::VSettings::updateServicePassword + * \details Updatest the service password with the given password. + * \param vPassword - the password to be saved as the service password. + */ +void View::VSettings::updateServicePassword(const QString &vPassword) +{ + servicePass(hashedPassword(vPassword, true)); +} + +void View::VSettings::checkServicePasswordSet() +{ + bool ok; + isDefaultServicePassword(encryption::isDefaultServicePassword(servicePass(), ok)); + //DEBUG qDebug() << __FUNCTION__ << ok; + encryptionPass(ok); +} +