Compare commits

...

9 Commits

20 changed files with 1776 additions and 41 deletions

View File

@ -1,6 +1,6 @@
QT += core
TARGET = update_sync_cust_repo
TARGET = ATBUpdateSync
VERSION="1.0.0"
win32 {
@ -64,9 +64,19 @@ contains( CONFIG, DesktopLinux ) {
}
SOURCES += \
main.cpp
main.cpp \
message_handler.cpp \
../common/src/utils_internal.cpp \
../common/src/command.cpp \
sync_command.cpp
# HEADERS += \
HEADERS += \
message_handler.h \
../common/include/utils_internal.h \
../common/include/command.h \
sync_command.h
##########################################################################################
# for running program on target through QtCreator

View File

@ -9,6 +9,28 @@
#include <QDir>
#include <QDebug>
#include "message_handler.h"
#include "utils_internal.h"
#include "sync_command.h"
int main(int argc, char **argv) {
QByteArray const value = qgetenv("LC_ALL");
if (value.isEmpty() || value != "C") {
qputenv("LC_ALL", "C");
}
openlog("ATB-UPDATE-SYNC", LOG_PERROR | LOG_PID | LOG_CONS, LOG_USER);
QCoreApplication a(argc, argv);
QCoreApplication::setApplicationName("ATBUpdateSync");
QCoreApplication::setApplicationVersion(APP_VERSION);
if (!messageHandlerInstalled()) { // change internal qt-QDebug-handling
atbInstallMessageHandler(nullptr);
//atbInstallMessageHandler(atbDebugOutput);
setDebugLevel(LOG_NOTICE);
}
return 0;
}

97
Sync/message_handler.cpp Executable file
View File

@ -0,0 +1,97 @@
#include "message_handler.h"
#include <QDateTime>
#include <cstring>
#include <QString>
#include <QFileInfo>
#include <QMessageLogContext>
static char const *DBG_NAME[] = { "DBG ", "WARN ", "CRIT ", "FATAL", "INFO " };
static bool installedMsgHandler = false;
static int debugLevel = LOG_NOTICE;
int getDebugLevel() { return debugLevel; }
void setDebugLevel(int newDebugLevel) {
debugLevel = newDebugLevel;
}
bool messageHandlerInstalled() {
return installedMsgHandler;
}
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler) {
installedMsgHandler = (handler != 0);
static QtMessageHandler prevHandler = nullptr;
if (handler) {
prevHandler = qInstallMessageHandler(handler);
return prevHandler;
} else {
return qInstallMessageHandler(prevHandler);
}
}
///
/// \brief Print message according to given debug level.
///
/// \note Install this function using qInstallMsgHandler().
///
/// int main(int argc, char **argv) {
/// installMsgHandler(atbDebugOutput);
/// QApplication app(argc, argv);
/// ...
/// return app.exec();
/// }
///
#if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
Q_UNUSED(context);
QString const localMsg = QString(DBG_NAME[type]) + msg.toLocal8Bit();
switch (debugLevel) {
case LOG_DEBUG: { // debug-level message
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
} break;
case LOG_INFO: { // informational message
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_NOTICE: { // normal, but significant, condition
if (type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_WARNING: { // warning conditions
if (type != QtInfoMsg && type != QtDebugMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ERR: { // error conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_CRIT: { // critical conditions
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_ALERT: { // action must be taken immediately
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
case LOG_EMERG: { // system is unusable
if (type != QtInfoMsg && type != QtDebugMsg && type != QtWarningMsg) {
syslog(LOG_DEBUG, "%s", localMsg.toStdString().c_str());
}
} break;
default: {
//fprintf(stderr, "%s No ErrorLevel defined! %s\n",
// datetime.toStdString().c_str(), msg.toStdString().c_str());
}
}
}
#endif

23
Sync/message_handler.h Executable file
View File

@ -0,0 +1,23 @@
#ifndef MESSAGE_HANDLER_H_INCLUDED
#define MESSAGE_HANDLER_H_INCLUDED
#include <QtGlobal>
#ifdef __linux__
#include <syslog.h>
#endif
int getDebugLevel();
void setDebugLevel(int newDebugLevel);
bool messageHandlerInstalled();
QtMessageHandler atbInstallMessageHandler(QtMessageHandler handler);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const char *);
void atbDebugOutput(QtMsgType type, const char *msg);
#elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
void atbDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
#endif
#endif // MESSAGE_HANDLER_H_INCLUDED

18
Sync/sync_command.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "sync_command.h"
#include "command.h"
#include "utils_internal.h"
using namespace internal;
#include <QDir>
#include <QDebug>
#include <QFlags>
#include <QRegularExpression>
SyncCommand::SyncCommand() {
}
bool SyncCommand::exec(QString const &cmd, QStringList const &options,
int start_timeout, int finish_timeout) {
return false;
}

14
Sync/sync_command.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef SYNC_COMMAND_H_INCLUDED
#define SYNC_COMMAND_H_INCLUDED
#include <QStringList>
class SyncCommand {
public:
SyncCommand();
bool exec(QString const &cmd, QStringList const &options,
int start_timeout = 100000, int finish_timeout = 100000);
};
#endif // SYNC_COMMAND_H_INCLUDED

View File

@ -218,6 +218,7 @@ EXTENDED_VERSION="$${VERSION}-$${GIT_COMMIT}"
# INCLUDEPATH += plugins
INCLUDEPATH += plugins \
$${_PRO_FILE_PWD_}/../common/ \
$${_PRO_FILE_PWD_}/../common/include
CONFIG += c++17
@ -294,7 +295,11 @@ SOURCES += \
worker.cpp \
commandline_parser.cpp \
work_process_list.cpp \
../common/src/utils_internal.cpp
$${_PRO_FILE_PWD_}/../common/src/utils_internal.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClient.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismTcpClient.cpp \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClientForUpdate.cpp
HEADERS += \
update.h \
@ -319,7 +324,11 @@ HEADERS += \
worker.h \
commandline_parser.h \
work_process_list.h \
../common/include/utils_internal.h
$${_PRO_FILE_PWD_}/../common/include/utils_internal.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClient.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismTcpClient.h \
$${_PRO_FILE_PWD_}/../common/ismas/ISMASData.h \
$${_PRO_FILE_PWD_}/../common/ismas/ApismClientForUpdate.h
OTHER_FILES += \

View File

@ -49,17 +49,6 @@
#define SERIAL_PORT "ttyUSB0"
#endif
//QString Orientation(Qt::ScreenOrientation orientation) {
// switch (orientation) {
// case Qt::PrimaryOrientation : return "Primary";
// case Qt::LandscapeOrientation : return "Landscape";
// case Qt::PortraitOrientation : return "Portrait";
// case Qt::InvertedLandscapeOrientation : return "Inverted landscape";
// case Qt::InvertedPortraitOrientation : return "Inverted portrait";
// default : return "Unknown";
// }
//}
// argv[1]: file to send to dc
int main(int argc, char *argv[]) {
QByteArray const value = qgetenv("LC_ALL");
@ -82,15 +71,6 @@ int main(int argc, char *argv[]) {
setDebugLevel(LOG_NOTICE);
}
//qCritical() << "Number of screens:" << QGuiApplication::screens().size();
//qCritical() << "Primary screen:" << QGuiApplication::primaryScreen()->name();
//foreach (QScreen *screen, QGuiApplication::screens()) {
// qDebug() << "Information for screen:" << screen->name();
// qDebug() << " Orientation:" << Orientation(screen->orientation());
//}
//return 0;
CommandLineParser parser;
parser.process(a);
parser.readSettings();

View File

@ -107,9 +107,9 @@ void Command::finished(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/) {
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int,QProcess::ExitStatus)));
if (m_command.contains("ATBDownloadDCJsonFiles")) {
m_worker->dcUpdate();
}
//if (m_command.contains("ATBDownloadDCJsonFiles")) {
// m_worker->dcUpdate();
//}
}
// TODO: nach UpdateCommand ziehen

View File

@ -1,5 +1,6 @@
#include "update_command.h"
#include "worker.h"
#include "utils_internal.h"
#include <QDebug>
#include <QProcess>
@ -19,20 +20,13 @@ bool UpdateCommand::stopUpdateOnFailure() {
}
void UpdateCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
qCritical() << __func__ << ":" << __LINE__ << m_command
<< "exitCode" << exitCode
<< "exitStatus" << exitStatus;
QProcess *p = qobject_cast<QProcess *>(sender());
if (p) {
// read all remaining data sent to the process, just in case
QString s = p->readAllStandardOutput().trimmed();
if (!s.isEmpty()) {
qCritical() << __func__ << ":" << __LINE__ << s;
m_commandResult += s;
}
qCritical() << __func__ << ":" << __LINE__ << "next command" << m_nextCommandIndex;
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardOutput()));
disconnect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(readyReadStandardError()));
@ -40,13 +34,101 @@ void UpdateCommand::finished(int exitCode, QProcess::ExitStatus exitStatus) {
}
if (!m_worker->workList().empty()) {
//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};
qCritical() << __func__ << ":" << __LINE__ << m_command
<< "exitCode" << exitCode
<< "exitStatus" << exitStatus;
if (exitCode == 0 && exitStatus == QProcess::ExitStatus::NormalExit) {
qCritical() << __func__ << ":" << __LINE__;
if (m_command.contains("ATBUpdateCheck")) {
// TODO: resultCodes richtig eintragen
if (m_command.contains("--ismas-connected")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_ISMAS_CONNECIVITY,
0,
"ATBUpdateCheck --ismas-connected",
"ISMAS connected");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("--update-requested")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_UPDATE_REQUEST,
0,
"ATBUpdateCheck --update-requested",
"update not necessary");
m_worker->clientForUpdate().sendCmdEvent(e);
}
} else
if (m_command.contains("ATBUpdateGit")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_CHECK_CUSTOMER_REPOSITORY,
0,
"ATBUpdateGit",
"customer repository clean and up-to-date");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateOpkg")) {
if (m_command.contains("--noaction")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_SW_PACKETS_NOACTION,
0,
"ATBUpdateOpkg --noaction",
"execute opkg-commands (dry-run)");
m_worker->clientForUpdate().sendCmdEvent(e);
} else {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_SW_PACKETS,
0,
"ATBUpdateOpkg",
"execute opkg-commands");
m_worker->clientForUpdate().sendCmdEvent(e);
}
} else
if (m_command.contains("ATBDownloadDCJsonFiles")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_INSTALL_DC_CONFIGURATION,
0,
"ATBDownloadDCJsonFiles",
"download json-files to device controller");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateSync")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_SYNCHRONIZE_REPO_AND_FILESYS,
0,
"ATBUpdateSync",
"sync customer repository with /etc");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBDownloadDCFirmware")) {
ISMAS::EventData e(ISMAS::CONTINUE,
internal::PERCENT_UPDATE_DC,
0,
"ATBDownloadDCFirmware",
"downloaded new dc-firmware. about to reboot...");
m_worker->clientForUpdate().sendCmdEvent(e);
} else
if (m_command.contains("ATBUpdateShowPSAInstalled")) {
// TODO
}
if (m_worker->workList().nextExec()) {
m_worker->workList().exec();
}
} else {
qCritical() << __func__ << ":" << __LINE__;
bool execShowStatus = true;
m_worker->workList().exec(execShowStatus);
}

View File

@ -244,27 +244,27 @@ Worker::Worker(int customerNr,
// *** send json files down to device controller ***
m_workList.push_back(
std::make_unique<UpdateJsonCommand>(
QString("echo UpdateJsonCommand")
QString("echo ATBDownloadDCJsonFiles")
//QString("/opt/app/tools/atbupdate/ATBDownloadDCJsonFiles --set-ppid %1").arg(QCoreApplication::applicationPid())
, this, ++next, false));
// sync json files in repo etc-directory with /etc fs-directory
m_workList.push_back(
std::make_unique<UpdateFileSystemCommand>(
QString("echo UpdateFileSystemCommand")
QString("echo ATBUpdateSync")
, this, ++next));
// send device-controller firmware down to device-controller-hardware
m_workList.push_back(
std::make_unique<UpdateDCCommand>(
QString("echo UpdateDCCommand")
QString("echo ATBDownloadDCFirmware")
// QString("/opt/app/tools/atbupdate/ATBDownloadDCFirmware --read-dc-version true")
, this, ++next));
// show/send software-status
m_workList.push_back(
std::make_unique<ShowSoftwareStatusCommand>(
QString("echo ShowSoftwareStatusCommand")
QString("echo ATBUpdateShowPSAInstalled")
, this, -1));
// reboot machine

View File

@ -19,6 +19,7 @@
#include "git/git_client.h"
#include "ismas/ismas_client.h"
#include "ismas/ApismClientForUpdate.h"
#include "utils.h"
#include "work_process_list.h"
@ -199,6 +200,7 @@ class Worker : public QThread{
QString m_apismVersion;
WorkList m_workList;
ApismClientForUpdate m_clForUpdate;
bool executeOpkgCommand(QString opkgCommand);
bool cleanUpOpkgCache();
@ -402,6 +404,9 @@ public:
return m_ismasClient;
}
ApismClientForUpdate &clientForUpdate() { return m_clForUpdate; }
ApismClientForUpdate const &clientForUpdate() const { return m_clForUpdate; }
Worker *GUI(QStringList const &lst = QStringList()) {
m_guiMsg = lst;
return this;

View File

@ -26,6 +26,20 @@ namespace internal {
static constexpr const char *UPDATE_DC_FIRMARE_SUCCESS{"success"};
static constexpr const char *OPKG_MARKER{"<OPKG>"};
static constexpr const char *SYNC_MARKER{"<SYNC>"};
static constexpr const char *DC_MARKER{"<DC>"};
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();

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

View 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

View 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);
}

View 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

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

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