Add new constant for progress in percent.
This commit is contained in:
		@@ -31,6 +31,16 @@ namespace internal {
 | 
			
		||||
    static constexpr const char *GIT_MARKER{"<GIT>"};
 | 
			
		||||
    static constexpr const char *ISMAS_MARKER{"<ISMAS>"};
 | 
			
		||||
 | 
			
		||||
    static constexpr const int PERCENT_CHECK_ISMAS_CONNECIVITY{10};
 | 
			
		||||
    static constexpr const int PERCENT_CHECK_UPDATE_REQUEST{20};
 | 
			
		||||
    static constexpr const int PERCENT_CHECK_CUSTOMER_REPOSITORY{30};
 | 
			
		||||
    static constexpr const int PERCENT_INSTALL_SW_PACKETS_NOACTION{40};
 | 
			
		||||
    static constexpr const int PERCENT_INSTALL_SW_PACKETS{50};
 | 
			
		||||
    static constexpr const int PERCENT_INSTALL_DC_CONFIGURATION{60};
 | 
			
		||||
    static constexpr const int PERCENT_SYNCHRONIZE_REPO_AND_FILESYS{70};
 | 
			
		||||
    static constexpr const int PERCENT_UPDATE_DC{80};
 | 
			
		||||
    static constexpr const int PERCENT_SHOW_FINAL_STATUS{90};
 | 
			
		||||
 | 
			
		||||
    int read1stLineOfFile(QString fileName);
 | 
			
		||||
    QString customerRepoRoot();
 | 
			
		||||
    QString customerRepoDir();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										336
									
								
								common/ismas/ApismClient.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								common/ismas/ApismClient.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,336 @@
 | 
			
		||||
#include "ApismClient.h"
 | 
			
		||||
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
#include <QHostAddress>
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include <QList>
 | 
			
		||||
#include <QListIterator>
 | 
			
		||||
#include <QScopedPointer>
 | 
			
		||||
#include <QProcess>
 | 
			
		||||
#include <QRegularExpression>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ApismClient::ApismClient(QObject *parent)
 | 
			
		||||
    : QObject(parent)
 | 
			
		||||
    , lastError(0)
 | 
			
		||||
    , lastErrorDescription("")
 | 
			
		||||
    , currentRequest(ISMAS::REQUEST::NO_REQUEST)
 | 
			
		||||
    , retryCounter(0)
 | 
			
		||||
    , flagValidParameter(false)
 | 
			
		||||
{   
 | 
			
		||||
    this->apismTcpSendClient.reset(new ApismTcpClient("127.0.0.1", "7777", ApismTcpClient::ExpectedResponse::STATE, this));
 | 
			
		||||
    this->apismTcpRequestResponseClient.reset(new ApismTcpClient("127.0.0.1", "7778", ApismTcpClient::ExpectedResponse::JSON,  this));
 | 
			
		||||
 | 
			
		||||
    this->apismTcpRequestResponseClient->setResponseTimeout(30000);
 | 
			
		||||
 | 
			
		||||
    connect(apismTcpRequestResponseClient.get(), &ApismTcpClient::receivedData,
 | 
			
		||||
            this, &ApismClient::onReceivedResponse);
 | 
			
		||||
    connect(apismTcpRequestResponseClient.get(), &ApismTcpClient::responseTimeout,
 | 
			
		||||
            this, &ApismClient::onRequestResponseClientResponseTimeout);
 | 
			
		||||
    connect(apismTcpRequestResponseClient.get(), &ApismTcpClient::connectTimeout,
 | 
			
		||||
            this, &ApismClient::onRequestResponseClientConnectTimeout);
 | 
			
		||||
    connect(apismTcpRequestResponseClient.get(), &ApismTcpClient::connectionClosedByRemoteHost,
 | 
			
		||||
            this, &ApismClient::onRequestResponseClientConnectionClosedByRemoteHost);
 | 
			
		||||
    connect(apismTcpRequestResponseClient.get(), &ApismTcpClient::connectionRefusedError,
 | 
			
		||||
            this, &ApismClient::restartApism);
 | 
			
		||||
 | 
			
		||||
    connect(apismTcpSendClient.get(), &ApismTcpClient::responseTimeout,
 | 
			
		||||
            this, &ApismClient::onSendClientResponseTimeout);
 | 
			
		||||
    connect(apismTcpSendClient.get(), &ApismTcpClient::connectionRefusedError,
 | 
			
		||||
            this, &ApismClient::restartApism);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // not needed as APISM closes the socket after we send data, so readyRead()
 | 
			
		||||
    // might not even fire
 | 
			
		||||
    // connect(&m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
 | 
			
		||||
 | 
			
		||||
    // defined for Qt >= 5.15, we have 5.12
 | 
			
		||||
    // qRegisterMetaType<QAbstractSocket::SocketError>();
 | 
			
		||||
    // connect(&m_socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
 | 
			
		||||
    //    this, SLOT(onSocketError(&QAbstractSocket::SocketError)));
 | 
			
		||||
 | 
			
		||||
    // switch of DEBUG:
 | 
			
		||||
    this->apismTcpSendClient->setDebug(true);
 | 
			
		||||
    this->apismTcpRequestResponseClient->setDebug(true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApismClient::~ApismClient() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClient::restartApism() {
 | 
			
		||||
    QProcess::startDetached("/bin/systemctl", {"restart", "apism"});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismClient::sendSelfTest() {
 | 
			
		||||
    this->currentRequest = ISMAS::REQUEST::SELF;
 | 
			
		||||
    this->apismTcpRequestResponseClient->sendData("#M=APISM#C=REQ_SELF#J={}");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClient::sendRequestParameter() {
 | 
			
		||||
    this->currentRequest = ISMAS::REQUEST::PARAMETER;
 | 
			
		||||
    this->apismTcpRequestResponseClient->sendData("#M=APISM#C=REQ_ISMASPARAMETER#J={}");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClient::handleISMASResponseError()
 | 
			
		||||
{    
 | 
			
		||||
    switch (this->currentRequest) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        qCritical() << "ApismClient::onReceivedResponse() for unknown Request: " << currentRequest;
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        //emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        //emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        //emit this->sendReqParameterResponse(nsApismInterface::RESULT_STATE::ERROR_BACKEND, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismClient::handleISMASOfflineResponseError()
 | 
			
		||||
{
 | 
			
		||||
    nsApismInterface::RESULT_STATE resultState;
 | 
			
		||||
 | 
			
		||||
    if (this->retryCounter < 50) {
 | 
			
		||||
        resultState = nsApismInterface::RESULT_STATE::ERROR_RETRY;
 | 
			
		||||
        this->retryCounter++;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        resultState = nsApismInterface::RESULT_STATE::ERROR_BACKEND;
 | 
			
		||||
        this->retryCounter = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismClient::handleISMASOfflineResponseError(): currentRequest is " << this->currentRequest;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    switch (this->currentRequest) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        //emit this->sendMininformPingResponse(resultState, QJsonObject());
 | 
			
		||||
        //break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        emit this->sendReqSelfResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        emit this->sendReqParameterResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    this->currentRequest = ISMAS::REQUEST::NO_REQUEST;
 | 
			
		||||
 | 
			
		||||
    // note: this does not work here, because a new request within this error handling method
 | 
			
		||||
    // must use a new socket connection!
 | 
			
		||||
    // This does not work with an allready open socket connection.
 | 
			
		||||
    /* Communication to APISM must be in the following way:
 | 
			
		||||
     *     1) establish socket connection
 | 
			
		||||
     *     2) send your request to this socket
 | 
			
		||||
     *     3) wait for a possible answer
 | 
			
		||||
     *     4) receive the answer
 | 
			
		||||
     *     5) close socket connection
 | 
			
		||||
     *
 | 
			
		||||
     * => a request can only be sent to apsim if the socket is closed!
 | 
			
		||||
     * => apism (socket) is blocked systemwide until socket is closed!
 | 
			
		||||
     * =>
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    //this->sendSelfTest();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismClient::private_handleErrorResponse(QString errorString)
 | 
			
		||||
{
 | 
			
		||||
    qCritical() << "------------------------------------------------------";
 | 
			
		||||
    qCritical() << "ApismClient::private_handleErrorResponse(" << errorString << ")";
 | 
			
		||||
    qCritical() << "        this->retryCounter = " << this->retryCounter;
 | 
			
		||||
    qCritical() << "------------------------------------------------------";
 | 
			
		||||
 | 
			
		||||
    this->handleISMASOfflineResponseError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
{\r\n    \"REQ_START#53365_Response\": {\r\n        \"Aknoledge\": \"OK\"\r\n    },
 | 
			
		||||
 \r\n    \"Response\": {\r\n        \"Result\": \"ERR:Ung<6E>ltiger Datums-String: \"\r\n    }\r\n}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
: ISMAS received:  "{\r\n    \"REQ_PING#30844_Response\": {\r\n        \"Aknoledge\": \"OK\"\r\n    },
 | 
			
		||||
     "PING": { "IsAviable": "TRUE" }
 | 
			
		||||
  }"
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
//void ApismClient::onSendClientResponseTimeout()
 | 
			
		||||
//{
 | 
			
		||||
//
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
void ApismClient::private_handleReqSelfResponse(QJsonObject response)
 | 
			
		||||
{
 | 
			
		||||
    bool ismasConnected  = response["ISMAS"].toBool();
 | 
			
		||||
    QString brokerConnectedString = response["Broker"].toString();
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismClient::handleReqSelfResponse() " << endl
 | 
			
		||||
                << "                  ismasConnected = " << ismasConnected << endl
 | 
			
		||||
                << "           brokerConnectedString = " << brokerConnectedString;
 | 
			
		||||
 | 
			
		||||
    /* possible values:    "Restart connection"
 | 
			
		||||
     *                     "Connected"
 | 
			
		||||
     *                     "NOT CONNECTED"
 | 
			
		||||
     */
 | 
			
		||||
    if (brokerConnectedString == "NOT CONNECTED") {
 | 
			
		||||
        qCritical() << "ApismClient: \"NOT CONNECTED\": restart APISM";
 | 
			
		||||
        this->restartApism();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * sample parameter response:
 | 
			
		||||
        {
 | 
			
		||||
            "REQ_ISMASPARAMETER#4210959_Response": {
 | 
			
		||||
                "Aknoledge": "OK"
 | 
			
		||||
            },
 | 
			
		||||
            "Dev_ID": {
 | 
			
		||||
                "Device_Type": "2020",
 | 
			
		||||
                "Custom_ID": 332,
 | 
			
		||||
                "Device_ID": 99
 | 
			
		||||
            },
 | 
			
		||||
            "Fileupload": {
 | 
			
		||||
                "TRG": ""
 | 
			
		||||
            },
 | 
			
		||||
            "Parameter": {
 | 
			
		||||
                "Location": "An Der Bahn 11",
 | 
			
		||||
                "Group": "Group1",
 | 
			
		||||
                "Zone": "Zone1",
 | 
			
		||||
                "Name": "PSA",
 | 
			
		||||
                "SecNumber": "0",
 | 
			
		||||
                "LastAcc": "0",
 | 
			
		||||
                "GPSLat": "49.6062",
 | 
			
		||||
                "GPSLon": "12.1244",
 | 
			
		||||
                "TarifID": "none",
 | 
			
		||||
                "UserTextID": "1"
 | 
			
		||||
                "TERMINALID": "",
 | 
			
		||||
                "TERMINALKEY": ""
 | 
			
		||||
            },
 | 
			
		||||
            "Lock": {
 | 
			
		||||
                "Locked": "0"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 */
 | 
			
		||||
void ApismClient::private_handleParameterResponse(QJsonObject response)
 | 
			
		||||
{
 | 
			
		||||
    // extract location info and store location info in persistent data:
 | 
			
		||||
    QJsonValue jsonSubVal_Location;
 | 
			
		||||
    jsonSubVal_Location = response["Location"];
 | 
			
		||||
 | 
			
		||||
    QString locationString = jsonSubVal_Location.toString("no location");
 | 
			
		||||
 | 
			
		||||
    //this->persistentData->setParameter("Location", locationString);
 | 
			
		||||
 | 
			
		||||
    // feature UserText
 | 
			
		||||
    QJsonValue jsonSubVal_UserText;
 | 
			
		||||
    jsonSubVal_UserText = response["UserTextID"];
 | 
			
		||||
    QString userTextIdString = jsonSubVal_UserText.toString("0");
 | 
			
		||||
    //this->persistentData->setParameter("UserTextID", userTextIdString);
 | 
			
		||||
 | 
			
		||||
    // TERMINALID
 | 
			
		||||
    //QJsonValue jsonSubVal_TERMINALID;
 | 
			
		||||
    //jsonSubVal_TERMINALID = response["TERMINALID"];
 | 
			
		||||
    //QString terminalIdString = jsonSubVal_TERMINALID.toString("");
 | 
			
		||||
    //QString terminalIdFile = this->m_config->getSystemDataPath() + "TERMINALID";
 | 
			
		||||
    //QString terminalIdStringOrigin = ATBSystem::readPSAConfigString(terminalIdFile);
 | 
			
		||||
    //if ( (terminalIdString.size() > 1) &&
 | 
			
		||||
    //     (terminalIdString != terminalIdStringOrigin))
 | 
			
		||||
    //{
 | 
			
		||||
    //    qCritical() << "ApismClient::handleParameterResponse(): new TERMINALID: " << terminalIdString;
 | 
			
		||||
    //    ATBSystem::setPSAConfigString(terminalIdFile, terminalIdString);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    // TERMINALKEY
 | 
			
		||||
    //QJsonValue jsonSubVal_TERMINALKEY;
 | 
			
		||||
    //jsonSubVal_TERMINALKEY = response["TERMINALKEY"];
 | 
			
		||||
    //QString terminalKeyString = jsonSubVal_TERMINALKEY.toString("");
 | 
			
		||||
    //QString terminalJeyFile = this->m_config->getSystemDataPath() + "TERMINALKEY";
 | 
			
		||||
    //QString terminalKeyStringOrigin = ATBSystem::readPSAConfigString(terminalJeyFile);
 | 
			
		||||
    //if ( (terminalKeyString.size() > 1) &&
 | 
			
		||||
    //     (terminalKeyString != terminalKeyStringOrigin))
 | 
			
		||||
    //{
 | 
			
		||||
    //    qCritical() << "ApismClient::handleParameterResponse(): new TERMINALKEY: " << terminalKeyString;
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    //    ATBSystem::setPSAConfigString(terminalJeyFile, terminalKeyString);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //qCritical() << "ApismClient::handleParameterResponse() " << endl
 | 
			
		||||
    //            << "                  Location = " << locationString   << endl
 | 
			
		||||
    //            << "                UserTextID = " << userTextIdString << endl
 | 
			
		||||
    //            << "                TERMINALID = " << terminalIdString << endl
 | 
			
		||||
    //            << "               TERMINALKEY = " << terminalKeyString;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (locationString != "no location") {
 | 
			
		||||
        this->flagValidParameter = true;
 | 
			
		||||
        emit this->sendReqParameterResponse(nsApismInterface::RESULT_STATE::SUCCESS, response);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClient::private_handleFileUploadResponse(QJsonObject response) {
 | 
			
		||||
    QJsonValue jsonSubVal_TRG{response["TRG"]};
 | 
			
		||||
    if (jsonSubVal_TRG.isUndefined()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool updateRequested = (jsonSubVal_TRG.toString("") == "WAIT");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/************************************************************************************************
 | 
			
		||||
 * operators
 | 
			
		||||
 */
 | 
			
		||||
QDebug operator<< (QDebug debug, ISMAS::REQUEST request)
 | 
			
		||||
{
 | 
			
		||||
    switch(request) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        debug << QString("ISMAS::REQUEST::NO_REQUEST");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        debug << QString("ISMAS::REQUEST::PING");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        debug << QString("ISMAS::REQUEST::SELF");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        debug << QString("ISMAS::REQUEST::PARAMETER");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return debug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString& operator<< (QString& str, ISMAS::REQUEST request)
 | 
			
		||||
{
 | 
			
		||||
    switch(request) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        str = QString("ISMAS::REQUEST::NO_REQUEST");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        str = QString("ISMAS::REQUEST::PING");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        str = QString("ISMAS::REQUEST::SELF");
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        str = QString("ISMAS::REQUEST::PARAMETER");
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										99
									
								
								common/ismas/ApismClient.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								common/ismas/ApismClient.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
#ifndef APISM_CLIENT_H_INCLUDED
 | 
			
		||||
#define APISM_CLIENT_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include "ISMASData.h"
 | 
			
		||||
#include "ApismTcpClient.h"
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QAbstractSocket>
 | 
			
		||||
#include <QTcpSocket>
 | 
			
		||||
#include <QJsonDocument>
 | 
			
		||||
#include <QJsonArray>
 | 
			
		||||
#include <QJsonObject>
 | 
			
		||||
#include <QJsonArray>
 | 
			
		||||
#include <QJsonParseError>
 | 
			
		||||
#include <QJsonValue>
 | 
			
		||||
#include <QScopedPointer>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, ISMAS::REQUEST request);
 | 
			
		||||
QString& operator<<(QString& str, ISMAS::REQUEST request);
 | 
			
		||||
 | 
			
		||||
namespace nsApismInterface {
 | 
			
		||||
    enum class RESULT_STATE : quint8 {
 | 
			
		||||
        SUCCESS = 1,
 | 
			
		||||
        ERROR_BACKEND = 2,   // error from backand (e.g. backend replies with error)
 | 
			
		||||
        ERROR_NETWORK = 3,   // error from network (e.g. host not available)
 | 
			
		||||
        ERROR_TIMEOUT = 4,   // the operation timed out
 | 
			
		||||
        ERROR_PROCESS = 5,   // internal plugin error (e.g. bug in implementation)
 | 
			
		||||
        ERROR_RETRY = 6,     // retry operation
 | 
			
		||||
        INFO = 7
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// class ISMAS::EventData;
 | 
			
		||||
class ApismClient : public QObject {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    explicit ApismClient(QObject *parent = 0);
 | 
			
		||||
    virtual ~ApismClient() = 0;
 | 
			
		||||
 | 
			
		||||
    quint32 getLastError();
 | 
			
		||||
    const QString & getLastErrorDescription();
 | 
			
		||||
 | 
			
		||||
    ISMAS::REQUEST getCurrentRequest() const { return currentRequest; }
 | 
			
		||||
    void setCurrentRequest(ISMAS::REQUEST r) { currentRequest = r; }
 | 
			
		||||
 | 
			
		||||
    void resetRetryCounter() { retryCounter = 0; }
 | 
			
		||||
    int getRetryCounter() const { return retryCounter; }
 | 
			
		||||
    int incrRetryCounter() { return ++retryCounter; }
 | 
			
		||||
 | 
			
		||||
    ApismTcpClient *tcpSendClient() { return apismTcpSendClient.get(); }
 | 
			
		||||
    ApismTcpClient *tcpRequestResponseClient() { return apismTcpRequestResponseClient.get(); }
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    void sendSelfTest();
 | 
			
		||||
    void sendRequestParameter();
 | 
			
		||||
    void restartApism();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void sendReqSelfResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
 | 
			
		||||
    void sendReqParameterResponse(nsApismInterface::RESULT_STATE result, QJsonObject response);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    virtual void onReceivedResponse(QByteArray response) = 0;
 | 
			
		||||
    virtual void onSendClientResponseTimeout() = 0;
 | 
			
		||||
    virtual void onRequestResponseClientResponseTimeout() = 0;
 | 
			
		||||
    virtual void onRequestResponseClientConnectTimeout() = 0;
 | 
			
		||||
    virtual void onRequestResponseClientConnectionClosedByRemoteHost() = 0;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QScopedPointer<ApismTcpClient> apismTcpSendClient;
 | 
			
		||||
    QScopedPointer<ApismTcpClient> apismTcpRequestResponseClient;
 | 
			
		||||
 | 
			
		||||
    quint32 lastError;
 | 
			
		||||
    QString lastErrorDescription;
 | 
			
		||||
 | 
			
		||||
    ISMAS::REQUEST currentRequest;
 | 
			
		||||
 | 
			
		||||
    // counter, incremented if we get an offline response from ISMAS
 | 
			
		||||
    // causes a resend of the currentRequest.
 | 
			
		||||
    int retryCounter;
 | 
			
		||||
 | 
			
		||||
    // true, if ISMAS REQ_ISAMASPARAMETER got a valid response
 | 
			
		||||
    bool flagValidParameter;
 | 
			
		||||
 | 
			
		||||
    void private_handlePingResponse(QJsonObject response);
 | 
			
		||||
    void private_handleReqSelfResponse(QJsonObject response);
 | 
			
		||||
    void private_handleReqPingResponse(QJsonObject response);
 | 
			
		||||
    void private_handleParameterResponse(QJsonObject response);
 | 
			
		||||
    void private_handleFileUploadResponse(QJsonObject response);
 | 
			
		||||
    void private_handleErrorResponse(QString errorString);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    void handleISMASResponseError();
 | 
			
		||||
    void handleISMASOfflineResponseError();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // APISM_CLIENT_H_INCLUDED
 | 
			
		||||
							
								
								
									
										216
									
								
								common/ismas/ApismClientForUpdate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								common/ismas/ApismClientForUpdate.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,216 @@
 | 
			
		||||
#include "ApismClientForUpdate.h"
 | 
			
		||||
 | 
			
		||||
#include <QRegularExpression>
 | 
			
		||||
 | 
			
		||||
ApismClientForUpdate::ApismClientForUpdate(QObject *parent)
 | 
			
		||||
    : ApismClient(parent) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApismClientForUpdate::~ApismClientForUpdate() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::sendCmdEvent(ISMAS::EventData eventData) {
 | 
			
		||||
    // QScopedPointer<ISMAS::EventData> eventData(new ISMAS::EventData());
 | 
			
		||||
 /*
 | 
			
		||||
    struct EventData : public QJsonObject {
 | 
			
		||||
        struct : public QJsonObject {
 | 
			
		||||
            QJsonValue reason;
 | 
			
		||||
            QJsonValue timestamp;
 | 
			
		||||
            QJsonValue eventID;
 | 
			
		||||
            QJsonValue event;
 | 
			
		||||
            QJsonValue eventState;
 | 
			
		||||
            struct : public QJsonObject {
 | 
			
		||||
                QJsonValue percent;
 | 
			
		||||
                QJsonValue resultCode;
 | 
			
		||||
                QJsonValue step;
 | 
			
		||||
                QJsonValue stepResult;
 | 
			
		||||
                QJsonValue Version;
 | 
			
		||||
            } parameter;
 | 
			
		||||
        } newsToIsmas;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
            "\"REASON\":\"SW_UP\","
 | 
			
		||||
            "\"TIMESTAMP\":\"%s\","
 | 
			
		||||
            "\"EVENT_ID\":\"0\","
 | 
			
		||||
            "\"EVENT\":\"%s\","
 | 
			
		||||
            "\"EVENTSTATE\":1,"
 | 
			
		||||
            "\"PARAMETER\": {"
 | 
			
		||||
                "\"PERCENT\" : %d,"
 | 
			
		||||
                "\"RESULTCODE\" : %d,"
 | 
			
		||||
                "\"STEP\" : \"%s\","
 | 
			
		||||
                "\"STEP_RESULT\" : \"%s\","
 | 
			
		||||
                "\"VERSION\" : \"%s\""
 | 
			
		||||
            "}"
 | 
			
		||||
        "}", ts.toStdStr
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
    //eventData->newsToIsmas.reason = data.newsToIsmas;
 | 
			
		||||
    eventData.insert("REASON", eventData.newsToIsmas.reason);
 | 
			
		||||
    eventData.insert("TIMESTAMP", eventData.newsToIsmas.timestamp);
 | 
			
		||||
    eventData.insert("EVENT_ID", eventData.newsToIsmas.eventID);
 | 
			
		||||
    eventData.insert("EVENT", eventData.newsToIsmas.event);
 | 
			
		||||
    eventData.insert("EVENT_STATE", eventData.newsToIsmas.eventState);
 | 
			
		||||
 | 
			
		||||
    QJsonObject parameterJsonObject;
 | 
			
		||||
    parameterJsonObject.insert("PERCENT", eventData.newsToIsmas.parameter.percent);
 | 
			
		||||
    parameterJsonObject.insert("RESULTCODE", eventData.newsToIsmas.parameter.resultCode);
 | 
			
		||||
    parameterJsonObject.insert("STEP", eventData.newsToIsmas.parameter.step);
 | 
			
		||||
    parameterJsonObject.insert("STEP_RESULT", eventData.newsToIsmas.parameter.stepResult);
 | 
			
		||||
    parameterJsonObject.insert("VERSION", eventData.newsToIsmas.parameter.version);
 | 
			
		||||
 | 
			
		||||
    eventData.insert("PARAMETER", parameterJsonObject);
 | 
			
		||||
 | 
			
		||||
    qCritical() << __func__ << ":" << __LINE__ << eventData;
 | 
			
		||||
 | 
			
		||||
    QJsonDocument jsonDoc(eventData);
 | 
			
		||||
    QByteArray data = "#M=APISM#C=CMD_EVENT#J=";
 | 
			
		||||
    data += jsonDoc.toJson(QJsonDocument::Compact);
 | 
			
		||||
 | 
			
		||||
    qCritical() << __func__ << ":" << __LINE__ << data;
 | 
			
		||||
 | 
			
		||||
    tcpSendClient()->sendData(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::onReceivedResponse(QByteArray response) {
 | 
			
		||||
    // get the root object
 | 
			
		||||
    QJsonParseError jsonParseError;
 | 
			
		||||
    QJsonDocument responseDoc = QJsonDocument::fromJson(response, &jsonParseError);
 | 
			
		||||
 | 
			
		||||
    if (jsonParseError.error != QJsonParseError::NoError) {
 | 
			
		||||
        qCritical() << "ApismClient::onReceivedResponse() response is no json data:";
 | 
			
		||||
        qCritical() << "     Error: " << jsonParseError.errorString();
 | 
			
		||||
 | 
			
		||||
        // workaround for REQ_SELF and offline
 | 
			
		||||
        if (this->getCurrentRequest() == ISMAS::REQUEST::SELF) {
 | 
			
		||||
            if (response.contains("Connecting...")) {
 | 
			
		||||
                qCritical() << "          -> Connecting...";
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (response.contains("ISMAS is offline")) {
 | 
			
		||||
                this->restartApism();
 | 
			
		||||
                qCritical() << "          -> Workaround: restart APISM";
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (response.contains("ISMAS is offline")) {
 | 
			
		||||
            this->handleISMASOfflineResponseError();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            this->handleISMASResponseError();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QJsonObject rootObject = responseDoc.object();
 | 
			
		||||
    QStringList rootObjectKeys = rootObject.keys();
 | 
			
		||||
 | 
			
		||||
    // DEBUG
 | 
			
		||||
    qCritical() << "ApismClient::onReceivedResponse(): objects: " << rootObjectKeys;
 | 
			
		||||
    // results to:
 | 
			
		||||
    //  ApismClient::onReceivedResponse(): objects:  ("REQ_START#60044_Response", "Response")
 | 
			
		||||
 | 
			
		||||
    if(rootObjectKeys.indexOf(QRegularExpression("^CMD_GET_APISMSTATUS.*")) >= 0) {
 | 
			
		||||
        resetRetryCounter();
 | 
			
		||||
        //this->private_handleReqSelfResponse(rootObject["CMD_GET_APISMSTATUS_RESPONSE#0"].toObject());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    if(rootObjectKeys.indexOf(QRegularExpression("^REQ_ISMASPARAMETER.*")) >= 0) {
 | 
			
		||||
        resetRetryCounter();
 | 
			
		||||
        //this->private_handleParameterResponse(rootObject["Parameter"].toObject());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    if(rootObjectKeys.indexOf(QRegularExpression("^REQ_PING.*")) >= 0) {
 | 
			
		||||
        resetRetryCounter();
 | 
			
		||||
        //this->private_handleReqPingResponse(rootObject["PING"].toObject());
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    if(rootObjectKeys.indexOf(QRegularExpression("^error")) >= 0) {
 | 
			
		||||
        // handle error objects
 | 
			
		||||
        //this->private_handleErrorResponse(rootObject["error"].toString());
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        qCritical() << "ApismClient::onReceivedResponse() for unknown Request: ";
 | 
			
		||||
        qCritical() << "                     currentRequestName: " << getCurrentRequest();
 | 
			
		||||
        qCritical() << "                      rootObject.keys(): " << rootObjectKeys;
 | 
			
		||||
        this->handleISMASResponseError();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->setCurrentRequest(ISMAS::REQUEST::NO_REQUEST);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::onSendClientResponseTimeout() {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::onRequestResponseClientResponseTimeout() {
 | 
			
		||||
    qCritical() << "ApismClient::onRequestResponseClientResponseTimeout(): currentRequest is " << this->getCurrentRequest();
 | 
			
		||||
 | 
			
		||||
    switch (this->getCurrentRequest()) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        //emit this->sendMininformPingResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        //emit this->sendReqSelfResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        //emit this->sendReqParameterResponse(nsApismInterface::RESULT_STATE::ERROR_TIMEOUT, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->setCurrentRequest(ISMAS::REQUEST::NO_REQUEST);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::onRequestResponseClientConnectTimeout() {
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismClient::onRequestResponseClientConnectTimeout(): currentRequest is " << this->getCurrentRequest();
 | 
			
		||||
 | 
			
		||||
    nsApismInterface::RESULT_STATE resultState;
 | 
			
		||||
 | 
			
		||||
    resultState = nsApismInterface::RESULT_STATE::ERROR_BACKEND;
 | 
			
		||||
 | 
			
		||||
    switch (this->getCurrentRequest()) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        qCritical() << "ApismClient::onRequestResponseClientConnectTimeout() for unknown Request: " << getCurrentRequest();
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        //emit this->sendMininformPingResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        //emit this->sendReqSelfResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        //emit this->sendReqParameterResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->setCurrentRequest(ISMAS::REQUEST::NO_REQUEST);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismClientForUpdate::onRequestResponseClientConnectionClosedByRemoteHost() {
 | 
			
		||||
    nsApismInterface::RESULT_STATE resultState = nsApismInterface::RESULT_STATE::ERROR_BACKEND;
 | 
			
		||||
 | 
			
		||||
    switch (this->getCurrentRequest()) {
 | 
			
		||||
    case ISMAS::REQUEST::NO_REQUEST:
 | 
			
		||||
        qCritical() << "ApismClient::onRequestResponseClientConnectionClosedByRemoteHost() for unknown Request: " << getCurrentRequest();
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PING:
 | 
			
		||||
        //emit this->sendMininformPingResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::SELF:
 | 
			
		||||
        emit this->sendReqSelfResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    case ISMAS::REQUEST::PARAMETER:
 | 
			
		||||
        emit this->sendReqParameterResponse(resultState, QJsonObject());
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->setCurrentRequest(ISMAS::REQUEST::NO_REQUEST);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								common/ismas/ApismClientForUpdate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								common/ismas/ApismClientForUpdate.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
#ifndef APISM_CLIENT_FOR_UPDATE_H_INCLUDED
 | 
			
		||||
#define APISM_CLIENT_FOR_UPDATE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
#include "ApismClient.h"
 | 
			
		||||
 | 
			
		||||
class ApismClientForUpdate : public ApismClient {
 | 
			
		||||
public:
 | 
			
		||||
    explicit ApismClientForUpdate(QObject *parent = 0);
 | 
			
		||||
    virtual ~ApismClientForUpdate();
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    void sendCmdEvent(ISMAS::EventData);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    virtual void onReceivedResponse(QByteArray response) override;
 | 
			
		||||
    virtual void onSendClientResponseTimeout() override;
 | 
			
		||||
    virtual void onRequestResponseClientResponseTimeout() override;
 | 
			
		||||
    virtual void onRequestResponseClientConnectTimeout() override;
 | 
			
		||||
    virtual void onRequestResponseClientConnectionClosedByRemoteHost() override;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // APISM_CLIENT_FOR_UPDATE_H_INCLUDED
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										564
									
								
								common/ismas/ApismTcpClient.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										564
									
								
								common/ismas/ApismTcpClient.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,564 @@
 | 
			
		||||
#include "ApismTcpClient.h"
 | 
			
		||||
 | 
			
		||||
#include <QHostAddress>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
 | 
			
		||||
ApismMessage::ApismMessage() : state(MessageState::INVALID) { }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
ApismTcpClient::ApismTcpClient(const QString & hostname,
 | 
			
		||||
                               const QString & port,
 | 
			
		||||
                               ExpectedResponse expectedResponse,
 | 
			
		||||
                               QObject *parent)
 | 
			
		||||
    : QObject(parent)
 | 
			
		||||
    , hostname(hostname)
 | 
			
		||||
    , port(port)
 | 
			
		||||
    , responseTimerTimeoutCounter(0)
 | 
			
		||||
    , flag_selfClosed(false)
 | 
			
		||||
    , resendCounter(0)
 | 
			
		||||
    , connectionRefusedCounter(0)
 | 
			
		||||
    , expectedResponse(expectedResponse)
 | 
			
		||||
    , isDebug(false)
 | 
			
		||||
{  
 | 
			
		||||
    this->responseTimeoutTimer = new QTimer(this);
 | 
			
		||||
    this->responseTimeoutTimer->setInterval(10000);
 | 
			
		||||
    this->responseTimeoutTimer->setSingleShot(true);
 | 
			
		||||
 | 
			
		||||
    connect(this->responseTimeoutTimer, SIGNAL(timeout()), this, SLOT(onResponseTimeoutTimerTimeout()));
 | 
			
		||||
 | 
			
		||||
    this->connectTimeoutTimer = new QTimer(this);
 | 
			
		||||
    this->connectTimeoutTimer->setInterval(10000);
 | 
			
		||||
    this->connectTimeoutTimer->setSingleShot(true);
 | 
			
		||||
 | 
			
		||||
    connect(this->connectTimeoutTimer, SIGNAL(timeout()), this, SLOT(onConnectTimeoutTimerTimeout()));
 | 
			
		||||
 | 
			
		||||
    socket = new QTcpSocket(this);
 | 
			
		||||
    connect(socket, SIGNAL(connected()),    this, SLOT(onSocketConnected()));
 | 
			
		||||
    connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
 | 
			
		||||
    connect(socket, SIGNAL(readyRead()),    this, SLOT(onSocketReadyRead()));
 | 
			
		||||
    connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(onSocketBytesWritten(qint64)));
 | 
			
		||||
    //  note: from Qt 5.15 onward a new signal "errorOccurred" will be introduced which could be used whithout this static_cast.
 | 
			
		||||
    //  see e.g. https://stackoverflow.com/questions/35655512/compile-error-when-connecting-qtcpsocketerror-using-the-new-qt5-signal-slot
 | 
			
		||||
    connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), this, &ApismTcpClient::onSocketErrorOccured);
 | 
			
		||||
    connect(socket, &QTcpSocket::stateChanged, this, &ApismTcpClient::onSocketStateChanged);
 | 
			
		||||
 | 
			
		||||
    this->currentMessage = ApismMessage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::setResponseTimeout(const quint32 timeout_ms)
 | 
			
		||||
{
 | 
			
		||||
    this->responseTimeoutTimer->setInterval(timeout_ms);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::setDebug(bool debug)
 | 
			
		||||
{
 | 
			
		||||
    this->isDebug = debug;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::connectToHost()
 | 
			
		||||
{
 | 
			
		||||
    this->connectTimeoutTimer->start();
 | 
			
		||||
    int portNumber = this->port.toInt();
 | 
			
		||||
    this->socket->connectToHost(QHostAddress(this->hostname), portNumber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::connectToHost(const QString & hostname, const QString & port)
 | 
			
		||||
{
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::connectToHost(" << hostname << ", " << port << ")";
 | 
			
		||||
 | 
			
		||||
    this->connectTimeoutTimer->start();
 | 
			
		||||
    int portNumber = port.toInt();
 | 
			
		||||
    socket->connectToHost(hostname, portNumber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::closeConnection()
 | 
			
		||||
{
 | 
			
		||||
    this->flag_selfClosed = true;
 | 
			
		||||
    socket->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApismTcpClient::isConnected()
 | 
			
		||||
{
 | 
			
		||||
    bool result = false;
 | 
			
		||||
    QAbstractSocket::SocketState socketState = socket->state();
 | 
			
		||||
 | 
			
		||||
    switch (socketState) {
 | 
			
		||||
        case QAbstractSocket::UnconnectedState:
 | 
			
		||||
            /* FALLTHRU */
 | 
			
		||||
        case QAbstractSocket::HostLookupState:
 | 
			
		||||
            /* FALLTHRU */
 | 
			
		||||
        case QAbstractSocket::ConnectingState:
 | 
			
		||||
            result = false;
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ConnectedState:
 | 
			
		||||
            /* FALLTHRU */
 | 
			
		||||
        case QAbstractSocket::BoundState:
 | 
			
		||||
            result = true;
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ClosingState:
 | 
			
		||||
            /* FALLTHRU */
 | 
			
		||||
        case QAbstractSocket::ListeningState:
 | 
			
		||||
            result = false;
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief ApismTcpClient::sendData
 | 
			
		||||
 * @param message
 | 
			
		||||
 *
 | 
			
		||||
 * Enqueue message, and try to send it
 | 
			
		||||
 */
 | 
			
		||||
void ApismTcpClient::sendData(const QByteArray &message)
 | 
			
		||||
{
 | 
			
		||||
    if (this->isDebug) {
 | 
			
		||||
        qCritical() << "ApismTcpClient::sendData(" << message << ")";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->sendQueue.enqueue(message);
 | 
			
		||||
 | 
			
		||||
    this->sendData();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief ApismTcpClient::sendData
 | 
			
		||||
 *
 | 
			
		||||
 * Check connection and try to send message from queue.
 | 
			
		||||
 */
 | 
			
		||||
void ApismTcpClient::sendData()
 | 
			
		||||
{
 | 
			
		||||
    if (this->sendQueue.size() == 0) {
 | 
			
		||||
        if (this->isDebug) {
 | 
			
		||||
            qCritical() << "ApismTcpClient::sendData()" << "no messages in send queue";
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // DEBUG
 | 
			
		||||
    if (this->isDebug) {
 | 
			
		||||
        qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::sendData() sendQueue.size() = " << this->sendQueue.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (this->currentMessage.state) {
 | 
			
		||||
        case ApismMessage::MessageState::INVALID:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::NEW:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::RESEND:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::ANSWERED:
 | 
			
		||||
            // allow send message
 | 
			
		||||
            if (this->isConnected()) {
 | 
			
		||||
                this->private_sendData();
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                this->connectToHost();
 | 
			
		||||
            }
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismMessage::MessageState::SENT:
 | 
			
		||||
            // wait for answer...
 | 
			
		||||
            if (this->isDebug) {
 | 
			
		||||
                qCritical() << "                ... wait for answer";
 | 
			
		||||
            }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief ApismTcpClient::private_sendData
 | 
			
		||||
 *
 | 
			
		||||
 * Precondition:
 | 
			
		||||
 *   - queue is not empty,
 | 
			
		||||
 *   - socket state is connected
 | 
			
		||||
 *   - current message is ANSWERED or INVALID
 | 
			
		||||
 */
 | 
			
		||||
void ApismTcpClient::private_sendData()
 | 
			
		||||
{
 | 
			
		||||
    // take message from queue
 | 
			
		||||
    this->currentMessage.data = this->sendQueue.dequeue();
 | 
			
		||||
    this->currentMessage.state = ApismMessage::MessageState::SENT;
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::send: " << QString(this->currentMessage.data);
 | 
			
		||||
 | 
			
		||||
    socket->write(this->currentMessage.data);
 | 
			
		||||
    socket->flush();
 | 
			
		||||
 | 
			
		||||
    // start timeoutTimer
 | 
			
		||||
    this->responseTimeoutTimer->start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketConnected()
 | 
			
		||||
{
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << "): Connected!";
 | 
			
		||||
 | 
			
		||||
    this->connectTimeoutTimer->stop();
 | 
			
		||||
    this->connectionRefusedCounter = 0;
 | 
			
		||||
 | 
			
		||||
    switch (this->currentMessage.state) {
 | 
			
		||||
        case ApismMessage::MessageState::INVALID:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::NEW:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::RESEND:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::ANSWERED:
 | 
			
		||||
            // allow send next message
 | 
			
		||||
            if (this->sendQueue.size() > 0) {
 | 
			
		||||
                this->private_sendData();
 | 
			
		||||
            }
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismMessage::MessageState::SENT:
 | 
			
		||||
            // wait for answer...
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketDisconnected()
 | 
			
		||||
{
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << "): Disconnected!";
 | 
			
		||||
 | 
			
		||||
    if (!this->flag_selfClosed) {
 | 
			
		||||
        qCritical() << "        -> SocketErrorString: " << socket->errorString();
 | 
			
		||||
    }
 | 
			
		||||
    this->flag_selfClosed = false;
 | 
			
		||||
 | 
			
		||||
    if ( (socket->error() == QAbstractSocket::SocketError::RemoteHostClosedError) &&
 | 
			
		||||
         (this->responseTimeoutTimer->isActive()) )
 | 
			
		||||
    {
 | 
			
		||||
        this->responseTimeoutTimer->stop();
 | 
			
		||||
        qCritical() << "        -> still waiting for response ";
 | 
			
		||||
 | 
			
		||||
        switch (this->expectedResponse) {
 | 
			
		||||
            case ApismTcpClient::ExpectedResponse::STATE:
 | 
			
		||||
                // try resend:
 | 
			
		||||
                this->currentMessage.state = ApismMessage::MessageState::RESEND;
 | 
			
		||||
                // enqeue current message for resend:
 | 
			
		||||
                this->sendQueue.prepend(this->currentMessage.data);
 | 
			
		||||
 | 
			
		||||
                this->sendData();
 | 
			
		||||
                break;
 | 
			
		||||
            case ApismTcpClient::ExpectedResponse::JSON:
 | 
			
		||||
                this->currentMessage.state = ApismMessage::MessageState::INVALID;
 | 
			
		||||
                emit this->connectionClosedByRemoteHost();
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketBytesWritten(qint64 bytes)
 | 
			
		||||
{
 | 
			
		||||
    if (this->isDebug) {
 | 
			
		||||
        qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::onSocketBytesWritten() -> " << bytes << " bytes written";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketReadyRead()
 | 
			
		||||
{
 | 
			
		||||
    QByteArray readData;
 | 
			
		||||
 | 
			
		||||
    // stop timeoutTimer
 | 
			
		||||
    this->responseTimeoutTimer->stop();
 | 
			
		||||
 | 
			
		||||
    readData = socket->readAll();
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::received: " << QString(readData);
 | 
			
		||||
 | 
			
		||||
    switch (this->expectedResponse) {
 | 
			
		||||
        case ApismTcpClient::ExpectedResponse::JSON:
 | 
			
		||||
            this->private_handleJSONResponse(readData);
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismTcpClient::ExpectedResponse::STATE:
 | 
			
		||||
            this->private_handleStateResponse(readData);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (this->sendQueue.size() > 0) {
 | 
			
		||||
        QTimer::singleShot(1000,
 | 
			
		||||
                           this,
 | 
			
		||||
                           [this]() { this->sendData(); }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        this->flag_selfClosed = true;
 | 
			
		||||
        this->socket->close();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 * response handler:
 | 
			
		||||
 */
 | 
			
		||||
void ApismTcpClient::private_handleJSONResponse(QByteArray & responseMessage)
 | 
			
		||||
{
 | 
			
		||||
    emit this->receivedData(responseMessage);
 | 
			
		||||
 | 
			
		||||
    // allow send next message:
 | 
			
		||||
    this->currentMessage.state = ApismMessage::MessageState::ANSWERED;
 | 
			
		||||
    this->resendCounter = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* possible answers:
 | 
			
		||||
 *    "RECORD SAVED"           --> everything is ok
 | 
			
		||||
 *    "RECORD WRITE ABORTED"   --> initiate a (delayed) resend
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
void ApismTcpClient::private_handleStateResponse(QByteArray & responseMessage)
 | 
			
		||||
{
 | 
			
		||||
    QString responseMessageString = QString(responseMessage);
 | 
			
		||||
 | 
			
		||||
    if (responseMessageString.contains("ABORTED")) {
 | 
			
		||||
        // Try to resend later:
 | 
			
		||||
        this->currentMessage.state = ApismMessage::MessageState::RESEND;
 | 
			
		||||
        // enqeue current message for resend:
 | 
			
		||||
        this->sendQueue.prepend(this->currentMessage.data);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    if (responseMessageString.contains("RECORD SAVED")) {
 | 
			
		||||
        // allow send next message:
 | 
			
		||||
        this->currentMessage.state = ApismMessage::MessageState::ANSWERED;
 | 
			
		||||
        this->resendCounter = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************************************************
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onResponseTimeoutTimerTimeout()
 | 
			
		||||
{
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::onResponseTimeoutTimerTimeout():";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    switch (this->currentMessage.state) {
 | 
			
		||||
        case ApismMessage::MessageState::INVALID:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::NEW:
 | 
			
		||||
            /* FALLTHROUGH */
 | 
			
		||||
        case ApismMessage::MessageState::ANSWERED:
 | 
			
		||||
            // ignore
 | 
			
		||||
            qCritical() << "                -> ignore timeout";
 | 
			
		||||
            return;
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismMessage::MessageState::RESEND:
 | 
			
		||||
            qCritical() << "                -> timeout (RESEND)";
 | 
			
		||||
            qCritical() << "                -> resendCounter = " << this->resendCounter;
 | 
			
		||||
            switch (this->expectedResponse) {
 | 
			
		||||
            case ApismTcpClient::ExpectedResponse::STATE:
 | 
			
		||||
                // try resend:
 | 
			
		||||
                this->currentMessage.state = ApismMessage::MessageState::RESEND;
 | 
			
		||||
                // enqeue current message for resend:
 | 
			
		||||
                this->sendQueue.prepend(this->currentMessage.data);
 | 
			
		||||
 | 
			
		||||
                this->sendData();
 | 
			
		||||
                break;
 | 
			
		||||
            case ApismTcpClient::ExpectedResponse::JSON:
 | 
			
		||||
                this->currentMessage.state = ApismMessage::MessageState::INVALID;
 | 
			
		||||
                emit this->responseTimeout();
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismMessage::MessageState::SENT:
 | 
			
		||||
            // we still do not have a response:
 | 
			
		||||
 | 
			
		||||
            switch (this->expectedResponse) {
 | 
			
		||||
                case ApismTcpClient::ExpectedResponse::STATE:
 | 
			
		||||
                    // try resend:
 | 
			
		||||
                    this->currentMessage.state = ApismMessage::MessageState::RESEND;
 | 
			
		||||
                    // enqeue current message for resend:
 | 
			
		||||
                    this->sendQueue.prepend(this->currentMessage.data);
 | 
			
		||||
 | 
			
		||||
                    this->sendData();
 | 
			
		||||
                    break;
 | 
			
		||||
                case ApismTcpClient::ExpectedResponse::JSON:
 | 
			
		||||
                    this->currentMessage.state = ApismMessage::MessageState::INVALID;
 | 
			
		||||
                    emit this->responseTimeout();
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // count resends
 | 
			
		||||
    this->resendCounter++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onConnectTimeoutTimerTimeout()
 | 
			
		||||
{
 | 
			
		||||
    if (this->sendQueue.size() == 0) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::onConnectTimeoutTimerTimeout() -> sendQueue.size() = " << this->sendQueue.size();
 | 
			
		||||
 | 
			
		||||
    emit this->connectTimeout();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketStateChanged(QAbstractSocket::SocketState socketState)
 | 
			
		||||
{
 | 
			
		||||
    QString msg;
 | 
			
		||||
 | 
			
		||||
    switch (socketState) {
 | 
			
		||||
        case QAbstractSocket::UnconnectedState:
 | 
			
		||||
            msg = "UnconnectedState";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::HostLookupState:
 | 
			
		||||
            msg = "HostLookupState";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ConnectingState:
 | 
			
		||||
            msg = "ConnectingState";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ConnectedState:
 | 
			
		||||
            msg = "ConnectedState";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::BoundState:
 | 
			
		||||
            msg = "BoundState";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ClosingState:
 | 
			
		||||
            msg = "ClosingState";
 | 
			
		||||
            break;
 | 
			
		||||
         case QAbstractSocket::ListeningState:
 | 
			
		||||
            msg = "ListeningState";
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this->isDebug) {
 | 
			
		||||
        qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::onSocketStateChanged() to: " << msg;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::onSocketErrorOccured(QAbstractSocket::SocketError socketError)
 | 
			
		||||
{
 | 
			
		||||
    QString msg;
 | 
			
		||||
    bool flagReconnect = false;
 | 
			
		||||
 | 
			
		||||
    switch (socketError) {
 | 
			
		||||
        case QAbstractSocket::ConnectionRefusedError:
 | 
			
		||||
            msg = "ConnectionRefusedError";
 | 
			
		||||
            flagReconnect = true;
 | 
			
		||||
            this->private_handleConnectionRefusedError();
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::RemoteHostClosedError:
 | 
			
		||||
            msg = "RemoteHostClosedError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::HostNotFoundError:
 | 
			
		||||
            msg = "HostNotFoundError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SocketAccessError:
 | 
			
		||||
            msg = "SocketAccessError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SocketResourceError:
 | 
			
		||||
            msg = "SocketResourceError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SocketTimeoutError:
 | 
			
		||||
            msg = "SocketTimeoutError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::DatagramTooLargeError:
 | 
			
		||||
            msg = "DatagramTooLargeError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::NetworkError:
 | 
			
		||||
            msg = "NetworkError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::AddressInUseError:
 | 
			
		||||
            msg = "AddressInUseError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SocketAddressNotAvailableError:
 | 
			
		||||
            msg = "SocketAddressNotAvailableError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::UnsupportedSocketOperationError:
 | 
			
		||||
            msg = "UnsupportedSocketOperationError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyAuthenticationRequiredError:
 | 
			
		||||
            msg = "ProxyAuthenticationRequiredError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SslHandshakeFailedError:
 | 
			
		||||
            msg = "SslHandshakeFailedError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::UnfinishedSocketOperationError:
 | 
			
		||||
            msg = "UnfinishedSocketOperationError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyConnectionRefusedError:
 | 
			
		||||
            msg = "ProxyConnectionRefusedError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyConnectionClosedError:
 | 
			
		||||
            msg = "ProxyConnectionClosedError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyConnectionTimeoutError:
 | 
			
		||||
            msg = "ProxyConnectionTimeoutError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyNotFoundError:
 | 
			
		||||
            msg = "ProxyNotFoundError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::ProxyProtocolError:
 | 
			
		||||
            msg = "ProxyProtocolError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::OperationError:
 | 
			
		||||
            msg = "OperationError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SslInternalError:
 | 
			
		||||
            msg = "SslInternalError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::SslInvalidUserDataError:
 | 
			
		||||
            msg = "SslInvalidUserDataError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::TemporaryError:
 | 
			
		||||
            msg = "TemporaryError";
 | 
			
		||||
            break;
 | 
			
		||||
        case QAbstractSocket::UnknownSocketError:
 | 
			
		||||
            msg = "UnknownSocketError";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::ApismTcpClient::onSocketErrorOccured() -> " << msg;
 | 
			
		||||
 | 
			
		||||
    if (flagReconnect) {
 | 
			
		||||
        // try to reconnect for sending:
 | 
			
		||||
        if (this->sendQueue.size() > 0) {
 | 
			
		||||
            QTimer::singleShot(1000,
 | 
			
		||||
                               this,
 | 
			
		||||
                               [this]() { this->connectToHost(); }
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ApismTcpClient::private_handleConnectionRefusedError()
 | 
			
		||||
{
 | 
			
		||||
    if (this->connectionRefusedCounter > 5) {
 | 
			
		||||
        qCritical() << "ApismTcpClient(" << this->expectedResponse << ")::ApismTcpClient::connectionRefusedError()";
 | 
			
		||||
        this->connectionRefusedCounter = 0;
 | 
			
		||||
        emit this->connectionRefusedError();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        this->connectionRefusedCounter++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, ApismTcpClient::ExpectedResponse response)
 | 
			
		||||
{
 | 
			
		||||
    switch (response) {
 | 
			
		||||
        case ApismTcpClient::ExpectedResponse::JSON:
 | 
			
		||||
            debug << "JSON";
 | 
			
		||||
        break;
 | 
			
		||||
        case ApismTcpClient::ExpectedResponse::STATE:
 | 
			
		||||
            debug << "STATE";
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return debug;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								common/ismas/ApismTcpClient.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								common/ismas/ApismTcpClient.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
#ifndef APISMTCPCLIENT_H
 | 
			
		||||
#define APISMTCPCLIENT_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
 | 
			
		||||
#include <QTcpSocket>
 | 
			
		||||
#include <QAbstractSocket>
 | 
			
		||||
#include <QByteArray>
 | 
			
		||||
#include <QQueue>
 | 
			
		||||
 | 
			
		||||
class QTimer;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ApismMessage
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    enum class MessageState {
 | 
			
		||||
        INVALID,
 | 
			
		||||
        NEW,
 | 
			
		||||
        SENT,
 | 
			
		||||
        ANSWERED,
 | 
			
		||||
        RESEND
 | 
			
		||||
     };
 | 
			
		||||
     MessageState state;
 | 
			
		||||
 | 
			
		||||
     QByteArray data;
 | 
			
		||||
 | 
			
		||||
     ApismMessage();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ApismTcpClient : public QObject
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    enum class ExpectedResponse {
 | 
			
		||||
        JSON,
 | 
			
		||||
        STATE
 | 
			
		||||
     };
 | 
			
		||||
 | 
			
		||||
    explicit ApismTcpClient(const QString & hostname, const QString & port, ExpectedResponse expectedResponse, QObject *parent = nullptr);
 | 
			
		||||
    bool isConnected();
 | 
			
		||||
 | 
			
		||||
    void connectToHost();
 | 
			
		||||
    void connectToHost(const QString & hostname, const QString & port);
 | 
			
		||||
    void setResponseTimeout(const quint32 timeout_ms);
 | 
			
		||||
 | 
			
		||||
    // socket is implicitely closed by APISM
 | 
			
		||||
    void closeConnection();
 | 
			
		||||
 | 
			
		||||
    void sendData(const QByteArray & message);
 | 
			
		||||
    void sendData();
 | 
			
		||||
 | 
			
		||||
    void setDebug(bool debug);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    // socket interface
 | 
			
		||||
    void onSocketConnected();
 | 
			
		||||
    void onSocketDisconnected();
 | 
			
		||||
    void onSocketBytesWritten(qint64 bytes);
 | 
			
		||||
    void onSocketReadyRead();
 | 
			
		||||
    void onSocketStateChanged(QAbstractSocket::SocketState socketState);
 | 
			
		||||
    void onSocketErrorOccured(QAbstractSocket::SocketError socketError);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
    void receivedData(QByteArray response);
 | 
			
		||||
 | 
			
		||||
    void responseTimeout();
 | 
			
		||||
    void connectTimeout();
 | 
			
		||||
    void connectionClosedByRemoteHost();
 | 
			
		||||
    void connectionRefusedError();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QTcpSocket *socket;
 | 
			
		||||
 | 
			
		||||
    QQueue<QByteArray> sendQueue;
 | 
			
		||||
    ApismMessage currentMessage;
 | 
			
		||||
 | 
			
		||||
    QString hostname;
 | 
			
		||||
    QString port;
 | 
			
		||||
 | 
			
		||||
    QTimer *responseTimeoutTimer;
 | 
			
		||||
    quint8 responseTimerTimeoutCounter;
 | 
			
		||||
 | 
			
		||||
    QTimer *connectTimeoutTimer;
 | 
			
		||||
 | 
			
		||||
    void private_sendData();
 | 
			
		||||
    bool flag_selfClosed;
 | 
			
		||||
 | 
			
		||||
    int resendCounter;
 | 
			
		||||
    int connectionRefusedCounter;
 | 
			
		||||
 | 
			
		||||
    ExpectedResponse expectedResponse;
 | 
			
		||||
 | 
			
		||||
    void private_handleJSONResponse(QByteArray & responseMessage);
 | 
			
		||||
    void private_handleTextResponse(QByteArray & responseMessage);
 | 
			
		||||
    void private_handleStateResponse(QByteArray & responseMessage);
 | 
			
		||||
 | 
			
		||||
    void private_handleConnectionRefusedError();
 | 
			
		||||
 | 
			
		||||
    bool isDebug;
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    void onResponseTimeoutTimerTimeout();
 | 
			
		||||
    void onConnectTimeoutTimerTimeout();
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
QDebug operator<<(QDebug debug, ApismTcpClient::ExpectedResponse response);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // APISMTCPCLIENT_H
 | 
			
		||||
							
								
								
									
										106
									
								
								common/ismas/ISMASData.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								common/ismas/ISMASData.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
#ifndef ISMASDATA_H
 | 
			
		||||
#define ISMASDATA_H
 | 
			
		||||
 | 
			
		||||
#include <QJsonObject>
 | 
			
		||||
#include <QJsonArray>
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
 | 
			
		||||
namespace ISMAS {
 | 
			
		||||
 | 
			
		||||
    static QString computeTimeStamp() {
 | 
			
		||||
        QDateTime const local(QDateTime::currentDateTime());
 | 
			
		||||
 | 
			
		||||
        QDateTime utc(local);
 | 
			
		||||
        utc.setTimeSpec(Qt::UTC);
 | 
			
		||||
 | 
			
		||||
        int const diff = (int)local.secsTo(utc); // diff between UTC and local time
 | 
			
		||||
 | 
			
		||||
        QTime t(0, 0, 0);
 | 
			
		||||
        t = t.addSecs((uint)diff);
 | 
			
		||||
 | 
			
		||||
        QString const st(QString("%1%2").arg(diff < 0 ? "-" : "+").arg(t.toString("hh:mm")));
 | 
			
		||||
        return QString (local.toString(Qt::ISODateWithMs) + st);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Note:
 | 
			
		||||
//    ! After U0002 immer ein CMD_SENDVERSION
 | 
			
		||||
//    ! Only U0002 and U0003 finish the Update process.
 | 
			
		||||
//    ! U0001: Update finished but not activated
 | 
			
		||||
//    ! U0002: Update finished and activated
 | 
			
		||||
//    ! U0003: Update finished but FAILed.
 | 
			
		||||
//
 | 
			
		||||
// #define _ISMAS_DONE                 "U0001" // 100%, Check: Resultcode: 0
 | 
			
		||||
// #define _ISMAS_SET_WAIT_OK          "U0002" // empty WAIT-button (""), ResultCode: 0
 | 
			
		||||
// #define _ISMAS_NO_UPDATE_NECESSARY  "M0100" // empty WAIT-button (""), ResultCode: 0
 | 
			
		||||
// #define _ISMAS_FAILURE              "U0003" // FAIL
 | 
			
		||||
// #define _ISMAS_CONTINUE             "U0010" // %-values: update running, result codes according running step
 | 
			
		||||
// #define _ISMAS_RESET_WAIT           "ISMAS" // reset WAIT-button to "WAIT"
 | 
			
		||||
// #define _ISMAS_TEST_TRIGGER         "U0099" // check the WAIT-button
 | 
			
		||||
 | 
			
		||||
    static constexpr const char *DONE               {"U0001"};
 | 
			
		||||
    static constexpr const char *SET_WAIT_OK        {"U0002"};
 | 
			
		||||
    static constexpr const char *NO_UPDATE_NECESSARY{"M0100"};
 | 
			
		||||
    static constexpr const char *FAILURE            {"U0003"};
 | 
			
		||||
    static constexpr const char *CONTINUE           {"U0010"};
 | 
			
		||||
    static constexpr const char *RESET_WAIT         {"ISMAS"};
 | 
			
		||||
    static constexpr const char *TEST_TRIGGER       {"U0099"};
 | 
			
		||||
 | 
			
		||||
    struct EventData : public QJsonObject {
 | 
			
		||||
        struct : public QJsonObject {
 | 
			
		||||
            QJsonValue reason;
 | 
			
		||||
            QJsonValue timestamp;
 | 
			
		||||
            QJsonValue eventID;
 | 
			
		||||
            QJsonValue event;
 | 
			
		||||
            QJsonValue eventState;
 | 
			
		||||
            struct : public QJsonObject {
 | 
			
		||||
                QJsonValue percent;
 | 
			
		||||
                QJsonValue resultCode;
 | 
			
		||||
                QJsonValue step;
 | 
			
		||||
                QJsonValue stepResult;
 | 
			
		||||
                QJsonValue version;
 | 
			
		||||
            } parameter;
 | 
			
		||||
        } newsToIsmas;
 | 
			
		||||
 | 
			
		||||
        explicit EventData(QString const &event,
 | 
			
		||||
                           int percent,
 | 
			
		||||
                           int resultCode,
 | 
			
		||||
                           QString const &step,
 | 
			
		||||
                           QString const &stepResult,
 | 
			
		||||
                           QString const &version = "",
 | 
			
		||||
                           QString const &reason = "SW_UP") {
 | 
			
		||||
            newsToIsmas.reason = reason;
 | 
			
		||||
            newsToIsmas.timestamp = computeTimeStamp();
 | 
			
		||||
            newsToIsmas.eventID = QString{"0"};
 | 
			
		||||
            newsToIsmas.event = event;
 | 
			
		||||
            newsToIsmas.eventState = 1;
 | 
			
		||||
            newsToIsmas.parameter.percent = percent;
 | 
			
		||||
            newsToIsmas.parameter.resultCode = resultCode;
 | 
			
		||||
            newsToIsmas.parameter.step = step;
 | 
			
		||||
            newsToIsmas.parameter.stepResult = stepResult;
 | 
			
		||||
            newsToIsmas.parameter.version = version;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct StateData : public QJsonObject {
 | 
			
		||||
        QJsonValue Timestamp;
 | 
			
		||||
        QJsonArray HW_States;
 | 
			
		||||
        struct : public QJsonObject {
 | 
			
		||||
            QJsonValue name;
 | 
			
		||||
            QJsonValue value;
 | 
			
		||||
            QJsonValue unit;
 | 
			
		||||
        } machineState;                  //
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    enum class REQUEST : quint8 {
 | 
			
		||||
        NO_REQUEST,
 | 
			
		||||
        PING,
 | 
			
		||||
        SELF,
 | 
			
		||||
        PARAMETER
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif // ISMASDATA_H
 | 
			
		||||
		Reference in New Issue
	
	Block a user