added new parser of packages

This commit is contained in:
Andrei Yankovich 2021-03-26 23:21:19 +03:00
parent 6ae5b749ee
commit 61bce748e4
12 changed files with 312 additions and 128 deletions

View File

@ -28,7 +28,7 @@ public:
* For each command will invoke the handleReceive method. * For each command will invoke the handleReceive method.
* @param data * @param data
*/ */
virtual void handleReceiveData(const QList<Feature>& data) = 0; virtual void handleReceiveData(const QSet<Feature>& data) = 0;
/** /**
* @brief handleReceive This method invoked when service receive a request from terminal. * @brief handleReceive This method invoked when service receive a request from terminal.

View File

@ -25,7 +25,7 @@ ServiceBase::~ServiceBase() {
delete d_ptr; delete d_ptr;
} }
void ServiceBase::handleReceiveData(const QList<Feature> &data) { void ServiceBase::handleReceiveData(const QSet<Feature> &data) {
auto list = supportedFeatures(); auto list = supportedFeatures();
QString commandList; QString commandList;

View File

@ -46,7 +46,7 @@ protected:
* @param data - is list of commands from controller * @param data - is list of commands from controller
* Default inplementation send message abount error, and invoke the . * Default inplementation send message abount error, and invoke the .
*/ */
void handleReceiveData(const QList<Feature> &data) override final; void handleReceiveData(const QSet<Feature> &data) override final;
/** /**
* @brief supportedFeatures * @brief supportedFeatures

View File

@ -14,6 +14,7 @@
#include <quasarapp.h> #include <quasarapp.h>
#include "package.h" #include "package.h"
#include "installersystemd.h" #include "installersystemd.h"
#include "parser.h"
namespace Patronum { namespace Patronum {
@ -23,6 +24,7 @@ ControllerPrivate::ControllerPrivate(const QString &name, const QString &service
_socket = new LocalSocket(name, this); _socket = new LocalSocket(name, this);
_serviceExe = servicePath; _serviceExe = servicePath;
_controller = controller; _controller = controller;
_parser = new Parser();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
_installer = new InstallerSystemD(name); _installer = new InstallerSystemD(name);
@ -37,6 +39,7 @@ ControllerPrivate::~ControllerPrivate() {
if (_installer) { if (_installer) {
delete _installer; delete _installer;
} }
delete _parser;
} }
bool ControllerPrivate::sendFeaturesRequest() { bool ControllerPrivate::sendFeaturesRequest() {
@ -196,57 +199,62 @@ void ControllerPrivate::handleReceve(QByteArray data) {
return; return;
} }
const Package package = Package::parsePackage(data); QList<Package> packages;
if (!_parser->parse(data, packages)) {
if (!package.isValid()) {
QuasarAppUtils::Params::log("Received invalid package!",
QuasarAppUtils::Debug);
_controller->handleError(ControllerError::InvalidPackage);
return; return;
} }
switch (package.cmd()) { for (const auto& pkg: qAsConst(packages)) {
if (!pkg.isValid()) {
case Command::Features: { QuasarAppUtils::Params::log("Received invalid package!",
QuasarAppUtils::Debug);
QDataStream stream(package.data()); _controller->handleError(ControllerError::InvalidPackage);
QList<Feature> features; continue;;
stream >> features; }
_features = features;
_controller->handleFeatures(features); switch (pkg.cmd()) {
break; case Command::Features: {
} QDataStream stream(pkg.data());
case Command::CloseConnection: { QList<Feature> features;
_responce = true; stream >> features;
break; _features = features;
}
case Command::FeatureResponce: { _controller->handleFeatures(features);
QDataStream stream(package.data()); break;
}
case Command::CloseConnection: {
_responce = true;
break;
}
case Command::FeatureResponce: {
QDataStream stream(pkg.data());
QVariantMap responce; QVariantMap responce;
stream >> responce; stream >> responce;
_controller->handleResponce(responce); _controller->handleResponce(responce);
break; break;
} }
default: { default: {
QuasarAppUtils::Params::log("Wrong command!", QuasarAppUtils::Params::log("Wrong command!",
QuasarAppUtils::Debug); QuasarAppUtils::Debug);
_controller->handleError(ControllerError::WrongCommand); _controller->handleError(ControllerError::WrongCommand);
break; break;
}
} }
} }

View File

@ -14,6 +14,7 @@ namespace Patronum {
class IController; class IController;
class LocalSocket; class LocalSocket;
class Installer; class Installer;
class Parser;
class ControllerPrivate: public QObject class ControllerPrivate: public QObject
{ {
@ -50,6 +51,7 @@ private:
QList<Feature> _features; QList<Feature> _features;
QString _serviceExe = ""; QString _serviceExe = "";
Installer *_installer = nullptr; Installer *_installer = nullptr;
Parser * _parser;
private slots: private slots:
void handleReceve(QByteArray data); void handleReceve(QByteArray data);

View File

@ -10,29 +10,27 @@
namespace Patronum { namespace Patronum {
Command Package::cmd() const { Command Package::cmd() const {
return static_cast<Command>(m_cmd); return static_cast<Command>(m_hdr.cmd);
} }
QByteArray Package::data() const { const QByteArray &Package::data() const {
return m_data; return m_data;
} }
bool Package::isValid() const { bool Package::isValid() const {
return m_cmd <= static_cast<int>(Command::FeatureResponce); return m_hdr.isValid() && m_hdr.size == m_data.size();
}
void Package::reset() {
} }
Package::Package() { Package::Package() {
} }
Package Package::parsePackage(const QByteArray &data) { bool Header::isValid() const {
if (!data.size()) { return cmd && cmd <= static_cast<int>(Command::CloseConnection);
return {};
}
Package pkg;
pkg.m_cmd = static_cast<unsigned char>(data.at(0));
pkg.m_data = data.right(data.size() - sizeof (pkg.m_cmd));
return pkg;
} }
} }

View File

@ -17,14 +17,41 @@ namespace Patronum {
class Feature; class Feature;
/**
* @brief The Command enum This is base commands for sending
*/
enum class Command: quint8 { enum class Command: quint8 {
/// This is invalid command
Undefined,
/// This comand is request for the available command of the service
FeaturesRequest, FeaturesRequest,
/// This comand is response of the FeaturesRequest command
Features, Features,
/// This is request to execute command
Feature, Feature,
/// This is response of the execute of command
FeatureResponce, FeatureResponce,
/// This command is finished command for the terminal. after receive this command terminal will closse connection.
CloseConnection CloseConnection
}; };
/**
* @brief The Header struct have as size 2 byte. maximus package size is 4095 bytes.
*/
struct Header {
/**
* @brief size This is size of package data (exclude header size).
*/
unsigned short size: 12;
/**
* @brief cmd This is workCommand
*/
unsigned char cmd: 4;
bool isValid() const;
};
/** /**
* @brief The Package class * @brief The Package class
* this is base package class with cmd and data * this is base package class with cmd and data
@ -32,27 +59,36 @@ enum class Command: quint8 {
class Package class Package
{ {
public: public:
Package();
/**
* @brief cmd This method return command of the package.
* @return command of the package.
*/
Command cmd() const; Command cmd() const;
QByteArray data() const;
/**
* @brief data This method return referense to the bytes array of the pacakge data.
* @return bytes array of the pacakge data.
*/
const QByteArray& data() const;
/**
* @brief isValid This method check this pacakge.
* @return true if the package is valid.
*/
bool isValid() const; bool isValid() const;
template<class DATA> /**
static QByteArray createPackage(Command cmd, const DATA &data) { * @brief reset This method reset all fields of the package to default value.
QByteArray result; * @note after invoke this method package will be invalid.
QDataStream stream(&result, QIODevice::WriteOnly); */
void reset();
stream << cmd;
stream << data;
return result;
}
static Package parsePackage(const QByteArray& data);
private: private:
Package(); Header m_hdr;
unsigned char m_cmd;
QByteArray m_data; QByteArray m_data;
friend class Parser;
}; };
} }

View File

@ -0,0 +1,75 @@
#include "parser.h"
#include <quasarapp.h>
namespace Patronum {
Parser::Parser()
{
}
bool Parser::parse(const QByteArray &array,
QList<Package> &result) {
if (!array.size()) {
return false;
}
int workIndex = 0;
const int headerSize = sizeof(Header);
const auto workArray = _hdrArray + array;
const int arraySize = workArray.size();
while (arraySize > workIndex) {
int offset = arraySize - workIndex;
if (_data.m_hdr.isValid()) {
// CASE 1: The Package data is still not collected, but the header is already collected. performs full or partial filling of packet data.
int dataLength = std::min(static_cast<int>(_data.m_hdr.size - _data.m_data.size()),
arraySize - workIndex);
_data.m_data.append(array.mid(workIndex + headerSize, dataLength));
workIndex += dataLength;
} else if (offset >= headerSize) {
// CASE 2: The header and package still do not exist and the amount of data allows you to create a new header. A header is created and will fill in all or part of the package data.
_data.reset();
memcpy(&_data.m_hdr,
array.data() + workIndex, headerSize);
int dataLength = std::min(static_cast<int>(_data.m_hdr.size),
arraySize - headerSize - workIndex);
_data.m_data.append(array.mid(workIndex + headerSize, dataLength));
workIndex += headerSize + dataLength;
} else {
// CASE 3: There is not enough data to initialize the header. The data will be placed in temporary storage and will be processed the next time the data is received.
unsigned char dataLength = static_cast<unsigned char>(arraySize - workIndex);
_hdrArray += array.mid(workIndex, dataLength);
workIndex += dataLength;
}
if (_data.isValid()) {
result.push_back(_data);
_data.reset();
_hdrArray.clear();
}
if (_data.m_data.size() > _data.m_hdr.size) {
QuasarAppUtils::Params::log("Invalid Package received. ",
QuasarAppUtils::Warning);
_data.reset();
_hdrArray.clear();
}
}
return true;
}
}

View File

@ -0,0 +1,52 @@
#ifndef PARSER_H
#define PARSER_H
#include <QByteArray>
#include "package.h"
namespace Patronum {
/**
* @brief The Parser class This is package parser of the Patronum packages.
*/
class Parser
{
public:
Parser();
/**
* @brief parse This method parse input data array @a array to the @result packages.
* @param array This is input data.
* @param result This is result packages.
* @return return true if parse finished successful
*/
bool parse(const QByteArray& array, QList<Package>& result);
template<class DATA>
/**
* @brief createPackage This method create a custom package with @a cmd and @a data
* @param cmd This is package command.
* @param data This is data object.
* @return bytes arra of the package.
*/
QByteArray createPackage(Command cmd, const DATA &data) {
QByteArray result;
QDataStream stream(&result, QIODevice::WriteOnly);
Header hdr;
hdr.cmd = static_cast<unsigned char>(cmd);
stream << data;
hdr.size = result.size();
result.insert(0, reinterpret_cast<char*>(&hdr), sizeof (hdr));
return result;
}
private:
Package _data;
QByteArray _hdrArray;
};
}
#endif // PARSER_H

View File

@ -13,6 +13,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QTimer> #include <QTimer>
#include <quasarapp.h> #include <quasarapp.h>
#include "parser.h"
namespace Patronum { namespace Patronum {
@ -22,11 +23,16 @@ Patronum::ServicePrivate::ServicePrivate(const QString &name, IService *service,
_service = service; _service = service;
_parser = new Parser();
QObject::connect(_socket, &LocalSocket::sigReceve, QObject::connect(_socket, &LocalSocket::sigReceve,
this, &ServicePrivate::handleReceve); this, &ServicePrivate::handleReceve);
} }
ServicePrivate::~ServicePrivate() {
delete _parser;
}
bool ServicePrivate::sendCmdResult(const QVariantMap &result) { bool ServicePrivate::sendCmdResult(const QVariantMap &result) {
if (!_socket->isValid()) { if (!_socket->isValid()) {
@ -35,7 +41,7 @@ bool ServicePrivate::sendCmdResult(const QVariantMap &result) {
return false; return false;
} }
return _socket->send(Package::createPackage(Command::FeatureResponce, result)); return _socket->send(_parser->createPackage(Command::FeatureResponce, result));
} }
bool ServicePrivate::sendCloseConnection() { bool ServicePrivate::sendCloseConnection() {
@ -60,7 +66,7 @@ void ServicePrivate::listen() const {
}; };
} }
bool ServicePrivate::handleStandartCmd(QList<Feature> *cmds) { bool ServicePrivate::handleStandartCmd(QSet<Feature> *cmds) {
if (!cmds) if (!cmds)
return false; return false;
@ -68,20 +74,17 @@ bool ServicePrivate::handleStandartCmd(QList<Feature> *cmds) {
if (!_service) if (!_service)
return false; return false;
for (int i = 0; i < cmds->size(); ++i) { if (cmds->contains(Feature{"stop"})) {
if (cmds->value(i).cmd() == "stop") { _service->onStop();
_service->onStop(); cmds->remove(Feature{"stop"});
cmds->removeAt(i);
i--; } else if (cmds->contains(Feature{"pause"})) {
} else if (cmds->value(i).cmd() == "pause") { _service->onPause();
_service->onPause(); cmds->remove(Feature{"pause"});
cmds->removeAt(i);
i--; } else if (cmds->contains(Feature{"resume"})) {
} else if (cmds->value(i).cmd() == "resume") { _service->onResume();
_service->onResume(); cmds->remove(Feature{"resume"});
cmds->removeAt(i);
i--;
}
} }
@ -90,66 +93,71 @@ bool ServicePrivate::handleStandartCmd(QList<Feature> *cmds) {
void ServicePrivate::handleReceve(QByteArray data) { void ServicePrivate::handleReceve(QByteArray data) {
const Package package = Package::parsePackage(data); QList<Package> packages;
if (!_parser->parse(data, packages)) {
if (!package.isValid()) {
QuasarAppUtils::Params::log("receive package is not valid!",
QuasarAppUtils::Warning);
return; return;
} }
if (!_service) { for (const auto &pkg: qAsConst(packages)) {
QuasarAppUtils::Params::log("System error, service is not inited!", if (!pkg.isValid()) {
QuasarAppUtils::Error); QuasarAppUtils::Params::log("receive package is not valid!",
return;; QuasarAppUtils::Warning);
} return;
if (!_socket->isValid()) {
QuasarAppUtils::Params::log("scoket is closed!",
QuasarAppUtils::Error);
return;
}
switch (package.cmd()) {
case Command::FeaturesRequest: {
QSet<Feature> features = _service->supportedFeatures();
QByteArray sendData;
QDataStream stream(&sendData, QIODevice::WriteOnly);
stream << static_cast<quint8>(Command::Features);
stream << features;
if (!_socket->send(sendData)) {
QuasarAppUtils::Params::log("Fail to send ",
QuasarAppUtils::Error);
} }
break; if (!_service) {
QuasarAppUtils::Params::log("System error, service is not inited!",
QuasarAppUtils::Error);
return;;
}
} if (!_socket->isValid()) {
QuasarAppUtils::Params::log("scoket is closed!",
QuasarAppUtils::Error);
return;
}
case Command::Feature: { switch (pkg.cmd()) {
QDataStream stream(package.data()); case Command::FeaturesRequest: {
QList<Feature> feature; QSet<Feature> features = _service->supportedFeatures();
stream >> feature; QByteArray sendData;
handleStandartCmd(&feature); QDataStream stream(&sendData, QIODevice::WriteOnly);
if (feature.size()) stream << static_cast<quint8>(Command::Features);
_service->handleReceiveData(feature); stream << features;
break; if (!_socket->send(sendData)) {
QuasarAppUtils::Params::log("Fail to send ",
QuasarAppUtils::Error);
}
} break;
default: { }
QuasarAppUtils::Params::log("Wrong command!",
QuasarAppUtils::Error); case Command::Feature: {
break;
} QDataStream stream(pkg.data());
QSet<Feature> feature;
stream >> feature;
handleStandartCmd(&feature);
if (feature.size())
_service->handleReceiveData(feature);
break;
}
default: {
QuasarAppUtils::Params::log("Wrong command!",
QuasarAppUtils::Error);
break;
}
}
} }

View File

@ -14,6 +14,7 @@ namespace Patronum {
class LocalSocket; class LocalSocket;
class IService; class IService;
class Parser;
class ServicePrivate: public QObject class ServicePrivate: public QObject
{ {
@ -21,6 +22,7 @@ class ServicePrivate: public QObject
public: public:
ServicePrivate(const QString& name, ServicePrivate(const QString& name,
IService* service = nullptr, QObject *parent = nullptr); IService* service = nullptr, QObject *parent = nullptr);
~ServicePrivate();
bool sendCmdResult(const QVariantMap& result); bool sendCmdResult(const QVariantMap& result);
bool sendCloseConnection(); bool sendCloseConnection();
@ -32,8 +34,9 @@ public:
private: private:
LocalSocket *_socket = nullptr; LocalSocket *_socket = nullptr;
IService *_service = nullptr; IService *_service = nullptr;
Parser *_parser = nullptr;
bool handleStandartCmd(QList<Feature> *cmds); bool handleStandartCmd(QSet<Feature> *cmds);
private slots: private slots:
void handleReceve(QByteArray data); void handleReceve(QByteArray data);

View File

@ -34,6 +34,8 @@ private slots:
testPatronum::testPatronum() { testPatronum::testPatronum() {
QuasarAppUtils::Params::setArg("verbose", "3"); QuasarAppUtils::Params::setArg("verbose", "3");
QFile::remove("/tmp/PTestPatronum");
} }
void testPatronum::testPing() { void testPatronum::testPing() {