diff --git a/CMakeLists.txt b/CMakeLists.txt index 4645f6f4..53f7bd45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ if (DEFINED TARGET_PLATFORM_TOOLCHAIN) endif() updateGitVars() -set(HEART_VERSION 1.2.${GIT_COMMIT_COUNT}.${GIT_COMMIT_HASH}) +set(HEART_VERSION 1.3.${GIT_COMMIT_COUNT}.${GIT_COMMIT_HASH}) if (ANDROID OR IOS) option(HEART_TESTS "Enable or disable tests of the heart library" OFF) diff --git a/HeartTests/tst_testprotockol.cpp b/HeartTests/tst_testprotockol.cpp index 7e3820aa..2a105d8a 100644 --- a/HeartTests/tst_testprotockol.cpp +++ b/HeartTests/tst_testprotockol.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #define TestCase(name, testClass) \ void name() { \ @@ -39,6 +40,8 @@ private slots: #endif TestCase(upgradeDataBaseTest, UpgradeDataBaseTest) + TestCase(multiVersionTest, MultiVersionTest) + // END TEST CASES @@ -67,7 +70,6 @@ testProtockol::testProtockol() { } testProtockol::~testProtockol() { - _app->exit(0); delete _app; } diff --git a/HeartTests/units/bigdatatest.cpp b/HeartTests/units/bigdatatest.cpp index 7dadf62b..15fdf707 100644 --- a/HeartTests/units/bigdatatest.cpp +++ b/HeartTests/units/bigdatatest.cpp @@ -12,7 +12,7 @@ #define LOCAL_TEST_PORT TEST_PORT + 4 class BigPackage: public QH::PKG::AbstractData { - QH_PACKAGE(BigPackage, "BigPackage") + QH_PACKAGE("BigPackage") public: BigPackage(){ @@ -22,13 +22,15 @@ class BigPackage: public QH::PKG::AbstractData { // StreamBase interface protected: - QDataStream &fromStream(QDataStream &stream) override{ + QDataStream &fromStream(QDataStream &stream) override { + stream >> data; return stream; }; - QDataStream &toStream(QDataStream &stream) const override{ + QDataStream &toStream(QDataStream &stream) const override { + stream << data; return stream; diff --git a/HeartTests/units/multiversiontest.cpp b/HeartTests/units/multiversiontest.cpp new file mode 100644 index 00000000..8930c353 --- /dev/null +++ b/HeartTests/units/multiversiontest.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2023-2023 QuasarApp. + * Distributed under the lgplv3 software license, see the accompanying + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. +*/ + +#include "multiversiontest.h" + +#define LOCAL_TEST_PORT TEST_PORT + 6 + +class MultiVersionPkg { +public: + int v2; + int v1; + bool responce = false; + int lastSerializedFrom = 0; + int lastSerializedto = 0; +}; + +class MultiVersionPkg1: public QH::PKG::MultiversionData, public MultiVersionPkg { + QH_PACKAGE("MultiVersionPkg") + +public: + MultiVersionPkg1(): QH::PKG::MultiversionData( + { + {0, // version 0 + { + [this](QDataStream& stream) -> QDataStream&{ // from + lastSerializedFrom = 0; + stream >> v1; + stream >> responce; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + lastSerializedto = 0; + + stream << v1; + stream << responce; + + return stream; + } + } + }, + {1, // version 1 + { + [this](QDataStream& stream) -> QDataStream&{ // from + lastSerializedFrom = 1; + + stream >> v1; + stream >> responce; + + stream >> v2; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + lastSerializedto = 1; + + stream << v1; + stream << responce; + + stream << v2; + return stream; + } + } + }, + } + ) {}; +}; + +class SingleVersionPkg: public QH::PKG::AbstractData, public MultiVersionPkg { + QH_PACKAGE("MultiVersionPkg") + + // StreamBase interface +protected: + QDataStream &fromStream(QDataStream &stream) override { + lastSerializedFrom = 0; + stream >> v1; + stream >> responce; + + return stream; + }; + QDataStream &toStream(QDataStream &stream) const override { + stream << v1; + stream << responce; + + return stream; + }; +}; + +class MultiVersionPkg2: public QH::PKG::MultiversionData, public MultiVersionPkg { + QH_PACKAGE("MultiVersionPkg") + +public: + MultiVersionPkg2(): QH::PKG::MultiversionData( + { + {0, // version 0 + { + [this](QDataStream& stream) -> QDataStream&{ // from + lastSerializedFrom = 0; + stream >> v1; + stream >> responce; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + lastSerializedto = 0; + + stream << v1; + stream << responce; + + return stream; + } + } + }, + {1, // version 1 + { + [this](QDataStream& stream) -> QDataStream&{ // from + lastSerializedFrom = 1; + + stream >> v1; + stream >> responce; + + stream >> v2; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + lastSerializedto = 1; + + stream << v1; + stream << responce; + + stream << v2; + return stream; + } + } + }, + {2, // version 2 + { + [this](QDataStream& stream) -> QDataStream&{ // from + lastSerializedFrom = 2; + + stream >> v2; + stream >> responce; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + lastSerializedto = 2; + + stream << v2; + stream << responce; + + return stream; + } + } + } + } + ) {}; +}; + +template +class TestAPI: public QH::iParser { +public: + TestAPI(QH::AbstractNode* parentNode): QH::iParser(parentNode) { + registerPackageType(); + } + + // iParser interface +public: + + QH::ParserResult parsePackage(const QSharedPointer &pkg, + const QH::Header &hdr, + QH::AbstractNodeInfo *sender) override { + + if (pkg->cmd() == Base::command()) { + data = pkg; + + if (!data.dynamicCast()->responce) { + data.dynamicCast()->responce = true; + if (!sendData(pkg.data(), sender, &hdr)) { + return QH::ParserResult::Error; + } + } + + return QH::ParserResult::Processed; + } + + return QH::ParserResult::NotProcessed; + }; + int version() const override {return 0;}; + QString parserId() const override {return "BigDataTestParser";}; + + QSharedPointer getData() const{ + return data; + }; + + void dropData() { + data = nullptr; + }; + +private: + QSharedPointer data = nullptr; + +}; + +template +class TestingClient: public QH::AbstractNode { + + // AbstractNode interface +public: + TestingClient() { + _parser = addApiParser>(); + } + + bool sendRequest(const Base& data, int port) { + return sendData(&data, QH::HostAddress(TEST_LOCAL_HOST, port)); + } + + NodeType nodeType() const override { + return NodeType::Node; + }; + + const QSharedPointer& parser() const { + return _parser; + } + +private: + QSharedPointer _parser; +}; + +MultiVersionTest::MultiVersionTest() { + _nodeV1 = new TestingClient; + _nodeV2 = new TestingClient; + _nodeOld = new TestingClient; + +} + +MultiVersionTest::~MultiVersionTest() { + _nodeV1->softDelete(); + _nodeV2->softDelete(); + _nodeOld->softDelete(); + +} + +void MultiVersionTest::test() { + QVERIFY(_nodeV1->run(TEST_LOCAL_HOST, LOCAL_TEST_PORT)); + + QVERIFY(connectFunc(_nodeV2, TEST_LOCAL_HOST, LOCAL_TEST_PORT)); + QVERIFY(connectFunc(_nodeOld, TEST_LOCAL_HOST, LOCAL_TEST_PORT)); + + testMultipacakges(); + testSinglePackages(); +} + +void MultiVersionTest::testMultipacakges() { + auto nodeWithV1 = dynamic_cast*>(_nodeV1); + auto nodeWithV2 = dynamic_cast*>(_nodeV2); + + // clean old data; + nodeWithV1->parser().staticCast>()->dropData(); + nodeWithV2->parser().staticCast>()->dropData(); + + // from new to old + { + + // initialize test values. + MultiVersionPkg2 pkg; + pkg.v1 = 10; + pkg.v2 = 20; + + // send test values into dist node + QVERIFY(nodeWithV2->sendRequest(pkg, LOCAL_TEST_PORT)); + + // should use the maximum available serialization. it is 1 + QVERIFY(pkg.lastSerializedto == 1); + + // wait for receive packge on distanation node + QVERIFY(wait([&nodeWithV1]() { + if (auto data = nodeWithV1->parser().staticCast>()-> + getData().staticCast()) { + return data->lastSerializedFrom == 1; + } + return false; + + }, WAIT_RESPOCE_TIME)); + + // wait for package on sender node. + QVERIFY(wait([&nodeWithV2]() { + if (auto data = nodeWithV2->parser().staticCast>()-> + getData().staticCast()) { + return data->responce == true; + } + return false; + + }, WAIT_RESPOCE_TIME)); + + // chec data all data should not be changed. + auto data = nodeWithV2->parser().staticCast>()-> + getData().staticCast(); + + QVERIFY(data->v1 == pkg.v1); + QVERIFY(data->v2 == pkg.v2); + } +} + +void MultiVersionTest::testSinglePackages() { + auto nodeWithSingle = dynamic_cast*>(_nodeOld); + auto nodeWithV1 = dynamic_cast*>(_nodeV1); + + // clean old data; + nodeWithSingle->parser().staticCast>()->dropData(); + nodeWithV1->parser().staticCast>()->dropData(); + + // from single package to new + + { + // initialize test values. + SingleVersionPkg pkg; + pkg.v1 = 10; + pkg.v2 = 20; + + // send test values into dist node + QVERIFY(nodeWithSingle->sendRequest(pkg, LOCAL_TEST_PORT)); + + // should use the maximum available serialization. it is 0 + QVERIFY(pkg.lastSerializedto == 0); + + // wait for receive packge on distanation node + QVERIFY(wait([&nodeWithV1]() { + if (auto data = nodeWithV1->parser().staticCast>()-> + getData().staticCast()) { + return data->lastSerializedFrom == 0; + } + return false; + + }, WAIT_RESPOCE_TIME)); + + // wait for package on sender node. + QVERIFY(wait([&nodeWithSingle]() { + if (auto data = nodeWithSingle->parser().staticCast>()-> + getData().staticCast()) { + return data->responce == true; + } + return false; + + }, WAIT_RESPOCE_TIME)); + + // chec data all data should not be changed. + auto data = nodeWithSingle->parser().staticCast>()-> + getData().staticCast(); + + QVERIFY(data->v1 == pkg.v1); + // v2 is not supported on the v0 parser + QVERIFY(data->v2 == 0); + } +} diff --git a/HeartTests/units/multiversiontest.h b/HeartTests/units/multiversiontest.h new file mode 100644 index 00000000..ac5c5f48 --- /dev/null +++ b/HeartTests/units/multiversiontest.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023-2023 QuasarApp. + * Distributed under the lgplv3 software license, see the accompanying + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. +*/ + +#ifndef MULTIVERSIONTEST_H +#define MULTIVERSIONTEST_H + + +#include "test.h" +#include "testutils.h" + +#include +#include + +/** + * @brief The MultiVersionTest class test work of packages with different versions. + */ +class MultiVersionTest: public Test, protected TestUtils +{ +public: + MultiVersionTest(); + ~MultiVersionTest(); + void test(); +protected: + + void testMultipacakges(); + void testSinglePackages(); + +private: + QH::AbstractNode *_nodeV1 = nullptr; + QH::AbstractNode *_nodeV2 = nullptr; + QH::AbstractNode *_nodeOld = nullptr; + +}; + +#endif // MULTIVERSIONTEST_H diff --git a/HeartTests/units/shedullertest.cpp b/HeartTests/units/shedullertest.cpp index 2b2e58a6..6e5203bb 100644 --- a/HeartTests/units/shedullertest.cpp +++ b/HeartTests/units/shedullertest.cpp @@ -40,6 +40,14 @@ class TestTask: public QH::AbstractTask { }; +#ifdef Q_OS_WIN +const int stepTime = 5000; +const int measurementError = 2000; +#else +const int stepTime = 2000; +const int measurementError = 1000; +#endif + ShedullerTest::ShedullerTest() { } @@ -56,13 +64,13 @@ void ShedullerTest::testSingleMode() { auto task = QSharedPointer::create(); task->setMode(QH::ScheduleMode::SingleWork); - task->setTime(2000); + task->setTime(stepTime); quint64 ct = QDateTime::currentMSecsSinceEpoch(); node->sheduleTask(task); QVERIFY(wait([&node](){return node->executedTime;}, WAIT_TIME)); - int diff = std::abs(static_cast(node->executedTime - (ct + 2000))); - QVERIFY(diff < 1000); + int diff = std::abs(static_cast(node->executedTime - (ct + stepTime))); + QVERIFY(diff < measurementError); node->executedTime = 0; QVERIFY(node->sheduledTaskCount() == 0); @@ -73,24 +81,24 @@ void ShedullerTest::testSingleMode() { void ShedullerTest::testRepeatMode() { ShedullerestNode *node = new ShedullerestNode(); auto task = QSharedPointer::create(); - task->setTime(2000); + task->setTime(stepTime); task->setMode(QH::ScheduleMode::Repeat); quint64 ct = QDateTime::currentMSecsSinceEpoch(); node->sheduleTask(task); QVERIFY(wait([&node](){return node->executedTime;}, WAIT_TIME)); - int diff = std::abs(static_cast(node->executedTime - (ct + 2000))); + int diff = std::abs(static_cast(node->executedTime - (ct + stepTime))); - QVERIFY(diff < 1000); + QVERIFY(diff < measurementError); node->executedTime = 0; QVERIFY(node->sheduledTaskCount() == 1); QVERIFY(wait([&node](){return node->executedTime;}, WAIT_TIME)); - diff = std::abs(static_cast(node->executedTime - (ct + 4000))); + diff = std::abs(static_cast(node->executedTime - (ct + stepTime * 2))); - QVERIFY(diff < 1000); + QVERIFY(diff < measurementError); QVERIFY(node->sheduledTaskCount() == 1); node->removeTask(task->taskId()); @@ -114,7 +122,7 @@ void ShedullerTest::testTimePointMode() { QVERIFY(wait([&node](){return node->executedTime;}, WAIT_TIME)); int diff = std::abs(static_cast(node->executedTime - requestTime)); - QVERIFY(diff < 1000); + QVERIFY(diff < measurementError); QVERIFY(node->sheduledTaskCount() == 0); node->softDelete(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6b3e7a3e..8e8cd926 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,7 @@ # of this license document, but changing it is not allowed. # -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.18) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/src/private/apiversion.cpp b/src/private/apiversion.cpp index f29c3398..51ccfd14 100644 --- a/src/private/apiversion.cpp +++ b/src/private/apiversion.cpp @@ -17,28 +17,45 @@ APIVersion::APIVersion() { QDataStream &APIVersion::fromStream(QDataStream &stream) { - stream >> _version; + stream >> _apisVersions; + stream >> _packagesVersions; return stream; } QDataStream &APIVersion::toStream(QDataStream &stream) const { - stream << _version; + stream << _apisVersions; + stream << _packagesVersions; + return stream; } -const VersionData &APIVersion::version() const { - return _version; +VersionData APIVersion::apisVersions() const { + return _apisVersions; +} + +void APIVersion::setApisVersions(const VersionData &newApisVersions) { + _apisVersions = newApisVersions; +} + +PackagesVersionData APIVersion::packagesVersions() const { + return _packagesVersions; } -void APIVersion::setVersion(const VersionData &newVersion) { - _version = newVersion; +void APIVersion::setPackagesVersions(const PackagesVersionData &newPackagesVersions) { + _packagesVersions = newPackagesVersions; } bool APIVersion::isValid() const { - if (_version.isEmpty()) + if (_apisVersions.isEmpty()) return false; - for (const auto & version: _version) { + for (const auto & version: _apisVersions) { + if (version.max() < version.min()) { + return false; + } + } + + for (const auto & version: _packagesVersions) { if (version.max() < version.min()) { return false; } diff --git a/src/private/apiversion.h b/src/private/apiversion.h index dd43df0e..77660c19 100644 --- a/src/private/apiversion.h +++ b/src/private/apiversion.h @@ -27,23 +27,32 @@ class APIVersion: public QH::PKG::AbstractData APIVersion(); static unsigned short command(){return PROTOCKOL_VERSION_COMMAND;} + static unsigned short version(){return 0;} + static QString commandText(){return "PROTOCKOL_VERSION_COMMAND";} unsigned short cmd() const override {return APIVersion::command();} QString cmdString() const override {return APIVersion::commandText();} - const VersionData &version() const; - void setVersion(const VersionData &newVersion); - bool isValid() const override; + /** + * @brief packagesVersions This method return list of available multi-version packages on the node. + * @return + */ + PackagesVersionData packagesVersions() const; + void setPackagesVersions(const PackagesVersionData &newPackagesVersions); + + VersionData apisVersions() const; + void setApisVersions(const VersionData &newApisVersions); + protected: - unsigned int localCode() const override {return typeid(APIVersion).hash_code();} QDataStream &fromStream(QDataStream &stream) override; QDataStream &toStream(QDataStream &stream) const override; private: - VersionData _version; + VersionData _apisVersions; + PackagesVersionData _packagesVersions; }; diff --git a/src/private/versionisreceived.h b/src/private/versionisreceived.h index a2f58c8e..a9352cce 100644 --- a/src/private/versionisreceived.h +++ b/src/private/versionisreceived.h @@ -22,6 +22,7 @@ class VersionIsReceived: public QH::PKG::AbstractData public: VersionIsReceived(); + static unsigned short version(){return 0;} static unsigned short command(){return PROTOCKOL_VERSION_RECEIVED_COMMAND;} static QString commandText(){return "PROTOCKOL_VERSION_RECEIVED_COMMAND";} @@ -31,7 +32,6 @@ class VersionIsReceived: public QH::PKG::AbstractData // StreamBase interface protected: - unsigned int localCode() const override {return typeid(VersionIsReceived).hash_code();} QDataStream &fromStream(QDataStream &stream) override; QDataStream &toStream(QDataStream &stream) const override; }; diff --git a/src/public/abstractnode.cpp b/src/public/abstractnode.cpp index f69d0836..6729dbaf 100644 --- a/src/public/abstractnode.cpp +++ b/src/public/abstractnode.cpp @@ -710,7 +710,8 @@ ParserResult AbstractNode::parsePackage(const QSharedPointer &pkg, return _apiVersionParser->parsePackage(pkg, pkgHeader, sender); } -QSharedPointer AbstractNode::genPackage(unsigned short cmd, AbstractNodeInfo *sender) const { +QSharedPointer AbstractNode::genPackage(unsigned short cmd, + AbstractNodeInfo *sender) const { if (_apiVersionParser) return _apiVersionParser->searchPackage(cmd, sender); @@ -759,9 +760,9 @@ unsigned int AbstractNode::sendData(const PKG::AbstractData *resp, Package pkg; bool convert = false; if (req && req->isValid()) { - convert = resp->toPackage(pkg, req->hash); + convert = resp->toPackage(pkg, node->multiVersionPackages().value(resp->cmd()),req->hash); } else { - convert = resp->toPackage(pkg); + convert = resp->toPackage(pkg, node->multiVersionPackages().value(resp->cmd())); } if (!convert) { diff --git a/src/public/abstractnode.h b/src/public/abstractnode.h index 8e8f7081..aa0fd7fe 100644 --- a/src/public/abstractnode.h +++ b/src/public/abstractnode.h @@ -762,8 +762,15 @@ private slots: const Header &pkgHeader, AbstractNodeInfo *sender); + /** + * @brief genPackage This method generate a package from the command and version + * @param cmd this is command of the package. + * @param sender This is sender that sent this requests. + * @return empty package object. + */ QSharedPointer - genPackage(unsigned short cmd, AbstractNodeInfo *sender) const; + genPackage(unsigned short cmd, + AbstractNodeInfo *sender) const; /** @note just disaable listen method in the node objects. diff --git a/src/public/abstractnodeinfo.cpp b/src/public/abstractnodeinfo.cpp index fda1eb38..70489c75 100644 --- a/src/public/abstractnodeinfo.cpp +++ b/src/public/abstractnodeinfo.cpp @@ -131,6 +131,14 @@ bool AbstractNodeInfo::confirmData() const { return _status != NodeCoonectionStatus::NotConnected && fVersionDelivered() && fVersionReceived(); } +const PackagesVersionData& AbstractNodeInfo::multiVersionPackages() const { + return _multiVersionPackages; +} + +void AbstractNodeInfo::setMultiVersionPackages(const PackagesVersionData &newMultiVersionPackages) { + _multiVersionPackages = newMultiVersionPackages; +} + bool AbstractNodeInfo::fVersionDelivered() const { return _fVersionDelivered; } diff --git a/src/public/abstractnodeinfo.h b/src/public/abstractnodeinfo.h index 015be850..c5b92a32 100644 --- a/src/public/abstractnodeinfo.h +++ b/src/public/abstractnodeinfo.h @@ -259,6 +259,18 @@ class HEARTSHARED_EXPORT AbstractNodeInfo: public QObject */ void addParser(QSharedPointer parser); + /** + * @brief multiVersionPackages This is list of packages of one api package tah support multiple versions. + * @return list of packages of one api package tah support multiple versions. + */ + const PackagesVersionData &multiVersionPackages() const; + + /** + * @brief setMultiVersionPackages This method sets new list of multi-version packages. + * @param newMultiVersionPackages This is new value of multi-version packages. + */ + void setMultiVersionPackages(const PackagesVersionData &newMultiVersionPackages); + public slots: /** * @brief removeSocket This method use for remove socket. @@ -335,6 +347,10 @@ public slots: QHash> _parsersKeysMap; QMutex _parsersListMutex; + /** + * @brief _multiVersionPackages contains packages list that has multiple versions on one api rest of this is universal packages with version 0. + */ + PackagesVersionData _multiVersionPackages; VersionData _version; bool _fVersionReceived = false; bool _fVersionDelivered = false; diff --git a/src/public/accesstoken.cpp b/src/public/accesstoken.cpp index 768a7c96..1a3c9b3b 100644 --- a/src/public/accesstoken.cpp +++ b/src/public/accesstoken.cpp @@ -59,6 +59,7 @@ bool AccessToken::operator !=(const AccessToken &other) const { AccessToken& AccessToken::operator =(const AccessToken &other) = default; QDataStream &AccessToken::fromStream(QDataStream &stream) { + stream >> _data; stream >> _duration; return stream; diff --git a/src/public/apiversionparser.cpp b/src/public/apiversionparser.cpp index 53200f7f..4399c138 100644 --- a/src/public/apiversionparser.cpp +++ b/src/public/apiversionparser.cpp @@ -232,7 +232,7 @@ QSharedPointer APIVersionParser::selectParser(const QString &parserKey, } QSharedPointer APIVersionParser::selectParserImpl(unsigned short cmd, - AbstractNodeInfo *sender) const{ + AbstractNodeInfo *sender) const { auto version = sender->version(); const auto availableParser = selectParser(version); for (const auto& parser: availableParser) { @@ -275,10 +275,18 @@ unsigned short APIVersionParser::minimumApiVersion(const QString &apiKey) const bool APIVersionParser::sendSupportedAPI(AbstractNodeInfo *dist) const { VersionData supportedAPIs; + PackagesVersionData multiVersionPackages; for (auto it = _apiParsers.begin(); it != _apiParsers.end(); ++it) { DistVersion supportVersions; + for (auto api = it->begin(); api != it->end(); ++api) { + const auto packages = api.value()->multiVersionPackages(); + for (auto pkg = packages.begin(); pkg != packages.end(); ++pkg) { + multiVersionPackages[pkg.key()] = pkg.value(); + } + } + supportVersions.setMax(it->lastKey()); supportVersions.setMin(it->firstKey()); @@ -290,7 +298,8 @@ bool APIVersionParser::sendSupportedAPI(AbstractNodeInfo *dist) const { return false; PKG::APIVersion versionInformationPkg; - versionInformationPkg.setVersion(supportedAPIs); + versionInformationPkg.setApisVersions(supportedAPIs); + versionInformationPkg.setPackagesVersions(multiVersionPackages); return sendData(&versionInformationPkg, dist); } @@ -299,8 +308,10 @@ bool APIVersionParser::processAppVersion(const QSharedPointer & QH::AbstractNodeInfo *sender, const QH::Header &) { - auto distVersion = message->version(); + auto distVersion = message->apisVersions(); sender->setVersion(distVersion); + sender->setMultiVersionPackages(message->packagesVersions()); + auto parser = selectParser(distVersion); for (auto parserKey = distVersion.keyBegin(); parserKey != distVersion.keyEnd(); ++parserKey) { diff --git a/src/public/apiversionparser.h b/src/public/apiversionparser.h index 3ff2012e..72b17a58 100644 --- a/src/public/apiversionparser.h +++ b/src/public/apiversionparser.h @@ -44,11 +44,13 @@ class APIVersionParser: public iParser * @brief searchPackage This method search package recursive in all registered pararsers. Searching will be in compatibility versions. * Before search methd choose compatibly verson. * @param cmd This is command for that shold be create pacakge object. + * @param ver This is version for that shold be create pacakge object. * @param sender This is node that sent @a the cmd. * @return Package generated from cmd. */ QSharedPointer - searchPackage(unsigned short cmd, AbstractNodeInfo *sender) const; + searchPackage(unsigned short cmd, + AbstractNodeInfo *sender) const; /** * @brief getSelectedApiParser This method return apiParser for selected node @@ -129,6 +131,8 @@ class APIVersionParser: public iParser */ bool sendSupportedAPI(AbstractNodeInfo *dist) const; + unsigned short selectPackageVersion(const VersionData& local, const VersionData& dist); + signals: /** diff --git a/src/public/distversion.cpp b/src/public/distversion.cpp index 005d6f50..07750412 100644 --- a/src/public/distversion.cpp +++ b/src/public/distversion.cpp @@ -16,7 +16,36 @@ void DistVersion::setMax(unsigned short newMax) { _max = newMax; } +int DistVersion::getMaxCompatible(const DistVersion &distVersion) const { + unsigned short midMax = std::min(distVersion.max(), _max); + unsigned short midMin = std::max(distVersion.min(), _min); + + if (midMax < midMin) + return -1; + + return midMax; +} + +int DistVersion::getMinCompatible(const DistVersion &distVersion) const { + unsigned short midMax = std::min(distVersion.max(), _max); + unsigned short midMin = std::max(distVersion.min(), _min); + + if (midMax < midMin) + return -1; + + return midMin; +} + +bool DistVersion::isSupport(unsigned short version) const { + return _min <= version && version <= _max; +} + +QString DistVersion::toString() const { + return QString("%0:%1").arg(_min).arg(_max); +} + QDataStream &DistVersion::fromStream(QDataStream &stream) { + stream >> _min; stream >> _max; return stream; @@ -28,6 +57,10 @@ QDataStream &DistVersion::toStream(QDataStream &stream) const { return stream; } +DistVersion::operator bool() const { + return _min || _max; +} + unsigned short DistVersion::min() const { return _min; } diff --git a/src/public/distversion.h b/src/public/distversion.h index 1942933b..89cb5c84 100644 --- a/src/public/distversion.h +++ b/src/public/distversion.h @@ -8,6 +8,7 @@ #ifndef DISTVERSION_H #define DISTVERSION_H +#include "humanreadableobject.h" #include #include @@ -15,17 +16,42 @@ namespace QH { /** - * @brief The DistVersion class This is infirmation of supported versions of the distanation api. + * @brief The DistVersion class This is information of supported versions of the destinations api. */ -class DistVersion: public StreamBase { +class HEARTSHARED_EXPORT DistVersion: public StreamBase, public QuasarAppUtils::iHRO { public: + operator bool() const; + unsigned short min() const; void setMin(unsigned short newMin); unsigned short max() const; void setMax(unsigned short newMax); + /** + * @brief getMaxСompatible return maximum available on booth nodes version. + * @param distVersion this is dis version. + * @return return maximum version. if this version is not found return "-1" + */ + int getMaxCompatible(const DistVersion& distVersion) const; + + /** + * @brief getMinСompatible return maximum available on booth nodes version. + * @param distVersion this is dis version. + * @return return minimum version. if this version is not found return "-1" + */ + int getMinCompatible(const DistVersion& distVersion) const; + + /** + * @brief isSupport This method return true if the @a version is supported of this versions range. + * @param version This is checked version. + * @return true if the @a version is supported of this versions range. + */ + bool isSupport(unsigned short version) const; + + QString toString() const override; + protected: QDataStream &fromStream(QDataStream &stream) override; @@ -33,7 +59,7 @@ class DistVersion: public StreamBase { private: - /// This is monimum supported version. + /// This is minimum supported version. unsigned short _min = 0; /// This is maximum supported version. @@ -41,11 +67,15 @@ class DistVersion: public StreamBase { }; - /** - * @brief VersionData This is array of all avalable apis and supported its versions. + * @brief VersionData This is array of all available apis and supported its versions. */ typedef QHash VersionData; +/** + * @brief PackagesVersionData This is some as VersionData but for int commands. + */ +typedef QHash PackagesVersionData; + } #endif // DISTVERSION_H diff --git a/src/public/header.cpp b/src/public/header.cpp index 7140c82b..ece65964 100644 --- a/src/public/header.cpp +++ b/src/public/header.cpp @@ -10,6 +10,7 @@ #include namespace QH { + Header::Header() { reset(); } @@ -18,7 +19,7 @@ bool Header::isValid() const { if (size > Package::maximumSize()) { return false; } - return command && hash; + return command && hash && headerVersion == 1 && unusedSpace1 == 0 && unusedSpace2 == 0 && unusedSpace3 == 0; } void Header::reset() { @@ -26,10 +27,14 @@ void Header::reset() { command = 0; triggerHash = 0; hash = 0; + unusedSpace1 = 0; + unusedSpace2 = 0; + unusedSpace3 = 0; + } QString Header::toString() const { - return QString("Header description: Size - %0, Command - %1, hash - %2, triggerHash - %3"). - arg(size).arg(command).arg(QString::number(hash, 16), QString::number(triggerHash, 16)); + return QString("Header description: HeaderVersion - %0, Size - %1, Command - %2, hash - %3, triggerHash - %4"). + arg(headerVersion).arg(size).arg(command).arg(QString::number(hash, 16), QString::number(triggerHash, 16)); } } diff --git a/src/public/header.h b/src/public/header.h index 49427377..89b996e3 100644 --- a/src/public/header.h +++ b/src/public/header.h @@ -13,30 +13,42 @@ namespace QH { /** - * @brief The Header struct 12 bytes. + * @brief The Header struct 32 bytes. */ #pragma pack(push, 1) struct HEARTSHARED_EXPORT Header { /** * @brief command of package for more information see the AbstractData::toPackage method. */ - unsigned short command; + unsigned short command = 0; //2 bytes + + /** + * @brief headerVersion This is version of the header struct + */ + const unsigned char headerVersion = 1; //3 bytes /** * @brief size This is size of package data (exclude header size). */ - unsigned int size; + unsigned int size = 0; //7 bytes /** * @brief hash This is unique id of a package. id calc with CRC32 function for Qt implementation. */ - unsigned int hash; + unsigned int hash = 0; //11 bytes /** * @brief triggerHash This is hash of request package that package has been responded. * The server should write to which command it responds. */ - unsigned int triggerHash; + unsigned int triggerHash = 0; //15 bytes + + /** + * @brief unusedSpace This is unused space for changes of the header struct in the future. + */ + unsigned long long unusedSpace1 = 0; //23 bytes + unsigned long long unusedSpace2 = 0; //31 bytes + unsigned char unusedSpace3 = 0; //32 bytes /** * @brief Header default constructor @@ -63,7 +75,6 @@ struct HEARTSHARED_EXPORT Header { }; #pragma pack(pop) - } diff --git a/src/public/iparser.cpp b/src/public/iparser.cpp index b9412c96..9e1d4fcf 100644 --- a/src/public/iparser.cpp +++ b/src/public/iparser.cpp @@ -31,8 +31,7 @@ QString iParser::pareseResultToString(const ParserResult &parseResult) { } } -const QHash > & -iParser::registeredTypes() const { +const PacksMap &iParser::registeredTypes() const { return _registeredTypes; } @@ -58,7 +57,10 @@ unsigned int iParser::sendData(const PKG::AbstractData *resp, const AbstractNodeInfo *dist, const Header *req) const { return node()->sendData(resp, dist, req); +} +const PackagesVersionData &iParser::multiVersionPackages() const { + return _multiVersionPackages; } void iParser::initSupportedCommands() {} diff --git a/src/public/iparser.h b/src/public/iparser.h index b6ee64bf..daaef14a 100644 --- a/src/public/iparser.h +++ b/src/public/iparser.h @@ -9,18 +9,21 @@ #ifndef IPARSER_H #define IPARSER_H +#include "distversion.h" #include "hostaddress.h" #include -#include +#include namespace QH { class AbstractNodeInfo; class AbstractNode; -namespace PKG { -class AbstractData; -} + +/** + * @brief PacksMap This is hash map where id is command of package and value is factory function. + */ +using PacksMap = QHash>; /** @@ -60,6 +63,13 @@ class HEARTSHARED_EXPORT iParser: public QObject, public QuasarAppUtils::iHRO _registeredTypes[T::command()] = [](){ return new T(); }; + + if constexpr(std::is_base_of_v) { + T tmp; + if (const DistVersion &distVersion = tmp.packageVersion()) { + _multiVersionPackages[tmp.cmd()] = distVersion; + } + } }; /** @@ -181,7 +191,7 @@ class HEARTSHARED_EXPORT iParser: public QObject, public QuasarAppUtils::iHRO * @return list of registered command. * @see iParser::registerPackageType */ - const QHash > ®isteredTypes() const; + const PacksMap ®isteredTypes() const; /** * @brief genPackage This is factory method that generate data pacakge objects by command. @@ -213,6 +223,12 @@ class HEARTSHARED_EXPORT iParser: public QObject, public QuasarAppUtils::iHRO virtual void initSupportedCommands(); QString toString() const override; + + /** + * @brief multiVersionPackages return list of the supported multiversions packages. + * @return list of the supported multiversions packages. + */ + const PackagesVersionData &multiVersionPackages() const; protected: AbstractNode *node() const; @@ -239,7 +255,10 @@ class HEARTSHARED_EXPORT iParser: public QObject, public QuasarAppUtils::iHRO const Header *req = nullptr) const; private: - QHash> _registeredTypes; + // command - {version - factory} + PacksMap _registeredTypes; + PackagesVersionData _multiVersionPackages; + AbstractNode *_node; friend class BigDataParserOld; diff --git a/src/public/package.cpp b/src/public/package.cpp index 710ead3f..4b7d1868 100644 --- a/src/public/package.cpp +++ b/src/public/package.cpp @@ -27,10 +27,7 @@ bool Package::isValid() const { if (hdr.size > maximumSize()) return false; - if (calcHash() == hdr.hash) - return true; - - return calcHashOld() == hdr.hash; + return calcHash() == hdr.hash; } void Package::reset() { @@ -41,7 +38,7 @@ void Package::reset() { QString Package::toString() const { return QString("Pakcage description: %0." " Data description: Data size - %1, Data: %2"). - arg(hdr.toString()).arg(data.size()).arg(QString(data.toHex().toUpper())); + arg(hdr.toString()).arg(data.size()).arg(QString(data.toHex().toUpper())); } unsigned int Package::calcHash() const { @@ -49,10 +46,6 @@ unsigned int Package::calcHash() const { return qa_common::hash32(tmp.constData(), tmp.size()); } -unsigned int Package::calcHashOld() const { - return qHash(data + QByteArray::number(hdr.command)); -} - unsigned int Package::maximumSize() { return 1024 * 1024; } diff --git a/src/public/package.h b/src/public/package.h index 3f8c8be0..b1e6327d 100644 --- a/src/public/package.h +++ b/src/public/package.h @@ -15,10 +15,9 @@ namespace QH { -class Abstract; /** * @brief The Package struct. This is base structure for transporting data by network between QH nodes. - * The Package contains a 12 bytes header and Package::data array. The size on the header should be equals size of Package::data array. + * The Package contains a 32 bytes header and Package::data array. The size on the header should be equals size of Package::data array. * */ class HEARTSHARED_EXPORT Package: public StreamBase { @@ -60,13 +59,6 @@ class HEARTSHARED_EXPORT Package: public StreamBase { */ unsigned int calcHash() const; - - /** - * @brief calcHash This method recalc hash sum for this pacakge. - * @return int32 hash of pacakge. - */ - unsigned int calcHashOld() const; - /** * @brief maximumSize This method return maximu size of pacakge. If pacakge large the maximum size then package will separate to BigDataPart in sending. * @return size in bytes of pacakge. diff --git a/src/public/packages/abstractdata.cpp b/src/public/packages/abstractdata.cpp index 16094622..d7cc47e6 100644 --- a/src/public/packages/abstractdata.cpp +++ b/src/public/packages/abstractdata.cpp @@ -21,36 +21,22 @@ AbstractData::AbstractData() { } bool AbstractData::toPackage(Package &package, + const DistVersion& , unsigned int triggerHash) const { - if (!checkCmd()) { - QuasarAppUtils::Params::log("You try send pacakge without QH_PACKAGE macross. Please add QH_PACKAGE macros to this class.", - QuasarAppUtils::Error); - return false; - } - if (!isValid()) { return false; } package.data = toBytes(); - package.hdr.command = cmd(); package.hdr.triggerHash = triggerHash; package.hdr.size = package.data.size(); - if (isOldPackage()) { - package.hdr.hash = package.calcHashOld(); - } else { - package.hdr.hash = package.calcHash(); - } + package.hdr.hash = package.calcHash(); - return package.isValid(); -} -bool AbstractData::checkCmd() const { - unsigned int code = typeid (*this).hash_code(); - return code == localCode(); + return package.isValid(); } bool AbstractData::isValid() const { @@ -58,9 +44,10 @@ bool AbstractData::isValid() const { } QString AbstractData::toString() const { - return QString("Type: %0, command: %1"). + return QString("Type: %0 \n" + "Command: %1 \n"). arg(cmdString()). - arg(cmd()); + arg(cmd()); } void AbstractData::fromPakcage(const Package &pkg) { @@ -71,9 +58,6 @@ AbstractData::~AbstractData() { } -bool QH::PKG::AbstractData::isOldPackage() const { - return false; -} } diff --git a/src/public/packages/abstractdata.h b/src/public/packages/abstractdata.h index 82710151..68f3bd37 100644 --- a/src/public/packages/abstractdata.h +++ b/src/public/packages/abstractdata.h @@ -7,8 +7,10 @@ #ifndef ABSTRACTDATA_H #define ABSTRACTDATA_H +#include "distversion.h" #include "humanreadableobject.h" #include "package.h" +#include #include #include @@ -23,26 +25,24 @@ #define PROTOCKOL_VERSION_RECEIVED_COMMAND PROTOCKOL_VERSION_COMMAND - 1 /** - * @brief QH_PACKAGE This macross prepare data to send and create a global id for package. For get global id use the cmd method. + * @brief QH_PACKAGE This macross prepare data to send and create a global id for package. + * For get global id use the cmd method. * For get quick access for global command use the ClassName::command() method. This method is static. + * @arg S This is unique id of the pacakge. Shold be some on all your network devices. + */ -#define QH_PACKAGE(X, S) \ +#define QH_PACKAGE(S) \ public: \ static unsigned short command(){\ QByteArray ba = QString(S).toLocal8Bit();\ return qa_common::hash16(ba.data(), ba.size());\ } \ static QString commandText(){return S;} \ - unsigned short cmd() const override {return X::command();} \ - QString cmdString() const override {return X::commandText();} \ - protected: \ - unsigned int localCode() const override {return typeid(X).hash_code();} \ - \ + unsigned short cmd() const override {return command();} \ +\ + QString cmdString() const override {return S;} \ private: - -#define QH_PACKAGE_AUTO(X) QH_PACKAGE(X,#X) - namespace QH { namespace PKG { @@ -54,6 +54,7 @@ namespace PKG { * \code{cpp} * class MyPackage: public QH::AbstractData { + QH_PACKAGE_AUTO_VER(MyPackage, 1) public: MyPackage(); @@ -103,11 +104,12 @@ class HEARTSHARED_EXPORT AbstractData : public StreamBase, public QuasarAppUtils /** * @brief toPackage This method convert this class object to the package. * For more info see Package class. - * @param package This is return value of Package class. + * @param package This is return value of Package class. + * @param reqVersion This is required version. This method create package of the needed version. * @param triggerHash This is hash of the package the current class is responding to. * @return True if convert to package finished successful. */ - bool toPackage(Package &package, unsigned int triggerHash = 0) const; + virtual bool toPackage(Package &package, const DistVersion &reqVersion, unsigned int triggerHash = 0) const; /** * @brief isValid This method check current object to valid. @@ -158,24 +160,6 @@ class HEARTSHARED_EXPORT AbstractData : public StreamBase, public QuasarAppUtils */ explicit AbstractData(); - /** - * @brief localCode This method return local code - * @return local command of this class. used for check QH_PACKAGE macro before send pacakge. - */ - virtual unsigned int localCode() const = 0; - - /** - * @brief isOldPackage This method mark package as a old, old pacakges use the Package::calcHashOld method for calculation hash sum of packages. - * @return true if the pacakge is old. - */ - virtual bool isOldPackage() const; -private: - /** - * @brief checkCmd This method check QH_PACKAGE macross. - * @return true if the QH_PACKAGE macross is enabled else fal. - */ - bool checkCmd() const;; - }; diff --git a/src/public/packages/badrequest.cpp b/src/public/packages/badrequest.cpp index 7872265d..1a1c5f71 100644 --- a/src/public/packages/badrequest.cpp +++ b/src/public/packages/badrequest.cpp @@ -28,7 +28,6 @@ BadRequest::BadRequest(const Package &package): } QDataStream &BadRequest::fromStream(QDataStream &stream) { - stream >> _errCode; stream >> _err; diff --git a/src/public/packages/badrequest.h b/src/public/packages/badrequest.h index 36ab5230..9a0b3162 100644 --- a/src/public/packages/badrequest.h +++ b/src/public/packages/badrequest.h @@ -33,7 +33,7 @@ struct ErrorData { */ class HEARTSHARED_EXPORT BadRequest : public AbstractData { - QH_PACKAGE(BadRequest, "BadRequest") + QH_PACKAGE("BadRequest") public: /** diff --git a/src/public/packages/bigdatabase.cpp b/src/public/packages/bigdatabase.cpp index ade92968..8b8ad7de 100644 --- a/src/public/packages/bigdatabase.cpp +++ b/src/public/packages/bigdatabase.cpp @@ -25,6 +25,7 @@ void BigDataBase::setPackageId(int newPackageId) { } QDataStream &BigDataBase::fromStream(QDataStream &stream) { + stream >> _packageId; return stream; diff --git a/src/public/packages/bigdatabase.h b/src/public/packages/bigdatabase.h index 3f296cca..1aab29fd 100644 --- a/src/public/packages/bigdatabase.h +++ b/src/public/packages/bigdatabase.h @@ -19,7 +19,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT BigDataBase: public AbstractData { - QH_PACKAGE(BigDataBase, "BigDataBase") + QH_PACKAGE("BigDataBase") public: BigDataBase(); diff --git a/src/public/packages/bigdataheader.h b/src/public/packages/bigdataheader.h index d3b5aca3..c016edd8 100644 --- a/src/public/packages/bigdataheader.h +++ b/src/public/packages/bigdataheader.h @@ -27,7 +27,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT BigDataHeader : public BigDataBase { - QH_PACKAGE(BigDataHeader, "BigDataHeader") + QH_PACKAGE("BigDataHeader") public: BigDataHeader(); @@ -66,7 +66,7 @@ class HEARTSHARED_EXPORT BigDataHeader : public BigDataBase private: int packagesCount; - unsigned short _command; + unsigned short _command = 0; }; } } diff --git a/src/public/packages/bigdatapart.h b/src/public/packages/bigdatapart.h index 5e465598..5056bdef 100644 --- a/src/public/packages/bigdatapart.h +++ b/src/public/packages/bigdatapart.h @@ -20,7 +20,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT BigDataPart : public BigDataBase { - QH_PACKAGE(BigDataPart, "BigDataPart") + QH_PACKAGE("BigDataPart") public: BigDataPart(); diff --git a/src/public/packages/bigdatarequest.h b/src/public/packages/bigdatarequest.h index 633d12b3..684c75c6 100644 --- a/src/public/packages/bigdatarequest.h +++ b/src/public/packages/bigdatarequest.h @@ -19,7 +19,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT BigDataRequest: public BigDataBase { - QH_PACKAGE(BigDataRequest, "BigDataRequest") + QH_PACKAGE("BigDataRequest") public: BigDataRequest(); diff --git a/src/public/packages/bigdatawraper.h b/src/public/packages/bigdatawraper.h index 685a5671..61b48272 100644 --- a/src/public/packages/bigdatawraper.h +++ b/src/public/packages/bigdatawraper.h @@ -19,7 +19,7 @@ namespace PKG { */ class BigDataWraper: public AbstractData { - QH_PACKAGE(BigDataWraper, "BigDataWraper") + QH_PACKAGE("BigDataWraper") public: BigDataWraper() = default; diff --git a/src/public/packages/closeconnection.h b/src/public/packages/closeconnection.h index 79d33b01..cdd85c3b 100644 --- a/src/public/packages/closeconnection.h +++ b/src/public/packages/closeconnection.h @@ -27,7 +27,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT CloseConnection: public AbstractData { - QH_PACKAGE(CloseConnection, "CloseConnection") + QH_PACKAGE("CloseConnection") public: CloseConnection(); diff --git a/src/public/packages/datapack.h b/src/public/packages/datapack.h index d7a508b3..2096d0f9 100644 --- a/src/public/packages/datapack.h +++ b/src/public/packages/datapack.h @@ -8,8 +8,7 @@ #ifndef DATAPACK_H #define DATAPACK_H -#include "universaldata.h" - +#include namespace QH { namespace PKG { @@ -25,7 +24,7 @@ namespace PKG { template class DataPack final: public AbstractData { - QH_PACKAGE(DataPack, Package::commandText() + "Pack") + QH_PACKAGE(Package::commandText() + "Pack") public: @@ -135,8 +134,8 @@ class DataPack final: public AbstractData QDataStream &toStream(QDataStream &stream) const override { stream << static_cast(_packData.size()); - for (const auto &ptr: qAsConst(_packData)) { - stream << *ptr; + for (const auto &data: qAsConst(_packData)) { + stream << *data; } stream << _data; @@ -144,6 +143,17 @@ class DataPack final: public AbstractData return stream; } + QDataStream &toStreamOf(QDataStream &stream, unsigned short version) const override { + stream << static_cast(_packData.size()); + + for (const auto &data: qAsConst(_packData)) { + data->toStreamOf(stream, version); + } + + stream << _data; + + return stream; + } private: QList> _packData; diff --git a/src/public/packages/dbobject.h b/src/public/packages/dbobject.h index e4a27996..3e8d4c34 100644 --- a/src/public/packages/dbobject.h +++ b/src/public/packages/dbobject.h @@ -18,12 +18,6 @@ class QSqlQuery; namespace QH { namespace PKG { -/** - * The ONLY_DATABASE_PACKAGE macross is base macros for all database objects that do not use stream and network functions. -*/ -#define ONLY_DATABASE_PACKAGE QH_PACKAGE(DBObject, "DBObject") - - /** * @brief The PrepareResult enum is result of work prepare sql query of dbobjects. */ @@ -95,7 +89,7 @@ typedef QMap DBVariantMap; */ class HEARTSHARED_EXPORT DBObject : public AbstractData { - QH_PACKAGE(DBObject, "DBObject") + QH_PACKAGE("DBObject") public: diff --git a/src/public/packages/dbobjectset.h b/src/public/packages/dbobjectset.h index 57b53289..2a51ab37 100644 --- a/src/public/packages/dbobjectset.h +++ b/src/public/packages/dbobjectset.h @@ -22,7 +22,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT DBObjectSet: public DBObject { - QH_PACKAGE(DBObjectSet, "DBObjectSet") + QH_PACKAGE("DBObjectSet") public: DBObjectSet(const QString table); diff --git a/src/public/packages/deleteobject.h b/src/public/packages/deleteobject.h index 9138e542..35e29317 100644 --- a/src/public/packages/deleteobject.h +++ b/src/public/packages/deleteobject.h @@ -25,7 +25,6 @@ namespace PKG { */ class HEARTSHARED_EXPORT DeleteObject: public DBObject, public IToken { - QH_PACKAGE(DeleteObject, "DeleteObject") public: DeleteObject(); diff --git a/src/public/packages/getmaxintegerid.h b/src/public/packages/getmaxintegerid.h index c5a5c1f9..50294119 100644 --- a/src/public/packages/getmaxintegerid.h +++ b/src/public/packages/getmaxintegerid.h @@ -19,7 +19,6 @@ namespace PKG { */ class HEARTSHARED_EXPORT GetMaxIntegerId: public DBObject { - QH_PACKAGE(GetMaxIntegerId, "GetMaxIntegerId") public: /** diff --git a/src/public/packages/getsinglevalue.h b/src/public/packages/getsinglevalue.h index f8e8c9f6..9ffc2203 100644 --- a/src/public/packages/getsinglevalue.h +++ b/src/public/packages/getsinglevalue.h @@ -38,7 +38,6 @@ namespace PKG { */ class HEARTSHARED_EXPORT GetSingleValue final: public DBObject { - QH_PACKAGE(GetSingleValue, "GetSingleValue") public: /** diff --git a/src/public/packages/multiversiondata.cpp b/src/public/packages/multiversiondata.cpp new file mode 100644 index 00000000..b8d4e6af --- /dev/null +++ b/src/public/packages/multiversiondata.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023-2023 QuasarApp. + * Distributed under the lgplv3 software license, see the accompanying + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. +*/ + +#include "multiversiondata.h" +#include "qaglobalutils.h" + +#include + +namespace QH { +namespace PKG { + +#define MAGIC "mver" + +MultiversionData::MultiversionData(const QMap& serializers): + _serializers(serializers) { + + if (serializers.size()) { + _packageVersion.setMax(serializers.lastKey()); + _packageVersion.setMin(serializers.firstKey()); + } +} + +QDataStream &MultiversionData::fromStream(QDataStream &stream) { + + if (!_serializers.size()) { + debug_assert(false, + "Your MultiversionData not support any serialized functions. " + "Please initialize it in constructor of the MultiversionData class."); + return stream; + } + + QByteArray magic; + stream >> magic; + + unsigned short version = 0; + if (magic == MAGIC) { + stream >> version; + } else { + stream.device()->seek(0); + } + + return _serializers.value(version).from(stream); +} + +QDataStream &MultiversionData::toStream(QDataStream &stream) const { + + if (!_serializers.size()) { + debug_assert(false, + "Your MultiversionData not support any serialized functions. " + "Please initialize it in constructor of the MultiversionData class."); + return stream; + } + + stream << QByteArray{MAGIC}; + stream << _serializers.lastKey(); + + return _serializers.last().to(stream); +} + +QByteArray MultiversionData::toBytesOf(const DistVersion& version) const { + QByteArray res; + QDataStream stream(&res, QIODevice::WriteOnly); + + if (parsingVersion()) { + stream.setVersion(parsingVersion()); + } + + toStreamOf(stream, version); + return res; +} + +QDataStream &MultiversionData::toStreamOf(QDataStream &stream, const DistVersion& version) const { + + unsigned short ver = _packageVersion.getMaxCompatible(version); + auto serializer = _serializers.value(ver, {}); + if (!serializer.to) { + debug_assert(false, + "Your MultiversionData not support the required version serialized functions. " + "Please initialize it in constructor of the MultiversionData class."); + return stream; + } + + if (ver) { + stream << QByteArray{MAGIC}; + stream << ver; + } + + return serializer.to(stream); +} + +bool MultiversionData::toPackage(Package &package, + const DistVersion& reqVersion, + unsigned int triggerHash) const { + if (!isValid()) { + return false; + } + + package.data = toBytesOf(reqVersion); + package.hdr.command = cmd(); + package.hdr.triggerHash = triggerHash; + package.hdr.size = package.data.size(); + + package.hdr.hash = package.calcHash(); + + + return package.isValid(); +} + +const DistVersion &MultiversionData::packageVersion() const { + return _packageVersion; +} + +} +} diff --git a/src/public/packages/multiversiondata.h b/src/public/packages/multiversiondata.h new file mode 100644 index 00000000..ce62f0c2 --- /dev/null +++ b/src/public/packages/multiversiondata.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023-2023 QuasarApp. + * Distributed under the lgplv3 software license, see the accompanying + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. +*/ + +#ifndef MULTIVERSIONDATA_H +#define MULTIVERSIONDATA_H + +#include "abstractdata.h" + + +namespace QH { +namespace PKG { + +struct SerializationBox { + std::function from = nullptr; + std::function to = nullptr; +}; + +/** + * @brief The MultiversionData class This class add support for multiple versions of ne command/package. + * If you need to add support multiple versions make your pakcage class children of the MultiversionData class. + * + * Example: + * + * @code{cpp} + * + * class MyPackage: public MultiversionData { + MyPackage(): QH::PKG::MultiversionData( + { + {0, // version 0 + { + [this](QDataStream& stream) -> QDataStream&{ // from + stream >> v1; + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + stream << v1; + + return stream; + } + } + }, + {1, // version 1 + { + [this](QDataStream& stream) -> QDataStream&{ // from + stream >> v1; + stream >> v2; + + return stream; + }, + [this](QDataStream& stream) -> QDataStream&{ // to + stream << v1; + stream << v2; + return stream; + } + } + } + } + ) {}; + * @endcode + * + * @note the default toBytes function of this class will be convert your class using latest version. + */ +class HEARTSHARED_EXPORT MultiversionData: public AbstractData +{ +public: + /** + * @brief MultiversionData main constructor + * @param serializers this is map of the all supported versions. + */ + MultiversionData(const QMap& serializers); + + /** + * @brief packageVersion This method should be return number of the pacakge version. + * @return pcakge version. by default return - 0 (any version) + */ + const DistVersion& packageVersion() const; + + QDataStream& fromStream(QDataStream& stream) override final; + QDataStream& toStream(QDataStream& stream) const override final; + + /** + * @brief toBytesOf This is overload method of StreamBase::toBytes for support multi versions of packages. + * @param version This is required version pacakge. + * @return bytes array for package. + * @note This is just wrapper method for the AbstractData::toStream method. + */ + QByteArray toBytesOf(const DistVersion &version) const; + + /** + * @brief toStreamOf This overrload of the base toStream method for support the multi version packages. + * @param stream this is stream object. + * @param version this is custom version of parsing function. + * @return stream object. + */ + QDataStream& toStreamOf(QDataStream& stream, const DistVersion &version) const; + + + bool toPackage(Package &package, const DistVersion &reqVersion, unsigned int triggerHash = 0) const override final; + + +private: + DistVersion _packageVersion; + QMap _serializers; + + +}; +} +} +#endif // MULTIVERSIONDATA_H diff --git a/src/public/packages/ping.cpp b/src/public/packages/ping.cpp index 308e9bb3..46745618 100644 --- a/src/public/packages/ping.cpp +++ b/src/public/packages/ping.cpp @@ -33,6 +33,7 @@ void Ping::setAnsver(bool ansver) { } QDataStream &Ping::fromStream(QDataStream &stream) { + stream >> _ansver; return stream; diff --git a/src/public/packages/ping.h b/src/public/packages/ping.h index 8bac4e43..0272af3e 100644 --- a/src/public/packages/ping.h +++ b/src/public/packages/ping.h @@ -20,7 +20,7 @@ namespace PKG { */ class HEARTSHARED_EXPORT Ping: public AbstractData { - QH_PACKAGE(Ping, "Ping") + QH_PACKAGE("Ping") public: Ping(); diff --git a/src/public/packages/setsinglevalue.h b/src/public/packages/setsinglevalue.h index 1e092197..40474337 100644 --- a/src/public/packages/setsinglevalue.h +++ b/src/public/packages/setsinglevalue.h @@ -32,8 +32,6 @@ namespace PKG { */ class HEARTSHARED_EXPORT SetSingleValue final: public DBObject { - QH_PACKAGE(SetSingleValue, "SetSingleValue") - public: /** * @brief SetSingleValue This is default constructor of the update query generator. diff --git a/src/public/packages/universaldata.cpp b/src/public/packages/universaldata.cpp index 55845422..75eda2b9 100644 --- a/src/public/packages/universaldata.cpp +++ b/src/public/packages/universaldata.cpp @@ -43,6 +43,7 @@ QVariant *UniversalData::ref(int key) { } QDataStream &UniversalData::fromStream(QDataStream &stream) { + stream >> _data; return stream; diff --git a/src/public/packages/universaldata.h b/src/public/packages/universaldata.h index 85dc2f20..3ff4a11f 100644 --- a/src/public/packages/universaldata.h +++ b/src/public/packages/universaldata.h @@ -22,7 +22,7 @@ namespace PKG { * @code{cpp} * class AuthRequest: public QH::PKG::UniversalData { - QH_PACKAGE_AUTO(RC::API::V4::AuthRequest) + QH_PACKAGE(RC::API::V4::AuthRequest) enum Filds{ UserId = 0