4
0
mirror of https://github.com/QuasarApp/Patronum.git synced 2025-05-08 21:19:34 +00:00

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

@ -28,7 +28,7 @@ public:
* For each command will invoke the handleReceive method.
* @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.

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

@ -46,7 +46,7 @@ protected:
* @param data - is list of commands from controller
* 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

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

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

@ -10,29 +10,27 @@
namespace Patronum {
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;
}
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::parsePackage(const QByteArray &data) {
if (!data.size()) {
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;
bool Header::isValid() const {
return cmd && cmd <= static_cast<int>(Command::CloseConnection);
}
}

@ -17,14 +17,41 @@ namespace Patronum {
class Feature;
/**
* @brief The Command enum This is base commands for sending
*/
enum class Command: quint8 {
/// This is invalid command
Undefined,
/// This comand is request for the available command of the service
FeaturesRequest,
/// This comand is response of the FeaturesRequest command
Features,
/// This is request to execute command
Feature,
/// This is response of the execute of command
FeatureResponce,
/// This command is finished command for the terminal. after receive this command terminal will closse connection.
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
* this is base package class with cmd and data
@ -32,27 +59,36 @@ enum class Command: quint8 {
class Package
{
public:
Package();
/**
* @brief cmd This method return command of the package.
* @return command of the package.
*/
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;
template<class DATA>
static QByteArray createPackage(Command cmd, const DATA &data) {
QByteArray result;
QDataStream stream(&result, QIODevice::WriteOnly);
stream << cmd;
stream << data;
return result;
}
static Package parsePackage(const QByteArray& data);
/**
* @brief reset This method reset all fields of the package to default value.
* @note after invoke this method package will be invalid.
*/
void reset();
private:
Package();
unsigned char m_cmd;
Header m_hdr;
QByteArray m_data;
friend class Parser;
};
}

@ -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;
}
}

@ -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

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

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

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