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

504 lines
14 KiB
C++

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