Adds q2 repos to migrate

This commit is contained in:
torsten committed 2026-07-14 12:07:36 +02:00
1 parent ad1f62c4ee
commit 17f1c61c9e
991 files changed
+178451

No files matched your search

+88
View File
@@ -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;
}
}
+45
View File
@@ -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;
};
}
+78
View File
@@ -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)
+59
View File
@@ -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;
};
}
+21
View File
@@ -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)
+62
View File
@@ -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();
}
+503
View File
@@ -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);
}
+61
View File
@@ -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 &timestamp,
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 &timestamp,
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 &timestamp,
const QString &filename,
uint lfdNr,
const QString &bytes);
void writeToLogfile(const QString &issi,
const QDateTime &sendeZeit,
const QDateTime &timestamp,
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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;ISSI by the central&lt;br /&gt;(if no ISSI is entered, ISSI 1 will be used)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
+90
View File
@@ -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
+410
View File
@@ -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;
}
}
+180
View File
@@ -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
+359
View File
@@ -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;
};
}
+842
View File
@@ -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;
}
}
+238
View File
@@ -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
+371
View File
@@ -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;
};
}
+127
View File
@@ -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);
}
}
}
+66
View File
@@ -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);
};
}
+182
View File
@@ -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);
}
}
+101
View File
@@ -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);
};
}
+124
View File
@@ -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;
}
+98
View File
@@ -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();
}
}
+84
View File
@@ -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
+419
View File
@@ -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);
}
+208
View File
@@ -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;
};
}
+382
View File
@@ -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";
}
}
}
+190
View File
@@ -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;
};
}
+23
View File
@@ -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;
};
}
+182
View File
@@ -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;
};
}
+74
View File
@@ -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;
}
}
+71
View File
@@ -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;
};
}
+125
View File
@@ -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;
}
}
+68
View File
@@ -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
#
+188
View File
@@ -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;
}
+38
View File
@@ -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";
}
+54
View File
@@ -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;
};
+111
View File
@@ -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.
+645
View File
@@ -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();
}
+128
View File
@@ -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;
};
+49
View File
@@ -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
+30
View File
@@ -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();
}
}
}
+18
View File
@@ -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());
}
}