#include "worker.h" #include #include #include #include #include #include #include #include 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::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 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 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(); } 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 : std::as_const(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 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 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(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(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(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(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(); }