2268 lines
82 KiB
C++
2268 lines
82 KiB
C++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "PrtCnvRadioThread.hpp"
|
|
#include "PrtCnvRadioTrafficThread.hpp"
|
|
#include "PrtCnvCentralThread.hpp"
|
|
#include "PrtCnvMember.hpp"
|
|
#include "PrtCnvTelegramLogThread.hpp"
|
|
#include "PrtCnvInjectionThread.hpp"
|
|
#include "PrtCnvThreadStarter.hpp"
|
|
#include "PrtCnvDbThread.hpp"
|
|
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
|
|
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
|
|
#include <CoPrtCnv/CoPrtCnvHexDumpHelper.hpp>
|
|
#include <TrainingAreaBase/training_area_telegrams.h>
|
|
|
|
#include <QtNetwork/QTcpSocket>
|
|
#include <QtNetwork/QNetworkInterface>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QStringList>
|
|
#include <QJsonDocument>
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include <time.h>
|
|
#include <iostream>
|
|
#include <math.h>
|
|
|
|
|
|
namespace PrtCnv
|
|
{
|
|
|
|
static BiteError getBiteError(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram,
|
|
size_t iOffset)
|
|
{
|
|
BiteError ret;
|
|
ret.canDeviceNumber = a_radioTelegram.getPayloadDataUInt8(iOffset + 0);
|
|
ret.subdeviceNumber = a_radioTelegram.getPayloadDataUInt8(iOffset + 1);
|
|
ret.uniqueId = a_radioTelegram.getPayloadDataUInt32(iOffset + 2, 4);
|
|
ret.errorNumber = a_radioTelegram.getPayloadDataUInt8(iOffset + 6);
|
|
|
|
return ret;
|
|
}
|
|
|
|
PrtCnvRadioThread::PrtCnvRadioThread(int exerciseId, PrtCnvThreadStarter *a_parent)
|
|
: PrtCnvSocketThread(a_parent, "PrtCnvRadioThread"),
|
|
m_CentralThread(nullptr),
|
|
m_radioTrafficThread(nullptr),
|
|
m_SendCounter(0),
|
|
m_HostISSI(0),
|
|
START_COUNTER(0),
|
|
END_COUNTER(256),
|
|
m_EnvironmentTelegramTimer(nullptr),
|
|
m_BroadcastISSI(0),
|
|
m_LoginPathWithPrefix(QString()),
|
|
m_ExerciseId(exerciseId),
|
|
m_DoLicenseDespiteSelftestOrConfigFailure(false),
|
|
m_DoCheckIdentity(true),
|
|
m_DoDumpReceivedTelegrams(false),
|
|
m_DoDumpSentTelegrams(false),
|
|
m_DoUseSDSTL(false),
|
|
m_EmergencyCallISSI(0),
|
|
m_EnvironmentTelegram(nullptr),
|
|
m_SendLoad(E_TrafficLoad::Unknown),
|
|
m_SendQueueFullLoad(0),
|
|
m_SendQueueHighLoad(0),
|
|
m_SendTelegramsNormalLoad(100),
|
|
m_SendTelegramsHighLoad(50),
|
|
m_SendTelegramsFullLoad(10),
|
|
m_SentTelegrams(0),
|
|
m_SentBytes(0),
|
|
m_TickCounter(0),
|
|
m_TicksPerSecond(0),
|
|
m_AverageIndex(0),
|
|
m_ReceivedTelegrams(0),
|
|
m_ReceivedBytes(0),
|
|
m_bUseScrambling(false),
|
|
m_loggerStats(getLogger("PrtCnvRadioThread.statistic")),
|
|
m_exerciseEquipmentAssignmentMutex(QMutex::Recursive)
|
|
{
|
|
m_Name = QStringLiteral("Radio");
|
|
for (int i = 0; i < AVERAGE_COUNT; ++i)
|
|
{
|
|
m_SentBytesAverage[i] = 0;
|
|
m_ReceivedBytesAverage[i] = 0;
|
|
}
|
|
qRegisterMetaType<N_CoPrtCnv::E_LicensingKind>("N_CoPrtCnv::E_LicensingKind");
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t PrtCnvRadioThread::create(NGL::Settings *settings)
|
|
{
|
|
INFO_LOG(m_logger) << "create";
|
|
PrtCnvSocketThread::create(settings);
|
|
m_BroadcastManager.create(settings);
|
|
|
|
m_CentralThread = qobject_cast<PrtCnvCentralThread *>(m_Parent->getOtherThread(PrtCnvApplication::e_Central));
|
|
connect(this, &PrtCnvRadioThread::checkSendToRadio,
|
|
this, &PrtCnvRadioThread::checkSend, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::checkMemberForValidLogin,
|
|
this, &PrtCnvRadioThread::onCheckMemberForValidLogin, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::checkSendToCentral,
|
|
m_CentralThread, &PrtCnvCentralThread::checkSend, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::stopped,
|
|
m_CentralThread, &PrtCnvCentralThread::radioStopped, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::cleant,
|
|
m_CentralThread, &PrtCnvCentralThread::radioCleant, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::tetraConnectionState,
|
|
m_CentralThread, &PrtCnvCentralThread::onTetraConnectionState, Qt::QueuedConnection);
|
|
connect(this, &PrtCnvRadioThread::playerLicencedChanged,
|
|
m_CentralThread, &PrtCnvCentralThread::onPlayerLicencedChanged, Qt::QueuedConnection);
|
|
connect(&m_BroadcastManager, &PrtCnvBroadcastMgr::areaEffectSimulationDeactivated,
|
|
this, &PrtCnvRadioThread::areaEffectSimulationDeactivated);
|
|
connect(&m_BroadcastManager, &PrtCnvBroadcastMgr::areaEffectSimulationActivated,
|
|
this, &PrtCnvRadioThread::areaEffectSimulationActivated);
|
|
|
|
auto mainApp = m_Parent->getMainApp();
|
|
connect(mainApp, &PrtCnvApplication::outgoingBroadcastTelegramsRequest,
|
|
this, &PrtCnvRadioThread::onBroadcastTelegramsRequestFromCentral);
|
|
|
|
connect(this, &PrtCnvRadioThread::warehouseMessageFromPtrCnv,
|
|
mainApp, &PrtCnvApplication::warehouseMessageFromPtrCnv);
|
|
|
|
static_assert(sizeof(N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader) == 4,
|
|
"This code expects a size of 4 for CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader (otherwise it will not work correct)"
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t PrtCnvRadioThread::destroy()
|
|
{
|
|
INFO_LOG(m_logger) << "Destroy";
|
|
PrtCnvSocketThread::destroy();
|
|
delete m_EnvironmentTelegramTimer; m_EnvironmentTelegramTimer = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t PrtCnvRadioThread::init()
|
|
{
|
|
INFO_LOG(m_logger) << "init";
|
|
PrtCnvSocketThread::init();
|
|
m_BroadcastManager.setDbThread(m_DbThread);
|
|
m_TickTimeout = m_Config->getInteger(QStringLiteral("Broadcast"), QStringLiteral("SendGap"), 500);
|
|
m_TicksPerSecond = 1000 / m_TickTimeout;
|
|
m_BroadcastManager.init();
|
|
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram::setEtsMode(m_Config->getBoolean(getName(), QStringLiteral("EtsMode"), false));
|
|
|
|
m_HostISSI = m_Config->getInteger(getName(), QStringLiteral("HostISSI"), 9998);
|
|
m_BroadcastISSI = m_Config->getInteger(getName(), QStringLiteral("BroadcastISSI"), 5000);
|
|
m_EmergencyCallISSI = m_Config->getInteger(getName(), QStringLiteral("EmergencyCallISSI"), 9000);
|
|
if (m_DoCleanDb)
|
|
m_DbThread->licenseMember(m_DbThread->addMember(0, m_BroadcastISSI));
|
|
|
|
QString groupString = m_Config->getString(getName(), QStringLiteral("ListenGroups"), QString());
|
|
const QVector<QStringRef> groups = groupString.splitRef(QLatin1Char(','), QString::SkipEmptyParts);
|
|
for (const QStringRef &sr : groups)
|
|
{
|
|
uint32_t group = sr.toUInt();
|
|
if (group > 0)
|
|
m_ListenGroups.insert(group);
|
|
}
|
|
if (m_ListenGroups.size() > 5)
|
|
{
|
|
while (m_ListenGroups.size() > 5)
|
|
m_ListenGroups.erase(m_ListenGroups.end());
|
|
WARNING_LOG(m_logger) << "More than 5 ListenGroups defined - ignoring all except the first 5";
|
|
}
|
|
|
|
m_LoginPathWithPrefix = m_Config->getString(QStringLiteral("Login"), QStringLiteral("LoginFilePathWithPrefix"), QStringLiteral("login"));
|
|
if (!QDir::isAbsolutePath(m_LoginPathWithPrefix))
|
|
m_LoginPathWithPrefix = QCoreApplication::applicationDirPath() + QLatin1String("/../") + m_LoginPathWithPrefix;
|
|
|
|
m_LoginPathWithPrefix = QDir::cleanPath(QDir::fromNativeSeparators(m_LoginPathWithPrefix));
|
|
QString path = m_LoginPathWithPrefix.left(m_LoginPathWithPrefix.lastIndexOf(QLatin1Char('/')));
|
|
QDir().mkpath(path);
|
|
|
|
m_DoLicenseDespiteSelftestOrConfigFailure = m_Config->getBoolean(QStringLiteral("Login"), QStringLiteral("DoLicenseDespiteSelftestOrConfigFailure"), false);
|
|
initIdentityArray();
|
|
|
|
m_bUseScrambling = m_Config->getBoolean(getName(), QStringLiteral("UseScrambling"), false);
|
|
m_DoDumpReceivedTelegrams = m_Config->getBoolean(getName(), QStringLiteral("DumpReceivedTelegrams"), false);
|
|
m_DoDumpSentTelegrams = m_Config->getBoolean(getName(), QStringLiteral("DumpSentTelegrams"), false);
|
|
m_DoUseSDSTL = m_Config->getBoolean(getName(), QStringLiteral("UseSDS_TL"), false);
|
|
|
|
const QString trafficControl(QStringLiteral("TrafficControl"));
|
|
m_SendQueueHighLoad = m_Config->getInteger(trafficControl, QStringLiteral("SendQueueHighLoad"), 1000);
|
|
m_SendQueueFullLoad = m_Config->getInteger(trafficControl, QStringLiteral("SendQueueFullLoad"), 2000);
|
|
m_SendTelegramsNormalLoad = m_Config->getInteger(trafficControl, QStringLiteral("SendTelegramsNormalLoad"), 100);
|
|
m_SendTelegramsHighLoad = m_Config->getInteger(trafficControl, QStringLiteral("SendTelegramsHighLoad"), 50);
|
|
m_SendTelegramsFullLoad = m_Config->getInteger(trafficControl, QStringLiteral("SendTelegramsFullLoad"), 10);
|
|
m_SendLoad = E_TrafficLoad::Unknown;
|
|
|
|
if (!m_DoCleanDb)
|
|
m_BroadcastManager.restoreAfterCrash();
|
|
|
|
emit initDone();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t PrtCnvRadioThread::start()
|
|
{
|
|
INFO_LOG(m_logger) << "Start";
|
|
PrtCnvSocketThread::start();
|
|
connectSocket();
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::connected()
|
|
{
|
|
INFO_LOG(m_logger) << "Radio-Socket connected m_Socket=" << m_Socket;
|
|
m_ReceiveCounter = END_COUNTER - 1;
|
|
m_SendCounter = END_COUNTER;
|
|
m_SentTelegrams = 0;
|
|
m_SentBytes = 0;
|
|
m_ReceivedTelegrams = 0;
|
|
m_ReceivedBytes = 0;
|
|
m_TickCounter = 0;
|
|
for (int i = 0; i < AVERAGE_COUNT; i++)
|
|
{
|
|
m_SentBytesAverage[i] = 0;
|
|
m_ReceivedBytesAverage[i] = 0;
|
|
}
|
|
m_AverageIndex = 0;
|
|
doRegister();
|
|
startTickTimer();
|
|
m_ConnectionTimer->stop();
|
|
m_State = PrtCnvSocketThread::e_REGISTERING;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::prepareCleanUp()
|
|
{
|
|
m_DoCleanup = true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::doStop()
|
|
{
|
|
m_DoCleanup = true;
|
|
if (m_EnvironmentTelegramTimer)
|
|
m_EnvironmentTelegramTimer->stop();
|
|
m_TickTimer->stop();
|
|
disconnectSocket();
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::onBroadcastTelegramsRequestFromCentral(int equipmentNumber, int broadcastIndex, int resetCounter)
|
|
{
|
|
m_BroadcastManager.handleNachforderungBroadcastTelegram(equipmentNumber, broadcastIndex, resetCounter);
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::doClean()
|
|
{
|
|
INFO_LOG(m_logger) << "doClean";
|
|
m_BroadcastManager.cleanAndInit(true);
|
|
m_DbThread->setResetCounter(m_DbThread->getResetCounter() + 1);
|
|
emit cleant();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::doStart()
|
|
{
|
|
m_DoCleanup = false;
|
|
connectSocket();
|
|
if (m_EnvironmentTelegramTimer && m_EnvironmentTelegramTimer->interval() > 0)
|
|
m_EnvironmentTelegramTimer->start();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::disconnected()
|
|
{
|
|
INFO_LOG(m_logger) << "Radio-Socket disconnected m_Socket=" << m_Socket;
|
|
|
|
|
|
if (e_WORKING == m_State || e_REGISTERING == m_State)
|
|
{
|
|
m_Socket->disconnectFromHost();
|
|
if (m_Socket->state() != QAbstractSocket::UnconnectedState)
|
|
m_Socket->waitForDisconnected();
|
|
}
|
|
m_State = e_DISCONNECTING;
|
|
if (m_DoCleanup)
|
|
{
|
|
m_ConnectionTimer->stop();
|
|
emit stopped();
|
|
}
|
|
else
|
|
connectSocket();
|
|
|
|
emit tetraConnectionState(false);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::readyRead()
|
|
{
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram::PrtCnvRadioGwHeader header;
|
|
|
|
while (true)
|
|
{
|
|
if (m_DoCleanup)
|
|
break;
|
|
|
|
|
|
qint64 bytesRead = m_Socket->peek(reinterpret_cast<char *>(&header), sizeof(header));
|
|
|
|
|
|
if (0 == bytesRead)
|
|
break;
|
|
|
|
|
|
if (sizeof(header) != bytesRead)
|
|
break;
|
|
|
|
|
|
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::MAGIC_NUMBER != header.m_MagicNumber)
|
|
{
|
|
ERROR_LOG(m_logger) << "Reconnect because radio-Telegram does not begin with magic-number 0xA5: I got 0x"
|
|
<< QString::number(header.m_MagicNumber, 16);
|
|
disconnectSocket();
|
|
break;
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
INFO_LOG(m_logger) << "not enough bytes available - waiting for more data";
|
|
break;
|
|
}
|
|
QByteArray buf(dataSize, Qt::Uninitialized);
|
|
|
|
bytesRead = 0;
|
|
while (bytesRead < dataSize)
|
|
{
|
|
qint64 br = m_Socket->read(&buf.data()[bytesRead], dataSize - bytesRead);
|
|
bytesRead += br;
|
|
}
|
|
|
|
if (!checkAndUpdateReceiveCounter(header.m_Counter, END_COUNTER, START_COUNTER))
|
|
{
|
|
|
|
m_LogThread->writeRadioTelegramLog(buf, true, 1);
|
|
ERROR_LOG(m_logger) << "Reconnect because frame-counter of received radio-telegram was wrong";
|
|
disconnectSocket();
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
handleReceivedTelegram(buf);
|
|
|
|
|
|
m_ReceivedTelegrams++;
|
|
m_ReceivedBytes += dataSize;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleReceivedTelegram(const QByteArray &data)
|
|
{
|
|
if (m_DoDumpReceivedTelegrams)
|
|
{
|
|
INFO_LOG(m_logger) << "Received radio-telegram:";
|
|
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, data);
|
|
}
|
|
|
|
switch (data.at(4))
|
|
{
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
|
|
handleRegisterRsp(data.at(5));
|
|
m_State = e_WORKING;
|
|
break;
|
|
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
|
|
handleDataIndReq(data, true);
|
|
emit checkSendToCentral();
|
|
return;
|
|
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq:
|
|
handleReportIndReq(data);
|
|
break;
|
|
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_StatusIndReq:
|
|
handleStatusIndReq(data);
|
|
break;
|
|
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
|
|
handleAirInterfaceLoadReq(data);
|
|
break;
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirLoadHyterra:
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG(m_logger) << "Radio-Telegram with PDU-Type=" << data.at(4)
|
|
<< " not implemented!";
|
|
break;
|
|
}
|
|
m_LogThread->writeRadioTelegramLog(data, true, 1);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleDataIndReq(const QByteArray &data, bool bIsMaybeScrambled)
|
|
{
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram radioTelegram(data);
|
|
const uint32_t dstISSI = radioTelegram.getDestISSI();
|
|
|
|
if (m_bUseScrambling && bIsMaybeScrambled && m_EmergencyCallISSI != dstISSI)
|
|
{
|
|
if (!unscrambleContent(radioTelegram))
|
|
return;
|
|
}
|
|
|
|
|
|
m_LogThread->writeRadioTelegramLog(radioTelegram.data(), true, 1);
|
|
|
|
|
|
if (radioTelegram.isNormalTelegram() && radioTelegram.getTelID() == GEF_ESW_MELDUNG)
|
|
return;
|
|
|
|
if (m_HostISSI == dstISSI || m_ListenGroups.find(dstISSI) != m_ListenGroups.end())
|
|
{
|
|
if (!m_radioTrafficThread)
|
|
m_radioTrafficThread = qobject_cast<PrtCnvRadioTrafficThread *>(m_Parent->getOtherThread(PrtCnvApplication::e_RadioTraffic));
|
|
if (m_radioTrafficThread)
|
|
m_radioTrafficThread->newTelegramArrived(radioTelegram.getPayloadSize());
|
|
|
|
|
|
PrtCnvMember *member = m_DbThread->getMemberByISSI(radioTelegram.getSrcISSI());
|
|
if (radioTelegram.isLoginTelegram() || (member != nullptr))
|
|
{
|
|
switch (radioTelegram.getFragmentStatus())
|
|
{
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment:
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_MiddleFragment:
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment:
|
|
|
|
if (member->m_Licensed)
|
|
handleFragmentedTelegram(radioTelegram);
|
|
else
|
|
{
|
|
|
|
sendLizenzierungTneToRadio(member->m_Id, member->m_ISSI,
|
|
N_CoPrtCnv::E_LicensingKind::Unlicense,
|
|
N_CoPrtCnv::E_LicensingErrorCode::Unlicense);
|
|
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorNotLicensed,
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment == radioTelegram.getFragmentStatus() ?
|
|
radioTelegram.getTelID() : GEF_BESTAETIGUNG);
|
|
WARNING_LOG(m_logger) << "TNE with id=" << member->m_Id
|
|
<< " tries to send a fragmented telegramm without beeing licensed";
|
|
}
|
|
break;
|
|
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_NotFragmented:
|
|
if (radioTelegram.isLoginTelegram())
|
|
handleLoginTelegram(radioTelegram);
|
|
else if (radioTelegram.isNormalTelegram())
|
|
{
|
|
if (radioTelegram.isCrcOk())
|
|
{
|
|
radioTelegram.fixupTelegramIdForEts(false);
|
|
|
|
gef_telegramm_id_et telId = radioTelegram.getTelID();
|
|
|
|
bool bIsBite = (GEF_BITE_MELDUNG2 == telId);
|
|
|
|
if (member->m_Licensed || bIsBite)
|
|
{
|
|
|
|
if (radioTelegram.isContentValid())
|
|
{
|
|
|
|
if (m_BroadcastManager.handleRadioTelegram(radioTelegram))
|
|
{
|
|
std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram>
|
|
centralTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(&radioTelegram));
|
|
if (centralTelegram)
|
|
{
|
|
|
|
|
|
|
|
if (sendAnswer(radioTelegram))
|
|
{
|
|
|
|
|
|
m_DbThread->storeCentralTelegram(centralTelegram, radioTelegram);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::InvalidByteCount,
|
|
telId);
|
|
ERROR_LOG(m_logger) << "The content of received telegram is wrong => thrown away";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
sendLizenzierungTneToRadio(member->m_Id, member->m_ISSI,
|
|
N_CoPrtCnv::E_LicensingKind::Unlicense,
|
|
N_CoPrtCnv::E_LicensingErrorCode::Unlicense);
|
|
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorNotLicensed,
|
|
telId);
|
|
WARNING_LOG(m_logger) << "TNE with id=" << member->m_Id
|
|
<< " tries to send a telegramm without beeing licensed";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "CRC of received telegram is wrong => thrown away";
|
|
sendFehlermeldungGefechtsfeldToCentral(radioTelegram.getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::WrongCrc,
|
|
radioTelegram.getTelID());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "Unknown non-fragmented telegram-type: 0x"
|
|
<< QString::number(radioTelegram.getFragmentCounter(), 16);
|
|
sendFehlermeldungGefechtsfeldToCentral(radioTelegram.getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
radioTelegram.getTelID());
|
|
}
|
|
break;
|
|
default:
|
|
ERROR_LOG(m_logger) << "Unknown payload-status: " << radioTelegram.getFragmentStatus();
|
|
sendFehlermeldungGefechtsfeldToCentral(radioTelegram.getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
radioTelegram.getTelID());
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "The TPU with id=" << radioTelegram.getIdentity()
|
|
<< " and ISSI=" << radioTelegram.getSrcISSI()
|
|
<< " has sent a telegram without be logged in";
|
|
|
|
bool hasId = !(radioTelegram.getFragmentStatus() == N_CoPrtCnv::CoPrtCnvRadioTelegram::e_MiddleFragment || radioTelegram.getFragmentStatus() == N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment);
|
|
sendLoginResponse(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginReject, hasId ? radioTelegram.getIdentity() : 0, radioTelegram.getSrcISSI());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_EmergencyCallISSI == dstISSI)
|
|
{
|
|
|
|
|
|
uint32_t arr[10];
|
|
|
|
for (int i = 0; i < 10; ++i)
|
|
arr[i] = static_cast<uint32_t>(radioTelegram.getPayloadDataUInt8(i - 1));
|
|
for (int i = 0; i < 10; ++i)
|
|
DEBUG_LOG(m_logger) << "arr[" << i << "] = 0x" << QString::number(arr[i], 16);
|
|
|
|
uint32_t x, y;
|
|
x = (arr[0] << 24) + (arr[1] << 16) + (arr[2] << 8) + arr[3];
|
|
x = (x >> 3) & 0x1FFFFFF;
|
|
y = (arr[3] << 24) + (arr[4] << 16) + (arr[5] << 8) + arr[6];
|
|
y = (y >> 3) & 0xFFFFFF;
|
|
|
|
TrainingAreaBase::TetraIcd::EmergencyCall data;
|
|
double longitude = x * (360 / pow(2, 25));
|
|
double latitude = y * (180 / pow(2, 24));
|
|
data.absPosition.lon = longitude * (M_PI / 180.0);
|
|
data.absPosition.lat = latitude * (M_PI / 180.0);
|
|
data.issi = radioTelegram.getSrcISSI();
|
|
data.timeStamp = QDateTime::currentMSecsSinceEpoch();
|
|
auto tele(std::make_shared<N_CoPrtCnv::CoPrtCnvCentralTelegram>(data));
|
|
|
|
DEBUG_LOG(m_logger) << "Notruf lon: " << data.absPosition.lon << ", lat: " << data.absPosition.lat << ", issi: " << data.issi;
|
|
|
|
m_DbThread->storeCentralTelegram(tele, radioTelegram);
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "Radio-Telegram with PDU-Type=" << QString::number(data.at(4))
|
|
<< " and telId=" << radioTelegram.getTelID()
|
|
<< " is for another host: " << radioTelegram.getDestISSI()
|
|
<< " (we are " << m_HostISSI << ")";
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleLoginTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram)
|
|
{
|
|
using namespace N_CoPrtCnv;
|
|
|
|
uint16_t identity = a_loginTelegram.getIdentity();
|
|
uint32_t issi = a_loginTelegram.getSrcISSI();
|
|
|
|
if (CoPrtCnvRadioTelegram::e_LoginRequest != (a_loginTelegram.getPayloadDataUInt8(2) & CoPrtCnvRadioTelegram::e_LoginStatusMask))
|
|
{
|
|
ERROR_LOG(m_logger) << QStringLiteral("Unexpected login-status: 0x%1").arg(a_loginTelegram.getPayloadDataUInt8(2), 2, 16, QLatin1Char('0'));
|
|
return;
|
|
}
|
|
|
|
|
|
if (0 == identity || m_BroadcastISSI == issi)
|
|
{
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::e_LoginReject, identity, issi);
|
|
sendFehlermeldungGefechtsfeldToCentral(identity, TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorLoginEquipmentNumber);
|
|
writeLoginLog(a_loginTelegram,
|
|
QStringLiteral("Login-failure: New TPU with Id=%1 and ISSI=%2 tries to use broadcast-id or -issi => rejected").arg(identity).arg(issi));
|
|
return;
|
|
}
|
|
|
|
const bool bIsMasteringFlag = (a_loginTelegram.getPayloadDataUInt8(2) & CoPrtCnvRadioTelegram::e_LoginMastering) == CoPrtCnvRadioTelegram::e_LoginMastering;
|
|
PrtCnvMember *member = m_DbThread->getMemberById(identity);
|
|
PrtCnvMember *issiMember = m_DbThread->getMemberByISSI(issi);
|
|
DEBUG_LOG(m_logger) << "TPU with id=" << identity << " and ISSI="
|
|
<< issi << " tries to log in, mastering: " << bIsMasteringFlag;
|
|
|
|
if (bIsMasteringFlag && member != issiMember)
|
|
{
|
|
removeMasteringMember(a_loginTelegram, member, issiMember, identity, issi);
|
|
member = nullptr;
|
|
issiMember = nullptr;
|
|
}
|
|
|
|
const int tpuExId = exerciseIdForIdentity(identity);
|
|
TRACE_LOG(m_logger) << "Exercise Id for EqId " << identity << " is: " << tpuExId;
|
|
|
|
|
|
if (member || issiMember)
|
|
{
|
|
|
|
|
|
if (member && (issi == uint32_t(member->m_ISSI)))
|
|
{
|
|
|
|
|
|
|
|
if (member->m_Licensed)
|
|
m_DbThread->licenseMember(member, false);
|
|
m_DbThread->updateExerciseIdForMember(member, m_ExerciseId);
|
|
|
|
sendLoginAccept(identity, issi, tpuExId, bIsMasteringFlag);
|
|
}
|
|
else
|
|
{
|
|
if (bIsMasteringFlag)
|
|
{
|
|
|
|
m_DbThread->addMember(identity, issi);
|
|
|
|
sendLoginAccept(identity, issi, tpuExId, bIsMasteringFlag);
|
|
sendFehlermeldungGefechtsfeldToCentral(identity, TrainingAreaBase::TetraIcd::CtaDataError::Reason::InfoLoginMasteringFlag);
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
int rejectCause = 0;
|
|
if (issiMember)
|
|
rejectCause |= CoPrtCnvRadioTelegram::e_LoginOtherISSI;
|
|
else if (member)
|
|
rejectCause |= CoPrtCnvRadioTelegram::e_LoginOtherEqId;
|
|
else
|
|
rejectCause |= CoPrtCnvRadioTelegram::e_LoginMastering;
|
|
rejectCause |= CoPrtCnvRadioTelegram::e_LoginReject;
|
|
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::E_LoginStatus(rejectCause),
|
|
identity, issi);
|
|
sendFehlermeldungGefechtsfeldToCentral(identity, TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorLoginEquipmentNumber);
|
|
writeLoginLog(a_loginTelegram,
|
|
QStringLiteral("Login-failure: Another TPU with Id=%1 and ISSI=%2 without mastering tried to log in => rejected").arg(identity).arg(issi));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
m_DbThread->addMember(identity, issi);
|
|
m_DbThread->updateExerciseIdForMember(issiMember, m_ExerciseId);
|
|
|
|
sendLoginAccept(identity, issi, tpuExId, bIsMasteringFlag);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_fragmentedTelegram)
|
|
{
|
|
PrtCnvMember *member = m_DbThread->getMemberByISSI(a_fragmentedTelegram.getSrcISSI());
|
|
if (member)
|
|
{
|
|
if (a_fragmentedTelegram.isCrcOk())
|
|
{
|
|
bool doAddFragment = true;
|
|
if (a_fragmentedTelegram.getFragmentStatus() == N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment)
|
|
{
|
|
|
|
|
|
if (!member->isFragmentedListEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "Did not get the last radio-telegram-fragment";
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
member->getFragmentedTelegramId());
|
|
member->clearFragmentedList();
|
|
}
|
|
}
|
|
else if (a_fragmentedTelegram.getFragmentStatus() == N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment)
|
|
{
|
|
|
|
|
|
if (member->isFragmentedListEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "Got a lonely last radio-telegram-fragment";
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
member->getFragmentedTelegramId());
|
|
}
|
|
else
|
|
{
|
|
|
|
member->addFragmentedTelegram(a_fragmentedTelegram);
|
|
const QByteArray unfragmentedRadioTelegramData = member->getUnfragmentedTelegramData();
|
|
if (!unfragmentedRadioTelegramData.isEmpty())
|
|
{
|
|
|
|
|
|
handleDataIndReq(unfragmentedRadioTelegramData, false);
|
|
}
|
|
else
|
|
sendFehlermeldungGefechtsfeldToCentral(member->m_Id,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
member->getFragmentedTelegramId());
|
|
}
|
|
member->clearFragmentedList();
|
|
doAddFragment = false;
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
if (member->isFragmentedListEmpty())
|
|
{
|
|
ERROR_LOG(m_logger) << "Did not get the first radio-telegram-fragment";
|
|
|
|
member->clearFragmentedList();
|
|
doAddFragment = false;
|
|
}
|
|
}
|
|
|
|
|
|
if (doAddFragment)
|
|
{
|
|
member->addFragmentedTelegram(a_fragmentedTelegram);
|
|
}
|
|
}
|
|
else
|
|
ERROR_LOG(m_logger) << "CRC of received fragmented telegram is wrong => thorw away";
|
|
}
|
|
else
|
|
ERROR_LOG(m_logger) << "Internal error: Did not found the TNE with ISSI=" << a_fragmentedTelegram.getSrcISSI() << " in member-list";
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PrtCnvRadioThread::handleBITEEXT_Telegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_BITE_RadioTelegram)
|
|
{
|
|
auto anmeldeart = static_cast<TrainingAreaBase::TetraIcd::BiteReport::LoginType>(a_BITE_RadioTelegram.getPayloadDataUInt32(7, 1));
|
|
uint32_t ergebnisCAN = a_BITE_RadioTelegram.getPayloadDataUInt32(8, 3);
|
|
uint32_t ergebnisAusstattung = a_BITE_RadioTelegram.getPayloadDataUInt8(11);
|
|
uint32_t konfigurationsvergleich = a_BITE_RadioTelegram.getPayloadDataUInt8(23);
|
|
uint32_t aktuelleKonfiguration = a_BITE_RadioTelegram.getPayloadDataUInt32(24, 3);
|
|
|
|
QList<PrtCnv::BiteError> fehlerListe;
|
|
const auto nonDynamic = getBiteError(a_BITE_RadioTelegram, 12);
|
|
if (nonDynamic.errorNumber != 0 ||
|
|
nonDynamic.canDeviceNumber != 0 ||
|
|
nonDynamic.subdeviceNumber != 0 ||
|
|
nonDynamic.uniqueId != 0)
|
|
fehlerListe.append(nonDynamic);
|
|
|
|
const uint8_t fehlerLen = a_BITE_RadioTelegram.getPayloadDataUInt8(19);
|
|
for (uint8_t i = 0; i < fehlerLen; ++i)
|
|
{
|
|
const uint8_t iPos = 27 + i * 7;
|
|
fehlerListe.append(getBiteError(a_BITE_RadioTelegram, iPos));
|
|
}
|
|
|
|
return handleBITE_Telegram(a_BITE_RadioTelegram,
|
|
anmeldeart,
|
|
ergebnisCAN,
|
|
ergebnisAusstattung,
|
|
aktuelleKonfiguration,
|
|
konfigurationsvergleich,
|
|
fehlerListe
|
|
);
|
|
}
|
|
|
|
bool PrtCnvRadioThread::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
|
|
)
|
|
{
|
|
switch (anmeldeart)
|
|
{
|
|
case TrainingAreaBase::TetraIcd::BiteReport::LoginType::InvalidLogin:
|
|
return true;
|
|
case TrainingAreaBase::TetraIcd::BiteReport::LoginType::NoLogin:
|
|
case TrainingAreaBase::TetraIcd::BiteReport::LoginType::NewLogin:
|
|
case TrainingAreaBase::TetraIcd::BiteReport::LoginType::Relogin:
|
|
{
|
|
const uint16_t identity = a_BITE_RadioTelegram.getIdentity();
|
|
bool doForwardBiteMeldung = true;
|
|
QString logText(QStringLiteral("Licensing: The TPU with Id=%1 and ISSI=%2").arg(identity).arg(a_BITE_RadioTelegram.getSrcISSI()));
|
|
|
|
|
|
PrtCnvMember *member = m_DbThread->getMemberById(identity);
|
|
if (member)
|
|
{
|
|
|
|
|
|
if (a_BITE_RadioTelegram.getSrcISSI() == uint32_t(member->m_ISSI))
|
|
{
|
|
|
|
|
|
if (isIdentityValid(identity))
|
|
{
|
|
m_DbThread->updateExerciseIdForMember(member, m_ExerciseId);
|
|
|
|
if (!member->m_Licensed)
|
|
{
|
|
m_DbThread->licenseMember(member);
|
|
if (TrainingAreaBase::TetraIcd::BiteReport::LoginType::NewLogin == anmeldeart)
|
|
logText += QStringLiteral(" is now licensed");
|
|
else
|
|
logText += QStringLiteral(" is now licensed (WIEDER_ANMELDUNG instead NEUE_ANMELDUNG used!)");
|
|
}
|
|
else
|
|
{
|
|
if (TrainingAreaBase::TetraIcd::BiteReport::LoginType::NewLogin == anmeldeart)
|
|
logText += QStringLiteral(" is now relicensed (NEUE_ANMELDUNG instead WIEDER_ANMELDUNG used!)");
|
|
else
|
|
logText += QStringLiteral(" is now relicensed");
|
|
}
|
|
|
|
N_CoPrtCnv::E_LicensingErrorCode errorCode = N_CoPrtCnv::E_LicensingErrorCode::Ok;
|
|
|
|
if (ergebnisAusstattung != 1)
|
|
{
|
|
|
|
errorCode = N_CoPrtCnv::E_LicensingErrorCode::Battery;
|
|
logText += QStringLiteral(", has battery-Warning");
|
|
}
|
|
if ((ergebnisCAN & aktuelleKonfiguration) != aktuelleKonfiguration)
|
|
{
|
|
|
|
errorCode = N_CoPrtCnv::E_LicensingErrorCode::Selftest;
|
|
logText += QStringLiteral(", but selftest is failed");
|
|
}
|
|
if (konfigurationsvergleich != 0)
|
|
{
|
|
|
|
errorCode = N_CoPrtCnv::E_LicensingErrorCode::Configuration;
|
|
logText += QStringLiteral(", but has configuration-failure: ");
|
|
}
|
|
bool selftestOrConfigError = (errorCode == N_CoPrtCnv::E_LicensingErrorCode::Configuration || errorCode == N_CoPrtCnv::E_LicensingErrorCode::Selftest);
|
|
bool doLicense = !(selftestOrConfigError && !m_DoLicenseDespiteSelftestOrConfigFailure);
|
|
|
|
prepareWareHouseMessage(identity, doLicense, ergebnisCAN, ergebnisAusstattung, aktuelleKonfiguration, konfigurationsvergleich, fehlerListe);
|
|
if (anmeldeart == TrainingAreaBase::TetraIcd::BiteReport::LoginType::NoLogin)
|
|
return true;
|
|
|
|
|
|
sendLizenzierungTneToRadio(identity, member->m_ISSI,
|
|
doLicense ? N_CoPrtCnv::E_LicensingKind::Success : N_CoPrtCnv::E_LicensingKind::Rejected,
|
|
errorCode);
|
|
|
|
|
|
if (doLicense)
|
|
m_DoNeedActPosResp.insert(identity);
|
|
if (selftestOrConfigError)
|
|
sendFehlermeldungGefechtsfeldToCentral(identity,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorConfigSelfTest,
|
|
a_BITE_RadioTelegram.getTelID());
|
|
|
|
|
|
|
|
if (selftestOrConfigError && !m_DoLicenseDespiteSelftestOrConfigFailure)
|
|
{
|
|
logText = QStringLiteral("Licensing: The TPU with Id=%1 and ISSI=%2").arg(identity).arg(a_BITE_RadioTelegram.getSrcISSI());
|
|
logText += QStringLiteral(" could not be licensed, because ");
|
|
if (errorCode == N_CoPrtCnv::E_LicensingErrorCode::Configuration)
|
|
logText += QStringLiteral("of configuration-failure");
|
|
else
|
|
logText += QStringLiteral("selftest failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
sendLizenzierungTneToRadio(identity, member->m_ISSI, N_CoPrtCnv::E_LicensingKind::Rejected, N_CoPrtCnv::E_LicensingErrorCode::IdentityDenied);
|
|
sendFehlermeldungGefechtsfeldToCentral(identity,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorSendingNamelist,
|
|
a_BITE_RadioTelegram.getTelID());
|
|
logText += QStringLiteral(" could not be licensed because PlayerID is not allowed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
sendLoginResponse(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginReject, identity, a_BITE_RadioTelegram.getSrcISSI());
|
|
logText += QStringLiteral(" could not be licensed because ISSI is wrong");
|
|
doForwardBiteMeldung = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
sendLoginResponse(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LoginReject, identity, a_BITE_RadioTelegram.getSrcISSI());
|
|
logText += QStringLiteral(" wants to be licensed but is not logged in");
|
|
doForwardBiteMeldung = false;
|
|
}
|
|
writeLoginLog(a_BITE_RadioTelegram, logText);
|
|
return doForwardBiteMeldung;
|
|
}
|
|
}
|
|
ERROR_LOG(m_logger) << "BITE-Meldung contains unknown anmeldeart: " << static_cast<int>(anmeldeart);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::checkSend()
|
|
{
|
|
|
|
if (m_DoCleanup)
|
|
return;
|
|
if (PrtCnvSocketThread::e_WORKING != m_State)
|
|
return;
|
|
|
|
if (isSendLimitReached())
|
|
return;
|
|
|
|
PrtCnvStoredRadioTelegram *handle = nullptr;
|
|
const auto radioTelegram = m_DbThread->getNextRadioTelegram(handle);
|
|
if (radioTelegram)
|
|
{
|
|
|
|
|
|
if (radioTelegram->isNormalTelegram())
|
|
{
|
|
switch (radioTelegram->getTelID())
|
|
{
|
|
case GEF_UMGEBUNGS_MELDUNG2:
|
|
{
|
|
const int exerciseId = radioTelegram->getPayloadDataUInt32(5, 2);
|
|
if (m_ExerciseId != exerciseId)
|
|
{
|
|
ERROR_LOG(m_logger) << "Started with exercise Id " << m_ExerciseId << " but got environment telegram for exercise " << exerciseId;
|
|
exit(5);
|
|
}
|
|
|
|
if (m_ExerciseId > 0 && !m_EnvironmentTelegramTimer)
|
|
{
|
|
m_EnvironmentTelegramTimer = new QTimer(this);
|
|
connect(m_EnvironmentTelegramTimer, &QTimer::timeout, this, &PrtCnvRadioThread::timeoutEnvironmentTelegram, Qt::QueuedConnection);
|
|
}
|
|
|
|
if (m_EnvironmentTelegramTimer)
|
|
{
|
|
INFO_LOG(m_logger) << "I got the Umgebungsmeldung-Telegram => I start now sending Umgebungsmeldung periodically";
|
|
m_EnvironmentTelegramTimer->setInterval(m_Config->getInteger(getName(), QStringLiteral("CyclicEnvironmentTime"), 30) * 1000);
|
|
m_EnvironmentTelegramTimer->start();
|
|
}
|
|
|
|
radioTelegram->setPayloadDataWithIndex(7, m_DbThread->getResetCounter(), 1);
|
|
break;
|
|
}
|
|
case GEF_WIRKFELD_STATUS_MINENSPERRE:
|
|
{
|
|
uint16_t wirkfeldKennung = radioTelegram->getPayloadDataUInt32(5, 2);
|
|
if (m_BroadcastManager.isEffectSimulationIdDeactivated(wirkfeldKennung))
|
|
{
|
|
WARNING_LOG(m_logger) << "Special Handling for GEF_WIRKFELD_STATUS_MINENSPERRE with id " << wirkfeldKennung
|
|
<< " -> do not send to CTA";
|
|
m_DbThread->radioTelegramSent(m_SendCounter, handle, 0);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
sendTelegram(radioTelegram, handle);
|
|
|
|
emit checkSendToRadio();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::timeoutTick()
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
if (m_DoCleanup) return;
|
|
startTickTimer();
|
|
|
|
checkSend();
|
|
if (!isSendLimitReached())
|
|
{
|
|
time_t currentTime = time(nullptr);
|
|
auto broadcastTelegram = m_BroadcastManager.getNextBroadcastTelegramToSend(0, currentTime);
|
|
while (broadcastTelegram)
|
|
{
|
|
uint16_t broadcastIndex = broadcastTelegram->getBroadcastIndex();
|
|
sendTelegram(broadcastTelegram, nullptr, false);
|
|
|
|
if (isSendLimitReached())
|
|
broadcastTelegram = nullptr;
|
|
else
|
|
broadcastTelegram = m_BroadcastManager.getNextBroadcastTelegramToSend(broadcastIndex, currentTime);
|
|
}
|
|
}
|
|
else
|
|
INFO_LOG(m_logger) << "send limit reached";
|
|
|
|
m_BroadcastManager.incTickAndSetTrafficLoad(E_TrafficLoad::HighLoad == m_SendLoad, E_TrafficLoad::FullLoad == m_SendLoad);
|
|
if (!isSendLimitReached())
|
|
{
|
|
auto radioTelegram = m_BroadcastManager.getNextNachforderungTelegramToSend();
|
|
if (radioTelegram)
|
|
sendTelegram(radioTelegram, nullptr, false);
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
N_CoPrtCnv::CoPrtCnvCentralTelegram *radioInjectTelegram;
|
|
while ((radioInjectTelegram = m_InjectionThread->getNextRadioInjectionTelegram()))
|
|
{
|
|
if (radioInjectTelegram->telegramId() == TrainingAreaBase::TetraIcd::TelegramId::NACHFORDERUNG_BROADCAST_TELEGRAMME)
|
|
{
|
|
const auto tele = TrainingAreaBase::TetraIcd::RequestBroadcastTelegrams::fromJson(radioInjectTelegram->jsonData());
|
|
onBroadcastTelegramsRequestFromCentral(tele.header.equipmentNumber, tele.broadcastIndex, tele.resetCounter);
|
|
delete radioInjectTelegram;
|
|
continue;
|
|
}
|
|
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>
|
|
radioTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(radioInjectTelegram));
|
|
if (radioTelegram)
|
|
sendTelegram(radioTelegram, nullptr, false);
|
|
delete radioInjectTelegram;
|
|
}
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
ERROR_LOG(m_logger) << "Catched an unexpected exception: " << e.what() << " => could not inject radio-telegram, but we proceed...";
|
|
}
|
|
|
|
|
|
if (m_DbThread->isEnvironmentResetCounterOld())
|
|
timeoutEnvironmentTelegram();
|
|
computeAndLogTrafficStatistics();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::timeoutAliveAndConnection()
|
|
{
|
|
INFO_LOG(m_logger) << "timeoutAliveAndConnection socketState=" << m_Socket->state() << " m_State=" << m_State;
|
|
switch (m_State)
|
|
{
|
|
case e_CONNECTING:
|
|
connectSocket();
|
|
break;
|
|
case e_REGISTERING:
|
|
case e_WORKING:
|
|
break;
|
|
case e_DISCONNECTING:
|
|
m_Socket->abort();
|
|
break;
|
|
default:
|
|
ERROR_LOG(m_logger) << "timeoutAlive for m_State=" << m_State << " not implemented";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::sendTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
|
|
PrtCnvStoredRadioTelegram *a_handle,
|
|
const bool a_doCheckWhetherExpired)
|
|
{
|
|
uint16_t broadcastIndex = 0;
|
|
|
|
bool isSpecialTelegram = !a_telegram->isNormalTelegram();
|
|
bool isLicensing = false;
|
|
PrtCnvMember *member = nullptr;
|
|
if (!isSpecialTelegram)
|
|
{
|
|
member = m_DbThread->getMemberById(a_telegram->getIdentity());
|
|
isLicensing = (GEF_LIZENSIERUNG_TNE == a_telegram->getTelID());
|
|
}
|
|
|
|
if (member || isSpecialTelegram || isLicensing)
|
|
{
|
|
|
|
if (!a_telegram->needsLicensing() || (member && member->m_Licensed))
|
|
{
|
|
if (!a_telegram->hasExpired() || !a_doCheckWhetherExpired)
|
|
{
|
|
if (a_telegram->isContentValid())
|
|
{
|
|
|
|
if (a_telegram->mustBeFragmented())
|
|
sendFragmentedTelegram(a_telegram);
|
|
else
|
|
broadcastIndex = sendToSocket(a_telegram);
|
|
|
|
m_LogThread->writeRadioTelegramLog(a_telegram->data(), false, a_telegram->getPriority());
|
|
}
|
|
else
|
|
{
|
|
sendFehlermeldungGefechtsfeldToCentral(a_telegram->getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::WrongParameter,
|
|
a_telegram->getTelID());
|
|
INFO_LOG(m_logger) << "Telegram with telId=" << a_telegram->getTelID() << " is invalid => thrown away";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (a_telegram->getBroadcastIndex() == 0)
|
|
sendFehlermeldungGefechtsfeldToCentral(a_telegram->getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::TelegramOutdated,
|
|
a_telegram->getTelID());
|
|
INFO_LOG(m_logger) << "Telegram with telId=" << a_telegram->getTelID() << " has expired => thrown away";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendFehlermeldungGefechtsfeldToCentral(a_telegram->getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorNotLicensed,
|
|
a_telegram->getTelID());
|
|
ERROR_LOG(m_logger) << "Central tries to send a telegram with telId=" << a_telegram->getTelID() << " to TNE with id=" << a_telegram->getIdentity() << ", but the TNE is not licensed!";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendFehlermeldungGefechtsfeldToCentral(a_telegram->getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorNotLoggedIn,
|
|
a_telegram->getTelID());
|
|
ERROR_LOG(m_logger) << "Central tries to send a telegram to TNE with id=" << a_telegram->getIdentity() << ", but the TNE is not logged in!";
|
|
|
|
}
|
|
|
|
if (a_handle)
|
|
m_DbThread->radioTelegramSent(m_SendCounter, a_handle, broadcastIndex);
|
|
|
|
if (broadcastIndex >= MAX_BROADCAST_INDEX)
|
|
{
|
|
m_BroadcastManager.handleOverrunOfBroadcastCounter();
|
|
sendFehlermeldungGefechtsfeldToCentral(0, TrainingAreaBase::TetraIcd::CtaDataError::Reason::BroadcastCounterOverflow);
|
|
timeoutEnvironmentTelegram();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::doRegister()
|
|
{
|
|
auto registerIndTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(m_HostISSI));
|
|
sendTelegram(registerIndTelegram, nullptr);
|
|
INFO_LOG(m_logger) << "Send Radio-Telegram Register-Ind with hostISSI=" << m_HostISSI;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::registerGroups()
|
|
{
|
|
if (m_ListenGroups.empty())
|
|
return;
|
|
auto registerGroupIndTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(m_HostISSI, m_ListenGroups));
|
|
sendTelegram(registerGroupIndTelegram, nullptr);
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleRegisterRsp(const uint8_t a_regsitrationStatus)
|
|
{
|
|
INFO_LOG(m_logger) << "Got Radio-Telegram Register-Rsp with status=" << std::to_string(a_regsitrationStatus);
|
|
std::stringstream logText;
|
|
switch (a_regsitrationStatus)
|
|
{
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_IssiSuccessfulRegistered:
|
|
emit tetraConnectionState(true);
|
|
registerGroups();
|
|
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().toStdString() << ", ";
|
|
logText << ")...";
|
|
}
|
|
break;
|
|
default:
|
|
logText << "Unknown registration status: " << std::to_string(a_regsitrationStatus);
|
|
break;
|
|
}
|
|
emit tetraConnectionState(false);
|
|
|
|
FATAL_LOG(m_logger) << logText.str();
|
|
std::cerr << logText.str() << std::endl;
|
|
exit(1);
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &os, const PrtCnvRadioThread::E_TrafficLoad tl)
|
|
{
|
|
switch (tl)
|
|
{
|
|
case PrtCnvRadioThread::E_TrafficLoad::FullLoad:
|
|
os << "E_TrafficLoad::FullLoad";
|
|
return os;
|
|
case PrtCnvRadioThread::E_TrafficLoad::HighLoad:
|
|
os << "E_TrafficLoad::HighLoad";
|
|
return os;
|
|
case PrtCnvRadioThread::E_TrafficLoad::NormalLoad:
|
|
os << "E_TrafficLoad::NormalLoad";
|
|
return os;
|
|
case PrtCnvRadioThread::E_TrafficLoad::Unknown:
|
|
os << "E_TrafficLoad::Unknown";
|
|
return os;
|
|
}
|
|
Q_ASSERT(false);
|
|
os << std::to_string(static_cast<int>(tl));
|
|
return os;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool PrtCnvRadioThread::sendAnswer(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram)
|
|
{
|
|
switch (a_radioTelegram.getTelID())
|
|
{
|
|
case GEF_BITE_MELDUNG2:
|
|
return handleBITEEXT_Telegram(a_radioTelegram);
|
|
case GEF_DEAKTIVIERUNG_EINZELMINE:
|
|
{
|
|
|
|
|
|
auto radioTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(GEF_DEAKTIVIERUNG_EINZELMINE, 13, 0));
|
|
radioTelegram->setPayload2Byte(0);
|
|
radioTelegram->setPayloadData(a_radioTelegram.getPayloadDataPtr(5), 8);
|
|
storeRadioTelegram(radioTelegram, 0);
|
|
emit checkSendToRadio();
|
|
break;
|
|
}
|
|
case GEF_POSITIONS_MELDUNG:
|
|
case GEF_POSITIONS_MELDUNG_LFZ:
|
|
case GEF_POSITIONS_MELDUNG_MIT_TEILNEHMER:
|
|
case GEF_IPS_POSITIONSDATEN:
|
|
{
|
|
auto it = m_DoNeedActPosResp.find(a_radioTelegram.getIdentity());
|
|
if (it != m_DoNeedActPosResp.end())
|
|
{
|
|
auto radioTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(GEF_ALLGEMEINE_NACHRICHT, 9, a_radioTelegram.getIdentity()));
|
|
radioTelegram->setPayload2Byte(0);
|
|
radioTelegram->setPayload1Byte(3);
|
|
radioTelegram->setPayload1Byte(0);
|
|
radioTelegram->setPayload2Byte(8);
|
|
storeRadioTelegram(radioTelegram, 0);
|
|
emit checkSendToRadio();
|
|
m_DoNeedActPosResp.erase(it);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t PrtCnvRadioThread::getDestinationISSI(const uint16_t a_TNE_Id) const
|
|
{
|
|
|
|
PrtCnvMember *member = m_DbThread->getMemberById(a_TNE_Id);
|
|
if (member)
|
|
return member->m_ISSI;
|
|
ERROR_LOG(m_logger) << "TPU with id=" << a_TNE_Id << " not logged in.";
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::timeoutEnvironmentTelegram()
|
|
{
|
|
|
|
if (m_DoCleanup) return;
|
|
if (PrtCnvSocketThread::e_WORKING != m_State) return;
|
|
|
|
auto envTelegram = m_DbThread->getEnvironmentTelegram();
|
|
if (envTelegram)
|
|
{
|
|
INFO_LOG(m_logger) << "It is time to send the Umgebungsmeldung";
|
|
sendTelegram(envTelegram, nullptr);
|
|
}
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "Got no Umgebungsmeldung from DB-Thread...";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::sendFehlermeldungGefechtsfeldToCentral(const uint16_t a_identity,
|
|
const TrainingAreaBase::TetraIcd::CtaDataError::Reason a_errorCause,
|
|
const gef_telegramm_id_et a_telegramId)
|
|
{
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram radioTelegram(GEF_FEHLERMELDUNG_GFD, 9, a_identity);
|
|
radioTelegram.setPayload4Byte(time(nullptr));
|
|
radioTelegram.setPayload1Byte(static_cast<uint8_t>(a_errorCause));
|
|
radioTelegram.setPayload1Byte(a_telegramId);
|
|
|
|
std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram>
|
|
centralTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(&radioTelegram));
|
|
if (centralTelegram)
|
|
{
|
|
m_DbThread->storeCentralTelegram(centralTelegram, radioTelegram);
|
|
emit checkSendToCentral();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::writeLoginLog(uint32_t identity, uint32_t issi, const QString &a_prosaText)
|
|
{
|
|
const QDateTime dtCur(QDateTime::currentDateTimeUtc());
|
|
QString txt =
|
|
QString::number(dtCur.toSecsSinceEpoch()) + QLatin1Char(';') +
|
|
QString::number(issi) + QLatin1Char(';') +
|
|
QString::number(identity) + QLatin1Char(';') +
|
|
QLatin1Char(';') +
|
|
a_prosaText + QLatin1Char(';') + dtCur.toString();
|
|
writeLoginLog(nullptr, txt);
|
|
}
|
|
|
|
void PrtCnvRadioThread::writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram, const QString &a_prosaText)
|
|
{
|
|
writeLoginLog(&a_radioTelegram, a_prosaText);
|
|
}
|
|
|
|
void PrtCnvRadioThread::writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram *a_radioTelegram, const QString &a_prosaText)
|
|
{
|
|
QByteArray logData;
|
|
if (a_radioTelegram)
|
|
{
|
|
|
|
int len = 3;
|
|
if (a_radioTelegram->getTelID() == GEF_BITE_MELDUNG2)
|
|
len = 27;
|
|
QStringList sl;
|
|
sl.reserve(len);
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
uint8_t data = a_radioTelegram->getPayloadDataUInt8(i);
|
|
sl += QStringLiteral("0x%1").arg(data, 2, 16, QLatin1Char('0'));
|
|
}
|
|
|
|
const QDateTime dtCur(QDateTime::currentDateTimeUtc());
|
|
|
|
logData =
|
|
QByteArray::number(dtCur.toSecsSinceEpoch()) + ";" +
|
|
QByteArray::number(a_radioTelegram->getSrcISSI()) + ";" +
|
|
QByteArray::number(a_radioTelegram->getIdentity()) + ";" +
|
|
sl.join(QLatin1Char(',')).toUtf8() + ";" +
|
|
a_prosaText.toUtf8() + ";" + dtCur.toString().toUtf8();
|
|
}
|
|
else
|
|
{
|
|
logData = a_prosaText.toUtf8();
|
|
}
|
|
logData += "\n";
|
|
|
|
INFO_LOG(m_logger) << logData;
|
|
|
|
|
|
QString date = QDate::currentDate().toString(QLatin1String("yyyyMMdd"));
|
|
QString fileName = m_LoginPathWithPrefix + date + QLatin1Char('_') + QString::number(m_ExerciseId) + QLatin1String(".log");
|
|
|
|
|
|
QFile file(fileName);
|
|
file.open(QIODevice::Append);
|
|
file.write(logData);
|
|
file.close();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus,
|
|
const uint16_t a_identity, const uint32_t a_ISSI)
|
|
{
|
|
auto answerTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(a_loginTelegramStatus, a_identity, a_ISSI));
|
|
storeRadioTelegram(answerTelegram, 0);
|
|
emit checkSendToRadio();
|
|
}
|
|
|
|
void PrtCnvRadioThread::sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus,
|
|
const uint16_t a_identity, const uint32_t a_ISSI, const TrainingAreaBase::TetraIcd::GroupIssiInformation &group)
|
|
{
|
|
using namespace N_CoPrtCnv;
|
|
|
|
auto answerTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(a_loginTelegramStatus, a_identity, a_ISSI));
|
|
if ((a_loginTelegramStatus & CoPrtCnvRadioTelegram::e_LoginOtherExercise) == CoPrtCnvRadioTelegram::e_LoginOtherExercise)
|
|
{
|
|
NOTICE_LOG(m_logger) << "sendLoginResponse for " << a_identity << " with issi " << a_ISSI
|
|
<< " and groups " << group.serverIssi << "/" << group.broadcastIssi1
|
|
<< "/" << group.broadcastIssi2;
|
|
answerTelegram->setLoginIssis(group.serverIssi, group.broadcastIssi1, group.broadcastIssi2);
|
|
}
|
|
storeRadioTelegram(answerTelegram, 0);
|
|
emit checkSendToRadio();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::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)
|
|
{
|
|
auto radioTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(GEF_LIZENSIERUNG_TNE, 7, a_identity));
|
|
radioTelegram->setPayload1Byte(static_cast<uint8_t>(a_licensingKind));
|
|
radioTelegram->setPayload1Byte(static_cast<uint8_t>(a_errorCause));
|
|
radioTelegram->setPayload2Byte(0x0013);
|
|
radioTelegram->setDestISSI(a_issi);
|
|
storeRadioTelegram(radioTelegram, 0);
|
|
emit checkSendToRadio();
|
|
emit playerLicencedChanged(a_identity, a_licensingKind);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::initIdentityArray()
|
|
{
|
|
const QString loginGroup(QStringLiteral("login"));
|
|
|
|
if (m_Config->getBoolean(loginGroup, QStringLiteral("DoCheckPlayerId"), true))
|
|
{
|
|
|
|
m_AllowedIdentities.clear();
|
|
|
|
QString identifyFileName = m_Config->getString(loginGroup, QStringLiteral("PlayerIdFilePath"), QString());
|
|
if (identifyFileName.isEmpty())
|
|
{
|
|
|
|
uint32_t lowest = m_Config->getInteger(loginGroup, QStringLiteral("LowestPlayerId"), 1);
|
|
uint32_t highest = m_Config->getInteger(loginGroup, QStringLiteral("HighestPlayerId"), MAX_IDENTITY);
|
|
for (uint32_t i = lowest; i <= highest; i++)
|
|
setIdentityVaild(i);
|
|
INFO_LOG(m_logger) << "PlayerIds from " << lowest << " .. " << highest << " will be used.";
|
|
}
|
|
else
|
|
{
|
|
|
|
INFO_LOG(m_logger) << "PlayerId-file '" << identifyFileName << "' will be used.";
|
|
|
|
|
|
QString path = identifyFileName;
|
|
if (!QDir::isAbsolutePath(path))
|
|
path = QCoreApplication::applicationDirPath() + QLatin1Char('/') + path;
|
|
|
|
|
|
QFile file(path);
|
|
if (file.open(QIODevice::ReadOnly))
|
|
{
|
|
uint32_t lineCount = 0;
|
|
while (!file.atEnd())
|
|
{
|
|
lineCount++;
|
|
QByteArray line(file.readLine());
|
|
int hashPos = line.indexOf('#');
|
|
if (-1 != hashPos)
|
|
line = line.left(hashPos);
|
|
if (!line.trimmed().isEmpty())
|
|
{
|
|
int pos = line.indexOf('-');
|
|
if (-1 == pos)
|
|
{
|
|
|
|
setIdentityVaild(readIdentity(line, lineCount, identifyFileName));
|
|
}
|
|
else
|
|
{
|
|
|
|
QByteArray playerId = line.left(pos).trimmed();
|
|
uint16_t left = readIdentity(playerId, lineCount, identifyFileName);
|
|
playerId = line.right(line.length() - pos - 1).trimmed();
|
|
uint16_t right = readIdentity(playerId, lineCount, identifyFileName);
|
|
for (int i = left; i <= right; i++)
|
|
setIdentityVaild(i);
|
|
}
|
|
}
|
|
}
|
|
file.close();
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not open the PlayerId-file '" << identifyFileName << "'!";
|
|
std::cout << std::endl << "Error: Could not open the PlayerId-file '" << qPrintable(identifyFileName) << "'!" << std::endl;
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
for (uint32_t i = 0; i <= MAX_IDENTITY; i++)
|
|
setIdentityVaild(i);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t PrtCnvRadioThread::readIdentity(const QByteArray &a_idAsText, const uint32_t a_line, const QString &a_fileName)
|
|
{
|
|
ulong playerId = a_idAsText.toULong();
|
|
if (0 == playerId || playerId > MAX_IDENTITY)
|
|
{
|
|
|
|
if (0 == playerId)
|
|
{
|
|
FATAL_LOG(m_logger) << a_fileName << ":" << a_line << ": could not read playerID - '"
|
|
<< a_idAsText << "'";
|
|
}
|
|
else
|
|
{
|
|
FATAL_LOG(m_logger) << a_fileName << ":" << a_line << ": PlayerID too big - '"
|
|
<< a_idAsText << "'";
|
|
}
|
|
exit(1);
|
|
}
|
|
return playerId;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PrtCnvRadioThread::isIdentityValid(uint16_t a_identity) const
|
|
{
|
|
return m_AllowedIdentities.find(a_identity) != m_AllowedIdentities.end();
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::setIdentityVaild(uint16_t a_identity)
|
|
{
|
|
m_AllowedIdentities.insert(a_identity);
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::sendFragmentedTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram)
|
|
{
|
|
|
|
uint8_t fragmentCounter = 0;
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram::E_FragmentStatus fragmentStatus;
|
|
do
|
|
{
|
|
bool error = false;
|
|
auto fragmentedTelegram(std::make_shared<N_CoPrtCnv::CoPrtCnvRadioTelegram>(a_telegram, fragmentCounter++, error));
|
|
if (!error)
|
|
{
|
|
fragmentedTelegram->setDestISSI(getDestinationISSI(a_telegram->getIdentity()));
|
|
|
|
sendTelegram(fragmentedTelegram, nullptr);
|
|
fragmentStatus = fragmentedTelegram->getFragmentStatus();
|
|
}
|
|
else
|
|
{
|
|
sendFehlermeldungGefechtsfeldToCentral(a_telegram->getIdentity(),
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::FragmentationError,
|
|
a_telegram->getTelID());
|
|
return;
|
|
}
|
|
}
|
|
while (N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment != fragmentStatus);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t PrtCnvRadioThread::sendToSocket(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram)
|
|
{
|
|
|
|
if (++m_SendCounter >= END_COUNTER)
|
|
m_SendCounter = START_COUNTER;
|
|
a_telegram->setZaehler(m_SendCounter);
|
|
|
|
a_telegram->setSrcISSI(m_HostISSI);
|
|
uint16_t broadcastIndex = 0;
|
|
gef_telegramm_id_et telId = a_telegram->isNormalTelegram() ? a_telegram->getTelID() : GEF_BESTAETIGUNG;
|
|
if (a_telegram->isNormalTelegram())
|
|
{
|
|
|
|
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq == a_telegram->getPDU_Type())
|
|
{
|
|
if (GEF_LIZENSIERUNG_TNE != a_telegram->getTelID())
|
|
a_telegram->setDestISSI(getDestinationISSI(a_telegram->getIdentity()));
|
|
}
|
|
if (GEF_TEILNEHMERMODE_SETZEN == telId && a_telegram->getDestISSI() != m_BroadcastISSI)
|
|
{
|
|
DEBUG_LOG(m_logger) << "Got MODE with bci: " << a_telegram->getBroadcastIndex();
|
|
if (a_telegram->getBroadcastIndex()<= 0)
|
|
a_telegram->setBroadcastIndex(m_BroadcastManager.getBroadcastIndex());
|
|
DEBUG_LOG(m_logger) << "MODE with bci is now: " << a_telegram->getBroadcastIndex();
|
|
}
|
|
|
|
if (GEF_UMGEBUNGS_MELDUNG2 == telId)
|
|
{
|
|
|
|
a_telegram->setBroadcastIndex(m_BroadcastManager.getBroadcastIndex());
|
|
a_telegram->setPayloadDataWithIndex(7, m_DbThread->getResetCounter(), 1);
|
|
a_telegram->setPayloadDataWithIndex(8, m_DbThread->getBattleFieldOperatingState(), 1);
|
|
a_telegram->setPayloadDataWithIndex(16, (uint32_t)time(nullptr), 4);
|
|
m_DbThread->setResetCounterOnEnvironment();
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
if (GEF_INIT != telId &&
|
|
a_telegram->mustBroadcastBeSet())
|
|
{
|
|
broadcastIndex = m_BroadcastManager.getNextBroadcastIndex();
|
|
a_telegram->setBroadcastIndex(broadcastIndex);
|
|
}
|
|
}
|
|
a_telegram->fixupTelegramIdForEts(true);
|
|
}
|
|
|
|
|
|
DEBUG_LOG(m_logger) << "Send telegram to radio: telId=" << telId << " wcounter=" << int(a_telegram->zaehler()) << " size=" << a_telegram->getSize();
|
|
if (m_DoUseSDSTL && N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq == a_telegram->getPDU_Type())
|
|
{
|
|
a_telegram->setProtocolIdentifier(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_SimpleTextMessagingUsingSDS_TL);
|
|
a_telegram->setMessageReference(telId);
|
|
}
|
|
|
|
if (m_DoDumpSentTelegrams)
|
|
{
|
|
INFO_LOG(m_logger) << "Send radio-telegram with size " << a_telegram->getSize()
|
|
<< " and payload size " << a_telegram->getPayloadSize();
|
|
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, a_telegram->data());
|
|
}
|
|
|
|
Q_ASSERT(QThread::currentThreadId() == m_currentThreadId);
|
|
|
|
if (m_bUseScrambling && a_telegram->getPDU_Type() == N_CoPrtCnv::CoPrtCnvRadioTelegram::E_PDU_Type::e_DataIndReq)
|
|
sendScrambledContent(a_telegram);
|
|
else
|
|
m_Socket->write(a_telegram->data());
|
|
|
|
|
|
m_SentTelegrams++;
|
|
m_SentBytes += a_telegram->getSize();
|
|
|
|
if (a_telegram->isNormalTelegram())
|
|
a_telegram->fixupTelegramIdForEts(false);
|
|
return broadcastIndex;
|
|
}
|
|
|
|
|
|
static inline void scramble(const unsigned char *dataIn, unsigned char *dataOut, size_t len, uint32_t initial, uint32_t polynom)
|
|
{
|
|
uint32_t lfsr = initial;
|
|
for (size_t j = 0; j < len; ++j)
|
|
{
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
unsigned char lsbBit = (lfsr & 0x01);
|
|
lfsr >>= 1;
|
|
if (lsbBit)
|
|
lfsr ^= polynom;
|
|
}
|
|
dataOut[len - j - 1] = dataIn[len - j - 1] ^ static_cast<unsigned char>(lfsr);
|
|
}
|
|
}
|
|
|
|
|
|
void PrtCnvRadioThread::sendScrambledContent(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram) const
|
|
{
|
|
const uint32_t initial = (a_telegram->getSrcISSI() % 0xFFFF) + 1;
|
|
constexpr uint32_t polynom = 0x8016;
|
|
unsigned char dataOut[N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH];
|
|
const uint32_t payloadOffset = N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1;
|
|
const uint32_t scrambleLen = a_telegram->getSize() - payloadOffset;
|
|
const unsigned char *dataIn = reinterpret_cast<const unsigned char *>(a_telegram->getData() + payloadOffset);
|
|
|
|
scramble(dataIn, dataOut, scrambleLen, initial, polynom);
|
|
|
|
QByteArray tmp (reinterpret_cast<char *>(dataOut), scrambleLen);
|
|
|
|
DEBUG_LOG(m_logger) << "Raw scrambled data: " << tmp.toHex(' ');
|
|
|
|
m_Socket->write(a_telegram->getData(), payloadOffset);
|
|
m_Socket->write(reinterpret_cast<char *>(dataOut), scrambleLen);
|
|
}
|
|
|
|
|
|
bool PrtCnvRadioThread::unscrambleContent(N_CoPrtCnv::CoPrtCnvRadioTelegram &a_telegram) const
|
|
{
|
|
const uint32_t initial = (a_telegram.getSrcISSI() % 0xFFFF) + 1;
|
|
constexpr uint32_t polynom = 0x8016;
|
|
const uint32_t payloadOffset = N_CoPrtCnv::CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH - 1;
|
|
const uint32_t scrambleLen = a_telegram.getSize() - payloadOffset;
|
|
unsigned char *dataIn = reinterpret_cast<unsigned char *>(a_telegram.getData() + payloadOffset);
|
|
unsigned char dataOut[N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH];
|
|
|
|
if (scrambleLen > N_CoPrtCnv::CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH)
|
|
{
|
|
ERROR_LOG(m_logger) << "Telegram length does not match - can't unscramble content, len: " << scrambleLen;
|
|
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Error, a_telegram.data());
|
|
Q_ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
scramble(dataIn, dataOut, scrambleLen, initial, polynom);
|
|
|
|
memcpy(dataIn, dataOut, scrambleLen);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleReportIndReq(const QByteArray &data)
|
|
{
|
|
const uint8_t *a_data = reinterpret_cast<const uint8_t *>(data.data());
|
|
|
|
uint32_t srcISSI = *(uint32_t *)&a_data[5];
|
|
uint32_t destISSI = *(uint32_t *)&a_data[9];
|
|
if (m_HostISSI == destISSI)
|
|
{
|
|
N_CoPrtCnv::CoPrtCnvRadioTelegram::E_DeliveryStatusForTL_Report delivery = (N_CoPrtCnv::CoPrtCnvRadioTelegram::E_DeliveryStatusForTL_Report)a_data[16];
|
|
switch (delivery)
|
|
{
|
|
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DeliveryFailed:
|
|
{
|
|
uint8_t messageReference = a_data[14];
|
|
uint16_t identity = 0;
|
|
PrtCnvMember *member = m_DbThread->getMemberByISSI(srcISSI);
|
|
if (member)
|
|
identity = member->m_Id;
|
|
sendFehlermeldungGefechtsfeldToCentral(identity,
|
|
TrainingAreaBase::TetraIcd::CtaDataError::Reason::ErrorSendingToExerciseControl,
|
|
static_cast<gef_telegramm_id_et>(messageReference));
|
|
break;
|
|
}
|
|
default:
|
|
ERROR_LOG(m_logger) << "reaction to delivery " << (int)a_data[16] << " not implemented";
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
ERROR_LOG(m_logger) << "Radio-Ack-Telegram from " << srcISSI << " is for another host: " << destISSI << " (we are " << m_HostISSI << ")";
|
|
}
|
|
|
|
void PrtCnvRadioThread::prepareWareHouseMessage(qint32 equipNr, bool doLicence, qint64 ergebnisCAN, qint64 ergebnisAusstattung, qint64 aktuelleKonfiguration, qint64 konfigurationsvergleich, const QList<BiteError> &fehlerListe)
|
|
{
|
|
QJsonObject obj;
|
|
obj.insert(QStringLiteral("equipNo"), equipNr);
|
|
obj.insert(QStringLiteral("doLicence"), doLicence);
|
|
obj.insert(QStringLiteral("biteState"), ergebnisCAN);
|
|
obj.insert(QStringLiteral("batteryState"), ergebnisAusstattung);
|
|
obj.insert(QStringLiteral("configState"), aktuelleKonfiguration);
|
|
obj.insert(QStringLiteral("configError"), konfigurationsvergleich);
|
|
QJsonArray errorArr;
|
|
|
|
for (const auto &error: fehlerListe)
|
|
{
|
|
QJsonObject errorObj;
|
|
errorObj.insert(QStringLiteral("errorNumber"), error.errorNumber);
|
|
errorObj.insert(QStringLiteral("canDeviceNumber"), error.canDeviceNumber);
|
|
errorObj.insert(QStringLiteral("subdeviceNumber"), error.subdeviceNumber);
|
|
errorObj.insert(QStringLiteral("uniqueId"), error.uniqueId);
|
|
errorArr.append(errorObj);
|
|
}
|
|
obj.insert(QStringLiteral("biteError"), errorArr);
|
|
|
|
|
|
QJsonDocument doc(obj);
|
|
emit warehouseMessageFromPtrCnv(doc.toJson());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleStatusIndReq(const QByteArray &data)
|
|
{
|
|
const uint8_t *a_data = reinterpret_cast<const uint8_t *>(data.data());
|
|
uint32_t srcISSI = *(uint32_t *)&a_data[5];
|
|
uint32_t dstISSI = *(uint32_t *)&a_data[9];
|
|
uint16_t status = *(uint16_t *)&a_data[13];
|
|
INFO_LOG(m_logger) << "Received Status from " << srcISSI << " for "
|
|
<< dstISSI << " - status: " << status;
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::handleAirInterfaceLoadReq(const QByteArray &data)
|
|
{
|
|
const uint8_t *a_data = reinterpret_cast<const uint8_t *>(data.data());
|
|
|
|
|
|
uint16_t nbrOfReports = *(uint16_t *)&a_data[5];
|
|
INFO_LOG(m_logger) << "Number of AirInterface-reports: " << nbrOfReports;
|
|
|
|
uint16_t sdsQueueLength = 0;
|
|
uint16_t index = 7;
|
|
for (int i = 0; i < nbrOfReports; i++)
|
|
{
|
|
uint8_t networkIndex = a_data[index++];
|
|
uint16_t baseStationID = *(uint16_t *)&a_data[index];
|
|
index += 2;
|
|
uint8_t channelID = a_data[index++];
|
|
uint8_t queueLength = a_data[index++];
|
|
INFO_LOG(m_logger) << "AirInterface-report[" << i << "]: networkIndex=" << (int)networkIndex << " baseStationID=" << baseStationID << " channelID=" << (int)channelID << " queueLength=" << (int)queueLength;
|
|
if (1 == networkIndex)
|
|
sdsQueueLength = queueLength;
|
|
}
|
|
E_TrafficLoad newLoad;
|
|
|
|
if (sdsQueueLength > m_SendQueueFullLoad)
|
|
{
|
|
newLoad = E_TrafficLoad::FullLoad;
|
|
INFO_LOG(m_logger) << "Set send-load to Full";
|
|
}
|
|
else if (sdsQueueLength > m_SendQueueHighLoad)
|
|
{
|
|
newLoad = E_TrafficLoad::HighLoad;
|
|
INFO_LOG(m_logger) << "Set send-load to High";
|
|
}
|
|
else
|
|
{
|
|
newLoad = E_TrafficLoad::NormalLoad;
|
|
INFO_LOG(m_logger) << "Set send-load to Normal";
|
|
}
|
|
|
|
m_SendLoad = newLoad;
|
|
}
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::computeAndLogTrafficStatistics()
|
|
{
|
|
if (++m_TickCounter >= m_TicksPerSecond)
|
|
{
|
|
m_TickCounter = 0;
|
|
m_SentBytesAverage[m_AverageIndex] = m_SentBytes;
|
|
m_ReceivedBytesAverage[m_AverageIndex] = m_ReceivedBytes;
|
|
if (++m_AverageIndex >= AVERAGE_COUNT) m_AverageIndex = 0;
|
|
uint32_t sendAverage = 0;
|
|
uint32_t receiveAverage = 0;
|
|
for (int i = 0; i < AVERAGE_COUNT; i++)
|
|
{
|
|
sendAverage += m_SentBytesAverage[i];
|
|
receiveAverage += m_ReceivedBytesAverage[i];
|
|
}
|
|
DEBUG_LOG(m_loggerStats) << "Send-Statistics: " << m_SentTelegrams << " telegrams (" << m_SentBytes << "/" << sendAverage << " bytes) sent; " << (sendAverage / 10) << " kB/s";
|
|
DEBUG_LOG(m_loggerStats) << "Receive-Statistics: " << m_ReceivedTelegrams << " telegrams (" << m_ReceivedBytes << "/" << receiveAverage << " bytes) received; " << (receiveAverage / 10) << " kB/s";
|
|
m_SentBytes = 0;
|
|
m_SentTelegrams = 0;
|
|
m_ReceivedBytes = 0;
|
|
m_ReceivedTelegrams = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool PrtCnvRadioThread::isSendLimitReached() const
|
|
{
|
|
switch (m_SendLoad)
|
|
{
|
|
case E_TrafficLoad::NormalLoad:
|
|
return m_SentTelegrams >= m_SendTelegramsNormalLoad;
|
|
case E_TrafficLoad::HighLoad:
|
|
return m_SentTelegrams >= m_SendTelegramsHighLoad;
|
|
case E_TrafficLoad::FullLoad:
|
|
return m_SentTelegrams >= m_SendTelegramsFullLoad;
|
|
case E_TrafficLoad::Unknown:
|
|
return false;
|
|
}
|
|
Q_ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PrtCnvRadioThread::sendEnvironmentTelegramTimerNow()
|
|
{
|
|
|
|
QTimer::singleShot(0, this, &PrtCnvRadioThread::onSendEnvironmentTelegramTimerNow);
|
|
}
|
|
|
|
void PrtCnvRadioThread::onSendEnvironmentTelegramTimerNow()
|
|
{
|
|
if (!m_EnvironmentTelegramTimer || !m_EnvironmentTelegramTimer->isActive())
|
|
return;
|
|
timeoutEnvironmentTelegram();
|
|
}
|
|
|
|
template <typename T>
|
|
void removeFromContainer(T &cont, int id)
|
|
{
|
|
auto it = cont.find(id);
|
|
if (it != cont.end())
|
|
cont.erase(it);
|
|
}
|
|
|
|
void PrtCnvRadioThread::exerciseEquipmentChanged(const TrainingAreaBase::TetraIcd::ExerciseEquipmentAssignmentComplete &data)
|
|
{
|
|
QMutexLocker l(&m_exerciseEquipmentAssignmentMutex);
|
|
m_exerciseEquipmentAssignments = data.exerciseEquipmentAssignments;
|
|
for (const auto &info : data.groupIssiInformation)
|
|
m_groupIssiInformation[info.exerciseId] = info;
|
|
|
|
emit checkMemberForValidLogin(-1, -1);
|
|
}
|
|
|
|
void PrtCnvRadioThread::onCheckMemberForValidLogin(int identity, int issi)
|
|
{
|
|
QMutexLocker l(&m_exerciseEquipmentAssignmentMutex);
|
|
if (identity == -1)
|
|
{
|
|
const auto knownIdentities = m_DbThread->getClaimStatistics();
|
|
for (const auto &p : knownIdentities)
|
|
{
|
|
if (p.first <= 0)
|
|
continue;
|
|
onCheckMemberForValidLogin(p.first, -1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isIdentityInCurrentExercise(identity))
|
|
{
|
|
DEBUG_LOG(m_logger) << "onCheckMemberForValidLogin: eqid: " << identity << " is in current exercise " << m_ExerciseId;
|
|
return;
|
|
}
|
|
PrtCnv::PrtCnvMember *member = m_DbThread->getMemberById(identity);
|
|
if (!member)
|
|
return;
|
|
issi = member->m_ISSI;
|
|
|
|
const int newExerciseId = exerciseIdForIdentity(identity);
|
|
if (newExerciseId != 0 && newExerciseId != member->m_loggedInExerciseId)
|
|
sendLoginAccept(identity, issi, newExerciseId, false);
|
|
}
|
|
}
|
|
|
|
void PrtCnvRadioThread::sendLoginAccept(int identity, int issi, int exerciseId, bool bWithMastering)
|
|
{
|
|
using namespace N_CoPrtCnv;
|
|
auto response = 0;
|
|
if (bWithMastering)
|
|
response |= CoPrtCnvRadioTelegram::e_LoginMastering;
|
|
|
|
PrtCnvMember *member = m_DbThread->getMemberById(identity);
|
|
const auto it = m_groupIssiInformation.find(exerciseId);
|
|
if (it == m_groupIssiInformation.end() || exerciseId == 0)
|
|
{
|
|
|
|
QString log;
|
|
if (exerciseId == 0)
|
|
log = QStringLiteral("Denied Login of TPU with Id=%1 and ISSI=%2 - Id is not assigned in any exercise.")
|
|
.arg(identity).arg(issi);
|
|
else
|
|
log = QStringLiteral("TPU with Id=%1 and ISSI=%2 should be in exercise %3 but could not find valid group information for this exercise!")
|
|
.arg(identity).arg(issi).arg(exerciseId);
|
|
if (bWithMastering)
|
|
log += QStringLiteral(" Mastering-Bit is set");
|
|
WARNING_LOG(m_logger) << log;
|
|
writeLoginLog(identity, issi, log);
|
|
if (m_ExerciseId != exerciseId)
|
|
response |= CoPrtCnvRadioTelegram::e_LoginNoExerciseAssignment;
|
|
response |= CoPrtCnvRadioTelegram::e_LoginReject;
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::E_LoginStatus(response), identity, issi);
|
|
if (m_ExerciseId == 0)
|
|
m_DbThread->updateExerciseIdForMember(member, exerciseId);
|
|
else
|
|
m_DbThread->removeMember(member);
|
|
return;
|
|
}
|
|
|
|
const TrainingAreaBase::TetraIcd::GroupIssiInformation &gi = it->second;
|
|
QString log;
|
|
if (m_ExerciseId != exerciseId)
|
|
log = QStringLiteral("TPU with Id=%1 and ISSI=%2 should switch to exercise %3 (%4/%5).")
|
|
.arg(identity).arg(issi).arg(exerciseId).arg(gi.broadcastIssi1).arg(gi.broadcastIssi2);
|
|
else
|
|
log = QStringLiteral("(Re)Login of TPU with Id=%1 and ISSI=%2.")
|
|
.arg(identity).arg(issi);
|
|
if (bWithMastering)
|
|
log += QStringLiteral(" Mastering-Bit is set");
|
|
DEBUG_LOG(m_logger) << log;
|
|
writeLoginLog(identity, issi, log);
|
|
|
|
if (m_ExerciseId != exerciseId)
|
|
response |= CoPrtCnvRadioTelegram::e_LoginOtherExercise;
|
|
response |= CoPrtCnvRadioTelegram::e_LoginAccept;
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::E_LoginStatus(response), identity, issi, gi);
|
|
if (m_ExerciseId == 0)
|
|
m_DbThread->updateExerciseIdForMember(member, exerciseId);
|
|
}
|
|
|
|
bool PrtCnvRadioThread::isIdentityInCurrentExercise(int identity) const
|
|
{
|
|
QMutexLocker l(&m_exerciseEquipmentAssignmentMutex);
|
|
if (m_exerciseEquipmentAssignments.empty())
|
|
return true;
|
|
const auto it = m_exerciseEquipmentAssignments.find(m_ExerciseId);
|
|
if (it == m_exerciseEquipmentAssignments.end())
|
|
return false;
|
|
const auto itIdentity = it->second.find(identity);
|
|
return (itIdentity != it->second.end());
|
|
}
|
|
|
|
int PrtCnvRadioThread::exerciseIdForIdentity(int identity) const
|
|
{
|
|
QMutexLocker l(&m_exerciseEquipmentAssignmentMutex);
|
|
for (const auto &s : m_exerciseEquipmentAssignments)
|
|
{
|
|
auto it = s.second.find(identity);
|
|
if (it != s.second.end())
|
|
return s.first;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void PrtCnvRadioThread::removeMasteringMember(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram,
|
|
PrtCnvMember *member,
|
|
PrtCnvMember *issiMember,
|
|
int masterIdentity,
|
|
int masterIssi)
|
|
{
|
|
using namespace N_CoPrtCnv;
|
|
|
|
|
|
if (member)
|
|
{
|
|
m_DbThread->updateExerciseIdForMember(member, 0);
|
|
|
|
if (member->m_Licensed)
|
|
{
|
|
sendLizenzierungTneToRadio(member->m_Id, member->m_ISSI, N_CoPrtCnv::E_LicensingKind::Unlicense, N_CoPrtCnv::E_LicensingErrorCode::Unlicense);
|
|
INFO_LOG(m_logger) << "Unlicense TPU with already used ID. Id="
|
|
<< member->m_Id << " ISSI=" << member->m_ISSI;
|
|
}
|
|
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::E_LoginStatus(CoPrtCnvRadioTelegram::e_LoginReject | CoPrtCnvRadioTelegram::e_LoginOtherEqId),
|
|
member->m_Id, member->m_ISSI);
|
|
INFO_LOG(m_logger) << "Reject TPU with already used ID. Id="
|
|
<< member->m_Id << " ISSI=" << member->m_ISSI;
|
|
writeLoginLog(a_loginTelegram,
|
|
QStringLiteral("Login-failure: TPU with Id=%1 and ISSI=%2 was removed from TETRA due to Mastering-Bit from Id=%3 and ISSI=%4")
|
|
.arg(member->m_Id).arg(member->m_ISSI).arg(masterIdentity).arg(masterIssi));
|
|
m_DbThread->removeMember(member);
|
|
}
|
|
|
|
|
|
if (issiMember)
|
|
{
|
|
m_DbThread->updateExerciseIdForMember(issiMember, 0);
|
|
|
|
if (issiMember->m_Licensed)
|
|
{
|
|
sendLizenzierungTneToRadio(issiMember->m_Id, issiMember->m_ISSI, N_CoPrtCnv::E_LicensingKind::Unlicense, N_CoPrtCnv::E_LicensingErrorCode::Unlicense);
|
|
INFO_LOG(m_logger) << "Unlicense TPU with already used ISSI. Id="
|
|
<< issiMember->m_Id << " ISSI=" << issiMember->m_ISSI;
|
|
}
|
|
|
|
sendLoginResponse(CoPrtCnvRadioTelegram::E_LoginStatus(CoPrtCnvRadioTelegram::e_LoginReject | CoPrtCnvRadioTelegram::e_LoginOtherISSI),
|
|
issiMember->m_Id, issiMember->m_ISSI);
|
|
INFO_LOG(m_logger) << "Reject TPU with already used ISSI. Id="
|
|
<< issiMember->m_Id << " ISSI=" << issiMember->m_ISSI;
|
|
writeLoginLog(a_loginTelegram,
|
|
QStringLiteral("Login-failure: TPU with Id=%1 and ISSI=%2 was removed from TETRA due to Mastering-Bit from Id=%3 and ISSI=%4")
|
|
.arg(issiMember->m_Id).arg(issiMember->m_ISSI).arg(masterIdentity).arg(masterIssi));
|
|
m_DbThread->removeMember(issiMember);
|
|
}
|
|
}
|
|
|
|
}
|