Files
qt6-migration-pipeline/TetraIF/PrtCnv/TetraTestServer/worker.cpp
T
2026-07-14 12:07:36 +02:00

646 lines
17 KiB
C++

#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();
}