Adds q2 repos to migrate
This commit is contained in:
1 parent
ad1f62c4ee
commit
17f1c61c9e
991 files changed
+178451
No files matched your search
@@ -0,0 +1,88 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "AppComponent.hpp"
|
||||
|
||||
namespace N_AppComponent
|
||||
{
|
||||
|
||||
int32_t AppComponent::create(NGL::Settings *settings)
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->create(settings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t AppComponent::destroy()
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t AppComponent::init()
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t AppComponent::deinit()
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t AppComponent::start()
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t AppComponent::stop()
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
c->stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void AppComponent::addChild(IAppComponent *a_child, const QString &a_name)
|
||||
{
|
||||
if (a_child)
|
||||
{
|
||||
a_child->setName(a_name);
|
||||
m_Childs.push_back(a_child);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IAppComponent *AppComponent::getChild(const QString &a_name)
|
||||
{
|
||||
for (IAppComponent *c : m_Childs)
|
||||
{
|
||||
if (a_name == c->getName())
|
||||
return c;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "IAppComponent.hpp"
|
||||
#include <PrtCnv/prtcnvlib_export.h>
|
||||
|
||||
namespace NGL {
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace N_AppComponent
|
||||
{
|
||||
class PRTCNVLIB_EXPORT AppComponent : public N_AppComponent::IAppComponent
|
||||
{
|
||||
public:
|
||||
AppComponent() = default;
|
||||
~AppComponent() = default;
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
int32_t deinit() override;
|
||||
int32_t destroy() override;
|
||||
int32_t init() override;
|
||||
int32_t start() override;
|
||||
int32_t stop() override;
|
||||
|
||||
protected:
|
||||
void addChild(IAppComponent* a_child, const QString& a_name = QString());
|
||||
IAppComponent* getChild(const QString& a_name);
|
||||
private:
|
||||
std::vector<IAppComponent*> m_Childs;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
|
||||
#ADD_SUBDIRECTORY(TetraTestServer)
|
||||
ADD_SUBDIRECTORY(LastTest)
|
||||
ADD_SUBDIRECTORY(LastTestAuswertung)
|
||||
|
||||
SET(PrtCnv_SRCS
|
||||
logger.h
|
||||
logger.cpp
|
||||
|
||||
AppComponent.cpp
|
||||
AppComponent.hpp
|
||||
IAppComponent.hpp
|
||||
PrtCnvApplication.cpp
|
||||
PrtCnvApplication.hpp
|
||||
PrtCnvBroadcastMgr.cpp
|
||||
PrtCnvBroadcastMgr.hpp
|
||||
PrtCnvCentralThread.cpp
|
||||
PrtCnvCentralThread.hpp
|
||||
PrtCnvDbThread.cpp
|
||||
PrtCnvDbThread.hpp
|
||||
PrtCnvInjectionSocket.cpp
|
||||
PrtCnvInjectionSocket.hpp
|
||||
PrtCnvInjectionThread.cpp
|
||||
PrtCnvInjectionThread.hpp
|
||||
PrtCnvMember.cpp
|
||||
PrtCnvMember.hpp
|
||||
PrtCnvRadioThread.cpp
|
||||
PrtCnvRadioThread.hpp
|
||||
PrtCnvRadioTrafficThread.cpp
|
||||
PrtCnvRadioTrafficThread.hpp
|
||||
PrtCnvSocketThread.cpp
|
||||
PrtCnvSocketThread.hpp
|
||||
PrtCnvStoredCentralTelegram.hpp
|
||||
PrtCnvStoredRadioTelegram.hpp
|
||||
PrtCnvStoredTelegram.hpp
|
||||
PrtCnvTelegramLogThread.cpp
|
||||
PrtCnvTelegramLogThread.hpp
|
||||
PrtCnvThread.cpp
|
||||
PrtCnvThread.hpp
|
||||
PrtCnvThreadStarter.cpp
|
||||
PrtCnvThreadStarter.hpp
|
||||
)
|
||||
|
||||
NGL_ADD_LIBRARY2(AAR SV PrtCnvLib SHARED ${PrtCnv_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(PrtCnvLib
|
||||
PRIVATE
|
||||
ngldb
|
||||
nglsettings
|
||||
LtsUtilsCoreHeaderOnly
|
||||
Qt5::Sql
|
||||
Qt5::WebSockets
|
||||
PUBLIC
|
||||
CoPrtCnv
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
NGLLogger
|
||||
)
|
||||
NGL_TARGET_INCLUDE_DIRECTORIES(PrtCnvLib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../
|
||||
)
|
||||
|
||||
NGL_ADD_EXECUTABLE(AAR SV PrtCnv PrtCnvMain.cpp)
|
||||
NGL_TARGET_LINK_LIBRARIES(PrtCnv
|
||||
PrtCnvLib
|
||||
Qt5::Core
|
||||
LtsCommandLineParser
|
||||
nglcoreapplication
|
||||
)
|
||||
NGL_INSTALL_TARGET(PrtCnv)
|
||||
NGL_INSTALL(AAR SV FILES log4cxx_prtcnv.props DESTINATION bin)
|
||||
NGL_INSTALL(AAR SV FILES PrtCnv.dat.tts DESTINATION bin)
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace NGL {
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace N_AppComponent
|
||||
{
|
||||
class IAppComponent
|
||||
{
|
||||
public:
|
||||
IAppComponent() = default;
|
||||
virtual ~IAppComponent() = default;
|
||||
|
||||
virtual int32_t create(NGL::Settings *settings) = 0;
|
||||
|
||||
|
||||
virtual int32_t deinit() = 0;
|
||||
|
||||
|
||||
virtual int32_t destroy() = 0;
|
||||
|
||||
|
||||
virtual int32_t init() = 0;
|
||||
|
||||
|
||||
virtual int32_t start() = 0;
|
||||
|
||||
|
||||
virtual int32_t stop() = 0;
|
||||
|
||||
|
||||
inline const QString& getName() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
inline void setName(const QString& newVal)
|
||||
{
|
||||
m_Name = newVal;
|
||||
}
|
||||
protected:
|
||||
QString m_Name;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(my_SRCS
|
||||
main.cpp
|
||||
tetra_if.cpp
|
||||
tetra_if.h
|
||||
)
|
||||
|
||||
NGL_ADD_EXECUTABLE(AAR SV PrtCnvLastTest ${my_SRCS})
|
||||
NGL_TARGET_LINK_LIBRARIES(PrtCnvLastTest
|
||||
Qt5::Core
|
||||
Qt5::Network
|
||||
CoPrtCnv
|
||||
NGLLogger
|
||||
ngltools
|
||||
)
|
||||
NGL_INSTALL_TARGET(PrtCnvLastTest)
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QStringList>
|
||||
#include <QHostAddress>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "tetra_if.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
|
||||
NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx_tetra.props", false);
|
||||
|
||||
NGL::INGLLogger &logger(NGL::NGLLoggerBootstrap::getNGLRootLogger());
|
||||
|
||||
const QStringList args = app.arguments().mid(1);
|
||||
if (args.count() < 4)
|
||||
TetraIF::usage();
|
||||
QHostAddress tetra(args[0]);
|
||||
if (tetra.isNull())
|
||||
{
|
||||
ERROR_LOG(logger) << "Invalid IP address " << args[0];
|
||||
TetraIF::usage();
|
||||
}
|
||||
bool bOk;
|
||||
unsigned short port = args[1].toUShort(&bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
ERROR_LOG(logger) << "Invalid IP port " << args[1];
|
||||
TetraIF::usage();
|
||||
}
|
||||
unsigned short issi = args[2].toUShort(&bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
ERROR_LOG(logger) << "Invalid ISSI id " << args[2];
|
||||
TetraIF::usage();
|
||||
}
|
||||
const QString mode = args[3].toLower();
|
||||
if (mode != QLatin1String("listen") &&
|
||||
mode != QLatin1String("send_random") &&
|
||||
mode != QLatin1String("send_file"))
|
||||
{
|
||||
ERROR_LOG(logger) << "Invalid mode " << args[3];
|
||||
TetraIF::usage();
|
||||
}
|
||||
TetraIF t(tetra, port, issi);
|
||||
if (mode == QLatin1String("listen"))
|
||||
t.listenMode(args.mid(4));
|
||||
else if (mode == QLatin1String("send_random"))
|
||||
t.randomMode(args.mid(4));
|
||||
else if (mode == QLatin1String("send_file"))
|
||||
t.fileMode(args.mid(4));
|
||||
|
||||
t.start();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
|
||||
|
||||
#include "tetra_if.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QNetworkInterface>
|
||||
#include <QStringList>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvHexDumpHelper.hpp>
|
||||
#include <PrtCnv/PrtCnvRadioThread.hpp>
|
||||
#include <PrtCnv/PrtCnvThreadStarter.hpp>
|
||||
|
||||
TetraIF::TetraIF(const QHostAddress &addr, unsigned short port, unsigned short issi)
|
||||
: QObject()
|
||||
, m_addr(addr)
|
||||
, m_port(port)
|
||||
, m_issi(issi)
|
||||
, m_socket(new QTcpSocket(this))
|
||||
, m_count(0)
|
||||
, m_telegramsPerSecond(0)
|
||||
, m_telegramsInCurSecond(0)
|
||||
, m_secondFragment(0)
|
||||
, m_timer(nullptr)
|
||||
, m_SendCounter(0)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger())
|
||||
|
||||
{
|
||||
connect(m_socket, &QTcpSocket::connected, this, &TetraIF::onConnected);
|
||||
connect(m_socket, &QTcpSocket::disconnected, this, &TetraIF::onDisconnected);
|
||||
connect(m_socket, &QTcpSocket::readyRead, this, &TetraIF::onReadyRead);
|
||||
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &TetraIF::onError);
|
||||
|
||||
m_outputDir = QCoreApplication::applicationDirPath();
|
||||
m_outputDir += QLatin1Char('/') + QString::number(m_issi) + QLatin1Char('-');
|
||||
}
|
||||
|
||||
void TetraIF::start()
|
||||
{
|
||||
m_socket->connectToHost(m_addr, m_port);
|
||||
}
|
||||
|
||||
void TetraIF::listenMode(const QStringList &args)
|
||||
{
|
||||
|
||||
if (args.count() > 1)
|
||||
usage();
|
||||
m_outputDir = QCoreApplication::applicationDirPath();
|
||||
if (args.count() == 1)
|
||||
m_outputDir = args[0];
|
||||
if (!QDir().mkpath(m_outputDir))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error creating directory "
|
||||
<< m_outputDir;
|
||||
usage();
|
||||
}
|
||||
|
||||
m_outputDir += QLatin1Char('/') + QString::number(m_issi) + QLatin1Char('-');
|
||||
}
|
||||
|
||||
void TetraIF::randomMode(const QStringList &args)
|
||||
{
|
||||
if (args.count() < 3)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid argument count";
|
||||
usage();
|
||||
}
|
||||
bool bOk;
|
||||
int dataSize = args[0].toInt(&bOk);
|
||||
if (!bOk || dataSize > 256 || dataSize < 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid data size " << args[0];
|
||||
usage();
|
||||
}
|
||||
m_count = args[1].toInt(&bOk);
|
||||
if (!bOk || m_count == 0 || m_count < -1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid count " << args[1];
|
||||
usage();
|
||||
}
|
||||
m_telegramsPerSecond = args[2].toInt(&bOk);
|
||||
if (!bOk || m_telegramsPerSecond < 1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid telegrams/second " << args[2];
|
||||
usage();
|
||||
}
|
||||
m_data.reserve(dataSize);
|
||||
for (int i = 0; i < dataSize; ++i)
|
||||
m_data += (i % 256);
|
||||
|
||||
parseISSIs(args.mid(3));
|
||||
|
||||
INFO_LOG(m_logger) << "Sending " << m_data.size()
|
||||
<< " bytes, " << m_telegramsPerSecond
|
||||
<< " telegrams/second for " << m_count
|
||||
<< " time(s) to issi(s) " << getIssisAsString();
|
||||
|
||||
setupTimer();
|
||||
}
|
||||
|
||||
void TetraIF::fileMode(const QStringList &args)
|
||||
{
|
||||
if (args.count() < 3)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid argument count";
|
||||
usage();
|
||||
}
|
||||
QFile f(args[0]);
|
||||
if (!f.exists() || f.size() > 256)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid file: " << args[0];
|
||||
usage();
|
||||
}
|
||||
bool bOk;
|
||||
m_count = args[1].toInt(&bOk);
|
||||
if (!bOk || m_count == 0 || m_count < -1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid count " << args[1];
|
||||
usage();
|
||||
}
|
||||
m_telegramsPerSecond = args[2].toInt(&bOk);
|
||||
if (!bOk || m_telegramsPerSecond < 1)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid telegrams/second " << args[2];
|
||||
usage();
|
||||
}
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error opening file "
|
||||
<< f.fileName()
|
||||
<< " for reading - error: "
|
||||
<< f.errorString();
|
||||
}
|
||||
m_data = f.readAll();
|
||||
|
||||
parseISSIs(args.mid(3));
|
||||
|
||||
INFO_LOG(m_logger) << "Sending " << m_data.size()
|
||||
<< " bytes, " << m_telegramsPerSecond
|
||||
<< " telegrams/second for " << m_count
|
||||
<< " time(s) to issi(s) " << getIssisAsString();
|
||||
|
||||
setupTimer();
|
||||
}
|
||||
|
||||
void TetraIF::usage()
|
||||
{
|
||||
NGL::INGLLogger &logger(NGL::NGLLoggerBootstrap::getNGLRootLogger());
|
||||
ERROR_LOG(logger) << "Usage : " << QCoreApplication::applicationFilePath() << " <ip> <port> <src_issi> <mode> <options>";
|
||||
ERROR_LOG(logger) << " <mode> : listen|send_random|send_file";
|
||||
ERROR_LOG(logger) << " <listen> : [<output_dir>]";
|
||||
ERROR_LOG(logger) << " <send_random> : <size> <count> <telegrams_per_second_and_dest> [<dest_issi1> .. <dest_issiN>]";
|
||||
ERROR_LOG(logger) << " <send_file> : <file> <count> <telegrams_per_second_and_dest> [<dest_issi1> .. <dest_issiN>]";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void TetraIF::onConnected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Connected to " << m_socket->peerName() << ":" << m_socket->peerPort();
|
||||
doInitialLogin();
|
||||
}
|
||||
|
||||
void TetraIF::onDisconnected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Disconnected - exiting application";
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
void TetraIF::onReadyRead()
|
||||
{
|
||||
do
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Got " << m_socket->bytesAvailable() << " bytes from socket";
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader header;
|
||||
if (m_socket->bytesAvailable() < int(sizeof(header)))
|
||||
return;
|
||||
|
||||
m_socket->peek(reinterpret_cast<char *>(&header), sizeof(header));
|
||||
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::MAGIC_NUMBER != header.m_MagicNumber)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Incorrect magic number (0x"
|
||||
<< QString::number(header.m_MagicNumber, 16)
|
||||
<< " - aborting";
|
||||
exit(2);
|
||||
}
|
||||
if (!N_CoPrtCnv::CoPrtCnvRadioTelegram::isTelegramLengthOk(header.m_Length))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Radio-Telegram length to big: "
|
||||
<< header.m_Length
|
||||
<< ", but I proceed with this telegram.";
|
||||
}
|
||||
|
||||
const quint16 dataSize = header.m_Length + sizeof(header);
|
||||
if (m_socket->bytesAvailable() < dataSize)
|
||||
{
|
||||
INFO_LOG(m_logger) << "not enough bytes available - waiting for more data";
|
||||
return;
|
||||
}
|
||||
QByteArray buf(dataSize, Qt::Uninitialized);
|
||||
m_socket->read(buf.data(), dataSize);
|
||||
handleReceivedTelegram(buf);
|
||||
|
||||
}
|
||||
while (m_socket->bytesAvailable());
|
||||
}
|
||||
|
||||
void TetraIF::onError(QAbstractSocket::SocketError err)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Socket error "
|
||||
<< err
|
||||
<< " ("
|
||||
<< m_socket->errorString()
|
||||
<< ") - aborting";
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void TetraIF::doInitialLogin()
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram tele(m_issi);
|
||||
m_socket->write(tele.data());
|
||||
m_socket->flush();
|
||||
}
|
||||
|
||||
void TetraIF::handleReceivedTelegram(const QByteArray &data)
|
||||
{
|
||||
const auto type = N_CoPrtCnv::CoPrtCnvRadioTelegram::E_PDU_Type(data.at(4));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
|
||||
handleRegisterRsp(data.at(5));
|
||||
break;
|
||||
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
|
||||
handleDataIndReq(data);
|
||||
break;
|
||||
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq:
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
INFO_LOG(m_logger) << "PDU Type: " << type;
|
||||
dumpData(data);
|
||||
}
|
||||
|
||||
void TetraIF::handleRegisterRsp(const uint8_t a_registrationStatus)
|
||||
{
|
||||
QByteArray logText;
|
||||
switch (a_registrationStatus)
|
||||
{
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_IssiSuccessfulRegistered:
|
||||
INFO_LOG(m_logger) << "Valid register response from Tetra ";
|
||||
if (m_timer)
|
||||
m_timer->start();
|
||||
return;
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DuplicateISSI:
|
||||
logText = "TETRA: Register-Response: Status=Duplicate ISSI";
|
||||
break;
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_InvalidISSI:
|
||||
logText = "TETRA: Register-Response: Status=Invalid ISSI";
|
||||
break;
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_InvalidIpAddress:
|
||||
logText = "TETRA: Register-Response: Status=Invalid IP address => Configure TETRA for accepting our IP-address (";
|
||||
{
|
||||
const QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
|
||||
for (const QHostAddress &addr : ipList)
|
||||
logText += addr.toString().toUtf8() + ", ";
|
||||
logText += ")...";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logText = "Unknown registration status: " + QByteArray::number(a_registrationStatus);
|
||||
break;
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << logText;
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void TetraIF::dumpData(const QByteArray &data)
|
||||
{
|
||||
|
||||
if (m_outputDir.isEmpty())
|
||||
return;
|
||||
const qint64 msecs = QDateTime::currentMSecsSinceEpoch();
|
||||
const QString timeStr = QStringLiteral("%1.%2").arg(msecs / 1000).arg(msecs % 1000);
|
||||
{
|
||||
QFile f(m_outputDir + timeStr + QLatin1String(".dat"));
|
||||
if (!f.open(QIODevice::WriteOnly))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error opening file "
|
||||
<< f.fileName()
|
||||
<< " for writing - error: "
|
||||
<< f.errorString();
|
||||
}
|
||||
f.write(data);
|
||||
f.close();
|
||||
}
|
||||
if (data.size() > 27)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram tele(data);
|
||||
INFO_LOG(m_logger) << "Got telegram from "
|
||||
<< tele.getSrcISSI()
|
||||
<< " with size "
|
||||
<< data.size()
|
||||
<< " and payload size "
|
||||
<< tele.getPayloadSize();
|
||||
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, tele.data());
|
||||
QFile f(m_outputDir + QStringLiteral("payload-%1-%2.dat").arg(QString::number(tele.getSrcISSI()), timeStr));
|
||||
if (!f.open(QIODevice::WriteOnly))
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Error opening file "
|
||||
<< f.fileName()
|
||||
<< " for writing - error: "
|
||||
<< f.errorString();
|
||||
}
|
||||
|
||||
f.write(&data.data()[27], data.size() - 27);
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
void TetraIF::onSendData()
|
||||
{
|
||||
++m_secondFragment;
|
||||
|
||||
if (m_secondFragment == 100)
|
||||
{
|
||||
m_secondFragment = 0;
|
||||
m_telegramsInCurSecond = 0;
|
||||
}
|
||||
|
||||
int telegramsToSendNow = (m_telegramsPerSecond - m_telegramsInCurSecond) / (100 - m_secondFragment);
|
||||
NOTICE_LOG(m_logger) << "telegramsToSendNow: " << telegramsToSendNow << ", " << m_telegramsPerSecond << ", " << m_telegramsInCurSecond << ", " << m_secondFragment;
|
||||
|
||||
m_telegramsInCurSecond += telegramsToSendNow;
|
||||
static uint8_t s_counter = 1;
|
||||
static char s_randomChar = 0;
|
||||
for (int i = 0; i < telegramsToSendNow; ++i)
|
||||
{
|
||||
for (unsigned short dest_issi : qAsConst(m_issiList))
|
||||
{
|
||||
|
||||
m_data[0] = s_randomChar++;
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram tele(GEF_BESTAETIGUNG, m_data.size(), dest_issi);
|
||||
tele.setPayloadDataComplete(m_data);
|
||||
tele.setSrcISSI(m_issi);
|
||||
tele.setDestISSI(dest_issi);
|
||||
tele.setZaehler(s_counter++);
|
||||
DEBUG_LOG(m_logger) << "Sending " << tele.getSize() << " bytes to Tetra (ISSI: " << dest_issi << ")";
|
||||
m_socket->write(tele.data());
|
||||
}
|
||||
if (m_count > 0)
|
||||
{
|
||||
if (--m_count <= 0)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Finsihed";
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
}
|
||||
m_socket->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void TetraIF::parseISSIs(const QStringList &issiList)
|
||||
{
|
||||
m_issiList.clear();
|
||||
|
||||
if (issiList.count() == 0)
|
||||
{
|
||||
m_issiList += 0;
|
||||
return;
|
||||
}
|
||||
for (const QString &issiStr : issiList)
|
||||
{
|
||||
bool bOk;
|
||||
unsigned short issi = issiStr.toUShort(&bOk);
|
||||
if (!bOk)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Not a valid issi number: " << issiStr;
|
||||
usage();
|
||||
}
|
||||
m_issiList += issi;
|
||||
}
|
||||
}
|
||||
|
||||
QString TetraIF::getIssisAsString() const
|
||||
{
|
||||
QString ret;
|
||||
for (unsigned short issi : qAsConst(m_issiList))
|
||||
ret += QString::number(issi) + QLatin1String(", ");
|
||||
return ret.left(ret.size() - 2);
|
||||
}
|
||||
|
||||
void TetraIF::handleDataIndReq(const QByteArray &data)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *radioTelegram = new N_CoPrtCnv::CoPrtCnvRadioTelegram(data);
|
||||
uint32_t m_HostISSI = 2;
|
||||
if (m_HostISSI == radioTelegram->getDestISSI())
|
||||
{
|
||||
|
||||
switch (radioTelegram->getFragmentStatus())
|
||||
{
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment:
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_MiddleFragment:
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment:
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_NotFragmented:
|
||||
handleLoginTelegram(radioTelegram);
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(m_logger) << "Unknown payload-status!: " << radioTelegram->getFragmentStatus();
|
||||
break;
|
||||
}
|
||||
|
||||
delete radioTelegram;
|
||||
}
|
||||
}
|
||||
|
||||
void TetraIF::handleLoginTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *a_loginTelegram)
|
||||
{
|
||||
uint16_t identity = a_loginTelegram->getIdentity();
|
||||
uint32_t issi = a_loginTelegram->getSrcISSI();
|
||||
|
||||
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginRequest == (a_loginTelegram->getPayloadDataUInt8(2) & N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginStatusMask))
|
||||
{
|
||||
bool sent = sendLoginResponse(issi);
|
||||
if (sent)
|
||||
INFO_LOG(m_logger) << "Login: New TPU with Id=" << identity << " and ISSI=" << issi << " logged in";
|
||||
else
|
||||
|
||||
INFO_LOG(m_logger) << "Login: New TPU with Id=" << identity << " and ISSI=" << issi << " is NOT logged in";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool TetraIF::sendLoginResponse(const uint32_t a_ISSI)
|
||||
{
|
||||
++m_SendCounter;
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *a_telegram = new N_CoPrtCnv::CoPrtCnvRadioTelegram(GEF_BESTAETIGUNG, 20, a_ISSI);
|
||||
a_telegram->setZaehler(m_SendCounter);
|
||||
|
||||
a_telegram->setDestISSI(a_ISSI);
|
||||
a_telegram->setSrcISSI(2);
|
||||
gef_telegramm_id_et telId = a_telegram->isNormalTelegram() ? a_telegram->getTelID() : GEF_BESTAETIGUNG;
|
||||
|
||||
|
||||
INFO_LOG(m_logger) << "Send telegram to radio: telId=" << telId << " wcounter=" << a_telegram->zaehler() << " size= 28";
|
||||
|
||||
|
||||
|
||||
a_telegram->setProtocolIdentifier(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_TDSMessageWithoutSDS_TL);
|
||||
a_telegram->setPayloadData(0x01);
|
||||
a_telegram->setPayloadData(0x04);
|
||||
|
||||
a_telegram->getData()[22] = 0x0d;
|
||||
|
||||
char *data = a_telegram->getData();
|
||||
|
||||
|
||||
data[02] = 0x18;
|
||||
|
||||
data[22] = 0x0d;
|
||||
data[23] = 0x00;
|
||||
|
||||
data[24] = 0x02;
|
||||
data[25] = 0x00;
|
||||
|
||||
data[13] = 0x00;
|
||||
data[26] = 0x01;
|
||||
data[27] = 0x40;
|
||||
|
||||
m_socket->write(data, 28);
|
||||
delete a_telegram;
|
||||
return m_socket->flush();
|
||||
|
||||
}
|
||||
|
||||
void TetraIF::setupTimer()
|
||||
{
|
||||
delete m_timer;
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &TetraIF::onSendData);
|
||||
m_timer->setInterval(10);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QByteArray>
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
|
||||
class QTcpSocket;
|
||||
class QTimer;
|
||||
class TetraIF : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TetraIF(const QHostAddress &addr, unsigned short port, unsigned short issi);
|
||||
|
||||
void start();
|
||||
void listenMode(const QStringList &args);
|
||||
void randomMode(const QStringList &args);
|
||||
void fileMode(const QStringList &args);
|
||||
|
||||
static void usage();
|
||||
protected Q_SLOTS:
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
void onReadyRead();
|
||||
void onError(QAbstractSocket::SocketError);
|
||||
void onSendData();
|
||||
protected:
|
||||
void doInitialLogin();
|
||||
void handleReceivedTelegram(const QByteArray &data);
|
||||
void handleRegisterRsp(const uint8_t a_registrationStatus);
|
||||
void dumpData(const QByteArray &data);
|
||||
|
||||
void parseISSIs(const QStringList &issiList);
|
||||
void handleDataIndReq(const QByteArray &data);
|
||||
void handleLoginTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *a_loginTelegram);
|
||||
bool sendLoginResponse(const uint32_t a_ISSI);
|
||||
QString getIssisAsString() const;
|
||||
|
||||
void setupTimer();
|
||||
|
||||
protected:
|
||||
QHostAddress m_addr;
|
||||
unsigned short m_port;
|
||||
unsigned short m_issi;
|
||||
QTcpSocket *m_socket;
|
||||
int m_count;
|
||||
int m_telegramsPerSecond;
|
||||
int m_telegramsInCurSecond;
|
||||
int m_secondFragment;
|
||||
QByteArray m_data;
|
||||
QList<unsigned short> m_issiList;
|
||||
QTimer *m_timer;
|
||||
uint8_t m_SendCounter;
|
||||
|
||||
QString m_outputDir;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
|
||||
SET(lta_SRCS
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
)
|
||||
|
||||
SET(lta_HDRS
|
||||
mainwindow.h
|
||||
)
|
||||
|
||||
SET(lta_UICS
|
||||
mainwindow.ui
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(lta_SRCS ${lta_UICS})
|
||||
NGL_ADD_EXECUTABLE(AAR SV NO_TRANSLATION LastTestAuswertung ${lta_SRCS} ${lta_HDRS} ${lta_UICS})
|
||||
NGL_TARGET_LINK_LIBRARIES(LastTestAuswertung
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2011-05-13T08:20:33
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
TARGET = LastTestAuswertung
|
||||
TEMPLATE = app
|
||||
|
||||
|
||||
SOURCES += main.cpp\
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += mainwindow.h
|
||||
|
||||
FORMS += mainwindow.ui
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <QApplication>
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include <QFileDialog>
|
||||
#include <QFile>
|
||||
#include <cmath>
|
||||
#include <QIntValidator>
|
||||
#include <QList>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
init();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
m_files.clear();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::changeEvent(QEvent *e)
|
||||
{
|
||||
QMainWindow::changeEvent(e);
|
||||
switch (e->type())
|
||||
{
|
||||
case QEvent::LanguageChange:
|
||||
ui->retranslateUi(this);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::init()
|
||||
{
|
||||
|
||||
connect(ui->pushButtonOrdnerFinden, &QPushButton::clicked, this, &MainWindow::slot_findFolder);
|
||||
connect(ui->pushButtonAnalysieren, &QPushButton::clicked, this, &MainWindow::slot_analysieren);
|
||||
|
||||
ui->progressBar->setValue(0);
|
||||
ui->labelResult->setText(QString());
|
||||
QIntValidator intVal;
|
||||
intVal.setBottom(0);
|
||||
intVal.setTop(100000);
|
||||
ui->lineEditISSI->setValidator(&intVal);
|
||||
}
|
||||
|
||||
void MainWindow::slot_findFolder()
|
||||
{
|
||||
|
||||
|
||||
|
||||
QString name = QFileDialog::getExistingDirectory(this, tr("Choose folder"), QDir::homePath(), QFileDialog::DontResolveSymlinks);
|
||||
ui->lineEditOrdner->setText(name);
|
||||
ui->labelResult->setText(QString());
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::slot_analysieren()
|
||||
{
|
||||
ui->labelResult->setText(QString());
|
||||
QString ISSI = ui->lineEditISSI->text();
|
||||
if (ISSI.isEmpty())
|
||||
ISSI = QLatin1String("1");
|
||||
m_logfileList.clear();
|
||||
|
||||
|
||||
QDir folder(ui->lineEditOrdner->text());
|
||||
m_files = folder.entryList();
|
||||
folder.~QDir();
|
||||
ui->progressBar->setValue(1);
|
||||
const QString paytext = ISSI + QLatin1String("-payload");
|
||||
const QString issitext = ISSI + QLatin1String("-1");
|
||||
for (int i = 0; i < m_files.count(); i++)
|
||||
{
|
||||
|
||||
QString name = m_files[i];
|
||||
if (name.contains(paytext))
|
||||
{
|
||||
m_files.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!name.contains(QLatin1String(".dat")))
|
||||
{
|
||||
m_files.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!name.contains(issitext))
|
||||
{
|
||||
m_files.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui->progressBar->setValue(10);
|
||||
if (m_files.count() == 0)
|
||||
ui->labelResult->setText(tr("no logfiles found"));
|
||||
else
|
||||
{
|
||||
openLogfile(ui->lineEditOrdner->text(), m_files.count());
|
||||
ui->progressBar->setValue(12);
|
||||
ui->progressBar->repaint();
|
||||
readFiles();
|
||||
ui->progressBar->setValue(90);
|
||||
ui->progressBar->repaint();
|
||||
QString ausgabetext = QLatin1String("\tANALYSIS COMPLETED - ") + QString::number(auswerten()) + QLatin1String(" missing messages");
|
||||
ui->labelResult->setText(ausgabetext);
|
||||
}
|
||||
ui->progressBar->setValue(100);
|
||||
ui->progressBar->repaint();
|
||||
}
|
||||
|
||||
void MainWindow::readFiles()
|
||||
{
|
||||
for (int j = 0; j < m_files.count(); j++)
|
||||
{
|
||||
QString filename = ui->lineEditOrdner->text();
|
||||
filename += QLatin1Char('/');
|
||||
filename += m_files[j];
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
|
||||
QByteArray ar = file.read(150);
|
||||
file.close();
|
||||
if (ar.count() > 36)
|
||||
{
|
||||
QString issi = getISSI(ar);
|
||||
QByteArray bytes = ar;
|
||||
|
||||
|
||||
bytes.remove(0, 34);
|
||||
bytes.remove(2, 150);
|
||||
uint nr = bytes[0] & 255;
|
||||
nr *= 256;
|
||||
nr += bytes[1] & 255;
|
||||
|
||||
|
||||
bytes = ar;
|
||||
bytes.remove(0, 30);
|
||||
bytes.remove(4, 150);
|
||||
|
||||
const QByteArray test = bytes.toHex();
|
||||
qint64 zeit = test.toLongLong(nullptr, 16);
|
||||
|
||||
const QDateTime timeStart(QDateTime::fromMSecsSinceEpoch(zeit * 1000));
|
||||
|
||||
writeToLogfile(issi, timeStart, getTimestamp(m_files[j]), m_files[j], nr);
|
||||
writeLogZeiten(timeStart, getTimestamp(m_files[j]), issi);
|
||||
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
double val = 70;
|
||||
val /= m_files.count();
|
||||
val *= (j + 1);
|
||||
val += 20;
|
||||
ui->progressBar->setValue(int(val));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::openLogfile(const QString &folder, int count)
|
||||
{
|
||||
m_logfile = folder;
|
||||
m_logZeiten = folder;
|
||||
m_logfile += QLatin1String("/logfile_analysis.txt");
|
||||
QFile file(m_logfile);
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
const QString msg = QStringLiteral("There are %1 logfiles evaluated.\nISSI SENDING TIME RECIEVING TIME FILENAME NO \n").arg(count);
|
||||
file.write(msg.toUtf8());
|
||||
file.close();
|
||||
}
|
||||
m_logZeiten += QLatin1String("/transmission_times.csv");
|
||||
file.setFileName(m_logZeiten);
|
||||
if (file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
file.write("ISSI;Sending Time ; Receiving Time\n");
|
||||
file.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::writeToLogfile(const QString &issi,
|
||||
const QDateTime ×tamp,
|
||||
const QString &filename,
|
||||
uint lfdNr,
|
||||
const QString &bytes)
|
||||
{
|
||||
QString text = issi;
|
||||
text += QLatin1String(": ");
|
||||
text += timestamp.toString(QLatin1String("dd.MM.yyyy hh:mm:ss"));
|
||||
text += QLatin1String(" - ");
|
||||
text += filename;
|
||||
text += QLatin1String(" -> ");
|
||||
text += bytes;
|
||||
text += QLatin1String(" --> ");
|
||||
text += QString::number(lfdNr, 10);
|
||||
text += QLatin1Char('\n');
|
||||
QFile file(m_logfile);
|
||||
if (file.open(QIODevice::Append | QIODevice::Text))
|
||||
{
|
||||
file.write(text.toLatin1());
|
||||
file.close();
|
||||
}
|
||||
LogfileInfo info;
|
||||
info.timestamp = timestamp;
|
||||
info.issi = issi;
|
||||
info.lfdNr = lfdNr;
|
||||
m_logfileList.append(info);
|
||||
}
|
||||
|
||||
void MainWindow::writeToLogfile(const QString &issi,
|
||||
const QDateTime &sendeZeit,
|
||||
const QDateTime ×tamp,
|
||||
const QString &filename,
|
||||
uint lfdNr)
|
||||
{
|
||||
QString text = issi;
|
||||
text += QLatin1String(": ");
|
||||
text += sendeZeit.toString(QLatin1String("hh:mm:ss"));
|
||||
text += QLatin1String(" --> ");
|
||||
text += timestamp.toString(QLatin1String("dd.MM.yyyy hh:mm:ss"));
|
||||
text += QLatin1String(" - ");
|
||||
text += filename;
|
||||
text += QLatin1String(" --> ");
|
||||
text += QString::number(lfdNr, 10);
|
||||
text += QLatin1Char('\n');
|
||||
QFile file(m_logfile);
|
||||
if (file.open(QIODevice::Append | QIODevice::Text))
|
||||
{
|
||||
file.write(text.toLatin1());
|
||||
file.close();
|
||||
}
|
||||
LogfileInfo info;
|
||||
info.timestamp = timestamp;
|
||||
info.issi = issi;
|
||||
info.lfdNr = lfdNr;
|
||||
m_logfileList.append(info);
|
||||
}
|
||||
|
||||
QDateTime MainWindow::getTimestamp(const QString &filename) const
|
||||
{
|
||||
const auto list1 = filename.splitRef(QLatin1Char('-'));
|
||||
if (list1.size() < 2)
|
||||
return {};
|
||||
|
||||
const QStringRef &fn = list1.at(1);
|
||||
const auto list2 = fn.split(QLatin1Char('.'));
|
||||
auto str = list2.at(0).toString();
|
||||
str.remove(10, 10);
|
||||
|
||||
return QDateTime::fromMSecsSinceEpoch(str.toLongLong() * 1000);
|
||||
}
|
||||
|
||||
QString MainWindow::getISSI(QByteArray ar)
|
||||
{
|
||||
|
||||
ar.remove(0, 5);
|
||||
ar.remove(4, 200);
|
||||
QByteArray issi = ar.toHex();
|
||||
|
||||
if (ar.count() == 4)
|
||||
{
|
||||
if (ar[2] == '\0' && ar[3] == '\0')
|
||||
{
|
||||
ar.remove(2, 2);
|
||||
issi = ar.toHex();
|
||||
int hex[4];
|
||||
for (int i = 3; i >= 0; i--)
|
||||
{
|
||||
char c = issi.at(i);
|
||||
if (c >= '0' && c <= '9')
|
||||
hex[i] = c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
hex[i] = c - 'a' + 10;
|
||||
|
||||
}
|
||||
int sum = 0;
|
||||
sum += hex[1];
|
||||
sum += hex[0] * 16;
|
||||
sum += hex[3] * 256;
|
||||
sum += hex[2] * 4096;
|
||||
issi = QByteArray::number(sum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return QString::fromLatin1(issi);
|
||||
}
|
||||
|
||||
int MainWindow::auswerten()
|
||||
{
|
||||
|
||||
|
||||
QFile f(m_logfile);
|
||||
if (!f.open(QIODevice::Append | QIODevice::Text))
|
||||
return -1;
|
||||
f.write("***************************RESULT************************************\n");
|
||||
QSet<QString> issis;
|
||||
issis.reserve(100);
|
||||
for (int i = 0; i < m_logfileList.count(); i++)
|
||||
{
|
||||
|
||||
issis += m_logfileList.at(i).issi;
|
||||
}
|
||||
ui->progressBar->setValue(94);
|
||||
ui->progressBar->repaint();
|
||||
int fehlcount = 0;
|
||||
QVector<uint> numList;
|
||||
int i = 0;
|
||||
for (const QString &issi : qAsConst(issis))
|
||||
{
|
||||
|
||||
numList.clear();
|
||||
for (int j = 0; j < m_logfileList.count(); j++)
|
||||
{
|
||||
if (issi == m_logfileList.at(j).issi)
|
||||
numList.append(m_logfileList.at(j).lfdNr);
|
||||
}
|
||||
QByteArray text = issi.toLatin1();
|
||||
text += ":\t";
|
||||
text += QByteArray::number(numList.count());
|
||||
text += " messages transmitted\n";
|
||||
text += "\t";
|
||||
f.write(text);
|
||||
|
||||
fehlcount += fehlendeNumsEintragen(numList, f);
|
||||
|
||||
|
||||
double val = 5;
|
||||
val /= issis.count();
|
||||
val *= (i + 1);
|
||||
val += 94;
|
||||
ui->progressBar->setValue(qRound(val));
|
||||
ui->progressBar->repaint();
|
||||
|
||||
++i;
|
||||
}
|
||||
return fehlcount;
|
||||
}
|
||||
|
||||
int MainWindow::fehlendeNumsEintragen(const QVector<uint> &numList, QFile &f)
|
||||
{
|
||||
|
||||
QVector<uint> fehlendeNums = getFehlendeNums(numList);
|
||||
QByteArray text = QByteArray::number(fehlendeNums.count());
|
||||
text += " lost messages\n";
|
||||
text += "\t\t following messages are missing (";
|
||||
for (int j = 0; j < fehlendeNums.count(); j++)
|
||||
{
|
||||
text += QByteArray::number(fehlendeNums.at(j));
|
||||
text += ", ";
|
||||
}
|
||||
|
||||
text += ")\n";
|
||||
|
||||
f.write(text);
|
||||
|
||||
return fehlendeNums.count();
|
||||
}
|
||||
|
||||
QVector<uint> MainWindow::getFehlendeNums(const QVector<uint> &numsDurchgang)
|
||||
{
|
||||
|
||||
uint count = numsDurchgang.count();
|
||||
uint maxnum = 0;
|
||||
uint minnum = 999999;
|
||||
for (uint k = 0; k < count; k++)
|
||||
{
|
||||
|
||||
if (numsDurchgang.at(k) > maxnum)
|
||||
maxnum = numsDurchgang.at(k);
|
||||
if (numsDurchgang.at(k) < minnum)
|
||||
minnum = numsDurchgang.at(k);
|
||||
}
|
||||
|
||||
QVector<uint> fehlendeNum;
|
||||
for (uint z = minnum; z <= maxnum; z++)
|
||||
{
|
||||
|
||||
bool vorhanden = false;
|
||||
for (uint j = 0; j < count; j++)
|
||||
{
|
||||
if (numsDurchgang.at(j) == z)
|
||||
{
|
||||
vorhanden = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!vorhanden)
|
||||
fehlendeNum.append(z);
|
||||
}
|
||||
return fehlendeNum;
|
||||
}
|
||||
|
||||
void MainWindow::writeLogZeiten(const QDateTime &beginn, const QDateTime &end, const QString &issi)
|
||||
{
|
||||
QString msg = issi;
|
||||
msg += QLatin1Char(';');
|
||||
msg += beginn.toString(QLatin1String("hh:mm:ss"));
|
||||
msg += QLatin1Char(';');
|
||||
msg += end.toString(QLatin1String("hh:mm:ss"));
|
||||
msg += QLatin1Char('\n');
|
||||
QFile file(m_logZeiten);
|
||||
file.open(QIODevice::Append | QIODevice::Text);
|
||||
file.write(msg.toUtf8());
|
||||
file.close();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
struct LogfileInfo
|
||||
{
|
||||
QString issi;
|
||||
QDateTime timestamp;
|
||||
uint lfdNr;
|
||||
};
|
||||
struct ZeitNum
|
||||
{
|
||||
uint zeit;
|
||||
int num;
|
||||
};
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
protected:
|
||||
void changeEvent(QEvent *e);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
void init();
|
||||
void readFiles();
|
||||
QStringList m_files;
|
||||
QDateTime getTimestamp(const QString &filename) const;
|
||||
QString m_logfile;
|
||||
QString m_logZeiten;
|
||||
void openLogfile(const QString &folder, int count);
|
||||
void writeToLogfile(const QString &issi,
|
||||
const QDateTime ×tamp,
|
||||
const QString &filename,
|
||||
uint lfdNr,
|
||||
const QString &bytes);
|
||||
void writeToLogfile(const QString &issi,
|
||||
const QDateTime &sendeZeit,
|
||||
const QDateTime ×tamp,
|
||||
const QString &filename,
|
||||
uint lfdNr);
|
||||
QString getISSI(QByteArray ar);
|
||||
int auswerten();
|
||||
QVector<uint> getFehlendeNums(const QVector<uint> &numsDurchgang);
|
||||
int fehlendeNumsEintragen(const QVector<uint> &numList, QFile &file);
|
||||
void writeLogZeiten(const QDateTime &beginn, const QDateTime &end, const QString &issi);
|
||||
|
||||
QVector<LogfileInfo> m_logfileList;
|
||||
|
||||
private slots:
|
||||
void slot_findFolder();
|
||||
void slot_analysieren();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>521</width>
|
||||
<height>234</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Logfiles analysieren</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
<widget class="QWidget" name="gridLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>80</y>
|
||||
<width>501</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="pushButtonAnalysieren">
|
||||
<property name="text">
|
||||
<string>analyse logfiles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelResult">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>9</x>
|
||||
<y>0</y>
|
||||
<width>501</width>
|
||||
<height>65</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditOrdner"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonOrdnerFinden">
|
||||
<property name="text">
|
||||
<string>choose folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEditISSI"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">ISSI by the central<br />(if no ISSI is entered, ISSI 1 will be used)</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menuBar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>521</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QToolBar" name="mainToolBar">
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,90 @@
|
||||
[Common]
|
||||
LogCentralTelegrams=1
|
||||
LogRadioTelegrams=1
|
||||
LogTelegramPath=logdata
|
||||
LogTelegramPrefix=PrtCnv_
|
||||
; virtual TeilnehmerId of protocol-converter
|
||||
VirtualIdPrtCnv=90
|
||||
|
||||
[Db]
|
||||
DatabaseFileName=db/telegrams.db
|
||||
|
||||
[Central]
|
||||
UseSocketsInsteadFiles=1
|
||||
HostName=ECCORSV01
|
||||
Port=9600
|
||||
; Milliseconds
|
||||
AckTimeout=900
|
||||
ConnectTimeout=2000
|
||||
DisconnectTimeout=10000
|
||||
AliveTimeout=3000
|
||||
; nbr of unsuccessful send-attempts, before telegram will be deleted without sending it
|
||||
NbrOfAttempts=5
|
||||
|
||||
[Radio]
|
||||
HostName=127.0.0.1
|
||||
Port=52010
|
||||
HostISSI=9999
|
||||
; Seconds
|
||||
CyclicEnvironmentTime=30
|
||||
; Milliseconds
|
||||
BroadcastISSI=5000
|
||||
ConnectTimeout=2000
|
||||
DisconnectTimeout=10000
|
||||
AliveTimeout=3000
|
||||
; 1 - UseSDS_TL
|
||||
UseSDS_TL=0
|
||||
DumpReceivedTelegrams=1
|
||||
DumpSentTelegrams=1
|
||||
; GSSIs which we would like to subscribe to - make sure to not subscribe to our own BroadcastISSI !
|
||||
ListenGroups=5001
|
||||
|
||||
[Login]
|
||||
LoginFilePathWithPrefix=login/pk_
|
||||
DoLicenseDespiteSelftestOrConfigFailure=0
|
||||
DoCheckPlayerId=1
|
||||
LowestPlayerId=500
|
||||
HighestPlayerId=9999
|
||||
PlayerIdFilePath=playerIds.dat
|
||||
|
||||
[Broadcast]
|
||||
; In seconds, delay between sending 1st and 2nd broadcast
|
||||
BroadcastDelay2=10
|
||||
; In seconds, delay between sending 2nd and 3rd broadcast
|
||||
BroadcastDelay3=20
|
||||
; the time to wait for NachforderungBroadcast from TNEs, before begin sending the broadcasts
|
||||
ClaimWindow=30
|
||||
; factor for extend the claimwindow when we have full load in downlink
|
||||
FullLoadFactor=3
|
||||
; if less TNEs ask for broadcasts, they will be send directed
|
||||
BroadcastLimit=10
|
||||
; the time to wait before sending next broadcast in milliseconds
|
||||
SendGap=500
|
||||
|
||||
[TrafficControl]
|
||||
; from PrtCnv to Tetra (Telegram-Counting)
|
||||
; Number of telegrams (0..255) reported by air-interface-report telegram when we have high load
|
||||
SendQueueHighLoad=150
|
||||
; Number of telegrams that will be sent per second, when we have high-load
|
||||
SendTelegramsHighLoad=50
|
||||
; Number of telegrams (0..255) reported by air-interface-report telegram when we have full load
|
||||
SendQueueFullLoad=200
|
||||
; Number of telegrams that will be sent per second, when we have full-load
|
||||
SendTelegramsFullLoad=10
|
||||
; from Tetra to PrtCnv (Byte-Counting)
|
||||
; high load in percent (0 .. 100)
|
||||
ReceiveQueueHighLoad=50
|
||||
; full load in percent (ReceiveQueueHighLoad .. 100)
|
||||
ReceiveQueueFullLoad=75
|
||||
; hysteresis in percent (0 .. 10)
|
||||
ReceiveQueueHysteresis=5
|
||||
; Averaging time in seconds
|
||||
ReceiveAveragingTime=5
|
||||
; Throughput in KB/s per Transceiver
|
||||
ReceiveThroughput=3.6
|
||||
; Transceiver count
|
||||
TransceiverCount=6
|
||||
|
||||
[Injection]
|
||||
Enable=true
|
||||
Port=46666
|
||||
@@ -0,0 +1,410 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvApplication.hpp"
|
||||
#include "PrtCnvThreadStarter.hpp"
|
||||
#include "PrtCnvThread.hpp"
|
||||
#include "PrtCnvCentralThread.hpp"
|
||||
#include "PrtCnvTelegramLogThread.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
static std::string threadIdToString(PrtCnvApplication::E_ThreadId id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case PrtCnvApplication::e_Central:
|
||||
return "Central";
|
||||
case PrtCnvApplication::e_Radio:
|
||||
return "Radio";
|
||||
case PrtCnvApplication::e_Db:
|
||||
return "Db";
|
||||
case PrtCnvApplication::e_Log:
|
||||
return "Log";
|
||||
case PrtCnvApplication::e_Injection:
|
||||
return "Injection";
|
||||
case PrtCnvApplication::e_RadioTraffic:
|
||||
return "RadioTraffic";
|
||||
case PrtCnvApplication::e_ThreadIdMax:
|
||||
break;
|
||||
}
|
||||
return std::to_string(id);
|
||||
}
|
||||
|
||||
PrtCnvApplication::PrtCnvApplication()
|
||||
: m_Config(nullptr)
|
||||
, m_exerciseId(0)
|
||||
, m_accessMutex(QMutex::Recursive)
|
||||
, m_logger(getLogger("PrtCnvApplication"))
|
||||
{}
|
||||
|
||||
|
||||
PrtCnvApplication::~PrtCnvApplication()
|
||||
{
|
||||
delete m_Config;
|
||||
for (const auto &ti : m_threadInfo)
|
||||
delete ti.thread;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvApplication::waitForCreate(const E_ThreadId a_threadId)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Wait for others until we can create " << threadIdToString(a_threadId);
|
||||
setConstructed(a_threadId);
|
||||
switch (a_threadId)
|
||||
{
|
||||
case e_Central:
|
||||
while (!(isConstructed(e_Db) &&
|
||||
isConstructed(e_Radio) &&
|
||||
isConstructed(e_Log) &&
|
||||
isConstructed(e_Injection)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Radio:
|
||||
while (!(isConstructed(e_Db) &&
|
||||
isConstructed(e_Central) &&
|
||||
isConstructed(e_Log) &&
|
||||
isConstructed(e_Injection)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Db:
|
||||
while (!(isConstructed(e_Radio) &&
|
||||
isConstructed(e_Central)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
INFO_LOG(m_logger) << "Now we can create " << threadIdToString(a_threadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvApplication::waitForInit(E_ThreadId a_threadId)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Wait for others until we can initialize " << threadIdToString(a_threadId);
|
||||
setCreated(a_threadId);
|
||||
switch (a_threadId)
|
||||
{
|
||||
case e_Central:
|
||||
while (!(isCreated(e_Db) &&
|
||||
isCreated(e_Radio)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Radio:
|
||||
while (!(isCreated(e_Db) &&
|
||||
isCreated(e_Central)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Db:
|
||||
while (!(isCreated(e_Radio) &&
|
||||
isCreated(e_Central)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
INFO_LOG(m_logger) << "Now we can initialize " << threadIdToString(a_threadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvApplication::waitForStart(E_ThreadId a_threadId)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Wait for others until we can start " << threadIdToString(a_threadId);
|
||||
setInitialized(a_threadId);
|
||||
switch (a_threadId)
|
||||
{
|
||||
case e_Central:
|
||||
while (!(isStarted(e_Db) &&
|
||||
isInitialized(e_Radio) &&
|
||||
isStarted(e_Log) &&
|
||||
isStarted(e_Injection)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Radio:
|
||||
while (!(isStarted(e_Db) &&
|
||||
isInitialized(e_Central) &&
|
||||
isStarted(e_Log) &&
|
||||
isStarted(e_Injection) &&
|
||||
isInitialized(e_RadioTraffic)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Db:
|
||||
while (!(isInitialized(e_Radio) &&
|
||||
isInitialized(e_Central) &&
|
||||
isStarted(e_Log)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case e_RadioTraffic:
|
||||
while (!(isInitialized(e_Radio) &&
|
||||
isStarted(e_Log)))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "wait...";
|
||||
QThread::sleep(1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
INFO_LOG(m_logger) << "Now we can start " << threadIdToString(a_threadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvApplication::waitForExec(E_ThreadId a_threadId)
|
||||
{
|
||||
setStarted(a_threadId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvApplication::startApp()
|
||||
{
|
||||
createApplication();
|
||||
init();
|
||||
start();
|
||||
}
|
||||
|
||||
void PrtCnvApplication::shutdownApp()
|
||||
{
|
||||
stop();
|
||||
deinit();
|
||||
destroy();
|
||||
for (const auto &ti : m_threadInfo)
|
||||
ti.thread->quit();
|
||||
for (const auto &ti : m_threadInfo)
|
||||
ti.thread->wait();
|
||||
for (auto &ti : m_threadInfo)
|
||||
{
|
||||
delete ti.thread;
|
||||
ti.thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PrtCnvApplication::setCleanDb(bool bCleanDb)
|
||||
{
|
||||
PrtCnvThread::m_DoCleanDb = bCleanDb;
|
||||
}
|
||||
|
||||
void PrtCnvApplication::setExerciseId(int exerciseId)
|
||||
{
|
||||
m_exerciseId = exerciseId;
|
||||
}
|
||||
|
||||
void PrtCnvApplication::setConfigFile(const QString &configFile)
|
||||
{
|
||||
m_ConfigFileName = configFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvApplication::createApplication()
|
||||
{
|
||||
setName(QStringLiteral("Common"));
|
||||
if (m_ConfigFileName.isEmpty())
|
||||
{
|
||||
m_ConfigFileName = QCoreApplication::applicationDirPath();
|
||||
if (m_exerciseId > 0)
|
||||
m_ConfigFileName += QStringLiteral("/PrtCnv_ex_") + QString::number(m_exerciseId) + QStringLiteral(".dat");
|
||||
else
|
||||
m_ConfigFileName += QStringLiteral("/PrtCnv.dat");
|
||||
}
|
||||
m_Config = new NGL::Settings(m_ConfigFileName);
|
||||
std::cout << "Use config-file: " << qPrintable(m_ConfigFileName) << std::endl;
|
||||
|
||||
for (int i = 0; i < e_ThreadIdMax; i++)
|
||||
m_threadInfo[i].thread = new PrtCnvThreadStarter((E_ThreadId)i, m_ConfigFileName, this);
|
||||
|
||||
N_AppComponent::AppComponent::create(m_Config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvApplication::start()
|
||||
{
|
||||
|
||||
for (const auto &ti : m_threadInfo)
|
||||
ti.thread->start();
|
||||
|
||||
N_AppComponent::AppComponent::start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnv::PrtCnvThreadStarter *PrtCnvApplication::getThreadStarter(const E_ThreadId a_threadId)
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return nullptr;
|
||||
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
return m_threadInfo[a_threadId].thread;
|
||||
}
|
||||
|
||||
void PrtCnvApplication::setDatabaseConnectionParams(const QString &driver,
|
||||
const QString &hostName,
|
||||
const QString &database,
|
||||
const QString &schemaName,
|
||||
const QString &userName,
|
||||
const QString &password)
|
||||
{
|
||||
m_dbDriver = driver;
|
||||
m_dbHostName = hostName;
|
||||
m_dbDatabaseName = database;
|
||||
m_dbSchemaName = schemaName;
|
||||
m_dbUserName = userName;
|
||||
m_dbPassword = password;
|
||||
}
|
||||
|
||||
void PrtCnvApplication::logWlsTelegram(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele,
|
||||
bool bIsSend)
|
||||
{
|
||||
const auto threadHandler = m_threadInfo[e_Log].thread;
|
||||
PrtCnvTelegramLogThread *t = static_cast<PrtCnvTelegramLogThread*>(threadHandler ? threadHandler->getThread() : nullptr);
|
||||
if (t)
|
||||
t->writeWlsTelegramLog(tele, bIsSend, 0);
|
||||
}
|
||||
|
||||
|
||||
bool PrtCnvApplication::isConstructed(const E_ThreadId a_threadId) const
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return false;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
return m_threadInfo[a_threadId].isConstructed;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvApplication::setConstructed(const E_ThreadId a_threadId)
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
m_threadInfo[a_threadId].isConstructed = true;
|
||||
}
|
||||
|
||||
|
||||
bool PrtCnvApplication::isInitialized(const E_ThreadId a_threadId) const
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return false;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
return m_threadInfo[a_threadId].isInitialized;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvApplication::setInitialized(const E_ThreadId a_threadId)
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
m_threadInfo[a_threadId].isInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
bool PrtCnvApplication::isCreated(const E_ThreadId a_threadId) const
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return false;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
return m_threadInfo[a_threadId].isCreated;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvApplication::setCreated(const E_ThreadId a_threadId)
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
m_threadInfo[a_threadId].isCreated = true;
|
||||
}
|
||||
|
||||
|
||||
bool PrtCnvApplication::isStarted(const E_ThreadId a_threadId) const
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return false;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
return m_threadInfo[a_threadId].isStarted;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvApplication::setStarted(const E_ThreadId a_threadId)
|
||||
{
|
||||
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
|
||||
return;
|
||||
QMutexLocker l(&m_accessMutex);
|
||||
m_threadInfo[a_threadId].isStarted = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include "AppComponent.hpp"
|
||||
#include "logger.h"
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <QtCore/QMutex>
|
||||
#include <PrtCnv/prtcnvlib_export.h>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvThreadStarter;
|
||||
|
||||
|
||||
class PRTCNVLIB_EXPORT PrtCnvApplication : public QObject, public N_AppComponent::AppComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
|
||||
|
||||
enum E_ThreadId
|
||||
{
|
||||
e_Central,
|
||||
e_Radio,
|
||||
e_Db,
|
||||
e_Log,
|
||||
e_Injection,
|
||||
e_RadioTraffic,
|
||||
e_ThreadIdMax
|
||||
};
|
||||
PrtCnvApplication();
|
||||
~PrtCnvApplication();
|
||||
|
||||
|
||||
|
||||
void startApp();
|
||||
void shutdownApp();
|
||||
static void setCleanDb(bool bCleanDb);
|
||||
void setExerciseId(int exerciseId);
|
||||
int exerciseId() const { return m_exerciseId; }
|
||||
void setConfigFile(const QString &configFile);
|
||||
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
|
||||
|
||||
void waitForInit(E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
void waitForStart(E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
void waitForCreate(const E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
void waitForExec(E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
PrtCnvThreadStarter* getThreadStarter(const E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
void setDatabaseConnectionParams(const QString &driver,
|
||||
const QString &hostName,
|
||||
const QString &database,
|
||||
const QString &schemaName,
|
||||
const QString &userName,
|
||||
const QString &password);
|
||||
QString databaseDriver() const { return m_dbDriver; }
|
||||
QString databaseHostName() const { return m_dbHostName; }
|
||||
QString databaseName() const { return m_dbDatabaseName; }
|
||||
QString databaseSchemaName() const { return m_dbSchemaName; }
|
||||
QString databaseUserName() const { return m_dbUserName; }
|
||||
QString databasePassword() const { return m_dbPassword; }
|
||||
|
||||
public slots:
|
||||
void logWlsTelegram(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool bIsSend);
|
||||
|
||||
signals:
|
||||
void textMessageFromPrtCnvToCentral(const QByteArray &ba);
|
||||
void textMessageFromCentralToPrtCnv(const QByteArray &ba);
|
||||
void outgoingBroadcastTelegramsRequest(int equipmentNumber, int broadcastIndex, int resetCounter);
|
||||
void warehouseMessageFromPtrCnv(const QByteArray &jsonData);
|
||||
protected:
|
||||
|
||||
|
||||
int32_t createApplication();
|
||||
|
||||
|
||||
|
||||
bool isConstructed(const E_ThreadId a_threadId) const;
|
||||
|
||||
|
||||
|
||||
void setConstructed(const E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
bool isInitialized(const E_ThreadId a_threadId) const;
|
||||
|
||||
|
||||
|
||||
void setInitialized(const E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
bool isCreated(const E_ThreadId a_threadId) const;
|
||||
|
||||
|
||||
|
||||
void setCreated(const E_ThreadId a_threadId);
|
||||
|
||||
|
||||
|
||||
bool isStarted(const E_ThreadId a_threadId) const;
|
||||
|
||||
|
||||
|
||||
void setStarted(const E_ThreadId a_threadId);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
NGL::Settings *m_Config;
|
||||
|
||||
struct ThreadInfo
|
||||
{
|
||||
PrtCnvThreadStarter *thread = nullptr;
|
||||
bool isConstructed = false;
|
||||
bool isCreated = false;
|
||||
bool isInitialized = false;
|
||||
bool isStarted = false;
|
||||
} m_threadInfo[e_ThreadIdMax];
|
||||
|
||||
|
||||
QString m_ConfigFileName;
|
||||
|
||||
|
||||
int m_exerciseId;
|
||||
|
||||
|
||||
|
||||
mutable QMutex m_accessMutex;
|
||||
|
||||
QString m_dbDriver;
|
||||
QString m_dbHostName;
|
||||
QString m_dbDatabaseName;
|
||||
QString m_dbSchemaName;
|
||||
QString m_dbUserName;
|
||||
QString m_dbPassword;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,359 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <CoPrtCnv/tetra_telegram_ids.h>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvRadioTelegram;
|
||||
}
|
||||
|
||||
#define MAX_BROADCAST_INDEX 65535
|
||||
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvDbThread;
|
||||
|
||||
|
||||
|
||||
class PrtCnvBroadcastMgr final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
|
||||
|
||||
enum E_State
|
||||
{
|
||||
e_ST_UNDEFINED,
|
||||
e_ST_COLLECT,
|
||||
e_ST_SEND_NACHFORDERUNG
|
||||
};
|
||||
|
||||
PrtCnvBroadcastMgr();
|
||||
|
||||
|
||||
|
||||
bool handleRadioTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings);
|
||||
|
||||
|
||||
|
||||
int32_t init();
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextBroadcastTelegramToSend(const uint16_t a_beginSearchWithBroadcastIndex,
|
||||
const time_t a_currentTime);
|
||||
|
||||
|
||||
|
||||
void setDbThread(PrtCnvDbThread *a_dbThread);
|
||||
|
||||
|
||||
|
||||
|
||||
void cleanAndInit(const bool a_deleteTelegrams);
|
||||
|
||||
|
||||
|
||||
|
||||
void handleOverrunOfBroadcastCounter(const bool a_doRestore = false);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextNachforderungTelegramToSend();
|
||||
|
||||
|
||||
|
||||
void dump();
|
||||
|
||||
|
||||
|
||||
|
||||
void restoreAfterCrash();
|
||||
|
||||
|
||||
|
||||
|
||||
uint16_t getBroadcastIndex() const;
|
||||
|
||||
|
||||
|
||||
uint16_t getNextBroadcastIndex();
|
||||
|
||||
|
||||
|
||||
void addBroadcastOnlyTelegramToMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_broadcastOnlyTelegram);
|
||||
|
||||
|
||||
|
||||
void incTickAndSetTrafficLoad(const bool a_isHighLoad, const bool a_isFullLoad);
|
||||
|
||||
|
||||
|
||||
bool isEffectSimulationIdDeactivated(const uint16_t effectsimulationId) const;
|
||||
|
||||
|
||||
|
||||
void handleNachforderungBroadcastTelegram(int identity, int broadcastIndex, int resetCounter);
|
||||
|
||||
protected:
|
||||
struct EquipNoBcIndex
|
||||
{
|
||||
int equipNo = 0;
|
||||
int bcIndex = 0;
|
||||
|
||||
EquipNoBcIndex() = default;
|
||||
EquipNoBcIndex(int e, int b) : equipNo(e), bcIndex(b) {}
|
||||
};
|
||||
|
||||
|
||||
std::vector<EquipNoBcIndex> m_claimerArray;
|
||||
|
||||
|
||||
uint16_t m_ClaimWindow;
|
||||
|
||||
|
||||
uint16_t m_HighLoadFactor;
|
||||
|
||||
|
||||
uint16_t m_BroadcastLimit;
|
||||
|
||||
|
||||
NGL::Settings *m_Config;
|
||||
|
||||
|
||||
uint16_t m_ActClaimWindow;
|
||||
|
||||
|
||||
PrtCnvBroadcastMgr::E_State m_State;
|
||||
uint16_t m_TickCounter;
|
||||
|
||||
|
||||
PrtCnvDbThread *m_DbThread;
|
||||
|
||||
|
||||
|
||||
|
||||
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_BroadcastMap;
|
||||
|
||||
std::vector<std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_broadcastTelegamsToSend;
|
||||
|
||||
|
||||
|
||||
std::map<gef_telegramm_id_et, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_LastTelegramMap;
|
||||
|
||||
|
||||
|
||||
|
||||
std::multimap<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_WirkfeldTelegramMapByKennung;
|
||||
|
||||
|
||||
|
||||
|
||||
std::multimap<time_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_WirkfeldTelegramMapByTime;
|
||||
|
||||
|
||||
|
||||
|
||||
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_FunktionsraumTelegramMap;
|
||||
|
||||
|
||||
|
||||
|
||||
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_TechnikFunktionsraumTelegramMap;
|
||||
|
||||
|
||||
|
||||
|
||||
uint16_t m_BroadcastIndex;
|
||||
|
||||
|
||||
|
||||
bool m_IsFullLoad;
|
||||
|
||||
|
||||
uint16_t m_FinishedBroadcastIndex;
|
||||
|
||||
|
||||
uint16_t m_FirstBroadcastIndexOfBc1;
|
||||
|
||||
|
||||
|
||||
void addToBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNachforderungsTelegram(int curBcIndex, int startBcIndex, int &nextBcIndex);
|
||||
|
||||
|
||||
|
||||
int getSammeltelegramIndex(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_sammelTelegram,
|
||||
const uint16_t a_kennung) const;
|
||||
|
||||
|
||||
|
||||
void moveTelegramInBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram,
|
||||
const uint16_t a_newBroadcastIndex,
|
||||
const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_attachTelegram = {});
|
||||
|
||||
|
||||
|
||||
void removeFromBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
void removeFromBroadcastMapFunc(const std::function<bool(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &)> &pred);
|
||||
void removeDusMineDeactivationFromBroadcastMap(int dusMineId);
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccumulationTelegram(int curBcIndex,
|
||||
int startBcIndex,
|
||||
gef_telegramm_id_et telId,
|
||||
const QMap<int, int> &bcIndex2Type,
|
||||
int maxIdsPerTelegram,
|
||||
int sizeOfSumField) const;
|
||||
|
||||
bool removeFromDeactivatedAesArea(int aesAreaId);
|
||||
bool addToDeactivatedAesArea(int aesAreaId, int bcIndex);
|
||||
|
||||
QHash<int, int> m_deactivatedAesArea2bcIndex;
|
||||
|
||||
QMap<int, int> m_deactivatedBcIndex2AesArea;
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>createAccuTeleDeactivatedAesAreas(int curBcIndex, int startBcIndex) const;
|
||||
|
||||
bool removeFromDeactivatedFuncArea(int aesAreaId);
|
||||
bool addToDeactivatedFuncArea(int aesAreaId, int bcIndex);
|
||||
|
||||
QHash<int, int> m_deactivatedFuncArea2bcIndex;
|
||||
|
||||
QMap<int, int> m_deactivatedBcIndex2FuncArea;
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>createAccuTeleDeactivatedFuncAreas(int curBcIndex, int startBcIndex) const;
|
||||
|
||||
bool removeFromDeactivatedDusMine(int dusMineId);
|
||||
bool addToDeactivatedDusMine(int dusMineId, int bcIndex);
|
||||
|
||||
QHash<int, int> m_deactivatedDusMine2bcIndex;
|
||||
|
||||
QMap<int, int> m_deactivatedBcIndex2DusMine;
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccuTeleDeactivatedDusMines(int curBcIndex, int startBcIndex) const;
|
||||
|
||||
bool removeFromActivatedDusMine(int dusMineId);
|
||||
void addToActivatedDusMine(int dusMineId);
|
||||
QSet<int> m_activatedDusMines;
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccuTeleActivatedDusMines(int curBcIndex);
|
||||
|
||||
|
||||
|
||||
bool removeFromSammeltelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_sammelTelegram,
|
||||
const uint16_t a_kennung,
|
||||
const uint16_t a_anzahlPos,
|
||||
const uint8_t a_anzahlSize,
|
||||
bool &a_sammelTelRemoved);
|
||||
|
||||
|
||||
|
||||
void replaceLastTelegramAndAddAtEnd(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_newRadioTelegram);
|
||||
|
||||
|
||||
|
||||
|
||||
std::string dumpSammeldeaktivierung(const N_CoPrtCnv::CoPrtCnvRadioTelegram *sammelTelegram);
|
||||
|
||||
|
||||
|
||||
void getAllFinishedBroadcastsFromDb(const bool a_doRestore = false);
|
||||
|
||||
|
||||
|
||||
|
||||
void removeExpiredTelegrams();
|
||||
|
||||
|
||||
|
||||
void removeWirkfeldTelegrams(const uint16_t a_kennung);
|
||||
|
||||
|
||||
|
||||
void addWirkfeldTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram,
|
||||
const uint16_t a_wirkfeldKennung, const gef_telegramm_id_et a_telId = GEF_BESTAETIGUNG,
|
||||
const uint16_t a_minenKennung = 0);
|
||||
|
||||
|
||||
|
||||
bool deactivateEinzelmineFromMinensperre(const uint16_t a_wirkfeldKennung, const uint16_t a_minenKennung, const uint16_t a_broadcastIndex);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNopTelegram(const uint16_t a_from, const uint16_t a_to);
|
||||
|
||||
|
||||
void removeFromAllMaps(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
void detachTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
void removeFromFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung);
|
||||
void removeFromTechnikFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung);
|
||||
|
||||
|
||||
|
||||
void removeFromWirkfeldAndBroadcastMaps(const std::multimap<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>>::iterator &a_wirkfeldTelegramMapByKennungIterator);
|
||||
|
||||
|
||||
|
||||
void reactivateDusMine(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
void deactivateSingleMine(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
void prepareForSendNachforderungen();
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> evaluateNextNachforderungToSend();
|
||||
|
||||
Q_SIGNALS:
|
||||
void areaEffectSimulationActivated(int wirkfeldKennung);
|
||||
void areaEffectSimulationDeactivated(int wirkfeldKennung);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
NGL::INGLLogger &m_loggerBcRequest;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvCentralThread.hpp"
|
||||
#include "PrtCnvRadioThread.hpp"
|
||||
#include "PrtCnvDbThread.hpp"
|
||||
#include "PrtCnvStoredCentralTelegram.hpp"
|
||||
#include "PrtCnvTelegramLogThread.hpp"
|
||||
#include "PrtCnvInjectionThread.hpp"
|
||||
#include "PrtCnvThreadStarter.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
|
||||
|
||||
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
||||
#include <LtsUtilsCore/dds_bitarray.h>
|
||||
|
||||
#include <QTimer>
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <time.h>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
PrtCnvCentralThread::PrtCnvCentralThread(PrtCnv::PrtCnvThreadStarter* a_parent)
|
||||
: PrtCnvThread(a_parent, "PrtCnvCentralThread")
|
||||
, m_webSocket(nullptr)
|
||||
, m_SendCounter(0)
|
||||
, m_RadioThread(nullptr)
|
||||
, m_RadioStopped(false)
|
||||
, m_CentralStopped(false)
|
||||
, m_LastSendCounter(0)
|
||||
, m_VirtualTeilnehmerId(0)
|
||||
, m_maxEqID(50000)
|
||||
, m_aliveTimer(nullptr)
|
||||
, m_sendAckTimer(nullptr)
|
||||
, m_reconnectTimer(nullptr)
|
||||
, m_DbThread(nullptr)
|
||||
, m_LogThread(nullptr)
|
||||
, m_InjectionThread(nullptr)
|
||||
, m_ReceiveCounter(0)
|
||||
, m_ReceiveAckCounter(0)
|
||||
, m_State(PrtCnvSocketThread::e_INITIAL)
|
||||
, m_bDirectConnection(false)
|
||||
, m_bRadioThreadInitDone(false)
|
||||
{
|
||||
m_Name = QStringLiteral("Central");
|
||||
}
|
||||
|
||||
PrtCnvCentralThread::~PrtCnvCentralThread()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvCentralThread::create(NGL::Settings *settings)
|
||||
{
|
||||
PrtCnvThread::create(settings);
|
||||
|
||||
INFO_LOG(m_logger) << "Create";
|
||||
|
||||
m_RadioThread = qobject_cast<PrtCnvRadioThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Radio));
|
||||
m_DbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
|
||||
m_LogThread = qobject_cast<PrtCnvTelegramLogThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Log));
|
||||
m_InjectionThread = qobject_cast<PrtCnvInjectionThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Injection));
|
||||
connect(m_DbThread, &PrtCnvDbThread::playerLicencedChanged,
|
||||
this, &PrtCnvCentralThread::onPlayerLicencedChanged, Qt::QueuedConnection);
|
||||
connect(this, &PrtCnvCentralThread::checkSendToCentral, this, &PrtCnvCentralThread::checkSend, Qt::QueuedConnection);
|
||||
connect(this, &PrtCnvCentralThread::checkSendToRadio, m_RadioThread, &PrtCnvRadioThread::checkSend, Qt::QueuedConnection);
|
||||
connect(this, &PrtCnvCentralThread::stopRadio, m_RadioThread, &PrtCnvRadioThread::doStop, Qt::QueuedConnection);
|
||||
connect(this, &PrtCnvCentralThread::cleanRadio, m_RadioThread, &PrtCnvRadioThread::doClean, Qt::QueuedConnection);
|
||||
connect(this, &PrtCnvCentralThread::startRadio, m_RadioThread, &PrtCnvRadioThread::doStart, Qt::QueuedConnection);
|
||||
connect(m_RadioThread, &PrtCnvRadioThread::initDone,
|
||||
this, &PrtCnvCentralThread::onRadioThreadInitDone);
|
||||
connect(m_RadioThread, &PrtCnvRadioThread::areaEffectSimulationActivated,
|
||||
this, &PrtCnvCentralThread::onAreaEffectSimulationActivated, Qt::QueuedConnection);
|
||||
connect(m_RadioThread, &PrtCnvRadioThread::areaEffectSimulationDeactivated,
|
||||
this, &PrtCnvCentralThread::onAreaEffectSimulationDeactivated, Qt::QueuedConnection);
|
||||
|
||||
auto mainApp = m_Parent->getMainApp();
|
||||
connect(this, &PrtCnvCentralThread::directTextMessage,
|
||||
mainApp, &PrtCnvApplication::textMessageFromPrtCnvToCentral);
|
||||
connect(mainApp, &PrtCnvApplication::textMessageFromCentralToPrtCnv,
|
||||
this, &PrtCnvCentralThread::onDirectTextMessageReceived);
|
||||
|
||||
m_aliveTimer = new QTimer(this);
|
||||
connect(m_aliveTimer, &QTimer::timeout, this, &PrtCnvCentralThread::timeoutAliveAndConnection);
|
||||
|
||||
m_sendAckTimer = new QTimer(this);
|
||||
connect(m_sendAckTimer, &QTimer::timeout, this, &PrtCnvCentralThread::timeoutTick);
|
||||
|
||||
const int disconnectTimeout = m_Config->getInteger(getName(), QStringLiteral("DisconnectTimeout"), 10000);
|
||||
m_reconnectTimer = new QTimer(this);
|
||||
m_reconnectTimer->setInterval(disconnectTimeout);
|
||||
m_reconnectTimer->setSingleShot(true);
|
||||
connect(m_reconnectTimer, &QTimer::timeout, this, &PrtCnvCentralThread::connectSocket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvCentralThread::destroy()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Destroy";
|
||||
PrtCnvThread::destroy();
|
||||
delete m_webSocket; m_webSocket = nullptr;
|
||||
delete m_aliveTimer; m_aliveTimer = nullptr;
|
||||
delete m_sendAckTimer; m_sendAckTimer = nullptr;
|
||||
delete m_reconnectTimer; m_reconnectTimer = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvCentralThread::init()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Init";
|
||||
PrtCnvThread::init();
|
||||
m_CentralStopped = false;
|
||||
m_RadioStopped = false;
|
||||
|
||||
m_aliveTimer->setInterval(m_Config->getInteger(getName(), QStringLiteral("AliveTimeout"), 3000));
|
||||
m_sendAckTimer->setInterval(m_Config->getInteger(getName(), QStringLiteral("AckTimeout"), 900));
|
||||
|
||||
m_VirtualTeilnehmerId = m_Config->getInteger(QStringLiteral("Common"), QStringLiteral("VirtualIdPrtCnv"), 90);
|
||||
m_maxEqID = m_Config->getInteger(QStringLiteral("Common"), QStringLiteral("MaxEqID"), 50000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t PrtCnvCentralThread::deinit()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Deinit";
|
||||
PrtCnvThread::deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvCentralThread::start()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Start";
|
||||
PrtCnvThread::start();
|
||||
if (m_DbThread->mustProceedCleanup())
|
||||
{
|
||||
INFO_LOG(m_logger) << "Proceed cleanup DB after crash...";
|
||||
m_CentralStopped = true;
|
||||
emit stopRadio();
|
||||
}
|
||||
else
|
||||
{
|
||||
connectSocket();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvCentralThread::stop()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Stop";
|
||||
PrtCnvThread::stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::connected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Central-Socket connected m_Socket=" << m_webSocket;
|
||||
m_SendCounter = 0;
|
||||
m_LastSendCounter = 0;
|
||||
m_ReceiveCounter = 0;
|
||||
m_ReceiveAckCounter = 0;
|
||||
m_aliveTimer->start();
|
||||
if (m_bRadioThreadInitDone)
|
||||
m_sendAckTimer->start();
|
||||
m_State = PrtCnvSocketThread::e_WORKING;
|
||||
emit checkSendToCentral();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::disconnected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "disconnected m_Socket=" << m_webSocket;
|
||||
|
||||
|
||||
if (PrtCnvSocketThread::e_WORKING == m_State && m_webSocket)
|
||||
m_webSocket->close();
|
||||
|
||||
m_State = PrtCnvSocketThread::e_DISCONNECTING;
|
||||
if (m_DoCleanup)
|
||||
{
|
||||
INFO_LOG(m_logger) << "cleanup while disconnection";
|
||||
m_sendAckTimer->stop();
|
||||
m_CentralStopped = true;
|
||||
if (m_RadioStopped)
|
||||
cleanAndReinit();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DbThread->prepareCentralTelegramsForReconnection();
|
||||
connectSocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::radioStopped()
|
||||
{
|
||||
m_RadioStopped = true;
|
||||
if (m_CentralStopped)
|
||||
cleanAndReinit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::cleanAndReinit()
|
||||
{
|
||||
INFO_LOG(m_logger) << "cleanAndReinit";
|
||||
emit cleanRadio();
|
||||
|
||||
m_ReceiveAckCounter = 0;
|
||||
m_DbThread->cleanAndInit();
|
||||
m_DoCleanup = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::radioCleant()
|
||||
{
|
||||
if (!m_DoCleanup)
|
||||
{
|
||||
m_DbThread->setMachineState(0);
|
||||
m_RadioStopped = false;
|
||||
m_CentralStopped = false;
|
||||
emit startRadio();
|
||||
connectSocket();
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Internal error: radioCleant, but central still cleaning up.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::onTextMessageReceived(const QString &msg)
|
||||
{
|
||||
|
||||
if (m_DoCleanup)
|
||||
return;
|
||||
if (PrtCnvSocketThread::e_WORKING != m_State)
|
||||
return;
|
||||
onDirectTextMessageReceived(msg.toUtf8());
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onDirectTextMessageReceived(const QByteArray &baJson)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (baJson.isEmpty())
|
||||
throw std::exception();
|
||||
const QJsonObject obj = QJsonDocument::fromJson(baJson).object();
|
||||
|
||||
const QJsonValue typeId = obj.value(QStringLiteral("typeId"));
|
||||
if (!typeId.isDouble())
|
||||
throw TrainingAreaBase::TetraIcd::JsonException(obj, QStringLiteral("typeId"));
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(baJson, obj, static_cast<TrainingAreaBase::TetraIcd::TelegramId>(typeId.toInt()));
|
||||
|
||||
m_LogThread->writeCentralTelegramLog(&telegram, false, 1);
|
||||
|
||||
m_aliveTimer->start();
|
||||
|
||||
|
||||
if (telegram.telegramId() == TrainingAreaBase::TetraIcd::BESTAETIGUNG)
|
||||
{
|
||||
|
||||
handleAck(&telegram);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Received telegram '" << baJson << "'";
|
||||
|
||||
handleTelegram(&telegram);
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "JSon conversion error happenend. Error:"
|
||||
<< e.what() << ", msg: " << baJson;
|
||||
|
||||
TrainingAreaBase::TetraIcd::CtaDataError tele;
|
||||
tele.header.equipmentNumber = m_VirtualTeilnehmerId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000LL;
|
||||
tele.reason = TrainingAreaBase::TetraIcd::CtaDataError::Reason::JsonDecodeFailure;
|
||||
tele.originTelegramId = 0;
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(tele);
|
||||
sendTelegram(&telegram);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvCentralThread::stateChanged(QAbstractSocket::SocketState newState)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Websocket changed to state " << newState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::handleTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "Receive Central-Telegram with telId=" << a_telegram->telegramId()
|
||||
<< "; rcounter=" << a_telegram->counter();
|
||||
if (checkAndUpdateReceiveCounter(a_telegram->counter()))
|
||||
{
|
||||
if (handleSpecialTelegram(a_telegram))
|
||||
{
|
||||
using namespace TrainingAreaBase::TetraIcd;
|
||||
if (a_telegram->telegramId() == TelegramId::DEAKTIVIERUNG_WIRKFELD)
|
||||
{
|
||||
const DeactivateEffectSimulation des(DeactivateEffectSimulation::fromJson(a_telegram->jsonData()));
|
||||
switch (des.aesType)
|
||||
{
|
||||
case AreaEffectSimulationType::MineArea:
|
||||
case AreaEffectSimulationType::ExplosionArea:
|
||||
case AreaEffectSimulationType::ExplosionSingle:
|
||||
case AreaEffectSimulationType::NContamination:
|
||||
case AreaEffectSimulationType::CContamination:
|
||||
case AreaEffectSimulationType::FunctionalArea:
|
||||
case AreaEffectSimulationType::DusSingleMineSimulator:
|
||||
if (m_deactivatedAreaEffectSimulations.contains(des.aesDdsId))
|
||||
{
|
||||
INFO_LOG(m_logger) << "AES with id " << des.aesDdsId << " already deactivated - ignoring new deactivation telegram";
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case AreaEffectSimulationType::TechnicalArea:
|
||||
case AreaEffectSimulationType::GasPipeDetonation:
|
||||
case AreaEffectSimulationType::SpecialArea:
|
||||
case AreaEffectSimulationType::Unknown:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
case AreaEffectSimulationType::BeStillWarning:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (a_telegram->telegramId() == TelegramId::WIRKFELD_STATUS_MINENSPERRE)
|
||||
{
|
||||
const MineAreaSingleMineState msms(MineAreaSingleMineState::fromJson(a_telegram->jsonData()));
|
||||
QSet<int> &oldTriggeredMines = m_alreadyTriggeredMines[msms.aesDdsId];
|
||||
QSet<int> newTriggeredMines;
|
||||
newTriggeredMines.reserve(oldTriggeredMines.size() + 5);
|
||||
bool bSomethingChanged = false;
|
||||
for (int i = 0; i < msms.mineCount; ++i)
|
||||
{
|
||||
if (!LtsUtils::BitArray::testBit(msms.mineState, i))
|
||||
{
|
||||
INFO_LOG(m_logger) << "Testing bit " << i << " ... not set = triggered";
|
||||
if (!oldTriggeredMines.contains(i))
|
||||
{
|
||||
INFO_LOG(m_logger) << "not in old one";
|
||||
bSomethingChanged = true;
|
||||
}
|
||||
newTriggeredMines.insert(i);
|
||||
}
|
||||
}
|
||||
if (!bSomethingChanged)
|
||||
bSomethingChanged = newTriggeredMines.size() != oldTriggeredMines.size();
|
||||
if (!bSomethingChanged)
|
||||
{
|
||||
INFO_LOG(m_logger) << "MineState telegram contains no new informations - skipping " << msms.aesDdsId;
|
||||
return;
|
||||
}
|
||||
INFO_LOG(m_logger) << "MineState telegram contains new informations... " << msms.aesDdsId;
|
||||
oldTriggeredMines = newTriggeredMines;
|
||||
}
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>
|
||||
radioTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(a_telegram));
|
||||
if (radioTelegram)
|
||||
{
|
||||
if (radioTelegram->getIdentity() <= m_maxEqID) {
|
||||
|
||||
m_DbThread->storeRadioTelegram(radioTelegram, a_telegram->counter());
|
||||
} else {
|
||||
WARNING_LOG(m_logger) << "Central telegram with telId=" << a_telegram->telegramId()
|
||||
<< " has invalid identity:" << radioTelegram->getIdentity();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DbThread->centralCounterReadyForAck(a_telegram->counter());
|
||||
WARNING_LOG(m_logger) << "Central telegram with telId=" << a_telegram->telegramId()
|
||||
<< " could not be converted";
|
||||
}
|
||||
}
|
||||
}
|
||||
emit checkSendToRadio();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool PrtCnvCentralThread::checkAndUpdateReceiveCounter(const int32_t a_counter)
|
||||
{
|
||||
TRACE_LOG(m_logger) << "checkAndUpdateReceiveCounter: " << a_counter << ", " << m_ReceiveCounter;
|
||||
|
||||
if (std::numeric_limits<int32_t>::max() == ++m_ReceiveCounter)
|
||||
m_ReceiveCounter = 0;
|
||||
|
||||
|
||||
if (m_ReceiveCounter != a_counter)
|
||||
{
|
||||
if (m_ReceiveCounter > a_counter)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "It seems, the telegram with counter " << a_counter << " was resent from "
|
||||
<< getName() << "! Expected: " << m_ReceiveCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "It seems, we lost one or several telegrams from " << getName()
|
||||
<< ": receive-counter of received telegram: " << a_counter << "; Expected: " << m_ReceiveCounter;
|
||||
}
|
||||
#if 0
|
||||
m_ReceiveCounter--;
|
||||
return false;
|
||||
#else
|
||||
m_ReceiveCounter = a_counter;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::timeoutTick()
|
||||
{
|
||||
|
||||
if (m_DoCleanup)
|
||||
return;
|
||||
if (PrtCnvSocketThread::e_WORKING != m_State)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *centralInjectTelegram;
|
||||
while ((centralInjectTelegram = m_InjectionThread->getNextCentralInjectionTelegram()))
|
||||
{
|
||||
sendTelegram(centralInjectTelegram);
|
||||
delete centralInjectTelegram;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Catched an unexpected exception: " << e.what()
|
||||
<< " => could not inject radio-telegram, but we proceed...";
|
||||
}
|
||||
|
||||
|
||||
sendAck();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::timeoutAliveAndConnection()
|
||||
{
|
||||
INFO_LOG(m_logger) << "timeoutAliveAndConnection socketState="
|
||||
<< int(m_webSocket ? m_webSocket->state() : -1)
|
||||
<< " m_State=" << m_State << " m_Socket=" << m_webSocket;
|
||||
switch (m_State)
|
||||
{
|
||||
case PrtCnvSocketThread::e_CONNECTING:
|
||||
connectSocket();
|
||||
break;
|
||||
case PrtCnvSocketThread::e_WORKING:
|
||||
if (!m_bDirectConnection)
|
||||
{
|
||||
disconnectSocket();
|
||||
m_reconnectTimer->start();
|
||||
}
|
||||
break;
|
||||
case PrtCnvSocketThread::e_DISCONNECTING:
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(m_logger) << "timeoutAlive for m_State=" << m_State << " not implemented";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::sendAck()
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::Acknowledge ack;
|
||||
ack.timeStamp = time(nullptr) * 1000;
|
||||
ack.ackCounter = m_ReceiveAckCounter;
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram tele(ack);
|
||||
|
||||
if (m_webSocket)
|
||||
m_webSocket->sendTextMessage(QString::fromUtf8(tele.data()));
|
||||
else
|
||||
emit directTextMessage(tele.data());
|
||||
|
||||
TRACE_LOG(m_logger) << "Wrote ack: " << tele.data();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
PrtCnvStoredCentralTelegram *handle = nullptr;
|
||||
sendTelegram(a_telegram, handle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram,
|
||||
PrtCnvStoredCentralTelegram *a_handle)
|
||||
{
|
||||
using namespace TrainingAreaBase::TetraIcd;
|
||||
if (a_telegram->telegramId() == TelegramId::DEAKTIVIERUNG_EINZELMINE)
|
||||
{
|
||||
const MineTriggered tele(MineTriggered::fromJson(a_telegram->jsonData()));
|
||||
m_alreadyTriggeredMines[tele.mineAreaId].insert(tele.mineIndex - 1);
|
||||
INFO_LOG(m_logger) << "Adding triggered mine with id " << tele.mineIndex << " to area id " << tele.mineAreaId;
|
||||
}
|
||||
|
||||
|
||||
const int writeCounter = a_handle ? a_handle->writeCounter() : 0;
|
||||
if (writeCounter != 0)
|
||||
a_telegram->setCounter(writeCounter);
|
||||
else
|
||||
a_telegram->setCounter(++m_SendCounter);
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "Send telegram to central: telId=" << a_telegram->telegramId()
|
||||
<< " wcounter=" << a_telegram->counter();
|
||||
|
||||
if (m_webSocket)
|
||||
m_webSocket->sendTextMessage(QString::fromUtf8(a_telegram->data()));
|
||||
else
|
||||
emit directTextMessage(a_telegram->data());
|
||||
|
||||
|
||||
if (a_handle)
|
||||
m_DbThread->centralTelegramSent(a_telegram->counter(), a_handle);
|
||||
m_LogThread->writeCentralTelegramLog(a_telegram, true, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::handleAnforderungStatistikBroadcastNachforderungen()
|
||||
{
|
||||
const auto nachforderungsStat = m_DbThread->getClaimStatistics();
|
||||
|
||||
|
||||
TrainingAreaBase::TetraIcd::BroadcastStatistics tele;
|
||||
tele.header.equipmentNumber = m_VirtualTeilnehmerId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000LL;
|
||||
tele.information.reserve(nachforderungsStat.size());
|
||||
for (const auto &it : nachforderungsStat)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::BroadcastInformation bi;
|
||||
bi.equipmentNumber = it.first;
|
||||
bi.broadcastRequests = it.second;
|
||||
tele.information.push_back(std::move(bi));
|
||||
}
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(tele);
|
||||
sendTelegram(&telegram);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::checkSend()
|
||||
{
|
||||
|
||||
if (m_DoCleanup) return;
|
||||
if (PrtCnvSocketThread::e_WORKING != m_State) return;
|
||||
PrtCnvStoredCentralTelegram *handle = nullptr;
|
||||
auto *centralTelegram = m_DbThread->getNextCentralTelegram(handle);
|
||||
if (centralTelegram)
|
||||
{
|
||||
sendTelegram(centralTelegram, handle);
|
||||
delete centralTelegram;
|
||||
emit checkSendToCentral();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::handleAck(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::Acknowledge ack(TrainingAreaBase::TetraIcd::Acknowledge::fromJson(a_telegram->jsonData()));
|
||||
|
||||
m_DbThread->ackCentralTelegrams(ack.ackCounter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvCentralThread::permanentStored(int32_t a_centralCounter)
|
||||
{
|
||||
if (m_DoCleanup) return;
|
||||
if (PrtCnvSocketThread::e_WORKING != m_State) return;
|
||||
DEBUG_LOG(m_logger) << "got permanentStored a_centralCounter=" << a_centralCounter;
|
||||
if (0 != a_centralCounter && a_centralCounter > m_ReceiveAckCounter)
|
||||
{
|
||||
m_ReceiveAckCounter = a_centralCounter;
|
||||
sendAck();
|
||||
}
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onTetraConnectionState(bool bConnected)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::ProxyConnectionState connState;
|
||||
connState.connectedToSystem = bConnected;
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(connState);
|
||||
sendTelegram(&telegram);
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onPlayerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::ParticipantLicenseChange lc;
|
||||
lc.equipmentNumber = equipNo;
|
||||
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unknown;
|
||||
switch (kind)
|
||||
{
|
||||
case N_CoPrtCnv::E_LicensingKind::Success:
|
||||
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Licensed;
|
||||
break;
|
||||
case N_CoPrtCnv::E_LicensingKind::Rejected:
|
||||
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Rejected;
|
||||
break;
|
||||
case N_CoPrtCnv::E_LicensingKind::Unlicense:
|
||||
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unlicense;
|
||||
break;
|
||||
}
|
||||
Q_ASSERT(lc.licenseState != TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unknown);
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(lc);
|
||||
sendTelegram(&telegram);
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onAreaEffectSimulationDeactivated(int wirkfeldKennung)
|
||||
{
|
||||
m_deactivatedAreaEffectSimulations.insert(wirkfeldKennung);
|
||||
m_alreadyTriggeredMines.remove(wirkfeldKennung);
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onAreaEffectSimulationActivated(int wirkfeldKennung)
|
||||
{
|
||||
m_deactivatedAreaEffectSimulations.remove(wirkfeldKennung);
|
||||
}
|
||||
|
||||
void PrtCnvCentralThread::onRadioThreadInitDone()
|
||||
{
|
||||
m_bRadioThreadInitDone = true;
|
||||
if (!m_sendAckTimer->isActive())
|
||||
m_sendAckTimer->start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool PrtCnvCentralThread::handleSpecialTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
using namespace TrainingAreaBase::TetraIcd;
|
||||
|
||||
switch (a_telegram->telegramId())
|
||||
{
|
||||
case Constraints<ExerciseStateData>::id():
|
||||
{
|
||||
ExerciseStateData tele(ExerciseStateData::fromJson(a_telegram->jsonData()));
|
||||
switch(tele.exerciseState)
|
||||
{
|
||||
case ExerciseStateData::ExerciseState::Terminated:
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got Betriebszustandsmeldung Uebung_Aufraeumen => disconnect, cleanup and reconnect";
|
||||
m_DbThread->setMachineState(1);
|
||||
|
||||
permanentStored(a_telegram->counter());
|
||||
m_DoCleanup = true;
|
||||
m_CentralStopped = false;
|
||||
m_RadioStopped = false;
|
||||
|
||||
m_sendAckTimer->stop();
|
||||
m_DbThread->waitUntilDbWritten();
|
||||
disconnectSocket();
|
||||
m_RadioThread->prepareCleanUp();
|
||||
emit stopRadio();
|
||||
m_reconnectTimer->start();
|
||||
break;
|
||||
}
|
||||
case ExerciseStateData::ExerciseState::Started:
|
||||
m_DbThread->setPause(false);
|
||||
m_RadioThread->sendEnvironmentTelegramTimerNow();
|
||||
break;
|
||||
case ExerciseStateData::ExerciseState::Paused:
|
||||
m_DbThread->setPause(true);
|
||||
m_RadioThread->sendEnvironmentTelegramTimerNow();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case Constraints<RequestBroadcastStatistics>::id():
|
||||
|
||||
m_DbThread->centralCounterReadyForAck(a_telegram->counter());
|
||||
handleAnforderungStatistikBroadcastNachforderungen();
|
||||
return false;
|
||||
|
||||
case Constraints<ExerciseEquipmentAssignmentComplete>::id():
|
||||
{
|
||||
const ExerciseEquipmentAssignmentComplete tele(ExerciseEquipmentAssignmentComplete::fromJson(a_telegram->jsonData()));
|
||||
m_RadioThread->exerciseEquipmentChanged(tele);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvCentralThread::connectSocket()
|
||||
{
|
||||
const QString host = m_Config->getString(getName(), QStringLiteral("HostName"), QStringLiteral("EXCTDS01"));
|
||||
if (host == QLatin1String("directConnection"))
|
||||
{
|
||||
m_bDirectConnection = true;
|
||||
connected();
|
||||
return;
|
||||
}
|
||||
m_bDirectConnection = false;
|
||||
|
||||
disconnectSocket();
|
||||
m_webSocket = new QWebSocket;
|
||||
|
||||
connect(m_webSocket, &QWebSocket::stateChanged,
|
||||
this, &PrtCnvCentralThread::stateChanged);
|
||||
connect(m_webSocket, &QWebSocket::textMessageReceived,
|
||||
this, &PrtCnvCentralThread::onTextMessageReceived);
|
||||
connect(m_webSocket, &QWebSocket::connected,
|
||||
this, &PrtCnvCentralThread::connected);
|
||||
connect(m_webSocket, &QWebSocket::disconnected,
|
||||
this, &PrtCnvCentralThread::disconnected);
|
||||
|
||||
const uint16_t port = static_cast<uint16_t>(m_Config->getInteger(getName(), QStringLiteral("Port"), 4934));
|
||||
const QUrl scmUrl(QStringLiteral("ws://%1:%2").arg(host).arg(port));
|
||||
|
||||
m_webSocket->open(scmUrl);
|
||||
m_State = PrtCnvSocketThread::e_CONNECTING;
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvCentralThread::disconnectSocket()
|
||||
{
|
||||
m_State = PrtCnvSocketThread::e_INITIAL;
|
||||
if (!m_webSocket)
|
||||
return;
|
||||
|
||||
m_State = PrtCnvSocketThread::e_DISCONNECTING;
|
||||
m_webSocket->disconnect(this);
|
||||
m_webSocket->close();
|
||||
m_webSocket->deleteLater();
|
||||
m_webSocket = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvSocketThread.hpp"
|
||||
#include "PrtCnvDbThread.hpp"
|
||||
#include <QWebSocket>
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvRadioTelegram;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvRadioThread;
|
||||
class PrtCnvThreadStarter;
|
||||
|
||||
|
||||
class PrtCnvCentralThread : public PrtCnv::PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
~PrtCnvCentralThread();
|
||||
|
||||
|
||||
|
||||
explicit PrtCnvCentralThread(PrtCnvThreadStarter* a_parent);
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
int32_t deinit() override;
|
||||
|
||||
|
||||
int32_t destroy() override;
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
int32_t stop() override;
|
||||
|
||||
|
||||
void onDirectTextMessageReceived(const QByteArray &baJson);
|
||||
|
||||
signals:
|
||||
|
||||
void directTextMessage(const QByteArray &baJson);
|
||||
|
||||
|
||||
|
||||
void checkSendToCentral();
|
||||
|
||||
|
||||
void checkSendToRadio();
|
||||
|
||||
|
||||
void stopRadio();
|
||||
|
||||
|
||||
void cleanRadio();
|
||||
|
||||
|
||||
void startRadio();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
QWebSocket *m_webSocket;
|
||||
|
||||
|
||||
|
||||
int32_t m_SendCounter;
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
void checkSend();
|
||||
|
||||
|
||||
void radioStopped();
|
||||
|
||||
|
||||
void radioCleant();
|
||||
|
||||
|
||||
void permanentStored(int32_t a_centralCounter);
|
||||
|
||||
|
||||
void onTetraConnectionState(bool bConnected);
|
||||
|
||||
void onPlayerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
|
||||
|
||||
protected slots:
|
||||
|
||||
void onAreaEffectSimulationDeactivated(int wirkfeldKennung);
|
||||
void onAreaEffectSimulationActivated(int wirkfeldKennung);
|
||||
|
||||
|
||||
void onRadioThreadInitDone();
|
||||
|
||||
|
||||
|
||||
void connected();
|
||||
|
||||
|
||||
void disconnected();
|
||||
|
||||
|
||||
void timeoutTick();
|
||||
|
||||
|
||||
void timeoutAliveAndConnection();
|
||||
|
||||
void onTextMessageReceived(const QString &msg);
|
||||
void stateChanged(QAbstractSocket::SocketState newState);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
void sendAck();
|
||||
|
||||
|
||||
void handleTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
bool checkAndUpdateReceiveCounter(const int32_t a_counter);
|
||||
|
||||
|
||||
void sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram,
|
||||
PrtCnvStoredCentralTelegram *a_handle);
|
||||
|
||||
|
||||
void handleAnforderungStatistikBroadcastNachforderungen();
|
||||
|
||||
|
||||
void handleAck(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
|
||||
|
||||
void cleanAndReinit();
|
||||
|
||||
|
||||
|
||||
bool handleSpecialTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
|
||||
|
||||
|
||||
void sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
|
||||
|
||||
|
||||
void connectSocket();
|
||||
|
||||
|
||||
void disconnectSocket();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
PrtCnvRadioThread *m_RadioThread;
|
||||
|
||||
|
||||
bool m_RadioStopped;
|
||||
|
||||
|
||||
bool m_CentralStopped;
|
||||
|
||||
|
||||
int32_t m_LastSendCounter;
|
||||
|
||||
|
||||
uint16_t m_VirtualTeilnehmerId;
|
||||
|
||||
|
||||
|
||||
uint16_t m_maxEqID;
|
||||
|
||||
|
||||
QTimer *m_aliveTimer;
|
||||
|
||||
|
||||
QTimer *m_sendAckTimer;
|
||||
|
||||
|
||||
QTimer *m_reconnectTimer;
|
||||
|
||||
|
||||
|
||||
QAtomicInt m_DoCleanup;
|
||||
PrtCnvDbThread *m_DbThread;
|
||||
|
||||
PrtCnvTelegramLogThread *m_LogThread;
|
||||
|
||||
PrtCnvInjectionThread *m_InjectionThread;
|
||||
|
||||
|
||||
|
||||
int32_t m_ReceiveCounter;
|
||||
|
||||
|
||||
int32_t m_ReceiveAckCounter;
|
||||
|
||||
|
||||
|
||||
PrtCnvSocketThread::E_State m_State;
|
||||
|
||||
|
||||
QSet<int> m_deactivatedAreaEffectSimulations;
|
||||
|
||||
|
||||
QHash<int, QSet<int>> m_alreadyTriggeredMines;
|
||||
|
||||
|
||||
bool m_bDirectConnection;
|
||||
|
||||
bool m_bRadioThreadInitDone;
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,371 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QAtomicInt>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QString>
|
||||
#include <QtSql/QSqlDatabase>
|
||||
#include "PrtCnvThread.hpp"
|
||||
#include "PrtCnvRadioThread.hpp"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvCentralTelegram;
|
||||
class CoPrtCnvRadioTelegram;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvThreadStarter;
|
||||
class PrtCnvStoredCentralTelegram;
|
||||
class PrtCnvStoredRadioTelegram;
|
||||
class PrtCnvMember;
|
||||
|
||||
|
||||
class PrtCnvDbThread : public PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
|
||||
PrtCnvDbThread(const QString &dbDriver,
|
||||
const QString &dbHostName,
|
||||
const QString &dbDatabaseName,
|
||||
const QString &schemaName,
|
||||
const QString &dbUserName,
|
||||
const QString &dbPassword,
|
||||
PrtCnvThreadStarter* a_parent);
|
||||
~PrtCnvDbThread();
|
||||
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
int32_t stop() override;
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
void ackCentralTelegrams(int32_t a_counter);
|
||||
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextCentralTelegram(PrtCnvStoredCentralTelegram *& a_handle);
|
||||
|
||||
|
||||
|
||||
void centralTelegramSent(const int32_t a_counter, PrtCnvStoredCentralTelegram *a_handle);
|
||||
|
||||
|
||||
|
||||
void centralTelegramCouldNotBeSent(PrtCnvStoredCentralTelegram *a_handle);
|
||||
|
||||
|
||||
|
||||
void radioTelegramSent(const uint32_t a_sendCounter, PrtCnvStoredRadioTelegram *a_handle, const uint32_t a_broadcastIndex);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextRadioTelegram(PrtCnvStoredRadioTelegram *&a_handle);
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextBroadcastTelegram();
|
||||
|
||||
|
||||
|
||||
void storeCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
|
||||
const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
|
||||
|
||||
|
||||
|
||||
void storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram, const int32_t a_readCounter);
|
||||
|
||||
|
||||
|
||||
void centralCounterReadyForAck(const int32_t a_centralCounter);
|
||||
|
||||
|
||||
|
||||
void addSqlCommand(const QString &sql);
|
||||
|
||||
|
||||
|
||||
|
||||
void cleanAndInit();
|
||||
|
||||
|
||||
|
||||
bool resendCentralTelegrams(const int32_t a_lastSendCounter);
|
||||
|
||||
|
||||
|
||||
void prepareCentralTelegramsForReconnection();
|
||||
|
||||
|
||||
|
||||
void waitUntilDbWritten();
|
||||
|
||||
|
||||
|
||||
PrtCnvMember* getMemberByISSI(const uint32_t a_ISSI) const;
|
||||
|
||||
|
||||
|
||||
PrtCnvMember* getMemberById(const uint16_t a_id);
|
||||
|
||||
|
||||
|
||||
bool licenseMember(PrtCnvMember *a_member, const bool a_doLicense = true);
|
||||
|
||||
|
||||
|
||||
void updateExerciseIdForMember(PrtCnvMember *a_member, int newExerciseId);
|
||||
|
||||
|
||||
|
||||
PrtCnvMember* addMember(const int a_identity, const int a_ISSI);
|
||||
|
||||
|
||||
|
||||
void removeMember(PrtCnvMember* a_member);
|
||||
|
||||
|
||||
|
||||
uint8_t getResetCounter() const;
|
||||
|
||||
|
||||
|
||||
void setResetCounter(uint8_t newVal);
|
||||
|
||||
|
||||
|
||||
uint8_t getBattleFieldOperatingState() const;
|
||||
|
||||
|
||||
|
||||
void setPause(const bool a_doSet = true);
|
||||
|
||||
|
||||
|
||||
void setPositionSendTime(unsigned int sendTime);
|
||||
|
||||
|
||||
|
||||
void setAirLoad(PrtCnvRadioThread::E_TrafficLoad airLoad);
|
||||
|
||||
|
||||
|
||||
bool isEnvironmentResetCounterOld() const;
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getEnvironmentTelegram();
|
||||
|
||||
|
||||
|
||||
void setResetCounterOnEnvironment();
|
||||
|
||||
|
||||
|
||||
void incClaimCount(int a_identity);
|
||||
|
||||
|
||||
|
||||
std::map<int32_t, int32_t> getClaimStatistics() const;
|
||||
|
||||
|
||||
|
||||
void setMachineState(const int a_newMachineState);
|
||||
|
||||
|
||||
|
||||
bool mustProceedCleanup() const;
|
||||
|
||||
protected:
|
||||
void closeDb();
|
||||
|
||||
|
||||
|
||||
std::vector<PrtCnvStoredCentralTelegram*> m_CentralList;
|
||||
|
||||
|
||||
|
||||
mutable QMutex m_BattleFieldOpStateMutex;
|
||||
|
||||
|
||||
|
||||
mutable QMutex m_MemberListByIdMutex;
|
||||
|
||||
|
||||
|
||||
mutable QMutex m_CentralListMutex;
|
||||
|
||||
|
||||
|
||||
QSqlDatabase m_db;
|
||||
|
||||
|
||||
|
||||
QAtomicInt m_CentralAckCounter;
|
||||
|
||||
|
||||
unsigned int m_OldCentralAckCounter;
|
||||
|
||||
|
||||
std::list<PrtCnvStoredRadioTelegram*> m_RadioList;
|
||||
|
||||
|
||||
mutable QMutex m_RadioListMutex;
|
||||
|
||||
|
||||
std::vector<QString> m_SqlCommandList;
|
||||
|
||||
|
||||
mutable QMutex m_SqlCmdListMutex;
|
||||
|
||||
|
||||
mutable QMutex m_BroadcastMapMutex;
|
||||
|
||||
|
||||
std::list<PrtCnvStoredRadioTelegram*> m_BroadcastList;
|
||||
|
||||
|
||||
bool m_DoClean;
|
||||
|
||||
|
||||
bool m_DoWaitUntilSqlWritten;
|
||||
|
||||
|
||||
std::unordered_map<int,PrtCnvMember*> m_MemberListById;
|
||||
|
||||
|
||||
std::unordered_map<int,PrtCnvMember*> m_MemberListByISSI;
|
||||
|
||||
|
||||
bool m_DoStorePermanent;
|
||||
|
||||
|
||||
uint8_t m_ResetCounter;
|
||||
|
||||
|
||||
uint8_t m_BattleFieldOperatingState;
|
||||
|
||||
|
||||
PrtCnvStoredRadioTelegram* m_EnvironmentTelegram;
|
||||
|
||||
|
||||
bool m_DoProceedCleanup;
|
||||
|
||||
|
||||
int32_t m_OldCentralCounter;
|
||||
|
||||
|
||||
QBasicAtomicInt m_StorePermanentPending;
|
||||
|
||||
|
||||
|
||||
uint32_t m_NbrOfAttempts;
|
||||
|
||||
|
||||
|
||||
bool createDatabase();
|
||||
|
||||
|
||||
|
||||
void sendStorePermanentSignal();
|
||||
|
||||
|
||||
|
||||
void storeCentralTelegrams();
|
||||
|
||||
|
||||
|
||||
int32_t storeRadioTelegrams();
|
||||
|
||||
|
||||
|
||||
void handleSqlCommandList();
|
||||
|
||||
|
||||
|
||||
void deleteTelegramsFromDbById(const QString &table);
|
||||
|
||||
|
||||
void updateTelegramsWriteCounter(const QString &table);
|
||||
|
||||
|
||||
|
||||
void updateTelegramsBroadcast(const QString &table);
|
||||
|
||||
|
||||
|
||||
void reinitDatabase();
|
||||
|
||||
QString toCentralTableName() const { return tableName(QStringLiteral("prtcnv_to_central")); }
|
||||
QString toRadioTableName() const { return tableName(QStringLiteral("prtcnv_to_radio")); }
|
||||
QString memberTableName() const { return tableName(QStringLiteral("prtcnv_member")); }
|
||||
QString statusTableName() const { return tableName(QStringLiteral("prtcnv_status")); }
|
||||
QString versionTableName() const { return tableName(QStringLiteral("prtcnv_version")); }
|
||||
QString tableName(const QString &table) const { return m_dbSchemaName.isEmpty() ? table : m_dbSchemaName + QLatin1Char('.') + table; }
|
||||
protected slots:
|
||||
|
||||
|
||||
void storePermanent();
|
||||
|
||||
|
||||
void clean();
|
||||
|
||||
public:
|
||||
signals:
|
||||
|
||||
|
||||
void doStorePermanent();
|
||||
|
||||
|
||||
void permanentStored(int32_t a_centralCounter);
|
||||
|
||||
|
||||
void doClean();
|
||||
|
||||
void playerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
std::vector<int64_t> m_telegramsToDelete;
|
||||
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateCounter;
|
||||
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateBC;
|
||||
std::vector<std::pair<PrtCnvStoredCentralTelegram*, int64_t> > m_telegramsToInsert;
|
||||
QString m_dbDriver;
|
||||
QString m_dbHostName;
|
||||
QString m_dbDatabaseName;
|
||||
QString m_dbSchemaName;
|
||||
QString m_dbUserName;
|
||||
QString m_dbPassword;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvInjectionSocket.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvInjectionHeader.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
||||
#include "PrtCnvInjectionThread.hpp"
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QTcpSocket>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
PrtCnvInjectionSocket::PrtCnvInjectionSocket()
|
||||
: m_Socket(nullptr)
|
||||
, m_Server(nullptr)
|
||||
, m_logger(getLogger("PrtCnvInjectionSocket"))
|
||||
{}
|
||||
|
||||
PrtCnvInjectionSocket::~PrtCnvInjectionSocket()
|
||||
{
|
||||
delete m_Socket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionSocket::disconnectClient()
|
||||
{
|
||||
INFO_LOG(m_logger) << "A client has disconnected from the injection-server";
|
||||
emit disconnected(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionSocket::processInputData()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Injection-server has data received";
|
||||
N_CoPrtCnv::CoPrtCnvInjectionHeader injectionHeader;
|
||||
qint64 bytesRead = m_Socket->peek((char*)&injectionHeader, sizeof(injectionHeader));
|
||||
if (sizeof(injectionHeader) == bytesRead)
|
||||
{
|
||||
if (injectionHeader.isIdCorrect())
|
||||
{
|
||||
|
||||
qint64 available = m_Socket->bytesAvailable();
|
||||
if (available < (injectionHeader.m_Size+sizeof(injectionHeader)))
|
||||
{
|
||||
INFO_LOG(m_logger) << "not enough bytes available - waiting for more data: "
|
||||
<< "headersize=" << sizeof(injectionHeader) << " id1="
|
||||
<< injectionHeader.m_Id1 << " id2=" << injectionHeader.m_Id2
|
||||
<< " kind=" << (int)injectionHeader.m_Kind << " size="
|
||||
<< injectionHeader.m_Size;
|
||||
return;
|
||||
}
|
||||
|
||||
m_Socket->read((char*)&injectionHeader, sizeof(injectionHeader));
|
||||
if (injectionHeader.isIdCorrect())
|
||||
{
|
||||
|
||||
|
||||
|
||||
QByteArray data(injectionHeader.m_Size, Qt::Uninitialized);
|
||||
m_Socket->read(data.data(), injectionHeader.m_Size);
|
||||
|
||||
switch (injectionHeader.m_Kind)
|
||||
{
|
||||
case N_CoPrtCnv::CoPrtCnvInjectionHeader::eCentral:
|
||||
case N_CoPrtCnv::CoPrtCnvInjectionHeader::eRadio:
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = new N_CoPrtCnv::CoPrtCnvCentralTelegram(data);
|
||||
|
||||
if (N_CoPrtCnv::CoPrtCnvInjectionHeader::eCentral == injectionHeader.m_Kind &&
|
||||
tele->telegramId() != TrainingAreaBase::TetraIcd::TelegramId::DATENVERKEHR_TDS_SRT)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got an injection-central-telegram with telId=" << tele->telegramId();
|
||||
m_Server->addToCentralList(tele);
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(m_logger) << "Got an injection-radio-telegram with telId=" << tele->telegramId();
|
||||
m_Server->addToRadioList(tele);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(m_logger) << "Unknown kind (" << injectionHeader.m_Kind << ") of injection-telegram";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Corrupt injection-telegram received => reconnect";
|
||||
m_Socket->disconnectFromHost();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrtCnvInjectionSocket::setSocket(QTcpSocket* a_socket, PrtCnvInjectionThread* a_server)
|
||||
{
|
||||
m_Socket = a_socket;
|
||||
m_Server = a_server;
|
||||
connect(this, &PrtCnvInjectionSocket::disconnected, m_Server, &PrtCnvInjectionThread::disconnectClient, Qt::QueuedConnection);
|
||||
if (m_Socket)
|
||||
{
|
||||
connect(m_Socket, &QTcpSocket::readyRead, this, &PrtCnvInjectionSocket::processInputData, Qt::QueuedConnection);
|
||||
connect(m_Socket, &QTcpSocket::disconnected, this, &PrtCnvInjectionSocket::disconnectClient, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include "logger.h"
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvInjectionThread;
|
||||
|
||||
|
||||
class PrtCnvInjectionSocket : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
PrtCnvInjectionSocket();
|
||||
~PrtCnvInjectionSocket();
|
||||
|
||||
|
||||
|
||||
void setSocket(QTcpSocket* a_socket, PrtCnvInjectionThread* a_server);
|
||||
|
||||
protected:
|
||||
QTcpSocket *m_Socket;
|
||||
|
||||
PrtCnvInjectionThread *m_Server;
|
||||
|
||||
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
protected slots:
|
||||
|
||||
|
||||
void disconnectClient();
|
||||
|
||||
|
||||
|
||||
void processInputData();
|
||||
|
||||
|
||||
public:
|
||||
signals:
|
||||
|
||||
|
||||
void disconnected(PrtCnvInjectionSocket* a_injectSocket);
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvInjectionThread.hpp"
|
||||
#include "PrtCnvInjectionSocket.hpp"
|
||||
|
||||
#include <QtNetwork/QTcpServer>
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
#include <QtCore/QMutex>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
PrtCnvInjectionThread::PrtCnvInjectionThread(PrtCnvThreadStarter* a_parent)
|
||||
: PrtCnvThread(a_parent, "PrtCnvInjectionThread")
|
||||
, m_Port(46666)
|
||||
, m_Enabled(false)
|
||||
, m_Server(nullptr)
|
||||
, m_CentralListMutex(nullptr)
|
||||
, m_RadioListMutex(nullptr)
|
||||
{
|
||||
m_Name = QStringLiteral("Injection");
|
||||
}
|
||||
|
||||
PrtCnvInjectionThread::~PrtCnvInjectionThread()
|
||||
{
|
||||
delete m_CentralListMutex;
|
||||
delete m_RadioListMutex;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvInjectionThread::create(NGL::Settings *settings)
|
||||
{
|
||||
INFO_LOG(m_logger) << "create";
|
||||
PrtCnvThread::create(settings);
|
||||
|
||||
m_CentralListMutex = new QMutex;
|
||||
m_RadioListMutex = new QMutex;
|
||||
|
||||
m_Enabled = m_Config->getBoolean(getName(), QStringLiteral("Enable"), false);
|
||||
|
||||
|
||||
if (m_Enabled)
|
||||
{
|
||||
m_Server = new QTcpServer(this);
|
||||
if (m_Server)
|
||||
connect(m_Server, &QTcpServer::newConnection, this, &PrtCnvInjectionThread::connectClient, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvInjectionThread::init()
|
||||
{
|
||||
INFO_LOG(m_logger) << "init";
|
||||
PrtCnvThread::init();
|
||||
m_Port = m_Config->getInteger(getName(), QStringLiteral("Port"), 46666);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvInjectionThread::start()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Start";
|
||||
PrtCnvThread::start();
|
||||
|
||||
|
||||
if (m_Enabled)
|
||||
{
|
||||
m_Server->setMaxPendingConnections(1);
|
||||
m_Server->listen(QHostAddress::Any, m_Port);
|
||||
INFO_LOG(m_logger) << "Injection-Server is listening on port " << m_Port;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionThread::connectClient()
|
||||
{
|
||||
INFO_LOG(m_logger) << "A client has connected to the injection-server";
|
||||
PrtCnvInjectionSocket* injectSocket = new PrtCnvInjectionSocket;
|
||||
m_SocketList.insert(injectSocket);
|
||||
INFO_LOG(m_logger) << "Now are " << m_SocketList.size() << " injection-client(s) connected";
|
||||
injectSocket->setSocket(m_Server->nextPendingConnection(), this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionThread::disconnectClient(PrtCnvInjectionSocket *a_injectionSocket)
|
||||
{
|
||||
auto it = m_SocketList.find(a_injectionSocket);
|
||||
if (it != m_SocketList.end())
|
||||
m_SocketList.erase(it);
|
||||
INFO_LOG(m_logger) << "Now are " << m_SocketList.size() << " injection-client(s) connected";
|
||||
delete a_injectionSocket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *PrtCnvInjectionThread::getNextCentralInjectionTelegram()
|
||||
{
|
||||
QMutexLocker l(m_CentralListMutex);
|
||||
|
||||
if (m_CentralTelegramList.empty())
|
||||
return nullptr;
|
||||
auto centralTelegram = m_CentralTelegramList.front();
|
||||
m_CentralTelegramList.pop();
|
||||
return centralTelegram;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *PrtCnvInjectionThread::getNextRadioInjectionTelegram()
|
||||
{
|
||||
QMutexLocker l(m_RadioListMutex);
|
||||
|
||||
if (m_RadioTelegramList.empty())
|
||||
return nullptr;
|
||||
auto radioTelegram = m_RadioTelegramList.front();
|
||||
m_RadioTelegramList.pop();
|
||||
return radioTelegram;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionThread::addToCentralList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
QMutexLocker l(m_CentralListMutex);
|
||||
m_CentralTelegramList.push(a_telegram);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrtCnvInjectionThread::addToRadioList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
|
||||
{
|
||||
QMutexLocker l(m_RadioListMutex);
|
||||
m_RadioTelegramList.push(a_telegram);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvThread.hpp"
|
||||
#include <queue>
|
||||
#include <set>
|
||||
|
||||
class QTcpServer;
|
||||
class QMutex;
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvCentralTelegram;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvInjectionSocket;
|
||||
|
||||
|
||||
class PrtCnvInjectionThread : public PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
~PrtCnvInjectionThread();
|
||||
|
||||
|
||||
|
||||
explicit PrtCnvInjectionThread(PrtCnvThreadStarter* a_parent);
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextCentralInjectionTelegram();
|
||||
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextRadioInjectionTelegram();
|
||||
|
||||
|
||||
|
||||
void addToCentralList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
|
||||
|
||||
|
||||
void addToRadioList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
|
||||
|
||||
protected:
|
||||
|
||||
uint16_t m_Port;
|
||||
|
||||
|
||||
bool m_Enabled;
|
||||
QTcpServer *m_Server;
|
||||
|
||||
|
||||
std::queue<N_CoPrtCnv::CoPrtCnvCentralTelegram *> m_CentralTelegramList;
|
||||
QMutex *m_CentralListMutex;
|
||||
|
||||
|
||||
std::queue<N_CoPrtCnv::CoPrtCnvCentralTelegram *> m_RadioTelegramList;
|
||||
QMutex *m_RadioListMutex;
|
||||
|
||||
|
||||
std::set<PrtCnvInjectionSocket*> m_SocketList;
|
||||
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
void connectClient();
|
||||
|
||||
|
||||
void disconnectClient(PrtCnvInjectionSocket* a_injectionSocket);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvApplication.hpp"
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
#include <QNetworkProxy>
|
||||
#ifndef WIN32
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
static void daemonize()
|
||||
{
|
||||
#ifndef WIN32
|
||||
pid_t pid, sid;
|
||||
|
||||
|
||||
if ( getppid() == 1 )
|
||||
return;
|
||||
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
umask(0);
|
||||
|
||||
|
||||
sid = setsid();
|
||||
if (sid < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((chdir("/")) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
freopen( "/dev/null", "r", stdin);
|
||||
freopen( "/dev/null", "w", stdout);
|
||||
freopen( "/dev/null", "w", stderr);
|
||||
#endif
|
||||
}
|
||||
|
||||
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
|
||||
{
|
||||
public:
|
||||
CmdLineParser()
|
||||
: LtsCommandLineParser(ProvideCommandLineParameterOptions(ExerciseId|CustomConfigFile))
|
||||
{
|
||||
addOption( { QStringList() << QStringLiteral("d") << QStringLiteral("daemonize"),
|
||||
QStringLiteral("Start in background") } );
|
||||
addOption( { QStringList() << QStringLiteral("c") << QStringLiteral("cleandb"),
|
||||
QStringLiteral("Start over with clean database") } );
|
||||
}
|
||||
|
||||
bool daemonize() const
|
||||
{
|
||||
return isSet(QStringLiteral("d"));
|
||||
}
|
||||
bool cleanDb() const
|
||||
{
|
||||
return isSet(QStringLiteral("c"));
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy());
|
||||
NGLCoreApplication app(argc, argv);
|
||||
|
||||
CmdLineParser options;
|
||||
if (!options.init())
|
||||
return -1;
|
||||
app.createLogger("PrtCnv", options.exerciseId());
|
||||
|
||||
if (options.daemonize())
|
||||
daemonize();
|
||||
|
||||
PrtCnv::PrtCnvApplication prtCnvApp;
|
||||
prtCnvApp.setCleanDb(options.cleanDb());
|
||||
prtCnvApp.setExerciseId(options.exerciseId());
|
||||
if (options.iniFileSet())
|
||||
prtCnvApp.setConfigFile(options.iniFile());
|
||||
|
||||
STARTUP_LOG(app.baseLogger());
|
||||
|
||||
prtCnvApp.startApp();
|
||||
int ret = app.exec();
|
||||
prtCnvApp.shutdownApp();
|
||||
|
||||
SHUTDOWN_LOG(app.baseLogger());
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvMember.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnvMember::PrtCnvMember(int a_id, int a_issi)
|
||||
: m_ISSI(a_issi)
|
||||
, m_ClaimCount(0)
|
||||
, m_loggedInExerciseId(0)
|
||||
, m_Id(a_id)
|
||||
, m_Licensed(false)
|
||||
{}
|
||||
|
||||
PrtCnvMember::~PrtCnvMember()
|
||||
{
|
||||
clearFragmentedList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvMember::addFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram)
|
||||
{
|
||||
m_FragmentedTelegramList.insert(std::make_pair(a_radioTelegram.getFragmentCounter(),
|
||||
new N_CoPrtCnv::CoPrtCnvRadioTelegram(a_radioTelegram)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gef_telegramm_id_et PrtCnvMember::getFragmentedTelegramId() const
|
||||
{
|
||||
|
||||
for (auto rit = m_FragmentedTelegramList.rbegin(); rit != m_FragmentedTelegramList.rend(); ++rit)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *firstFragment = rit->second;
|
||||
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment == firstFragment->getFragmentStatus())
|
||||
return firstFragment->getTelID();
|
||||
}
|
||||
return GEF_BESTAETIGUNG;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvMember::clearFragmentedList()
|
||||
{
|
||||
for (auto it : m_FragmentedTelegramList)
|
||||
delete it.second;
|
||||
m_FragmentedTelegramList.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
QByteArray PrtCnvMember::getUnfragmentedTelegramData() const
|
||||
{
|
||||
return N_CoPrtCnv::CoPrtCnvRadioTelegram::getUnfragmentedData(m_FragmentedTelegramList);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool PrtCnvMember::isFragmentedListEmpty() const
|
||||
{
|
||||
return m_FragmentedTelegramList.empty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoPrtCnv/tetra_telegram_ids.h>
|
||||
#include <map>
|
||||
#include <QByteArray>
|
||||
|
||||
|
||||
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvRadioTelegram;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
class PrtCnvMember final
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
|
||||
enum E_MemberStatus
|
||||
{
|
||||
e_Ok,
|
||||
e_AlreadyLoggedIn,
|
||||
e_MasteringLogIn,
|
||||
e_NotLoggedIn
|
||||
};
|
||||
~PrtCnvMember();
|
||||
|
||||
|
||||
|
||||
PrtCnvMember(int a_id, int a_issi);
|
||||
|
||||
|
||||
|
||||
void addFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
|
||||
|
||||
|
||||
gef_telegramm_id_et getFragmentedTelegramId() const;
|
||||
|
||||
|
||||
void clearFragmentedList();
|
||||
|
||||
|
||||
QByteArray getUnfragmentedTelegramData() const;
|
||||
|
||||
|
||||
bool isFragmentedListEmpty() const;
|
||||
|
||||
public:
|
||||
|
||||
int m_ISSI;
|
||||
|
||||
int m_ClaimCount;
|
||||
|
||||
int m_loggedInExerciseId;
|
||||
|
||||
int m_Id;
|
||||
|
||||
bool m_Licensed;
|
||||
|
||||
private:
|
||||
|
||||
std::multimap<uint16_t,N_CoPrtCnv::CoPrtCnvRadioTelegram*> m_FragmentedTelegramList;
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,419 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvSocketThread.hpp"
|
||||
#include "PrtCnvBroadcastMgr.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <set>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
#define MAX_IDENTITY 65535
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
struct BiteError
|
||||
{
|
||||
int errorNumber = 0;
|
||||
int canDeviceNumber = 0;
|
||||
int subdeviceNumber = 0;
|
||||
int uniqueId = 0;
|
||||
};
|
||||
|
||||
class PrtCnvCentralThread;
|
||||
class PrtCnvMember;
|
||||
class PrtCnvRadioTrafficThread;
|
||||
class PrtCnvStoredRadioTelegram;
|
||||
|
||||
|
||||
class PrtCnvRadioThread : public PrtCnv::PrtCnvSocketThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
PrtCnvRadioThread(int exerciseId, PrtCnvThreadStarter* a_parent);
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
|
||||
int32_t destroy() override;
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
|
||||
void prepareCleanUp();
|
||||
|
||||
|
||||
|
||||
void sendEnvironmentTelegramTimerNow();
|
||||
|
||||
void exerciseEquipmentChanged(const TrainingAreaBase::TetraIcd::ExerciseEquipmentAssignmentComplete &data);
|
||||
protected:
|
||||
void onCheckMemberForValidLogin(int identity, int issi);
|
||||
void sendLoginAccept(int identity, int issi, int exerciseId, bool bWithMastering);
|
||||
bool isIdentityInCurrentExercise(int identity) const;
|
||||
int exerciseIdForIdentity(int identity) const;
|
||||
void removeMasteringMember(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram, PrtCnvMember *member,
|
||||
PrtCnvMember *issiMember,
|
||||
int masterIdentity,
|
||||
int masterIssi);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
PrtCnvCentralThread *m_CentralThread;
|
||||
|
||||
|
||||
|
||||
PrtCnvRadioTrafficThread *m_radioTrafficThread;
|
||||
|
||||
public:
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
|
||||
void checkSendToCentral();
|
||||
|
||||
|
||||
void checkSendToRadio();
|
||||
|
||||
|
||||
void cleant();
|
||||
|
||||
|
||||
void stopped();
|
||||
|
||||
|
||||
|
||||
void checkMemberForValidLogin(int identity, int issi);
|
||||
|
||||
|
||||
|
||||
void tetraConnectionState(bool connected);
|
||||
|
||||
|
||||
void areaEffectSimulationActivated(int wirkfeldKennung);
|
||||
void areaEffectSimulationDeactivated(int wirkfeldKennung);
|
||||
|
||||
|
||||
void initDone();
|
||||
|
||||
|
||||
void playerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
|
||||
void warehouseMessageFromPtrCnv(const QByteArray &jsonData);
|
||||
protected:
|
||||
|
||||
|
||||
uint32_t m_SendCounter;
|
||||
|
||||
|
||||
uint32_t m_HostISSI;
|
||||
|
||||
|
||||
const uint32_t START_COUNTER;
|
||||
|
||||
|
||||
const uint32_t END_COUNTER;
|
||||
|
||||
|
||||
QTimer *m_EnvironmentTelegramTimer;
|
||||
|
||||
|
||||
uint32_t m_BroadcastISSI;
|
||||
|
||||
|
||||
QString m_LoginPathWithPrefix;
|
||||
|
||||
|
||||
int m_ExerciseId;
|
||||
|
||||
|
||||
std::set<uint16_t> m_AllowedIdentities;
|
||||
|
||||
|
||||
bool m_DoLicenseDespiteSelftestOrConfigFailure;
|
||||
|
||||
|
||||
bool m_DoCheckIdentity;
|
||||
PrtCnvBroadcastMgr m_BroadcastManager;
|
||||
|
||||
|
||||
bool m_DoDumpReceivedTelegrams;
|
||||
|
||||
|
||||
bool m_DoDumpSentTelegrams;
|
||||
|
||||
|
||||
bool m_DoUseSDSTL;
|
||||
|
||||
|
||||
|
||||
std::set<uint16_t> m_DoNeedActPosResp;
|
||||
|
||||
|
||||
|
||||
uint32_t m_EmergencyCallISSI;
|
||||
|
||||
|
||||
|
||||
void sendTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
|
||||
PrtCnvStoredRadioTelegram *a_handle,
|
||||
const bool a_doCheckWhetherExpired = true);
|
||||
|
||||
|
||||
|
||||
|
||||
void doRegister();
|
||||
|
||||
|
||||
|
||||
void registerGroups();
|
||||
|
||||
|
||||
|
||||
void handleRegisterRsp(const uint8_t a_regsitrationStatus);
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
enum class E_TrafficLoad
|
||||
{
|
||||
Unknown = -1,
|
||||
NormalLoad = 0,
|
||||
HighLoad,
|
||||
FullLoad
|
||||
};
|
||||
protected:
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *m_EnvironmentTelegram;
|
||||
|
||||
|
||||
|
||||
E_TrafficLoad m_SendLoad;
|
||||
|
||||
uint32_t m_SendQueueFullLoad;
|
||||
uint32_t m_SendQueueHighLoad;
|
||||
uint32_t m_SendTelegramsNormalLoad;
|
||||
uint32_t m_SendTelegramsHighLoad;
|
||||
uint32_t m_SendTelegramsFullLoad;
|
||||
|
||||
|
||||
uint32_t m_SentTelegrams;
|
||||
|
||||
|
||||
uint32_t m_SentBytes;
|
||||
|
||||
|
||||
uint32_t m_TickCounter;
|
||||
|
||||
|
||||
uint32_t m_TicksPerSecond;
|
||||
|
||||
|
||||
static const int AVERAGE_COUNT = 10;
|
||||
|
||||
|
||||
uint32_t m_SentBytesAverage[AVERAGE_COUNT];
|
||||
|
||||
|
||||
uint8_t m_AverageIndex;
|
||||
|
||||
|
||||
uint32_t m_ReceivedTelegrams;
|
||||
|
||||
|
||||
uint32_t m_ReceivedBytes;
|
||||
|
||||
|
||||
uint32_t m_ReceivedBytesAverage[AVERAGE_COUNT];
|
||||
|
||||
|
||||
|
||||
std::set<uint32_t> m_ListenGroups;
|
||||
|
||||
|
||||
|
||||
bool m_bUseScrambling;
|
||||
|
||||
|
||||
NGL::INGLLogger &m_loggerStats;
|
||||
|
||||
|
||||
std::unordered_map<int, std::unordered_set<int>> m_exerciseEquipmentAssignments;
|
||||
std::unordered_map<int, TrainingAreaBase::TetraIcd::GroupIssiInformation> m_groupIssiInformation;
|
||||
mutable QMutex m_exerciseEquipmentAssignmentMutex;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
bool sendAnswer(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
|
||||
|
||||
|
||||
void handleReceivedTelegram(const QByteArray &data);
|
||||
|
||||
|
||||
void handleDataIndReq(const QByteArray &data, bool bIsMaybeScrambled);
|
||||
|
||||
|
||||
void handleLoginTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram);
|
||||
|
||||
|
||||
void handleFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_fragmentedTelegram);
|
||||
|
||||
|
||||
void sendFehlermeldungGefechtsfeldToCentral(const uint16_t a_identity,
|
||||
const TrainingAreaBase::TetraIcd::CtaDataError::Reason a_errorCause,
|
||||
const gef_telegramm_id_et a_telegramId = GEF_BESTAETIGUNG);
|
||||
|
||||
|
||||
uint32_t getDestinationISSI(const uint16_t a_TNE_Id) const;
|
||||
|
||||
|
||||
bool handleBITEEXT_Telegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_BITE_RadioTelegram);
|
||||
bool handleBITE_Telegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_BITE_RadioTelegram,
|
||||
TrainingAreaBase::TetraIcd::BiteReport::LoginType anmeldeart,
|
||||
uint32_t ergebnisCAN,
|
||||
uint32_t ergebnisAusstattung,
|
||||
uint32_t aktuelleKonfiguration,
|
||||
uint32_t konfigurationsvergleich,
|
||||
const QList<PrtCnv::BiteError> &fehlerListe
|
||||
);
|
||||
|
||||
|
||||
|
||||
void sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus, const uint16_t a_identity, const uint32_t a_ISSI);
|
||||
void sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus,
|
||||
const uint16_t a_identity, const uint32_t a_ISSI, const TrainingAreaBase::TetraIcd::GroupIssiInformation &group);
|
||||
|
||||
|
||||
|
||||
void sendLizenzierungTneToRadio(const uint16_t a_identity, const uint32_t a_issi,
|
||||
const N_CoPrtCnv::E_LicensingKind a_licensingKind,
|
||||
const N_CoPrtCnv::E_LicensingErrorCode a_errorCause);
|
||||
|
||||
|
||||
|
||||
void writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram, const QString &a_prosaText);
|
||||
void writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram *a_radioTelegram, const QString &a_prosaText);
|
||||
void writeLoginLog(uint32_t identity, uint32_t issi, const QString &a_prosaText);
|
||||
|
||||
|
||||
void initIdentityArray();
|
||||
|
||||
|
||||
uint16_t readIdentity(const QByteArray& a_idAsText, const uint32_t a_line, const QString& a_fileName);
|
||||
|
||||
|
||||
bool isIdentityValid(uint16_t a_identity) const;
|
||||
|
||||
|
||||
|
||||
void setIdentityVaild(uint16_t a_identity);
|
||||
|
||||
|
||||
|
||||
void sendFragmentedTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram);
|
||||
|
||||
|
||||
|
||||
uint16_t sendToSocket(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram);
|
||||
|
||||
|
||||
|
||||
void handleReportIndReq(const QByteArray &data);
|
||||
void prepareWareHouseMessage(qint32 equipNr,
|
||||
bool doLicence,
|
||||
qint64 ergebnisCAN,
|
||||
qint64 ergebnisAusstattung,
|
||||
qint64 aktuelleKonfiguration,
|
||||
qint64 konfigurationsvergleich,
|
||||
const QList<PrtCnv::BiteError> &fehlerListe);
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
|
||||
void onSendEnvironmentTelegramTimerNow();
|
||||
|
||||
|
||||
void timeoutAliveAndConnection() override;
|
||||
|
||||
|
||||
void timeoutTick() override;
|
||||
|
||||
|
||||
void checkSend();
|
||||
|
||||
|
||||
void readyRead() override;
|
||||
|
||||
|
||||
void disconnected() override;
|
||||
|
||||
|
||||
void connected() override;
|
||||
|
||||
|
||||
void timeoutEnvironmentTelegram();
|
||||
|
||||
|
||||
void doClean();
|
||||
|
||||
|
||||
void doStart();
|
||||
|
||||
|
||||
void doStop();
|
||||
|
||||
|
||||
|
||||
void onBroadcastTelegramsRequestFromCentral(int equipmentNumber, int broadcastIndex, int resetCounter);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void handleStatusIndReq(const QByteArray &data);
|
||||
|
||||
|
||||
|
||||
void handleAirInterfaceLoadReq(const QByteArray &data);
|
||||
|
||||
|
||||
|
||||
void computeAndLogTrafficStatistics();
|
||||
|
||||
|
||||
|
||||
bool isSendLimitReached() const;
|
||||
|
||||
|
||||
void sendScrambledContent(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram) const;
|
||||
bool unscrambleContent(N_CoPrtCnv::CoPrtCnvRadioTelegram &a_telegram) const;
|
||||
|
||||
|
||||
friend class PrtCnvCentralThread;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const PrtCnvRadioThread::E_TrafficLoad tl);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvRadioTrafficThread.hpp"
|
||||
#include "PrtCnvDbThread.hpp"
|
||||
#include "PrtCnvThreadStarter.hpp"
|
||||
#include <QtCore/QTimer>
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
|
||||
#include <time.h>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
PrtCnvRadioTrafficThread::PrtCnvRadioTrafficThread(PrtCnvThreadStarter* a_parent)
|
||||
: PrtCnvThread(a_parent, "PrtCnvRadioTrafficThread")
|
||||
, m_receiveLoad(PrtCnvRadioThread::E_TrafficLoad::Unknown)
|
||||
, m_fullLoad(100)
|
||||
, m_highLoad(50)
|
||||
, m_hystLoad(5)
|
||||
, m_calcTimeout(1000)
|
||||
, m_cleanupTimeout(5000)
|
||||
, m_mutex(QMutex::Recursive)
|
||||
{}
|
||||
|
||||
void PrtCnvRadioTrafficThread::newTelegramArrived(unsigned int size)
|
||||
{
|
||||
time_t curTime = time(nullptr);
|
||||
|
||||
QMutexLocker l(&m_mutex);
|
||||
DEBUG_LOG(m_logger) << "newTelegramArrived with size " << size << " at " << curTime;
|
||||
m_data[curTime] += size;
|
||||
m_telegrams[curTime]++;
|
||||
}
|
||||
|
||||
int32_t PrtCnvRadioTrafficThread::start()
|
||||
{
|
||||
|
||||
const QString trafficControlGroup(QStringLiteral("TrafficControl"));
|
||||
int highLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueHighLoad"), 60);
|
||||
int fullLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueFullLoad"), 80);
|
||||
int hystLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueHysteresis"), 5);
|
||||
int avgTime = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveAveragingTime"), 5);
|
||||
float trafficPerReceiverKB = m_Config->getFloat(trafficControlGroup, QStringLiteral("ReceiveThroughput"), 3.6f);
|
||||
int cntTransceiver = m_Config->getInteger(trafficControlGroup, QStringLiteral("TransceiverCount"), 6);
|
||||
|
||||
|
||||
if (highLoad < 0 || highLoad > 100)
|
||||
highLoad = 60;
|
||||
if (fullLoad < highLoad || fullLoad > 100)
|
||||
fullLoad = 80;
|
||||
if (hystLoad < 0 || hystLoad > 10)
|
||||
hystLoad = 5;
|
||||
if (avgTime <= 0 || avgTime > 60)
|
||||
avgTime = 5;
|
||||
if (trafficPerReceiverKB < 1 || trafficPerReceiverKB > 1000)
|
||||
trafficPerReceiverKB = 3.6f;
|
||||
if (cntTransceiver < 1 || cntTransceiver > 100)
|
||||
cntTransceiver = 6;
|
||||
|
||||
float maxTrafficBytes = trafficPerReceiverKB * cntTransceiver * 1024;
|
||||
|
||||
m_fullLoad = maxTrafficBytes * fullLoad / 100.f;
|
||||
m_highLoad = maxTrafficBytes * highLoad / 100.f;
|
||||
m_hystLoad = maxTrafficBytes * hystLoad / 100.f;
|
||||
m_cleanupTimeout = avgTime * 1000;
|
||||
|
||||
DEBUG_LOG(m_logger) << "Read config ReceiveQueueHighLoad: " << highLoad;
|
||||
DEBUG_LOG(m_logger) << "Read config ReceiveQueueFullLoad: " << fullLoad;
|
||||
DEBUG_LOG(m_logger) << "Read config ReceiveQueueHysteresis: " << hystLoad;
|
||||
DEBUG_LOG(m_logger) << "Read config ReceiveAveragingTime: " << avgTime;
|
||||
DEBUG_LOG(m_logger) << "Read config ReceiveThroughput: " << trafficPerReceiverKB;
|
||||
DEBUG_LOG(m_logger) << "Read config TransceiverCount: " << cntTransceiver;
|
||||
|
||||
INFO_LOG(m_logger) << "Using to following limits: high - " << m_highLoad
|
||||
<< " full - " << m_fullLoad
|
||||
<< " hyst - " << m_hystLoad
|
||||
<< " avg. time - " << m_cleanupTimeout / 1000;
|
||||
|
||||
QTimer::singleShot(m_calcTimeout, this, &PrtCnvRadioTrafficThread::onCalculateTrafficLoad);
|
||||
QTimer::singleShot(m_cleanupTimeout, this, &PrtCnvRadioTrafficThread::onCleanupTimeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PrtCnvRadioTrafficThread::onCalculateTrafficLoad()
|
||||
{
|
||||
const time_t curTime = time(nullptr);
|
||||
|
||||
unsigned int payload = 0;
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
|
||||
for (int i = 1; i <= m_cleanupTimeout / 1000; ++i)
|
||||
payload += m_data.value(curTime - i);
|
||||
}
|
||||
|
||||
|
||||
payload /= (m_cleanupTimeout / 1000);
|
||||
|
||||
INFO_LOG(m_logger) << "Average Payload in the last "
|
||||
<< m_cleanupTimeout << " Milliseconds: "
|
||||
<< payload << " Bytes/sec, last sec: "
|
||||
<< m_data[curTime - 1] << " Bytes, #telegrams: " << m_telegrams[curTime - 1];
|
||||
|
||||
PrtCnvRadioThread::E_TrafficLoad newReceiveLoad;
|
||||
if (payload >= m_fullLoad)
|
||||
{
|
||||
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::FullLoad;
|
||||
|
||||
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::HighLoad)
|
||||
{
|
||||
|
||||
|
||||
if(payload < m_fullLoad + m_hystLoad)
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
|
||||
}
|
||||
}
|
||||
else if (payload >= m_highLoad)
|
||||
{
|
||||
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
|
||||
|
||||
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::NormalLoad)
|
||||
{
|
||||
|
||||
|
||||
if (payload < m_highLoad + m_hystLoad)
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::NormalLoad;
|
||||
}
|
||||
|
||||
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::FullLoad)
|
||||
{
|
||||
|
||||
|
||||
if (payload >= m_fullLoad - m_hystLoad)
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::FullLoad;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::NormalLoad;
|
||||
|
||||
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::HighLoad)
|
||||
{
|
||||
|
||||
|
||||
if(payload >= m_highLoad - m_hystLoad)
|
||||
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
|
||||
}
|
||||
}
|
||||
if (m_receiveLoad != newReceiveLoad)
|
||||
{
|
||||
m_receiveLoad = newReceiveLoad;
|
||||
|
||||
unsigned int wartezeit_posmldg = (static_cast<unsigned int>(m_receiveLoad) * 10) + 1;
|
||||
|
||||
INFO_LOG(m_logger) << "New traffic load: " << m_receiveLoad;
|
||||
PrtCnvDbThread *dbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
|
||||
if (dbThread)
|
||||
{
|
||||
dbThread->setAirLoad(m_receiveLoad);
|
||||
dbThread->setPositionSendTime(wartezeit_posmldg);
|
||||
|
||||
Q_ASSERT(QThread::currentThreadId() == m_currentThreadId);
|
||||
}
|
||||
else
|
||||
ERROR_LOG(m_logger) << "Could not get PrtCnvDbThread!";
|
||||
}
|
||||
|
||||
QTimer::singleShot(m_calcTimeout, this, &PrtCnvRadioTrafficThread::onCalculateTrafficLoad);
|
||||
}
|
||||
|
||||
void PrtCnvRadioTrafficThread::onCleanupTimeout()
|
||||
{
|
||||
const time_t curTime = time(nullptr);
|
||||
{
|
||||
QMutexLocker l(&m_mutex);
|
||||
QMap<time_t, unsigned int>::iterator it = m_data.begin();
|
||||
while (it != m_data.end())
|
||||
{
|
||||
const time_t key = it.key();
|
||||
|
||||
if (key < curTime - m_cleanupTimeout - 10)
|
||||
{
|
||||
m_data.erase(it++);
|
||||
m_telegrams.remove(key);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QTimer::singleShot(m_cleanupTimeout, this, &PrtCnvRadioTrafficThread::onCleanupTimeout);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvThread.hpp"
|
||||
#include "PrtCnvRadioThread.hpp"
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QMutex>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
class PrtCnvRadioTrafficThread final : public PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PrtCnvRadioTrafficThread(PrtCnvThreadStarter* a_parent);
|
||||
|
||||
|
||||
|
||||
void newTelegramArrived(unsigned int size);
|
||||
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
|
||||
void onCalculateTrafficLoad();
|
||||
|
||||
|
||||
|
||||
void onCleanupTimeout();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
PrtCnvRadioThread::E_TrafficLoad m_receiveLoad;
|
||||
|
||||
|
||||
|
||||
unsigned int m_fullLoad;
|
||||
|
||||
|
||||
|
||||
unsigned int m_highLoad;
|
||||
|
||||
|
||||
|
||||
unsigned int m_hystLoad;
|
||||
|
||||
|
||||
|
||||
int m_calcTimeout;
|
||||
|
||||
|
||||
|
||||
int m_cleanupTimeout;
|
||||
|
||||
|
||||
|
||||
QMutex m_mutex;
|
||||
|
||||
|
||||
|
||||
QMap<time_t, unsigned int> m_data;
|
||||
QMap<time_t, unsigned int> m_telegrams;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvSocketThread.hpp"
|
||||
#include "PrtCnvDbThread.hpp"
|
||||
#include "PrtCnvTelegramLogThread.hpp"
|
||||
#include "PrtCnvThreadStarter.hpp"
|
||||
#include "PrtCnvInjectionThread.hpp"
|
||||
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnvSocketThread::PrtCnvSocketThread(PrtCnv::PrtCnvThreadStarter* a_parent, const char *loggerName)
|
||||
: PrtCnvThread(a_parent, loggerName)
|
||||
, m_ConnectionTimer(nullptr)
|
||||
, m_Socket(nullptr)
|
||||
, m_TickTimer(nullptr)
|
||||
, m_ReceiveCounter(0)
|
||||
, m_ReceiveAckCounter(0)
|
||||
, m_State(PrtCnvSocketThread::e_INITIAL)
|
||||
, m_DbThread(nullptr)
|
||||
, m_ConnectTimeout(2000)
|
||||
, m_DisconnectTimeout(10000)
|
||||
, m_AliveTimeout(3000)
|
||||
, m_TickTimeout(900)
|
||||
, m_DoCleanup(false)
|
||||
, m_LogThread(nullptr)
|
||||
, m_InjectionThread(nullptr)
|
||||
, m_currentHostAdressIndex(0)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
PrtCnvSocketThread::~PrtCnvSocketThread()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvSocketThread::create(NGL::Settings *settings)
|
||||
{
|
||||
PrtCnvThread::create(settings);
|
||||
|
||||
createNewSocket();
|
||||
|
||||
m_ConnectionTimer = new QTimer(this);
|
||||
connect(m_ConnectionTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutAliveAndConnection);
|
||||
|
||||
m_TickTimer = new QTimer(this);
|
||||
connect(m_TickTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutTick);
|
||||
|
||||
m_DbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
|
||||
m_LogThread = qobject_cast<PrtCnvTelegramLogThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Log));
|
||||
m_InjectionThread = qobject_cast<PrtCnvInjectionThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Injection));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::createNewSocket()
|
||||
{
|
||||
INFO_LOG(m_logger) << "createNewSocket m_oldSocket=" << m_Socket;
|
||||
Q_ASSERT(QThread::currentThreadId() == m_currentThreadId);
|
||||
|
||||
if (m_Socket)
|
||||
{
|
||||
m_Socket->disconnectFromHost();
|
||||
m_Socket->disconnect();
|
||||
if (m_Socket->state() != QAbstractSocket::UnconnectedState)
|
||||
m_Socket->waitForDisconnected();
|
||||
m_Socket->deleteLater();
|
||||
}
|
||||
|
||||
m_Socket = new QTcpSocket(this);
|
||||
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
|
||||
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
|
||||
|
||||
connect(m_Socket, &QTcpSocket::connected, this, &PrtCnvSocketThread::connected, Qt::QueuedConnection);
|
||||
connect(m_Socket, &QTcpSocket::disconnected, this, &PrtCnvSocketThread::disconnected, Qt::QueuedConnection);
|
||||
connect(m_Socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &PrtCnvSocketThread::error, Qt::QueuedConnection);
|
||||
connect(m_Socket, &QTcpSocket::hostFound, this, &PrtCnvSocketThread::hostFound, Qt::QueuedConnection);
|
||||
connect(m_Socket, &QTcpSocket::stateChanged, this, &PrtCnvSocketThread::stateChanged);
|
||||
connect(m_Socket, &QTcpSocket::readyRead, this, &PrtCnvSocketThread::readyRead, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvSocketThread::destroy()
|
||||
{
|
||||
PrtCnvThread::destroy();
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PrtCnvSocketThread::cleanup()
|
||||
{
|
||||
delete m_Socket; m_Socket = nullptr;
|
||||
delete m_ConnectionTimer; m_ConnectionTimer = nullptr;
|
||||
delete m_TickTimer; m_TickTimer = nullptr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvSocketThread::init()
|
||||
{
|
||||
INFO_LOG(m_logger) << "PrtCnvSocketThread::init()";
|
||||
PrtCnvThread::init();
|
||||
m_State = e_INITIAL;
|
||||
|
||||
m_ConnectTimeout = m_Config->getInteger(getName(), QStringLiteral("ConnectTimeout"), 2000);
|
||||
m_DisconnectTimeout = m_Config->getInteger(getName(), QStringLiteral("DisconnectTimeout"), 10000);
|
||||
m_AliveTimeout = m_Config->getInteger(getName(), QStringLiteral("AliveTimeout"), 3000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::connectSocket()
|
||||
{
|
||||
int sockState = m_Socket ? m_Socket->state() : -1;
|
||||
INFO_LOG(m_logger) << "connectSocket m_Socket=" << m_Socket << " state=" << m_State << " socketState=" << sockState;
|
||||
if (e_WORKING != m_State && e_REGISTERING != m_State)
|
||||
{
|
||||
if (m_Socket)
|
||||
{
|
||||
m_Socket->disconnectFromHost();
|
||||
if (m_Socket->state() != QAbstractSocket::UnconnectedState)
|
||||
m_Socket->waitForDisconnected();
|
||||
}
|
||||
|
||||
createNewSocket();
|
||||
|
||||
if(m_hostAdresses.isEmpty())
|
||||
readHostAdresses();
|
||||
const QString hostname = m_hostAdresses.at(m_currentHostAdressIndex).first;
|
||||
const uint16_t port = m_hostAdresses.at(m_currentHostAdressIndex).second;
|
||||
m_currentHostAdressIndex = ((m_currentHostAdressIndex + 1) % m_hostAdresses.size());
|
||||
INFO_LOG(m_logger) << "Connecting to " << hostname << ":" << port << " m_Socket=" << m_Socket;
|
||||
m_Socket->connectToHost(hostname, port);
|
||||
m_State = e_CONNECTING;
|
||||
startConnectionTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING_LOG(m_logger) << getName() << ": connectSocket called, but m_State is " << m_State;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::disconnectSocket()
|
||||
{
|
||||
m_Socket->disconnectFromHost();
|
||||
startDisconnectionTimer();
|
||||
m_State = e_DISCONNECTING;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::connected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "connected (implement connected() in inherited class, if you want use this signal...)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::disconnected()
|
||||
{
|
||||
INFO_LOG(m_logger) << "got signal 'disconnected' (implement disconnected() in inherited class, if you want use this signal...)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::readyRead()
|
||||
{
|
||||
INFO_LOG(m_logger) << "got signal 'newData' (implement newData() in inherited class, if you want use this signal...)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::timeoutAliveAndConnection()
|
||||
{
|
||||
INFO_LOG(m_logger) << "got signal 'reconnectTimeout' (implement reconnectTimeout() in inherited class, if you want use this signal...)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::error(QAbstractSocket::SocketError a_socketError)
|
||||
{
|
||||
switch (a_socketError)
|
||||
{
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
ERROR_LOG(m_logger) << getName() << ": ConnectionRefusedError m_State=" << m_State;
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
ERROR_LOG(m_logger) << getName() << ": RemoteHostClosedError";
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(m_logger) << getName() << ": got signal 'error(QAbstractSocket::SocketError=" << a_socketError << ")'";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::hostFound()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::stateChanged(QAbstractSocket::SocketState a_socketState)
|
||||
{
|
||||
Q_UNUSED(a_socketState);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
|
||||
const uint32_t a_readCounter)
|
||||
{
|
||||
m_DbThread->storeRadioTelegram(a_telegram, a_readCounter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::startAliveTimer()
|
||||
{
|
||||
m_ConnectionTimer->setInterval(m_AliveTimeout);
|
||||
m_ConnectionTimer->start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::startConnectionTimer()
|
||||
{
|
||||
m_ConnectionTimer->setInterval(m_ConnectTimeout);
|
||||
m_ConnectionTimer->start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::startDisconnectionTimer()
|
||||
{
|
||||
m_ConnectionTimer->setInterval(m_DisconnectTimeout);
|
||||
m_ConnectionTimer->start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvSocketThread::startTickTimer()
|
||||
{
|
||||
|
||||
m_TickTimer->setInterval(m_TickTimeout);
|
||||
m_TickTimer->start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool PrtCnvSocketThread::checkAndUpdateReceiveCounter(const uint32_t a_counter, const uint32_t a_maxCounter, const uint32_t a_startCounter)
|
||||
{
|
||||
TRACE_LOG(m_logger) << "checkAndUpdateReceiveCounter: " << a_counter << ", " << m_ReceiveCounter;
|
||||
|
||||
if (a_maxCounter == ++m_ReceiveCounter)
|
||||
{
|
||||
m_ReceiveCounter = a_startCounter;
|
||||
}
|
||||
|
||||
if (m_ReceiveCounter != a_counter)
|
||||
{
|
||||
|
||||
{
|
||||
if (m_ReceiveCounter > a_counter)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "It seems, the telegram with counter " << a_counter << " was resent from "
|
||||
<< getName() << "! Expected: " << m_ReceiveCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << "It seems, we lost one or several telegrams from " << getName()
|
||||
<< ": receive-counter of received telegram: " << a_counter << "; Expected: " << m_ReceiveCounter;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
m_ReceiveCounter--;
|
||||
return false;
|
||||
#else
|
||||
m_ReceiveCounter = a_counter;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrtCnvSocketThread::readHostAdresses()
|
||||
{
|
||||
QString host = m_Config->getString(getName(), QStringLiteral("HostName"), QStringLiteral("127.0.0.1"));
|
||||
uint16_t port = m_Config->getInteger(getName(), QStringLiteral("Port"), 4934);
|
||||
m_hostAdresses.push_back(qMakePair(host, port));
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
const QString hostName = QLatin1String("HostName") + QString::number(i);
|
||||
const QString portName = QLatin1String("Port") + QString::number(i);
|
||||
host = m_Config->getString(getName(), hostName, QString());
|
||||
port = m_Config->getInteger(getName(), portName, 0);
|
||||
if (host.isEmpty() || port == 0)
|
||||
continue;
|
||||
m_hostAdresses.push_back(qMakePair(host, port));
|
||||
}
|
||||
for (const auto &p : qAsConst(m_hostAdresses)) {
|
||||
INFO_LOG(m_logger) << "Read Host Adress " << p.first
|
||||
<< ":" << p.second
|
||||
<< " from settings";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvThread.hpp"
|
||||
|
||||
#include <QtNetwork/QAbstractSocket>
|
||||
#include <memory>
|
||||
|
||||
class QTimer;
|
||||
class QTcpSocket;
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvRadioTelegram;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvThreadStarter;
|
||||
class PrtCnvDbThread;
|
||||
class PrtCnvTelegramLogThread;
|
||||
class PrtCnvInjectionThread;
|
||||
|
||||
|
||||
class PrtCnvSocketThread : public PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
|
||||
|
||||
enum E_State
|
||||
{
|
||||
e_INITIAL,
|
||||
e_CONNECTING,
|
||||
e_REGISTERING,
|
||||
e_WORKING,
|
||||
e_DISCONNECTING,
|
||||
e_REINIT
|
||||
};
|
||||
|
||||
|
||||
PrtCnvSocketThread(PrtCnvThreadStarter* a_parent, const char *loggerName);
|
||||
~PrtCnvSocketThread();
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
|
||||
|
||||
void createNewSocket();
|
||||
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
int32_t destroy() override;
|
||||
void cleanup();
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
QTimer *m_ConnectionTimer;
|
||||
|
||||
|
||||
|
||||
|
||||
QTcpSocket *m_Socket;
|
||||
|
||||
|
||||
|
||||
void readHostAdresses();
|
||||
|
||||
protected slots:
|
||||
|
||||
|
||||
virtual void connected();
|
||||
|
||||
|
||||
virtual void readyRead();
|
||||
|
||||
|
||||
virtual void timeoutAliveAndConnection();
|
||||
|
||||
|
||||
virtual void timeoutTick() {}
|
||||
|
||||
|
||||
virtual void error(QAbstractSocket::SocketError a_socketError);
|
||||
|
||||
|
||||
virtual void hostFound();
|
||||
|
||||
|
||||
virtual void stateChanged(QAbstractSocket::SocketState a_socketState);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
QTimer *m_TickTimer;
|
||||
|
||||
|
||||
uint32_t m_ReceiveCounter;
|
||||
|
||||
|
||||
uint32_t m_ReceiveAckCounter;
|
||||
|
||||
|
||||
PrtCnvSocketThread::E_State m_State;
|
||||
|
||||
|
||||
PrtCnvDbThread *m_DbThread;
|
||||
|
||||
|
||||
|
||||
int32_t m_ConnectTimeout;
|
||||
|
||||
|
||||
int32_t m_DisconnectTimeout;
|
||||
|
||||
|
||||
int32_t m_AliveTimeout;
|
||||
|
||||
|
||||
int32_t m_TickTimeout;
|
||||
|
||||
|
||||
QAtomicInt m_DoCleanup;
|
||||
|
||||
|
||||
PrtCnvTelegramLogThread *m_LogThread;
|
||||
|
||||
PrtCnvInjectionThread *m_InjectionThread;
|
||||
|
||||
|
||||
|
||||
QVector<QPair<QString, uint16_t> > m_hostAdresses;
|
||||
|
||||
|
||||
|
||||
int m_currentHostAdressIndex;
|
||||
|
||||
|
||||
|
||||
virtual void disconnected();
|
||||
|
||||
|
||||
void startAliveTimer();
|
||||
|
||||
|
||||
void startConnectionTimer();
|
||||
|
||||
|
||||
void startDisconnectionTimer();
|
||||
|
||||
|
||||
|
||||
void connectSocket();
|
||||
|
||||
|
||||
void disconnectSocket();
|
||||
|
||||
|
||||
|
||||
void startTickTimer();
|
||||
|
||||
|
||||
|
||||
bool checkAndUpdateReceiveCounter(const uint32_t a_counter, const uint32_t a_maxCounter, const uint32_t a_startCounter);
|
||||
|
||||
|
||||
|
||||
void storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram, const uint32_t a_readCounter);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvStoredTelegram.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvStoredCentralTelegram : public PrtCnv::PrtCnvStoredTelegram
|
||||
{
|
||||
public:
|
||||
PrtCnvStoredCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
|
||||
const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram)
|
||||
: PrtCnvStoredTelegram(a_radioTelegram)
|
||||
, m_CentralTelegram(a_centralTelegram)
|
||||
{}
|
||||
|
||||
int writeCounter() const { return m_WriteCounter; }
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> m_CentralTelegram;
|
||||
|
||||
|
||||
uint32_t m_Attempts = 0;
|
||||
|
||||
|
||||
int m_WriteCounter = 0;
|
||||
|
||||
|
||||
bool m_DoResend = false;
|
||||
|
||||
|
||||
bool m_DoDelete = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvStoredTelegram.hpp"
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvStoredRadioTelegram : public PrtCnv::PrtCnvStoredTelegram
|
||||
{
|
||||
public:
|
||||
using PrtCnv::PrtCnvStoredTelegram::PrtCnvStoredTelegram;
|
||||
|
||||
|
||||
|
||||
int32_t m_ReadCounter = 0;
|
||||
|
||||
|
||||
uint16_t m_BroadcastIndex = 0;
|
||||
|
||||
|
||||
uint8_t m_WriteCounter = 0;
|
||||
|
||||
|
||||
bool m_DoDelete = false;
|
||||
|
||||
|
||||
bool m_IsSent = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvStoredTelegram
|
||||
{
|
||||
public:
|
||||
explicit PrtCnvStoredTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram)
|
||||
: m_RadioTelegram(a_telegram)
|
||||
{}
|
||||
|
||||
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> m_RadioTelegram;
|
||||
int64_t m_Id = 0;
|
||||
bool m_WriteCounterChanged = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvTelegramLogThread.hpp"
|
||||
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvLogHeader.hpp>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QDateTime>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
|
||||
static int gettimeofday(struct timeval *__p, void *__t)
|
||||
{
|
||||
union {
|
||||
unsigned long long ns100;
|
||||
FILETIME ft;
|
||||
} now;
|
||||
|
||||
constexpr long long SECONDS_SINCE_1601 = 11644473600LL;
|
||||
constexpr long long USEC_IN_SEC = 1000000LL;
|
||||
|
||||
GetSystemTimeAsFileTime(&now.ft);
|
||||
__p->tv_usec = (long)((now.ns100 / 10LL) % USEC_IN_SEC);
|
||||
__p->tv_sec = (long)(((now.ns100 / 10LL) / USEC_IN_SEC) - SECONDS_SINCE_1601);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
PrtCnvTelegramLogThread::PrtCnvTelegramLogThread(PrtCnvThreadStarter* a_parent)
|
||||
: PrtCnvThread(a_parent, "PrtCnvTelegramLogThread")
|
||||
, m_DoLogCentralTelegram(true)
|
||||
, m_DoLogWlsTelegram(true)
|
||||
, m_DoLogRadioTelegram(true)
|
||||
{
|
||||
m_Name = QStringLiteral("TelegramLog");
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvTelegramLogThread::create(NGL::Settings *settings)
|
||||
{
|
||||
PrtCnvThread::create(settings);
|
||||
|
||||
connect(this, &PrtCnvTelegramLogThread::telegramLogReceived,
|
||||
this, &PrtCnvTelegramLogThread::writeTelegramLog,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t PrtCnvTelegramLogThread::init()
|
||||
{
|
||||
PrtCnvThread::init();
|
||||
m_DoLogCentralTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogCentralTelegrams"), true);
|
||||
m_DoLogWlsTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogWlsTelegrams"), true);
|
||||
m_DoLogRadioTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogRadioTelegrams"), true);
|
||||
QString path = m_Config->getString(QStringLiteral("Common"), QStringLiteral("LogTelegramPath"), QStringLiteral("."));
|
||||
if (!QDir::isAbsolutePath(path))
|
||||
path = QCoreApplication::applicationDirPath() + QStringLiteral("/../") + path;
|
||||
|
||||
m_LogTelegramPathWithPrefix = path + QLatin1Char('/') + m_Config->getString(QStringLiteral("Common"), QStringLiteral("LogTelegramPrefix"), QStringLiteral("PrtCnv_"));
|
||||
if (!m_LogTelegramPathWithPrefix.endsWith(QLatin1Char('_')))
|
||||
m_LogTelegramPathWithPrefix += QLatin1Char('_');
|
||||
|
||||
|
||||
QFileInfo fileInfo(m_LogTelegramPathWithPrefix);
|
||||
QDir dir;
|
||||
dir.mkpath(fileInfo.absolutePath());
|
||||
|
||||
INFO_LOG(m_logger);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int setSend(int protocol, bool bIsSend)
|
||||
{
|
||||
return (protocol | (bIsSend ? N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend : N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvTelegramLogThread::writeCentralTelegramLog(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_centralTelegram, bool a_isSend, int a_priority)
|
||||
{
|
||||
if (!m_DoLogCentralTelegram)
|
||||
return;
|
||||
|
||||
emit telegramLogReceived(a_centralTelegram->data(),
|
||||
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral, a_isSend),
|
||||
a_priority);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvTelegramLogThread::writeWlsTelegramLog(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool a_isSend, int a_priority)
|
||||
{
|
||||
if (!m_DoLogWlsTelegram)
|
||||
return;
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram central(*tele);
|
||||
emit telegramLogReceived(central.data(),
|
||||
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls, a_isSend),
|
||||
a_priority);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvTelegramLogThread::writeRadioTelegramLog(const QByteArray &tele, bool a_isSend, int a_priority)
|
||||
{
|
||||
if (!m_DoLogRadioTelegram)
|
||||
return;
|
||||
|
||||
emit telegramLogReceived(tele,
|
||||
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra, a_isSend),
|
||||
a_priority);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvTelegramLogThread::writeTelegramLog(const QByteArray &a_data, int a_protocolType, int a_priority)
|
||||
{
|
||||
|
||||
timeval tv;
|
||||
gettimeofday(&tv, nullptr);
|
||||
uint64_t exactTime = tv.tv_sec;
|
||||
exactTime = exactTime << 32;
|
||||
exactTime += tv.tv_usec;
|
||||
|
||||
|
||||
QString fileName = QStringLiteral("%1%2.krlog")
|
||||
.arg(m_LogTelegramPathWithPrefix, QDateTime::fromSecsSinceEpoch(tv.tv_sec).toString(QStringLiteral("yyMMdd_hh")));
|
||||
|
||||
|
||||
QFile telegramFile(fileName);
|
||||
telegramFile.open(QIODevice::WriteOnly | QIODevice::Append);
|
||||
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvLogHeader lh;
|
||||
lh.m_Length = a_data.size() + sizeof(N_CoPrtCnv::CoPrtCnvLogHeader);
|
||||
lh.m_Version = 3;
|
||||
lh.m_Priority = a_priority;
|
||||
lh.m_Protocol = a_protocolType;
|
||||
lh.m_Time = exactTime;
|
||||
lh.m_Reserved = 0;
|
||||
telegramFile.write(reinterpret_cast<const char*>(&lh), sizeof(N_CoPrtCnv::CoPrtCnvLogHeader));
|
||||
|
||||
|
||||
telegramFile.write(a_data);
|
||||
telegramFile.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvThread.hpp"
|
||||
|
||||
namespace N_CoPrtCnv
|
||||
{
|
||||
class CoPrtCnvCentralTelegram;
|
||||
}
|
||||
|
||||
namespace TrainingAreaBase
|
||||
{
|
||||
namespace TetraIcd
|
||||
{
|
||||
struct TrainingAreaTelegram;
|
||||
}
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
class PrtCnvTelegramLogThread final : public PrtCnvThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
|
||||
explicit PrtCnvTelegramLogThread(PrtCnvThreadStarter* a_parent);
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
public slots:
|
||||
|
||||
|
||||
void writeCentralTelegramLog(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_centralTelegram, bool a_isSend, int a_priority);
|
||||
|
||||
|
||||
|
||||
void writeWlsTelegramLog(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool a_isSend, int a_priority);
|
||||
|
||||
|
||||
|
||||
void writeRadioTelegramLog(const QByteArray &tele, bool a_isSend, int a_priority);
|
||||
|
||||
private:
|
||||
signals:
|
||||
|
||||
|
||||
void telegramLogReceived(const QByteArray &a_data, int a_protocolType, int a_priority);
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
void writeTelegramLog(const QByteArray &a_data, int a_protocolType, int a_priority);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
QString m_LogTelegramPathWithPrefix;
|
||||
|
||||
|
||||
bool m_DoLogCentralTelegram;
|
||||
|
||||
|
||||
bool m_DoLogWlsTelegram;
|
||||
|
||||
|
||||
bool m_DoLogRadioTelegram;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvThread.hpp"
|
||||
#include <QThread>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
bool PrtCnvThread::m_DoCleanDb = false;
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnvThread::PrtCnvThread(PrtCnv::PrtCnvThreadStarter* a_parent, const char *loggerName)
|
||||
: m_Parent(a_parent)
|
||||
, m_currentThreadId(nullptr)
|
||||
, m_logger(getLogger(loggerName))
|
||||
{}
|
||||
|
||||
int32_t PrtCnvThread::create(NGL::Settings *settings)
|
||||
{
|
||||
m_currentThreadId = QThread::currentThreadId();
|
||||
m_Config = settings;
|
||||
|
||||
INFO_LOG(m_logger) << "create " << m_Name
|
||||
<< " - current thread id is " << m_currentThreadId;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvThread::init()
|
||||
{
|
||||
INFO_LOG(m_logger) << "init " << m_Name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvThread::start()
|
||||
{
|
||||
INFO_LOG(m_logger) << "start " << m_Name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvThread::stop()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvThread::deinit()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int32_t PrtCnvThread::destroy()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IAppComponent.hpp"
|
||||
#include "logger.h"
|
||||
#include <QObject>
|
||||
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvThreadStarter;
|
||||
|
||||
|
||||
class PrtCnvThread : public QObject, public N_AppComponent::IAppComponent
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
|
||||
|
||||
NGL::Settings* m_Config;
|
||||
|
||||
|
||||
PrtCnvThreadStarter *m_Parent;
|
||||
|
||||
|
||||
Qt::HANDLE m_currentThreadId;
|
||||
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
static bool m_DoCleanDb;
|
||||
|
||||
|
||||
PrtCnvThread(PrtCnvThreadStarter* a_parent, const char *loggerName);
|
||||
|
||||
|
||||
int32_t create(NGL::Settings *settings) override;
|
||||
|
||||
|
||||
int32_t init() override;
|
||||
|
||||
|
||||
int32_t start() override;
|
||||
|
||||
|
||||
int32_t stop() override;
|
||||
|
||||
|
||||
int32_t deinit() override;
|
||||
|
||||
|
||||
int32_t destroy() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "PrtCnvThreadStarter.hpp"
|
||||
#include "PrtCnvCentralThread.hpp"
|
||||
#include "PrtCnvRadioThread.hpp"
|
||||
#include "PrtCnvDbThread.hpp"
|
||||
#include "PrtCnvTelegramLogThread.hpp"
|
||||
#include "PrtCnvInjectionThread.hpp"
|
||||
#include "PrtCnvRadioTrafficThread.hpp"
|
||||
#include "PrtCnvApplication.hpp"
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnvThreadStarter::PrtCnvThreadStarter(const PrtCnv::PrtCnvApplication::E_ThreadId a_id,
|
||||
const QString &configFileName,
|
||||
PrtCnv::PrtCnvApplication* a_mainApp)
|
||||
: m_Thread(nullptr)
|
||||
, m_Application(a_mainApp)
|
||||
, m_Id(a_id)
|
||||
, m_settings(new NGL::Settings(configFileName))
|
||||
, m_logger(getLogger("PrtCnvThreadStarter"))
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "Using Config file '" << configFileName << "'";
|
||||
}
|
||||
|
||||
|
||||
PrtCnvThreadStarter::~PrtCnvThreadStarter()
|
||||
{
|
||||
delete m_settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void PrtCnvThreadStarter::run()
|
||||
{
|
||||
INFO_LOG(m_logger) << "Entering event loop for thread " << this << ", id: " << m_Id;
|
||||
|
||||
switch (m_Id)
|
||||
{
|
||||
case PrtCnvApplication::e_Central:
|
||||
m_Thread = new PrtCnvCentralThread(this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_Radio:
|
||||
m_Thread = new PrtCnvRadioThread(m_Application->exerciseId(), this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_Db:
|
||||
m_Thread = new PrtCnvDbThread(m_Application->databaseDriver(),
|
||||
m_Application->databaseHostName(),
|
||||
m_Application->databaseName(),
|
||||
m_Application->databaseSchemaName(),
|
||||
m_Application->databaseUserName(),
|
||||
m_Application->databasePassword(),
|
||||
this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_Log:
|
||||
m_Thread = new PrtCnvTelegramLogThread(this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_Injection:
|
||||
m_Thread = new PrtCnvInjectionThread(this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_RadioTraffic:
|
||||
m_Thread = new PrtCnvRadioTrafficThread(this);
|
||||
break;
|
||||
|
||||
case PrtCnvApplication::e_ThreadIdMax:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_Thread)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Unknown threadId: " << m_Id;
|
||||
return;
|
||||
}
|
||||
|
||||
while (!m_Application)
|
||||
sleep(1);
|
||||
|
||||
m_Application->waitForCreate(m_Id);
|
||||
m_Thread->create(m_settings);
|
||||
m_Application->waitForInit(m_Id);
|
||||
m_Thread->init();
|
||||
m_Application->waitForStart(m_Id);
|
||||
m_Thread->start();
|
||||
m_Application->waitForExec(m_Id);
|
||||
|
||||
exec();
|
||||
|
||||
INFO_LOG(m_logger) << "Leaving event loop for thread " << this << ", id: " << m_Id;
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PrtCnvThread *PrtCnvThreadStarter::getOtherThread(PrtCnvApplication::E_ThreadId a_threadId) const
|
||||
{
|
||||
return m_Application->getThreadStarter(a_threadId)->m_Thread;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PrtCnvApplication.hpp"
|
||||
#include <QtCore/QThread>
|
||||
|
||||
namespace NGL {
|
||||
class Settings;
|
||||
}
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
class PrtCnvThread;
|
||||
|
||||
|
||||
class PrtCnvThreadStarter : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
|
||||
|
||||
PrtCnvThread *m_Thread;
|
||||
|
||||
|
||||
PrtCnvApplication *m_Application;
|
||||
|
||||
|
||||
PrtCnvApplication::E_ThreadId m_Id;
|
||||
|
||||
|
||||
NGL::Settings *m_settings;
|
||||
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
PrtCnvThreadStarter(const PrtCnvApplication::E_ThreadId a_id,
|
||||
const QString &settingsFile,
|
||||
PrtCnvApplication *a_mainApp);
|
||||
|
||||
~PrtCnvThreadStarter();
|
||||
|
||||
PrtCnvThread *getThread() const { return m_Thread; }
|
||||
PrtCnvApplication *getMainApp() const { return m_Application; }
|
||||
|
||||
|
||||
PrtCnvThread *getOtherThread(PrtCnvApplication::E_ThreadId a_threadId) const;
|
||||
protected:
|
||||
|
||||
|
||||
void run() override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
SET(tts_SRCS
|
||||
main.cpp
|
||||
server.cpp
|
||||
worker.cpp
|
||||
|
||||
log4cxx_tetratestserver.props
|
||||
)
|
||||
|
||||
SET(tts_HDRS
|
||||
server.h
|
||||
worker.h
|
||||
)
|
||||
|
||||
NGL_ADD_EXECUTABLE(AAR SV tts ${tts_SRCS} ${tts_HDRS})
|
||||
NGL_TARGET_LINK_LIBRARIES(tts
|
||||
CoPrtCnv
|
||||
NGLLogger
|
||||
LtsCommandLineParser
|
||||
nglsettings
|
||||
nglcoreapplication
|
||||
Qt5::Network
|
||||
)
|
||||
NGL_INSTALL_TARGET(tts)
|
||||
|
||||
SET(LOG4CXX_PROPS_FILE log4cxx_tetratestserver.props)
|
||||
NGL_INSTALL(FILES SV ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} DESTINATION "bin")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
log4j.reset=true
|
||||
log4j.rootLogger=WARN, stdout
|
||||
#log4j.rootLogger=WARN, file, stdout
|
||||
|
||||
log4j.logger.TTWorker=INFO
|
||||
log4j.logger.TTServer=INFO
|
||||
|
||||
|
||||
|
||||
# Appnd_File
|
||||
###########################################################
|
||||
log4j.appender.file=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.file.MaxFileSize=1MB
|
||||
log4j.appender.file.MaxBackupIndex=9
|
||||
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_TetraTestServer.log
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Console
|
||||
###########################################################
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n
|
||||
|
||||
#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n
|
||||
|
||||
|
||||
# Appnd_Syslog
|
||||
###########################################################
|
||||
log4j.appender.syslog=org.apache.log4j.SyslogAppender
|
||||
log4j.appender.syslog.Threshold=ERROR
|
||||
log4j.appender.syslog.SysLogHost=localhost
|
||||
log4j.appender.syslog.Facility=LOCAL7
|
||||
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
|
||||
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMin=FATAL
|
||||
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
|
||||
|
||||
|
||||
###########################################################
|
||||
###########################################################
|
||||
###########################################################
|
||||
# 1) Available Levels in LOG4CXX Version 0.10.0:
|
||||
# TRACE,
|
||||
# DEBUG,
|
||||
# INFO,
|
||||
# WARN,
|
||||
# ERROR
|
||||
# FATAL
|
||||
#
|
||||
# d = date/time
|
||||
# p = priority (Level)
|
||||
# c = category/channel
|
||||
# m = message
|
||||
# F = file
|
||||
# L = line
|
||||
# n = platform dependent line separator character or characters.
|
||||
# ....
|
||||
#
|
||||
# Examples for console:
|
||||
# a)
|
||||
# %d %p %c #%m# (%.50F:%L)%n
|
||||
# date/time
|
||||
# priority
|
||||
# category/channel
|
||||
# #message#
|
||||
# (Maximal 50Character des Filename von rechts:Line)
|
||||
# line separator
|
||||
#
|
||||
# b)
|
||||
# %d (%.15F:%L) %-5p %c |#| %m%n
|
||||
# date/time
|
||||
# (Maximal 15Character des Filename von rechts:Line)
|
||||
# priority, Right pad with spaces if the priority name is less than 5 characters long.
|
||||
# category/channel
|
||||
# message
|
||||
# line separator
|
||||
#
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
|
||||
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QPointF>
|
||||
#include <QtCore/QStringList>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <nglapplication/nglcoreapplication.h>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
|
||||
#include <LtsCommandLineParser/LtsCommandLineParser.h>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QCoreApplication::setApplicationName(QLatin1String("TTS"));
|
||||
QCoreApplication::setOrganizationName(QLatin1String("RME"));
|
||||
NGLCoreApplication app(argc, argv);
|
||||
|
||||
app.createLogger("tts", "log4cxx_tetratestserver.props");
|
||||
NGL::INGLLogger &logger = app.baseLogger();
|
||||
QScopedPointer<QSettings> settings(new QSettings(app.organizationName(), app.applicationName()));
|
||||
|
||||
ERROR_LOG(logger) << "Settings Directory: " << settings->fileName();
|
||||
|
||||
LtsCommandLineParser::LtsCommandLineParser options(LtsCommandLineParser::LtsCommandLineParser::CustomConfigFile);
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("p") << QLatin1String("player")
|
||||
, QLatin1String("number of players to be simulated.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("5")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("n") << QLatin1String("northing")
|
||||
, QLatin1String("initial position UTM north.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("77777")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("e") << QLatin1String("easting")
|
||||
, QLatin1String("initial position UTM east.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("99999")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("f") << QLatin1String("first-id")
|
||||
, QLatin1String("first equipment ID to use.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("1101")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("r") << QLatin1String("pprow")
|
||||
, QLatin1String("players per row.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("25")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("s") << QLatin1String("pspacing")
|
||||
, QLatin1String("utm based spacing between the players.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("100")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("z") << QLatin1String("issi")
|
||||
, QLatin1String("DestinationIssi / HostISSI")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("2")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("m") << QLatin1String("movements")
|
||||
, QLatin1String("how many movements per milliseconds to make.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("2000")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("d") << QLatin1String("death")
|
||||
, QLatin1String("minimum times, after the movements timer is triggered, a dead player can revive.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("100")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("o") << QLatin1String("death-offset")
|
||||
, QLatin1String("maximum times, after the movements timer is triggered, a dead player can revive.")
|
||||
, QLatin1String("(0 -> identical to min)")
|
||||
, QLatin1String("0")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("t") << QLatin1String("pattern")
|
||||
, QLatin1String("pattern the player has to walk [enter pattern by hand or pattern alias-name from settings file]\r\nfor more info, enter '--pattern-help'.")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("3,30,20#3,30,-20#3,-30,-20#3,-30,20")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("demand-old-broadcast-telegrams")
|
||||
, QLatin1String("player demand old broadcast telegrams starting at BCI <default value>")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("0")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("deactivate-single-mines")
|
||||
, QLatin1String("the first player deactivates single mines with ids from minimum-mine-id to maximum-mine-id")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("minimum-mine-id")
|
||||
, QLatin1String("sets the value for minimum-mine-id used within deactivate-single-mines command")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("minimum-mine-id 2048")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("maximum-mine-id")
|
||||
, QLatin1String("sets the value for maximum-mine-Id used within deactivate-single-mines command")
|
||||
, QLatin1String("Default value")
|
||||
, QLatin1String("maximum-mine-id 2100")
|
||||
});
|
||||
|
||||
options.addOption({ QStringList() << QLatin1String("pattern-help")
|
||||
, QLatin1String("help for the pattern interpreter.")
|
||||
});
|
||||
|
||||
|
||||
if (!options.init())
|
||||
{
|
||||
options.showHelp();
|
||||
return -1;
|
||||
}
|
||||
QSettings serverSettings(app.applicationDirPath() + QLatin1String("/") + options.iniFile(), QSettings::IniFormat);
|
||||
|
||||
TTSArgs ttsArgs;
|
||||
|
||||
ttsArgs.destIssi = options.isSet(QLatin1String("issi")) ? options.value(QLatin1String("issi")).toInt() : 2;
|
||||
ttsArgs.cntPlayer = options.isSet(QLatin1String("player")) ? options.value(QLatin1String("player")).toInt() : 0;
|
||||
int northPos_arg = options.isSet(QLatin1String("northing")) ? options.value(QLatin1String("northing")).toInt() : 77777;
|
||||
int eastPos_arg = options.isSet(QLatin1String("easting")) ? options.value(QLatin1String("easting")).toInt() : 90000;
|
||||
ttsArgs.position = QPointF(eastPos_arg, northPos_arg);
|
||||
ttsArgs.eqIdStart = options.isSet(QLatin1String("first-id")) ? options.value(QLatin1String("first-id")).toInt() : 1101;
|
||||
ttsArgs.pprow = options.isSet(QLatin1String("pprow")) ? options.value(QLatin1String("pprow")).toInt() : 25;
|
||||
ttsArgs.pspacing = options.isSet(QLatin1String("pspacing")) ? options.value(QLatin1String("pspacing")).toInt() : 100;
|
||||
ttsArgs.movs_per_msec = options.isSet(QLatin1String("movements")) ? options.value(QLatin1String("movements")).toInt() : 2000;
|
||||
ttsArgs.minimumDeathTicks = options.isSet(QLatin1String("death")) ? options.value(QLatin1String("death")).toInt() : 10;
|
||||
ttsArgs.maximumDeathTicks = options.isSet(QLatin1String("death-offset")) ? options.value(QLatin1String("death-offset")).toInt() : 20;
|
||||
ttsArgs.demandOldBraodcastTelegrams = options.isSet(QLatin1String("demand-old-broadcast-telegrams")) ? true : false;
|
||||
if (ttsArgs.demandOldBraodcastTelegrams)
|
||||
ttsArgs.demandOldBraodcastTelegramsCounter = options.value(QLatin1String("demand-old-broadcast-telegrams")).toInt();
|
||||
ttsArgs.deactivationSingleMines = options.isSet(QLatin1String("deactivate-single-mines")) ? true : false;
|
||||
ttsArgs.minimumMineId = options.isSet(QLatin1String("minimum-mine-id")) ? options.value(QLatin1String("minimum-mine-id")).toInt() : 2048;
|
||||
ttsArgs.maximumMineId = options.isSet(QLatin1String("maximum-mine-id")) ? options.value(QLatin1String("maximum-mine-id")).toInt() : 2100;
|
||||
ttsArgs.pattern = options.isSet(QLatin1String("pattern")) ? options.value(QLatin1String("pattern")) : QLatin1String("3,30,20#3,30,-20#3,-30,-20#3,-30,20");
|
||||
|
||||
if (options.isSet(QLatin1String("pattern-help")))
|
||||
{
|
||||
options.showHelp();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (ttsArgs.maximumDeathTicks == 0)
|
||||
ttsArgs.maximumDeathTicks = ttsArgs.minimumDeathTicks;
|
||||
|
||||
WARNING_LOG(logger) << "Simulating " << ttsArgs.cntPlayer << " players";
|
||||
WARNING_LOG(logger) << " ... DestinationIssi/HostIssi: " << ttsArgs.destIssi;
|
||||
WARNING_LOG(logger) << " ... on Position n/e: " << ttsArgs.position.y() << "/" << ttsArgs.position.x();
|
||||
WARNING_LOG(logger) << " ... first equipmentID is: " << ttsArgs.eqIdStart;
|
||||
WARNING_LOG(logger) << " ... player is reborn randomly after minimum " << ttsArgs.minimumDeathTicks
|
||||
<< " ticks and maximum " << ttsArgs.maximumDeathTicks << " ticks";
|
||||
WARNING_LOG(logger) << " ... pattern = " << ttsArgs.pattern;
|
||||
WARNING_LOG(logger) << " ... max. " << ttsArgs.pprow << " players per row";
|
||||
WARNING_LOG(logger) << " ... spacing of '" << ttsArgs.pspacing << " UTM east' between players";
|
||||
WARNING_LOG(logger) << " ... one player will be moved after " << ttsArgs.movs_per_msec << " msecs";
|
||||
|
||||
TTServer *s = new TTServer(ttsArgs);
|
||||
|
||||
if (!s->listen(QHostAddress::Any, serverSettings.value(QLatin1String("Radio/Port"), 4200).toInt()))
|
||||
{
|
||||
ERROR_LOG(logger) << "Error listening on localhost:" << serverSettings.value(QLatin1String("Radio/Port"), 52010).toInt() << ": "
|
||||
<< s->errorString();
|
||||
delete s;
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG(logger) << "Starting Server";
|
||||
|
||||
int ret = app.exec();
|
||||
|
||||
delete s;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
|
||||
#include "worker.h"
|
||||
|
||||
TTServer::TTServer(const TTSArgs &ttsArgs)
|
||||
: m_ttsArgs(ttsArgs)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTServer"))
|
||||
{
|
||||
connect(this, &TTServer::newConnection,
|
||||
this, &TTServer::onNewConnection);
|
||||
}
|
||||
|
||||
void TTServer::onNewConnection()
|
||||
{
|
||||
QTcpSocket *sock = nextPendingConnection();
|
||||
if (!sock)
|
||||
return;
|
||||
NOTICE_LOG(m_logger) << "New incoming connection from "
|
||||
<< sock->peerName() << ":"
|
||||
<< sock->peerPort();
|
||||
|
||||
TTWorker *ttw = new TTWorker(sock, m_ttsArgs);
|
||||
if (!ttw->init())
|
||||
exit(5);
|
||||
|
||||
connect(sock, &QTcpSocket::disconnected,
|
||||
this, &TTServer::onDisconnect);
|
||||
}
|
||||
|
||||
void TTServer::onDisconnect()
|
||||
{
|
||||
NOTICE_LOG(m_logger) << "Disconnected";
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QPointF>
|
||||
#include <QtNetwork/QTcpServer>
|
||||
#include <QtCore/QSettings>
|
||||
#include <nglsettings/nglsettings_settings.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
|
||||
struct runningPattern
|
||||
{
|
||||
int steps = 0;
|
||||
int ethering = 0;
|
||||
int northering = 0;
|
||||
};
|
||||
|
||||
struct TTSArgs
|
||||
{
|
||||
QPointF position;
|
||||
QString pattern;
|
||||
QSettings *settings = nullptr;
|
||||
int destIssi = 0;
|
||||
unsigned int cntPlayer = 0;
|
||||
unsigned int eqIdStart = 0;
|
||||
unsigned int minimumDeathTicks = 0;
|
||||
unsigned int maximumDeathTicks = 0;
|
||||
unsigned int pprow = 0;
|
||||
unsigned int pspacing = 0;
|
||||
unsigned int movs_per_msec = ~0U;
|
||||
unsigned int minimumMineId = 0;
|
||||
unsigned int maximumMineId = 0;
|
||||
bool demandOldBraodcastTelegrams = false;
|
||||
int demandOldBraodcastTelegramsCounter = 0;
|
||||
bool deactivationSingleMines = false;
|
||||
};
|
||||
|
||||
class TTServer : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TTServer(const TTSArgs &m_ttsArgs);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onNewConnection();
|
||||
void onDisconnect();
|
||||
|
||||
protected:
|
||||
TTSArgs m_ttsArgs;
|
||||
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
TTS Tetra Test Server:
|
||||
Das Testprogramm tts wird als Ersatz des Tetra-Server gestartet. Es dient dazu, Lasttests im Gesamtsystem zu starten.
|
||||
-----------------------------------------------
|
||||
Teilnehmer anlegen:
|
||||
|
||||
In der Opperator-App in die Übung einloggen
|
||||
Vorplanung->Teilnehmer Truppe->Bearbeite Teilnehmer
|
||||
Einen Zug selektieren und Erstellen
|
||||
Eine Anfangs-Ausstattungsnummer wählen und mit Enter jede Menge Teilnehmer mit aufsteigender Ausstattungsnummererzeugen.
|
||||
-----------------------------------------------
|
||||
Verschlüsselung abschalten:
|
||||
auf dem Server EXCTDS01 die Datei /etc/lts-cc.conf öffnen
|
||||
|
||||
[TrainingAreaBase]
|
||||
UseTetraScrambling=false
|
||||
|
||||
-----------------------------------------------
|
||||
Tabelle pdm.pdm_radio_group aendern:
|
||||
Dazu vorher den Namen der Gruppe herausfinden:
|
||||
Als Administrator in der Operator App Vorplanung->Übungen->Übungen den Namen der Tetra Gruppe von der aktuellen Übung nehmen.
|
||||
|
||||
Die Einstellungen, die geändert werden vorher aufschreiben, um wieder zum Betrieb mit dem Gefechtsfeld zurückzukommen.
|
||||
In der Tabelle pdm.pdm_radio_group den Namen "radiohost" auf localhost und issi auf 2 einstellen
|
||||
In der Tabelle pdm.pdm_radio_group den radio_port auf 4200 einstellen
|
||||
-----------------------------------------------
|
||||
Servernamen in PrtCnv.dat ändern
|
||||
Auf dem Server (EXCTDS01) ala user ctc_sv im Verzeichnis bin die Datei PrtCnv.dat öffnen
|
||||
[Central]
|
||||
HostName=localhost
|
||||
-----------------------------------------------
|
||||
Serversoftware neu starten
|
||||
-----------------------------------------------
|
||||
tts starten:
|
||||
Auf dem Server (EXCTDS01) ala user ctc_sv im Verzeichnis bin
|
||||
./tts -p 800 -f 1200
|
||||
|
||||
Bedeutung:
|
||||
-p 800 Positionsmeldungen für 800 Teilnehmer erzeugen
|
||||
-f 1200 Anfangsausstattungsnummer vom Teilnehmer anlegen hier eingeben.
|
||||
|
||||
|
||||
-----------------------------------------------
|
||||
-----------------------------------------------
|
||||
Relevante Tabellen sind:
|
||||
1) tab_prtcnv_member
|
||||
2) ex_nn.prtcnv_to_radio
|
||||
Hier werden die Telegramme (Rtg Gefechtsfeld) zwischengespeichert, bis sie quittiert sind.
|
||||
|
||||
Bei Problemen hat es sich als sinnvoll erwiesen, diese Tabellen zu löschen
|
||||
-----------------------------------------------
|
||||
-----------------------------------------------
|
||||
|
||||
-----------------------------------------------
|
||||
Optionen mit tts --help anzeigen lassen:
|
||||
|
||||
./tts --help
|
||||
Could not find log4cxx configuration file tetra_log4cxx.props
|
||||
Using default logging to stdout
|
||||
2020-09-03 13:19:15.300 ERROR tts |#| Settings Directory: /shd/CTC/TOOLS/.config/RME/TTS.conf (TDS-source/TetraIF/PrtCnv/TetraTestServer/main.cpp:30)
|
||||
Usage: ./tts [options]
|
||||
|
||||
Options:
|
||||
-i, --ini <filename> Default: /etc/lts-cc.conf
|
||||
-h, --help Displays this help.
|
||||
-v, --version Displays version
|
||||
information.
|
||||
-p, --player <Default value> number of players to be
|
||||
simulated.
|
||||
-n, --northing <Default value> initial position UTM north.
|
||||
-e, --easting <Default value> initial position UTM east.
|
||||
-f, --first-id <Default value> first equipment ID to use.
|
||||
-r, --pprow <Default value> players per row.
|
||||
-s, --pspacing <Default value> utm based spacing between
|
||||
the players.
|
||||
-m, --movements <Default value> how many movements per
|
||||
milliseconds to make.
|
||||
-d, --death <Default value> minimum times, after the
|
||||
movements timer is
|
||||
triggered, a dead player can
|
||||
revive.
|
||||
-o, --death-offset <(0 -> identical to min)> maximum times, after the
|
||||
movements timer is
|
||||
triggered, a dead player can
|
||||
revive.
|
||||
-t, --pattern <Default value> pattern the player has to
|
||||
walk [enter pattern by hand
|
||||
or pattern alias-name from
|
||||
settings file]
|
||||
for more info, enter
|
||||
'--pattern-help'.
|
||||
--demand-old-broadcast-telegrams <Default value> player demand old broadcast
|
||||
telegrams starting at BCI
|
||||
<default value>
|
||||
--deactivate-single-mines the first player
|
||||
deactivates single mines
|
||||
with ids from
|
||||
minimum-mine-id to
|
||||
maximum-mine-id
|
||||
--minimum-mine-id <Default value> sets the value for
|
||||
minimum-mine-id used within
|
||||
deactivate-single-mines
|
||||
command
|
||||
--maximum-mine-id <Default value> sets the value for
|
||||
maximum-mine-Id used within
|
||||
deactivate-single-mines
|
||||
command
|
||||
--pattern-help help for the pattern
|
||||
interpreter.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,645 @@
|
||||
|
||||
|
||||
|
||||
#include "worker.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include <time.h>
|
||||
#include <random>
|
||||
#include <iostream>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
|
||||
TTWorker::TTWorker(QTcpSocket *socket, const TTSArgs &ttsArgs)
|
||||
: m_socket(socket)
|
||||
, m_timer(new QTimer(this))
|
||||
, m_counter(0)
|
||||
, m_toogle(true)
|
||||
, m_ttsArgs(ttsArgs)
|
||||
, m_patternList(interpretPattern(ttsArgs.pattern))
|
||||
, m_generalPosition(ttsArgs.position)
|
||||
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTWorker"))
|
||||
{
|
||||
qsrand(time(nullptr));
|
||||
}
|
||||
|
||||
bool TTWorker::init()
|
||||
{
|
||||
connect(m_socket, &QTcpSocket::disconnected,
|
||||
this, &TTWorker::deleteLater);
|
||||
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
|
||||
this, &TTWorker::onSocketError);
|
||||
connect(m_socket, &QTcpSocket::readyRead,
|
||||
this, &TTWorker::onReadyRead);
|
||||
connect(m_timer, &QTimer::timeout,
|
||||
this, &TTWorker::onSendPlayerPositions);
|
||||
|
||||
if (m_ttsArgs.movs_per_msec < 200)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Invalid value for 'movements per milliseconds'! Please choose a number greater than 200 msecs or equal. Program stopped.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TTWorker::startExecution()
|
||||
{
|
||||
|
||||
m_timer->start(m_ttsArgs.movs_per_msec);
|
||||
NOTICE_LOG(m_logger) << "Start Exe login all Player";
|
||||
|
||||
for (unsigned int i = 0; i < m_ttsArgs.cntPlayer; i++)
|
||||
{
|
||||
int eqId = m_ttsArgs.eqIdStart + i;
|
||||
int issi = m_ttsArgs.eqIdStart + i + 2000;
|
||||
QPointF posUTM((m_generalPosition.x() + (i / m_ttsArgs.pprow) * m_ttsArgs.pspacing),
|
||||
(m_generalPosition.y() + (i % m_ttsArgs.pprow) * m_ttsArgs.pspacing));
|
||||
Player *p = new Player(eqId, issi, m_ttsArgs.minimumDeathTicks, m_ttsArgs.maximumDeathTicks, posUTM);
|
||||
m_playerList.append(p);
|
||||
|
||||
sendLogin(p->eqId, p->issi);
|
||||
}
|
||||
|
||||
if (m_ttsArgs.deactivationSingleMines)
|
||||
QTimer::singleShot(1000, this, &TTWorker::onSendDeaktivierungEinzelminen);
|
||||
|
||||
if (m_ttsArgs.demandOldBraodcastTelegrams)
|
||||
QTimer::singleShot(1000, this, &TTWorker::onSendNachforderungBroadcastTelegramm);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
QVector<runningPattern> TTWorker::interpretPattern(QString pattern) const
|
||||
{
|
||||
if (pattern.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "INVALID PATTERN - pattern-size < 1";
|
||||
exit(8);
|
||||
}
|
||||
|
||||
if (pattern.at(0) == QLatin1Char('%'))
|
||||
{
|
||||
pattern.remove(0, 1);
|
||||
|
||||
QString patternAlias = pattern;
|
||||
|
||||
pattern = m_ttsArgs.settings->value(QLatin1String("pattern/") + patternAlias).toString();
|
||||
|
||||
if (pattern.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "INVALID PATTERN - alias '" << patternAlias << "' is not available in settings file! Program halted.";
|
||||
exit(9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (pattern.at(pattern.count() - 1) == QLatin1Char('#'))
|
||||
pattern.remove(pattern.count() - 1, 1);
|
||||
|
||||
QVector<runningPattern> newPatternList;
|
||||
runningPattern rp;
|
||||
QStringList strings = pattern.split(QLatin1Char('#'));
|
||||
QStringList stringy;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < strings.count(); i++)
|
||||
{
|
||||
stringy = strings.at(i).split(QLatin1Char(','));
|
||||
|
||||
if (stringy.count() != 3)
|
||||
break;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
rp.steps = stringy.at(0).toInt(&ok); if (!ok) break;
|
||||
rp.ethering = stringy.at(1).toInt(&ok); if (!ok) break;
|
||||
rp.northering = stringy.at(2).toInt(&ok); if (!ok) break;
|
||||
|
||||
newPatternList.append(rp);
|
||||
|
||||
if (i == strings.count() - 1)
|
||||
return newPatternList;
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "INVALID PATTERN: '" << strings.at(i) << "'. Cannot start without correct pattern!";
|
||||
|
||||
exit(5);
|
||||
|
||||
return QVector<runningPattern>();
|
||||
}
|
||||
|
||||
void TTWorker::sendLogin(uint32_t eqId, uint32_t issi)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *tele = new N_CoPrtCnv::CoPrtCnvRadioTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginRequest, eqId, issi);
|
||||
|
||||
DEBUG_LOG(m_logger) << "SEND LOGIN FOR ISSI=" << issi << ", eqId=" << eqId;
|
||||
sendTelegram(tele, issi);
|
||||
}
|
||||
|
||||
void TTWorker::onReadyRead()
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader header;
|
||||
qint64 bytesRead = m_socket->peek((char *)&header, sizeof(header));
|
||||
if (sizeof(header) != bytesRead)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "To few bytes to read from socket";
|
||||
return;
|
||||
}
|
||||
|
||||
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::MAGIC_NUMBER != header.m_MagicNumber)
|
||||
{
|
||||
ERROR_LOG(m_logger) << "Disconnect because radio-Telegram does not begin with magic-number A5: I got "
|
||||
<< header.m_MagicNumber;
|
||||
m_socket->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!N_CoPrtCnv::CoPrtCnvRadioTelegram::isTelegramLengthOk(header.m_Length))
|
||||
{
|
||||
WARNING_LOG(m_logger) << "Radio-Telegram length to big: " << header.m_Length
|
||||
<< " - but I proceed with this telegram.";
|
||||
}
|
||||
|
||||
|
||||
uint16_t dataSize = header.m_Length + sizeof(header);
|
||||
if (m_socket->bytesAvailable() < dataSize)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << "not enough bytes available - waiting for more data";
|
||||
return;
|
||||
}
|
||||
QByteArray buf(dataSize, Qt::Uninitialized);
|
||||
|
||||
bytesRead = 0;
|
||||
while (bytesRead < dataSize)
|
||||
{
|
||||
qint64 br = m_socket->read(&buf.data()[bytesRead], dataSize - bytesRead);
|
||||
bytesRead += br;
|
||||
}
|
||||
handleReceivedTelegram(buf);
|
||||
|
||||
if (m_socket->bytesAvailable())
|
||||
QTimer::singleShot(0, this, &TTWorker::onReadyRead);
|
||||
}
|
||||
|
||||
void TTWorker::handleReceivedTelegram(const QByteArray &data)
|
||||
{
|
||||
INFO_LOG(m_logger) << data.size();
|
||||
|
||||
switch (data.at(4))
|
||||
{
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd:
|
||||
handleRegisterInd();
|
||||
startExecution();
|
||||
break;
|
||||
|
||||
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
|
||||
handleDataIndReq(data);
|
||||
|
||||
break;
|
||||
default:
|
||||
INFO_LOG(m_logger) << "Radio-Telegram with PDU-Type=" << (int)data[4]
|
||||
<< " not implemented!";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TTWorker::sendTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *radioTelegram, uint32_t issi)
|
||||
{
|
||||
radioTelegram->setZaehler(m_counter++);
|
||||
radioTelegram->setDestISSI(m_ttsArgs.destIssi);
|
||||
radioTelegram->setSrcISSI(issi);
|
||||
|
||||
m_socket->write(radioTelegram->data());
|
||||
m_socket->flush();
|
||||
delete radioTelegram;
|
||||
}
|
||||
|
||||
void TTWorker::onSendNachforderungBroadcastTelegramm()
|
||||
{
|
||||
Player *p = m_playerList.at(0);
|
||||
|
||||
if (p->isLogin)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram tel(GEF_NACHFORDERUNG_BROADCAST_TELEGRAMME,
|
||||
10, p->eqId);
|
||||
tel.setZaehler(0);
|
||||
tel.setBroadcastIndex(0);
|
||||
tel.setDestISSI(m_ttsArgs.destIssi);
|
||||
tel.setSrcISSI(p->issi);
|
||||
tel.setPayload4Byte(0);
|
||||
tel.setPayload1Byte(0);
|
||||
tel.setPayload2Byte(m_ttsArgs.demandOldBraodcastTelegramsCounter);
|
||||
m_socket->write(tel.data());
|
||||
}
|
||||
else
|
||||
QTimer::singleShot(1000, this, &TTWorker::onSendNachforderungBroadcastTelegramm);
|
||||
}
|
||||
|
||||
void TTWorker::onSendDeaktivierungEinzelminen()
|
||||
{
|
||||
if (m_playerList.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << "No player for onSendDeaktivierungEinzelminen...";
|
||||
return;
|
||||
}
|
||||
Player *p = m_playerList.at(0);
|
||||
static unsigned int mineId = m_ttsArgs.minimumMineId;
|
||||
|
||||
NOTICE_LOG(m_logger) << "onSendDeaktivierungEinzelminen " << p->isLogin;
|
||||
if (p->isLogin)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::MineTriggered tele;
|
||||
tele.header.equipmentNumber = p->eqId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
tele.mineIndex = mineId++;
|
||||
tele.mineAreaId = 0;
|
||||
|
||||
sendTelegram(tele, p->issi);
|
||||
}
|
||||
|
||||
if (mineId <= m_ttsArgs.maximumMineId)
|
||||
QTimer::singleShot(1000, this, &TTWorker::onSendDeaktivierungEinzelminen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TTWorker::sendTaktischerStatus(uint32_t eqId, uint32_t issi, TrainingAreaBase::TetraIcd::VulnerabilityState vbg)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::StateChange tele;
|
||||
tele.header.equipmentNumber = eqId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
|
||||
tele.tasVitality = 90;
|
||||
tele.tasVulnerabilityKindId = 459008;
|
||||
tele.vulnerabilityBits = 0;
|
||||
|
||||
for (Player *p : qAsConst(m_playerList))
|
||||
{
|
||||
if (p->eqId == eqId)
|
||||
{
|
||||
p->vbg = vbg;
|
||||
tele.vulnerabilityState = vbg;
|
||||
DEBUG_LOG(m_logger) << "Player (" << p->eqId << ") - VBG: " << int(vbg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sendTelegram(tele, issi);
|
||||
}
|
||||
|
||||
void TTWorker::sendSanitationReport(uint32_t eqId, uint32_t issi)
|
||||
{
|
||||
m_toogle = false;
|
||||
return;
|
||||
TrainingAreaBase::TetraIcd::SanitaryMaterialStockReport tele;
|
||||
tele.header.equipmentNumber = eqId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
int matCount = 14;
|
||||
tele.sanitaryMaterialCount = matCount;
|
||||
std::vector<int> mat;
|
||||
mat.reserve(matCount);
|
||||
for (int i = 1; i <= matCount; i++)
|
||||
mat.emplace_back(i);
|
||||
tele.sanitaryMaterialStock = mat;
|
||||
sendTelegram(tele, issi);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void TTWorker::sendBITE(uint32_t eqId, uint32_t issi)
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::BiteReport tele;
|
||||
tele.header.equipmentNumber = eqId;
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
tele.loginType = TrainingAreaBase::TetraIcd::BiteReport::LoginType::Relogin;
|
||||
tele.configError = false;
|
||||
sendTelegram(tele, issi);
|
||||
sendTaktischerStatus(eqId, issi, TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady);
|
||||
}
|
||||
|
||||
void TTWorker::handleDataIndReq(const QByteArray &data)
|
||||
{
|
||||
QScopedPointer<N_CoPrtCnv::CoPrtCnvRadioTelegram> radioTelegram(new N_CoPrtCnv::CoPrtCnvRadioTelegram(data));
|
||||
int telID = radioTelegram->getTelID();
|
||||
|
||||
switch (telID)
|
||||
{
|
||||
#if 0
|
||||
TODO
|
||||
case GEF_AUSLOESUNG_DUS_TREFFER:
|
||||
{
|
||||
|
||||
|
||||
DEBUG_LOG(mLogger) << "handleAusloeserDUSTreffer: eqId=" << radioTelegram->getIdentity() <<
|
||||
" issi=" << radioTelegram->getDestISSI();
|
||||
|
||||
|
||||
XDR_Wrapper_it *wrapper = nullptr;
|
||||
if (!N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(wrapper, radioTelegram.data()))
|
||||
if (!wrapper)
|
||||
return;
|
||||
const ausloesung_dus_treffer_rt &rt_data = static_cast<XDR_AUSLOESUNG_DUS_TREFFER_Wrapper *>(wrapper)->get_rt();
|
||||
|
||||
if (rt_data.bek_wirkung > 90)
|
||||
sendTaktischerStatus(radioTelegram->getIdentity(), radioTelegram->getDestISSI(), TOTALAUSFALL);
|
||||
else
|
||||
sendTaktischerStatus(radioTelegram->getIdentity(), radioTelegram->getDestISSI(), ANNAEHENDER_TREFFER);
|
||||
delete wrapper;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
case GEF_TAKTISCHER_STATUS:
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(radioTelegram.data());
|
||||
TrainingAreaBase::TetraIcd::StateChange sc = TrainingAreaBase::TetraIcd::StateChange::fromJson(tele->jsonData());
|
||||
delete tele;
|
||||
DEBUG_LOG(m_logger) << " Try to Resetnd State " << sc.header.equipmentNumber << " we got state: " << static_cast<int>(sc.vulnerabilityState) << " and this bits here: " << sc.vulnerabilityBits;
|
||||
|
||||
sc.header.timeStamp = time(nullptr) * 1000;
|
||||
uint32_t issi = 0;
|
||||
for (int i = 0; i < m_playerList.count(); i++)
|
||||
{
|
||||
if (radioTelegram->getIdentity() == m_playerList.at(i)->eqId)
|
||||
{
|
||||
issi = m_playerList.at(i)->issi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << " Try to Resetnd S2tate " << sc.header.equipmentNumber << " we got state: " << static_cast<int>(sc.vulnerabilityState) << " and this bits here: " << sc.vulnerabilityBits;
|
||||
sendTelegram(sc, issi);
|
||||
return;
|
||||
}
|
||||
case GEF_SANITAETSDIENST_INSTANDSETZUNG_PVSAN:
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(radioTelegram.data());
|
||||
TrainingAreaBase::TetraIcd::Sanitation sc = TrainingAreaBase::TetraIcd::Sanitation::fromJson(tele->jsonData());
|
||||
delete tele;
|
||||
|
||||
DEBUG_LOG(m_logger) << " Try to Resetnd State " << sc.header.equipmentNumber << " we got procedure: " << sc.proceduresTaken;
|
||||
|
||||
sc.header.timeStamp = time(nullptr) * 1000;
|
||||
uint32_t issi = 0;
|
||||
for (int i = 0; i < m_playerList.count(); i++)
|
||||
{
|
||||
if (radioTelegram->getIdentity() == m_playerList.at(i)->eqId)
|
||||
{
|
||||
issi = m_playerList.at(i)->issi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << " Try to Resetnd S2tate " << sc.header.equipmentNumber << " we got state: " << sc.proceduresTaken;
|
||||
sendTelegram(sc, issi);
|
||||
return;
|
||||
}
|
||||
case GEF_TREFFER_AUSLOESUNG:
|
||||
case GEF_INIT:
|
||||
{
|
||||
TrainingAreaBase::TetraIcd::StateChange tele;
|
||||
uint32_t issi = 0;
|
||||
for (int i = 0; i < m_playerList.count(); i++)
|
||||
{
|
||||
if (radioTelegram->getIdentity() == m_playerList.at(i)->eqId)
|
||||
{
|
||||
issi = m_playerList.at(i)->issi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TrainingAreaBase::TetraIcd::VulnerabilityState state = (telID == GEF_TREFFER_AUSLOESUNG)
|
||||
? static_cast<TrainingAreaBase::TetraIcd::VulnerabilityState>(radioTelegram->getPayloadDataUInt8(7))
|
||||
: TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady;
|
||||
|
||||
tele.originatorType = TrainingAreaBase::TetraIcd::OriginatorType::Initialisation;
|
||||
tele.header.equipmentNumber = radioTelegram->getIdentity();
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
uint32_t vulcomplete = radioTelegram->getPayloadDataUInt32(8, 2);
|
||||
uint32_t vitality = vulcomplete &= 0xF000;
|
||||
vitality >>= 12;
|
||||
|
||||
tele.tasVulnerabilityKindId = vulcomplete &= 0x0FFF;
|
||||
|
||||
tele.tasVulnerabilityKindId <<= 16;
|
||||
tele.tasVulnerabilityKindId |= 0xB1;
|
||||
|
||||
tele.tasVitality = (vitality - 1) * 10;
|
||||
if (tele.tasVitality < 0)
|
||||
tele.tasVitality = 100;
|
||||
|
||||
if (state == TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady && vitality > 0)
|
||||
tele.vulnerabilityState = TrainingAreaBase::TetraIcd::VulnerabilityState::MediumDamage;
|
||||
else
|
||||
tele.vulnerabilityState = state;
|
||||
|
||||
sendTelegram(tele, issi);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (radioTelegram->isLoginTelegram())
|
||||
{
|
||||
|
||||
DEBUG_LOG(m_logger) << "player cont: " << m_playerList.count();
|
||||
for (int i = 0; i < m_playerList.count(); i++)
|
||||
{
|
||||
Player *p = m_playerList.at(i);
|
||||
if (radioTelegram->getDestISSI() == p->issi)
|
||||
{
|
||||
if (p->isLogin)
|
||||
return;
|
||||
DEBUG_LOG(m_logger) << "Player #" << i << " with eqId=" << p->eqId << " and issi=" << p->issi << " is now logged in.";
|
||||
p->isLogin = true;
|
||||
sendBITE(p->eqId, p->issi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERROR_LOG(m_logger) << "Invalid player was logged in. issi: '" << radioTelegram->getDestISSI() << "'";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << "Received unused or invalid telegram with Telegram-ID: " << telID;
|
||||
}
|
||||
|
||||
static void fillHeader(char *data, int len)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader *hdr
|
||||
= (N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader *)data;
|
||||
hdr->m_MagicNumber = N_CoPrtCnv::CoPrtCnvRadioTelegram::MAGIC_NUMBER;
|
||||
hdr->m_Counter = 0;
|
||||
hdr->m_Length = len;
|
||||
}
|
||||
|
||||
void TTWorker::handleRegisterInd()
|
||||
{
|
||||
m_counter = 1;
|
||||
|
||||
QByteArray buf;
|
||||
buf.resize(6);
|
||||
fillHeader(buf.data(), 2);
|
||||
buf[4] = N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp;
|
||||
buf[5] = N_CoPrtCnv::CoPrtCnvRadioTelegram::e_IssiSuccessfulRegistered;
|
||||
|
||||
m_socket->write(buf);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void TTWorker::movePlayer(Player *p)
|
||||
{
|
||||
|
||||
|
||||
if (p->patternInList > m_patternList.count() - 1)
|
||||
p->patternInList = 0;
|
||||
else
|
||||
{
|
||||
if (p->stepInPattern > m_patternList.at(p->patternInList).steps)
|
||||
{
|
||||
p->stepInPattern = 1;
|
||||
if (p->patternInList == m_patternList.count() - 1)
|
||||
p->patternInList = 0;
|
||||
else
|
||||
p->patternInList++;
|
||||
}
|
||||
}
|
||||
|
||||
p->posUTM.setX(p->posUTM.x() +
|
||||
m_patternList.at(p->patternInList).northering);
|
||||
p->posUTM.setY(p->posUTM.y() +
|
||||
m_patternList.at(p->patternInList).ethering);
|
||||
|
||||
p->stepInPattern++;
|
||||
}
|
||||
|
||||
void TTWorker::onSendPlayerPositions()
|
||||
{
|
||||
|
||||
qint64 bytesSent = 0;
|
||||
int playerCount = 0;
|
||||
TrainingAreaBase::TetraIcd::PositionReport tele;
|
||||
for (int i = 0; i < m_playerList.count(); i++)
|
||||
{
|
||||
Player *p = m_playerList.at(i);
|
||||
|
||||
if (!p->isLogin)
|
||||
{
|
||||
INFO_LOG(m_logger) << "Player with eqId=" << p->eqId << " and issi=" << p->issi << " is not logged in.";
|
||||
break;
|
||||
}
|
||||
playerCount++;
|
||||
|
||||
|
||||
if (p->vbg != TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady && p->isEndOfTicks())
|
||||
sendTaktischerStatus(p->eqId, p->issi, TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady);
|
||||
|
||||
tele.header.equipmentNumber = p->eqId;
|
||||
if (m_toogle)
|
||||
sendSanitationReport(p->eqId, p->issi);
|
||||
|
||||
|
||||
movePlayer(p);
|
||||
tele.relPosition.offsetY = p->posUTM.y();
|
||||
tele.relPosition.offsetX = p->posUTM.x();
|
||||
|
||||
tele.header.timeStamp = time(nullptr) * 1000;
|
||||
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram ct(tele);
|
||||
N_CoPrtCnv::CoPrtCnvRadioTelegram *tel = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(&ct);
|
||||
tel->setZaehler(m_counter++);
|
||||
tel->setDestISSI(m_ttsArgs.destIssi);
|
||||
tel->setSrcISSI(p->issi);
|
||||
m_socket->write(tel->data());
|
||||
bytesSent += tel->getPayloadSize();
|
||||
delete tel;
|
||||
}
|
||||
bool bFlushed = m_socket->flush();
|
||||
INFO_LOG(m_logger) << "Sent " << bytesSent << "bytes (" << playerCount << " players), bFlushed=" << bFlushed;
|
||||
}
|
||||
|
||||
void TTWorker::onSocketError()
|
||||
{
|
||||
if (m_socket)
|
||||
m_socket->disconnectFromHost();
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QPointF>
|
||||
#include <QtCore/QMap>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
|
||||
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
|
||||
#include <TrainingAreaBase/training_area_telegrams.h>
|
||||
#include "server.h"
|
||||
|
||||
|
||||
class QTcpSocket;
|
||||
class QTimer;
|
||||
|
||||
class Player
|
||||
{
|
||||
private:
|
||||
void randomizeDeathTicks()
|
||||
{
|
||||
deathTicks = ((qrand() % (maximumDeathTicks - minimumDeathTicks + 1)) + minimumDeathTicks);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
uint32_t eqId;
|
||||
uint32_t issi;
|
||||
QPointF posUTM;
|
||||
|
||||
|
||||
bool isLogin;
|
||||
bool isValidBITE;
|
||||
TrainingAreaBase::TetraIcd::VulnerabilityState vbg;
|
||||
|
||||
|
||||
int patternInList;
|
||||
int stepInPattern;
|
||||
|
||||
|
||||
uint32_t deathTicks;
|
||||
uint32_t minimumDeathTicks;
|
||||
uint32_t maximumDeathTicks;
|
||||
|
||||
Player(int eqId_, int issi_, uint32_t minimumDeathTicks_, uint32_t maximumDeathTicks_, const QPointF &posUTM_)
|
||||
: eqId(eqId_)
|
||||
, issi(issi_)
|
||||
, posUTM(posUTM_)
|
||||
, isLogin(false)
|
||||
, isValidBITE(false)
|
||||
, vbg(TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady)
|
||||
, patternInList(0)
|
||||
, stepInPattern(1)
|
||||
, deathTicks(0)
|
||||
, minimumDeathTicks(minimumDeathTicks_)
|
||||
, maximumDeathTicks(maximumDeathTicks_)
|
||||
{
|
||||
randomizeDeathTicks();
|
||||
}
|
||||
|
||||
bool isEndOfTicks()
|
||||
{
|
||||
deathTicks--;
|
||||
if (deathTicks < 1)
|
||||
{
|
||||
randomizeDeathTicks();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class TTWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TTWorker(QTcpSocket *socket, const TTSArgs &ttsArgs);
|
||||
|
||||
bool init();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onReadyRead();
|
||||
void onSendPlayerPositions();
|
||||
|
||||
void onSocketError();
|
||||
void onSendNachforderungBroadcastTelegramm();
|
||||
void onSendDeaktivierungEinzelminen();
|
||||
|
||||
protected:
|
||||
void startExecution();
|
||||
QVector<runningPattern> interpretPattern(QString pattern) const;
|
||||
void movePlayer(Player *p);
|
||||
void handleReceivedTelegram(const QByteArray &data);
|
||||
void handleRegisterInd();
|
||||
void handleDataIndReq(const QByteArray &data);
|
||||
|
||||
void sendTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *radioTelegram, uint32_t issi);
|
||||
template <typename T>
|
||||
void sendTelegram(const T &tele, uint32_t issi)
|
||||
{
|
||||
N_CoPrtCnv::CoPrtCnvCentralTelegram ct(tele);
|
||||
sendTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(&ct), issi);
|
||||
}
|
||||
void sendLogin(uint32_t eqId, uint32_t issi);
|
||||
void sendBITE(uint32_t eqId, uint32_t issi);
|
||||
void sendTaktischerStatus(uint32_t eqId, uint32_t issi, TrainingAreaBase::TetraIcd::VulnerabilityState vbg);
|
||||
void sendSanitationReport(uint32_t eqId, uint32_t issi);
|
||||
|
||||
private:
|
||||
QTcpSocket *m_socket;
|
||||
QTimer *m_timer;
|
||||
QByteArray m_buffer;
|
||||
unsigned int m_counter;
|
||||
bool m_toogle;
|
||||
|
||||
TTSArgs m_ttsArgs;
|
||||
|
||||
QVector<Player *> m_playerList;
|
||||
QVector<runningPattern> m_patternList;
|
||||
QPointF m_generalPosition;
|
||||
NGL::INGLLogger &m_logger;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# Logger-Config für PrtCnv
|
||||
#
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
#
|
||||
# Aus: grep getNGLLogger * | cut -d \" -f 2 | sort -u
|
||||
#
|
||||
log4j.reset=true
|
||||
#log4j.rootLogger=DEBUG, stdout
|
||||
log4j.rootLogger=WARN, file
|
||||
|
||||
#log4j.logger.CoPrtCnvCentralTelegram=WARN
|
||||
#log4j.logger.CoPrtCnvRadioTelegram=WARN
|
||||
#log4j.logger.CoPrtCnvTelegramConverter=WARN
|
||||
#log4j.logger.PrtCnvApplication=WARN
|
||||
#log4j.logger.PrtCnvBroadcastMgr=WARN
|
||||
#log4j.logger.PrtCnvCentralThread=WARN
|
||||
#log4j.logger.PrtCnvDbThread=WARN
|
||||
#log4j.logger.PrtCnvInjectionSocket=WARN
|
||||
#log4j.logger.PrtCnvInjectionThread=WARN
|
||||
#log4j.logger.PrtCnvMain=WARN
|
||||
#log4j.logger.PrtCnvMember=WARN
|
||||
#log4j.logger.PrtCnvRadioThread=WARN
|
||||
#log4j.logger.PrtCnvRadioThread.statistic=WARN
|
||||
#log4j.logger.PrtCnvRadioTrafficThread=WARN
|
||||
#log4j.logger.PrtCnvStoredCentralTelegram=WARN
|
||||
#log4j.logger.PrtCnvStoredRadioTelegram=WARN
|
||||
#log4j.logger.PrtCnvStoredRadioTelegram=WARN
|
||||
#log4j.logger.PrtCnvTelegramLogThread=WARN
|
||||
#log4j.logger.PrtCnvThreadStarter=WARN
|
||||
|
||||
# Appender
|
||||
############
|
||||
log4j.appender.file=Lts::RollingFileAppender
|
||||
log4j.appender.file.MaxFileSize=10MB
|
||||
log4j.appender.file.MaxBackupIndex=9
|
||||
#will be replace by ngllogger
|
||||
#log4j.appender.file.file=${HOME}/log/PrtCnv.log
|
||||
log4j.appender.file.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
|
||||
|
||||
# Appnd_Console
|
||||
###########################################################
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
LOGGER_T getLogger(const char *name)
|
||||
{
|
||||
static const char ns[] = "PRT.";
|
||||
std::string loggerName;
|
||||
const size_t len = strlen(name);
|
||||
loggerName.reserve(sizeof(ns) + len);
|
||||
loggerName.append(ns, sizeof(ns) - 1);
|
||||
loggerName.append(name, len);
|
||||
|
||||
try {
|
||||
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
|
||||
getNGLLogger( loggerName );
|
||||
} catch ( const std::exception &ex )
|
||||
{
|
||||
fprintf(stderr, "Error retrieving logger: %s", ex.what());
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
namespace PrtCnv
|
||||
{
|
||||
|
||||
typedef NGL::INGLLogger& LOGGER_T;
|
||||
|
||||
LOGGER_T getLogger(const char *name);
|
||||
inline LOGGER_T getLogger(const QString &loggerName)
|
||||
{
|
||||
return getLogger(loggerName.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user