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:
parent
6ae5b749ee
commit
61bce748e4
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
75
Patronum/src/Private/parser.cpp
Normal file
75
Patronum/src/Private/parser.cpp
Normal 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;
|
||||
}
|
||||
}
|
52
Patronum/src/Private/parser.h
Normal file
52
Patronum/src/Private/parser.h
Normal 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
|
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user