Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
//
// Implementation of the Class AppComponent
// **************************************************************************
#include "AppComponent.hpp"
namespace N_AppComponent
{
int32_t AppComponent::create(NGL::Settings *settings)
{
for (IAppComponent *c : m_Childs)
c->create(settings);
return 0;
}
int32_t AppComponent::destroy()
{
for (IAppComponent *c : m_Childs)
c->destroy();
return 0;
}
int32_t AppComponent::init()
{
for (IAppComponent *c : m_Childs)
c->init();
return 0;
}
int32_t AppComponent::deinit()
{
for (IAppComponent *c : m_Childs)
c->deinit();
return 0;
}
int32_t AppComponent::start()
{
for (IAppComponent *c : m_Childs)
c->start();
return 0;
}
int32_t AppComponent::stop()
{
for (IAppComponent *c : m_Childs)
c->stop();
return 0;
}
void AppComponent::addChild(IAppComponent *a_child, const QString &a_name)
{
if (a_child)
{
a_child->setName(a_name);
m_Childs.push_back(a_child);
}
}
IAppComponent *AppComponent::getChild(const QString &a_name)
{
for (IAppComponent *c : m_Childs)
{
if (a_name == c->getName())
return c;
}
return nullptr;
}
} // namespace N_AppComponent
+45
View File
@@ -0,0 +1,45 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
//
// Implementation of the Class AppComponent
// **************************************************************************
#pragma once
#include <vector>
#include "IAppComponent.hpp"
#include <PrtCnv/prtcnvlib_export.h>
namespace NGL {
class Settings;
}
namespace N_AppComponent
{
class PRTCNVLIB_EXPORT AppComponent : public N_AppComponent::IAppComponent
{
public:
AppComponent() = default;
~AppComponent() = default;
int32_t create(NGL::Settings *settings) override;
int32_t deinit() override;
int32_t destroy() override;
int32_t init() override;
int32_t start() override;
int32_t stop() override;
protected:
void addChild(IAppComponent* a_child, const QString& a_name = QString());
IAppComponent* getChild(const QString& a_name);
private:
std::vector<IAppComponent*> m_Childs;
};
}
+78
View File
@@ -0,0 +1,78 @@
#
# $Id$
# $HeadURL$
# $Author$
#
#ADD_SUBDIRECTORY(TetraTestServer)
ADD_SUBDIRECTORY(LastTest)
ADD_SUBDIRECTORY(LastTestAuswertung)
SET(PrtCnv_SRCS
logger.h
logger.cpp
AppComponent.cpp
AppComponent.hpp
IAppComponent.hpp
PrtCnvApplication.cpp
PrtCnvApplication.hpp
PrtCnvBroadcastMgr.cpp
PrtCnvBroadcastMgr.hpp
PrtCnvCentralThread.cpp
PrtCnvCentralThread.hpp
PrtCnvDbThread.cpp
PrtCnvDbThread.hpp
PrtCnvInjectionSocket.cpp
PrtCnvInjectionSocket.hpp
PrtCnvInjectionThread.cpp
PrtCnvInjectionThread.hpp
PrtCnvMember.cpp
PrtCnvMember.hpp
PrtCnvRadioThread.cpp
PrtCnvRadioThread.hpp
PrtCnvRadioTrafficThread.cpp
PrtCnvRadioTrafficThread.hpp
PrtCnvSocketThread.cpp
PrtCnvSocketThread.hpp
PrtCnvStoredCentralTelegram.hpp
PrtCnvStoredRadioTelegram.hpp
PrtCnvStoredTelegram.hpp
PrtCnvTelegramLogThread.cpp
PrtCnvTelegramLogThread.hpp
PrtCnvThread.cpp
PrtCnvThread.hpp
PrtCnvThreadStarter.cpp
PrtCnvThreadStarter.hpp
)
NGL_ADD_LIBRARY2(AAR SV PrtCnvLib SHARED ${PrtCnv_SRCS})
NGL_TARGET_LINK_LIBRARIES(PrtCnvLib
PRIVATE
ngldb
nglsettings
LtsUtilsCoreHeaderOnly
Qt5::Sql
Qt5::WebSockets
PUBLIC
CoPrtCnv
Qt5::Core
Qt5::Network
NGLLogger
)
NGL_TARGET_INCLUDE_DIRECTORIES(PrtCnvLib
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_ADD_EXECUTABLE(AAR SV PrtCnv PrtCnvMain.cpp)
NGL_TARGET_LINK_LIBRARIES(PrtCnv
PrtCnvLib
Qt5::Core
LtsCommandLineParser
nglcoreapplication
)
NGL_INSTALL_TARGET(PrtCnv)
NGL_INSTALL(AAR SV FILES log4cxx_prtcnv.props DESTINATION bin)
NGL_INSTALL(AAR SV FILES PrtCnv.dat.tts DESTINATION bin)
+59
View File
@@ -0,0 +1,59 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
//
// Implementation of the Class IAppComponent
// **************************************************************************
#pragma once
#include <QtCore/QString>
namespace NGL {
class Settings;
}
namespace N_AppComponent
{
class IAppComponent
{
public:
IAppComponent() = default;
virtual ~IAppComponent() = default;
virtual int32_t create(NGL::Settings *settings) = 0;
virtual int32_t deinit() = 0;
virtual int32_t destroy() = 0;
virtual int32_t init() = 0;
virtual int32_t start() = 0;
virtual int32_t stop() = 0;
inline const QString& getName() const
{
return m_Name;
}
inline void setName(const QString& newVal)
{
m_Name = newVal;
}
protected:
QString m_Name;
};
}
+21
View File
@@ -0,0 +1,21 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(my_SRCS
main.cpp
tetra_if.cpp
tetra_if.h
)
NGL_ADD_EXECUTABLE(AAR SV PrtCnvLastTest ${my_SRCS})
NGL_TARGET_LINK_LIBRARIES(PrtCnvLastTest
Qt5::Core
Qt5::Network
CoPrtCnv
NGLLogger
ngltools
)
NGL_INSTALL_TARGET(PrtCnvLastTest)
+65
View File
@@ -0,0 +1,65 @@
/**
* $Id$
* $HeadURL$
* $Author$
*/
#include <QCoreApplication>
#include <QStringList>
#include <QHostAddress>
#include <stdio.h>
#include <stdlib.h>
#include "tetra_if.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
// Check alle 30 Sekunden
NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxx_tetra.props", false);
NGL::INGLLogger &logger(NGL::NGLLoggerBootstrap::getNGLRootLogger());
const QStringList args = app.arguments().mid(1);
if (args.count() < 4)
TetraIF::usage();
QHostAddress tetra(args[0]);
if (tetra.isNull())
{
ERROR_LOG(logger) << "Invalid IP address " << args[0];
TetraIF::usage();
}
bool bOk;
unsigned short port = args[1].toUShort(&bOk);
if (!bOk)
{
ERROR_LOG(logger) << "Invalid IP port " << args[1];
TetraIF::usage();
}
unsigned short issi = args[2].toUShort(&bOk);
if (!bOk)
{
ERROR_LOG(logger) << "Invalid ISSI id " << args[2];
TetraIF::usage();
}
const QString mode = args[3].toLower();
if (mode != QLatin1String("listen") &&
mode != QLatin1String("send_random") &&
mode != QLatin1String("send_file"))
{
ERROR_LOG(logger) << "Invalid mode " << args[3];
TetraIF::usage();
}
TetraIF t(tetra, port, issi);
if (mode == QLatin1String("listen"))
t.listenMode(args.mid(4));
else if (mode == QLatin1String("send_random"))
t.randomMode(args.mid(4));
else if (mode == QLatin1String("send_file"))
t.fileMode(args.mid(4));
t.start();
return app.exec();
}
+507
View File
@@ -0,0 +1,507 @@
/**
* Tetra-Interface für Lasttest
* $Id$
* $HeadURL$
* $Author$
*/
#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)
{
// take input and write them to and output directory
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";
// read the 4-byte frame-header for find out the length of whole telegram
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)
{
// response to register indication
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
handleRegisterRsp(data.at(5)); // registration status
break;
// payload telegram
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
handleDataIndReq(data);
break;
// ack-telegram - not handled here
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq:
// handleReportIndReq(data);
break;
// air-interface-load-telegram - not handled here
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
// handleAirInterfaceLoadReq(data);
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)
{
// im Listen-Mode -> Ausgabe in Directory
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();
}
// 27 Bytes Header entfernen
f.write(&data.data()[27], data.size() - 27);
f.close();
}
}
void TetraIF::onSendData()
{
++m_secondFragment;
// we're called every 10ms
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))
{
// GEF_BESTAETIGUNG -> Dummy, gets overridden by setPayloadDataComplete()
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();
// broadcast
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; // this deletes also a_data
}
}
void TetraIF::handleLoginTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *a_loginTelegram)
{
uint16_t identity = a_loginTelegram->getIdentity();
uint32_t issi = a_loginTelegram->getSrcISSI();
// is it a login request?
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);
// set our ISSI
a_telegram->setDestISSI(a_ISSI);
a_telegram->setSrcISSI(2);
gef_telegramm_id_et telId = a_telegram->isNormalTelegram() ? a_telegram->getTelID() : GEF_BESTAETIGUNG;
// send telegram to radio
INFO_LOG(m_logger) << "Send telegram to radio: telId=" << telId << " wcounter=" << a_telegram->zaehler() << " size= 28";
// Protokoll-Byte wird hier mit als Datenbyte missbraucht...
a_telegram->setProtocolIdentifier(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_TDSMessageWithoutSDS_TL);
a_telegram->setPayloadData(0x01);
a_telegram->setPayloadData(0x04);
// Bitanzahl anpassen
a_telegram->getData()[22] = 0x0d; // Warum 13 (bzw. 21) ??
char *data = a_telegram->getData();
// Header - complete Payload in Bytes
data[02] = 0x18; // 24 Bytes
// Len in Bits
data[22] = 0x0d;
data[23] = 0x00;
// Len in Bytes
data[24] = 0x02;
data[25] = 0x00;
// Data
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);
}
+65
View File
@@ -0,0 +1,65 @@
/**
* Tetra-Interface für Lasttest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include <QHostAddress>
#include <QByteArray>
#include <NGLLogger/INGLLogger.h>
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
class QTcpSocket;
class QTimer;
class TetraIF : public QObject
{
Q_OBJECT
public:
TetraIF(const QHostAddress &addr, unsigned short port, unsigned short issi);
void start();
void listenMode(const QStringList &args);
void randomMode(const QStringList &args);
void fileMode(const QStringList &args);
static void usage();
protected Q_SLOTS:
void onConnected();
void onDisconnected();
void onReadyRead();
void onError(QAbstractSocket::SocketError);
void onSendData();
protected:
void doInitialLogin();
void handleReceivedTelegram(const QByteArray &data);
void handleRegisterRsp(const uint8_t a_registrationStatus);
void dumpData(const QByteArray &data);
void parseISSIs(const QStringList &issiList);
void handleDataIndReq(const QByteArray &data);
void handleLoginTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *a_loginTelegram);
bool sendLoginResponse(const uint32_t a_ISSI);
QString getIssisAsString() const;
void setupTimer();
protected:
QHostAddress m_addr;
unsigned short m_port;
unsigned short m_issi;
QTcpSocket *m_socket;
int m_count;
int m_telegramsPerSecond;
int m_telegramsInCurSecond;
int m_secondFragment;
QByteArray m_data;
QList<unsigned short> m_issiList;
QTimer *m_timer;
uint8_t m_SendCounter;
QString m_outputDir;
NGL::INGLLogger &m_logger;
};
@@ -0,0 +1,25 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(lta_SRCS
main.cpp
mainwindow.cpp
)
SET(lta_HDRS
mainwindow.h
)
SET(lta_UICS
mainwindow.ui
)
NGL_WRAP_UI(lta_SRCS ${lta_UICS})
NGL_ADD_EXECUTABLE(AAR SV NO_TRANSLATION LastTestAuswertung ${lta_SRCS} ${lta_HDRS} ${lta_UICS})
NGL_TARGET_LINK_LIBRARIES(LastTestAuswertung
Qt5::Core
Qt5::Widgets
)
@@ -0,0 +1,16 @@
#-------------------------------------------------
#
# Project created by QtCreator 2011-05-13T08:20:33
#
#-------------------------------------------------
TARGET = LastTestAuswertung
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp
HEADERS += mainwindow.h
FORMS += mainwindow.ui
@@ -0,0 +1,10 @@
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
@@ -0,0 +1,435 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QFile>
#include <cmath>
#include <QIntValidator>
#include <QList>
#include <QSet>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
init();
}
MainWindow::~MainWindow()
{
m_files.clear();
//m_logfile.~QString();
//delete m_logfileList;
// m_logZeiten.~QString();
//delete ui;
}
void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type())
{
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void MainWindow::init()
{
//Connect Signals & Slots
connect(ui->pushButtonOrdnerFinden, &QPushButton::clicked, this, &MainWindow::slot_findFolder);
connect(ui->pushButtonAnalysieren, &QPushButton::clicked, this, &MainWindow::slot_analysieren);
//m_logfileList = new QList<LogfileInfo>();
ui->progressBar->setValue(0);
ui->labelResult->setText(QString());
QIntValidator intVal;
intVal.setBottom(0);
intVal.setTop(100000);
ui->lineEditISSI->setValidator(&intVal);
}
void MainWindow::slot_findFolder()
{
//hier liegt der fehler???, der erst beim zweiten mal ausführen des Dateibrowsers in erscheinung tritt
/*QFileDialog dial(this);
dial.setFileMode(QFileDialog::Directory);
dial.setViewMode(QFileDialog::List);
QStringList files;
if (dial.exec())
files = dial.selectedFiles();
QString name = files.at(0);
//testblabla
*/
QString name = QFileDialog::getExistingDirectory(this, tr("Choose folder"), QDir::homePath(), QFileDialog::DontResolveSymlinks);
ui->lineEditOrdner->setText(name);
ui->labelResult->setText(QString());
}
void MainWindow::slot_analysieren()
{
ui->labelResult->setText(QString());
QString ISSI = ui->lineEditISSI->text();
if (ISSI.isEmpty())
ISSI = QLatin1String("1");
m_logfileList.clear();
//alle DAteien in dem Ordner finden
QDir folder(ui->lineEditOrdner->text());
m_files = folder.entryList();
folder.~QDir();
ui->progressBar->setValue(1);
const QString paytext = ISSI + QLatin1String("-payload");
const QString issitext = ISSI + QLatin1String("-1");
for (int i = 0; i < m_files.count(); i++)
{
//Dateinamen entfernen, die keine Payload-Logs sind
QString name = m_files[i];
if (name.contains(paytext))
{
m_files.removeAt(i);
i--;
}
else
{
if (!name.contains(QLatin1String(".dat")))
{
m_files.removeAt(i);
i--;
}
else
{
if (!name.contains(issitext))
{
m_files.removeAt(i);
i--;
}
}
}
}
ui->progressBar->setValue(10);
if (m_files.count() == 0)
ui->labelResult->setText(tr("no logfiles found"));
else
{
openLogfile(ui->lineEditOrdner->text(), m_files.count());
ui->progressBar->setValue(12);
ui->progressBar->repaint();
readFiles();
ui->progressBar->setValue(90);
ui->progressBar->repaint();
QString ausgabetext = QLatin1String("\tANALYSIS COMPLETED - ") + QString::number(auswerten()) + QLatin1String(" missing messages");
ui->labelResult->setText(ausgabetext);
}
ui->progressBar->setValue(100);
ui->progressBar->repaint();
}
void MainWindow::readFiles()
{
for (int j = 0; j < m_files.count(); j++)
{
QString filename = ui->lineEditOrdner->text();
filename += QLatin1Char('/');
filename += m_files[j];
QFile file(filename);
if (file.open(QIODevice::ReadOnly))
{
//Datei auslesen
QByteArray ar = file.read(150);
file.close();
if (ar.count() > 36)
{
QString issi = getISSI(ar);
QByteArray bytes = ar;
//Laufende Nummer
bytes.remove(0, 34);
bytes.remove(2, 150);
uint nr = bytes[0] & 255;
nr *= 256;
nr += bytes[1] & 255;
//BytesNr 34+35
//Sendezeit
bytes = ar;
bytes.remove(0, 30);
bytes.remove(4, 150);
const QByteArray test = bytes.toHex();
qint64 zeit = test.toLongLong(nullptr, 16);
//Byte 30-33
const QDateTime timeStart(QDateTime::fromMSecsSinceEpoch(zeit * 1000));
writeToLogfile(issi, timeStart, getTimestamp(m_files[j]), m_files[j], nr);
writeLogZeiten(timeStart, getTimestamp(m_files[j]), issi);
}
}
file.close();
double val = 70;
val /= m_files.count();
val *= (j + 1);
val += 20;
ui->progressBar->setValue(int(val));
// ui->progressBar->repaint();
}
}
void MainWindow::openLogfile(const QString &folder, int count)
{
m_logfile = folder;
m_logZeiten = folder;
m_logfile += QLatin1String("/logfile_analysis.txt");
QFile file(m_logfile);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
const QString msg = QStringLiteral("There are %1 logfiles evaluated.\nISSI SENDING TIME RECIEVING TIME FILENAME NO \n").arg(count);
file.write(msg.toUtf8());
file.close();
}
m_logZeiten += QLatin1String("/transmission_times.csv");
file.setFileName(m_logZeiten);
if (file.open(QIODevice::WriteOnly))
{
file.write("ISSI;Sending Time ; Receiving Time\n");
file.close();
}
}
void MainWindow::writeToLogfile(const QString &issi,
const QDateTime &timestamp,
const QString &filename,
uint lfdNr,
const QString &bytes)
{
QString text = issi;
text += QLatin1String(": ");
text += timestamp.toString(QLatin1String("dd.MM.yyyy hh:mm:ss"));
text += QLatin1String(" - ");
text += filename;
text += QLatin1String(" -> ");
text += bytes;
text += QLatin1String(" --> ");
text += QString::number(lfdNr, 10);
text += QLatin1Char('\n');
QFile file(m_logfile);
if (file.open(QIODevice::Append | QIODevice::Text))
{
file.write(text.toLatin1());
file.close();
}
LogfileInfo info;
info.timestamp = timestamp;
info.issi = issi;
info.lfdNr = lfdNr;
m_logfileList.append(info);
}
void MainWindow::writeToLogfile(const QString &issi,
const QDateTime &sendeZeit,
const QDateTime &timestamp,
const QString &filename,
uint lfdNr)
{
QString text = issi;
text += QLatin1String(": ");
text += sendeZeit.toString(QLatin1String("hh:mm:ss"));
text += QLatin1String(" --> ");
text += timestamp.toString(QLatin1String("dd.MM.yyyy hh:mm:ss"));
text += QLatin1String(" - ");
text += filename;
text += QLatin1String(" --> ");
text += QString::number(lfdNr, 10);
text += QLatin1Char('\n');
QFile file(m_logfile);
if (file.open(QIODevice::Append | QIODevice::Text))
{
file.write(text.toLatin1());
file.close();
}
LogfileInfo info;
info.timestamp = timestamp;
info.issi = issi;
info.lfdNr = lfdNr;
m_logfileList.append(info);
}
QDateTime MainWindow::getTimestamp(const QString &filename) const
{
const auto list1 = filename.splitRef(QLatin1Char('-'));
if (list1.size() < 2)
return {};
//filename = list.at(3);//für 2-payload-xxx-Dateien
const QStringRef &fn = list1.at(1);//für 2-xxx -Dateien
const auto list2 = fn.split(QLatin1Char('.'));
auto str = list2.at(0).toString();
str.remove(10, 10);
return QDateTime::fromMSecsSinceEpoch(str.toLongLong() * 1000);
}
QString MainWindow::getISSI(QByteArray ar)
{
//wird nur für die "2-timestamp"-Dateien verwendet
ar.remove(0, 5);
ar.remove(4, 200);
QByteArray issi = ar.toHex();
//umrechnung von hex nach dez
if (ar.count() == 4)
{
if (ar[2] == '\0' && ar[3] == '\0')
{
ar.remove(2, 2);
issi = ar.toHex();
int hex[4];
for (int i = 3; i >= 0; i--)
{
char c = issi.at(i);
if (c >= '0' && c <= '9')
hex[i] = c - '0';
else if (c >= 'a' && c <= 'f')
hex[i] = c - 'a' + 10;
}
int sum = 0;
sum += hex[1];
sum += hex[0] * 16;
sum += hex[3] * 256;
sum += hex[2] * 4096;
issi = QByteArray::number(sum);
}
}
return QString::fromLatin1(issi);
}
int MainWindow::auswerten()
{
//Logfile vorbereiten
QFile f(m_logfile);
if (!f.open(QIODevice::Append | QIODevice::Text))
return -1;
f.write("***************************RESULT************************************\n");
QSet<QString> issis;
issis.reserve(100);
for (int i = 0; i < m_logfileList.count(); i++)
{
//Unterschiedliche Issis finden
issis += m_logfileList.at(i).issi;
}
ui->progressBar->setValue(94);
ui->progressBar->repaint();
int fehlcount = 0;
QVector<uint> numList;
int i = 0;
for (const QString &issi : qAsConst(issis))
{
//für jede Issi die Nummern in timestampMap einlesen --> geordnet nach Zeitstempel
numList.clear();
for (int j = 0; j < m_logfileList.count(); j++)
{
if (issi == m_logfileList.at(j).issi)
numList.append(m_logfileList.at(j).lfdNr);
}
QByteArray text = issi.toLatin1();
text += ":\t";
text += QByteArray::number(numList.count());
text += " messages transmitted\n";
text += "\t";
f.write(text);
fehlcount += fehlendeNumsEintragen(numList, f);
// delete numList;
double val = 5;
val /= issis.count();
val *= (i + 1);
val += 94;
ui->progressBar->setValue(qRound(val));
ui->progressBar->repaint();
++i;
}
return fehlcount;
}
int MainWindow::fehlendeNumsEintragen(const QVector<uint> &numList, QFile &f)
{
//schreibt die fehlenden Nummern in das Logfile und die GUI
QVector<uint> fehlendeNums = getFehlendeNums(numList);
QByteArray text = QByteArray::number(fehlendeNums.count());
text += " lost messages\n";
text += "\t\t following messages are missing (";
for (int j = 0; j < fehlendeNums.count(); j++)
{
text += QByteArray::number(fehlendeNums.at(j));
text += ", ";
}
// delete fehlendeNums;
text += ")\n";
f.write(text);
return fehlendeNums.count();
}
QVector<uint> MainWindow::getFehlendeNums(const QVector<uint> &numsDurchgang)
{
//ermittelt die fehlenden Nummern pro Durchgang
uint count = numsDurchgang.count();
uint maxnum = 0;
uint minnum = 999999;
for (uint k = 0; k < count; k++)
{
//höchste und niedrigste Num finden
if (numsDurchgang.at(k) > maxnum)
maxnum = numsDurchgang.at(k);
if (numsDurchgang.at(k) < minnum)
minnum = numsDurchgang.at(k);
}
QVector<uint> fehlendeNum;
for (uint z = minnum; z <= maxnum; z++)
{
//liste nach fehlenden werten durchsuchen
bool vorhanden = false;
for (uint j = 0; j < count; j++)
{
if (numsDurchgang.at(j) == z)
{
vorhanden = true;
break;
}
}
if (!vorhanden)
fehlendeNum.append(z);
}
return fehlendeNum;
}
void MainWindow::writeLogZeiten(const QDateTime &beginn, const QDateTime &end, const QString &issi)
{
QString msg = issi;
msg += QLatin1Char(';');
msg += beginn.toString(QLatin1String("hh:mm:ss"));
msg += QLatin1Char(';');
msg += end.toString(QLatin1String("hh:mm:ss"));
msg += QLatin1Char('\n');
QFile file(m_logZeiten);
file.open(QIODevice::Append | QIODevice::Text);
file.write(msg.toUtf8());
file.close();
//delete end;
}
@@ -0,0 +1,69 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QDateTime>
#include <QFile>
namespace Ui
{
class MainWindow;
}
struct LogfileInfo
{
QString issi;
QDateTime timestamp;
uint lfdNr;
};
struct ZeitNum
{
uint zeit;
int num;
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void changeEvent(QEvent *e);
private:
Ui::MainWindow *ui;
void init();
void readFiles();
QStringList m_files;
QDateTime getTimestamp(const QString &filename) const;
QString m_logfile;
QString m_logZeiten;
void openLogfile(const QString &folder, int count);
void writeToLogfile(const QString &issi,
const QDateTime &timestamp,
const QString &filename,
uint lfdNr,
const QString &bytes);
void writeToLogfile(const QString &issi,
const QDateTime &sendeZeit,
const QDateTime &timestamp,
const QString &filename,
uint lfdNr);
QString getISSI(QByteArray ar);//Für alte TPUS
int auswerten();
QVector<uint> getFehlendeNums(const QVector<uint> &numsDurchgang);
int fehlendeNumsEintragen(const QVector<uint> &numList, QFile &file);
void writeLogZeiten(const QDateTime &beginn, const QDateTime &end, const QString &issi);
QVector<LogfileInfo> m_logfileList;
private slots:
void slot_findFolder();
void slot_analysieren();
};
#endif // MAINWINDOW_H
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>234</height>
</rect>
</property>
<property name="windowTitle">
<string>Logfiles analysieren</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>501</width>
<height>101</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="pushButtonAnalysieren">
<property name="text">
<string>analyse logfiles</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelResult">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>9</x>
<y>0</y>
<width>501</width>
<height>65</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEditOrdner"/>
</item>
<item>
<widget class="QPushButton" name="pushButtonOrdnerFinden">
<property name="text">
<string>choose folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="lineEditISSI"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;ISSI by the central&lt;br /&gt;(if no ISSI is entered, ISSI 1 will be used)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
+90
View File
@@ -0,0 +1,90 @@
[Common]
LogCentralTelegrams=1
LogRadioTelegrams=1
LogTelegramPath=logdata
LogTelegramPrefix=PrtCnv_
; virtual TeilnehmerId of protocol-converter
VirtualIdPrtCnv=90
[Db]
DatabaseFileName=db/telegrams.db
[Central]
UseSocketsInsteadFiles=1
HostName=ECCORSV01
Port=9600
; Milliseconds
AckTimeout=900
ConnectTimeout=2000
DisconnectTimeout=10000
AliveTimeout=3000
; nbr of unsuccessful send-attempts, before telegram will be deleted without sending it
NbrOfAttempts=5
[Radio]
HostName=127.0.0.1
Port=52010
HostISSI=9999
; Seconds
CyclicEnvironmentTime=30
; Milliseconds
BroadcastISSI=5000
ConnectTimeout=2000
DisconnectTimeout=10000
AliveTimeout=3000
; 1 - UseSDS_TL
UseSDS_TL=0
DumpReceivedTelegrams=1
DumpSentTelegrams=1
; GSSIs which we would like to subscribe to - make sure to not subscribe to our own BroadcastISSI !
ListenGroups=5001
[Login]
LoginFilePathWithPrefix=login/pk_
DoLicenseDespiteSelftestOrConfigFailure=0
DoCheckPlayerId=1
LowestPlayerId=500
HighestPlayerId=9999
PlayerIdFilePath=playerIds.dat
[Broadcast]
; In seconds, delay between sending 1st and 2nd broadcast
BroadcastDelay2=10
; In seconds, delay between sending 2nd and 3rd broadcast
BroadcastDelay3=20
; the time to wait for NachforderungBroadcast from TNEs, before begin sending the broadcasts
ClaimWindow=30
; factor for extend the claimwindow when we have full load in downlink
FullLoadFactor=3
; if less TNEs ask for broadcasts, they will be send directed
BroadcastLimit=10
; the time to wait before sending next broadcast in milliseconds
SendGap=500
[TrafficControl]
; from PrtCnv to Tetra (Telegram-Counting)
; Number of telegrams (0..255) reported by air-interface-report telegram when we have high load
SendQueueHighLoad=150
; Number of telegrams that will be sent per second, when we have high-load
SendTelegramsHighLoad=50
; Number of telegrams (0..255) reported by air-interface-report telegram when we have full load
SendQueueFullLoad=200
; Number of telegrams that will be sent per second, when we have full-load
SendTelegramsFullLoad=10
; from Tetra to PrtCnv (Byte-Counting)
; high load in percent (0 .. 100)
ReceiveQueueHighLoad=50
; full load in percent (ReceiveQueueHighLoad .. 100)
ReceiveQueueFullLoad=75
; hysteresis in percent (0 .. 10)
ReceiveQueueHysteresis=5
; Averaging time in seconds
ReceiveAveragingTime=5
; Throughput in KB/s per Transceiver
ReceiveThroughput=3.6
; Transceiver count
TransceiverCount=6
[Injection]
Enable=true
Port=46666
+410
View File
@@ -0,0 +1,410 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvApplication
// **************************************************************************
#include "PrtCnvApplication.hpp"
#include "PrtCnvThreadStarter.hpp"
#include "PrtCnvThread.hpp"
#include "PrtCnvCentralThread.hpp"
#include "PrtCnvTelegramLogThread.hpp"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <iostream>
#include <QCoreApplication>
#include <QThread>
namespace PrtCnv
{
static std::string threadIdToString(PrtCnvApplication::E_ThreadId id)
{
switch (id)
{
case PrtCnvApplication::e_Central:
return "Central";
case PrtCnvApplication::e_Radio:
return "Radio";
case PrtCnvApplication::e_Db:
return "Db";
case PrtCnvApplication::e_Log:
return "Log";
case PrtCnvApplication::e_Injection:
return "Injection";
case PrtCnvApplication::e_RadioTraffic:
return "RadioTraffic";
case PrtCnvApplication::e_ThreadIdMax:
break;
}
return std::to_string(id);
}
PrtCnvApplication::PrtCnvApplication()
: m_Config(nullptr)
, m_exerciseId(0)
, m_accessMutex(QMutex::Recursive)
, m_logger(getLogger("PrtCnvApplication"))
{}
PrtCnvApplication::~PrtCnvApplication()
{
delete m_Config;
for (const auto &ti : m_threadInfo)
delete ti.thread;
}
// ---------------------------------------------------------------
// Synchronisation of threads before create().
// ---------------------------------------------------------------
void PrtCnvApplication::waitForCreate(const E_ThreadId a_threadId)
{
INFO_LOG(m_logger) << "Wait for others until we can create " << threadIdToString(a_threadId);
setConstructed(a_threadId);
switch (a_threadId)
{
case e_Central:
while (!(isConstructed(e_Db) &&
isConstructed(e_Radio) &&
isConstructed(e_Log) &&
isConstructed(e_Injection)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Radio:
while (!(isConstructed(e_Db) &&
isConstructed(e_Central) &&
isConstructed(e_Log) &&
isConstructed(e_Injection)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Db:
while (!(isConstructed(e_Radio) &&
isConstructed(e_Central)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
default:
break;
}
INFO_LOG(m_logger) << "Now we can create " << threadIdToString(a_threadId);
}
// ---------------------------------------------------------------
// Synchronisation of threads before init().
// ---------------------------------------------------------------
void PrtCnvApplication::waitForInit(E_ThreadId a_threadId)
{
INFO_LOG(m_logger) << "Wait for others until we can initialize " << threadIdToString(a_threadId);
setCreated(a_threadId);
switch (a_threadId)
{
case e_Central:
while (!(isCreated(e_Db) &&
isCreated(e_Radio)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Radio:
while (!(isCreated(e_Db) &&
isCreated(e_Central)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Db:
while (!(isCreated(e_Radio) &&
isCreated(e_Central)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
default:
break;
}
INFO_LOG(m_logger) << "Now we can initialize " << threadIdToString(a_threadId);
}
// ---------------------------------------------------------------
// Synchronisation of threads before start().
// ---------------------------------------------------------------
void PrtCnvApplication::waitForStart(E_ThreadId a_threadId)
{
INFO_LOG(m_logger) << "Wait for others until we can start " << threadIdToString(a_threadId);
setInitialized(a_threadId);
switch (a_threadId)
{
case e_Central:
while (!(isStarted(e_Db) &&
isInitialized(e_Radio) &&
isStarted(e_Log) &&
isStarted(e_Injection)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Radio:
while (!(isStarted(e_Db) &&
isInitialized(e_Central) &&
isStarted(e_Log) &&
isStarted(e_Injection) &&
isInitialized(e_RadioTraffic)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_Db:
while (!(isInitialized(e_Radio) &&
isInitialized(e_Central) &&
isStarted(e_Log)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
case e_RadioTraffic:
while (!(isInitialized(e_Radio) &&
isStarted(e_Log)))
{
DEBUG_LOG(m_logger) << "wait...";
QThread::sleep(1);
}
break;
default:
break;
}
INFO_LOG(m_logger) << "Now we can start " << threadIdToString(a_threadId);
}
// ---------------------------------------------------------------
// Synchronisation of threads before execution of thread.
// ---------------------------------------------------------------
void PrtCnvApplication::waitForExec(E_ThreadId a_threadId)
{
setStarted(a_threadId);
}
// ---------------------------------------------------------------
// Starts the main-application
// ---------------------------------------------------------------
void PrtCnvApplication::startApp()
{
createApplication();
init();
start();
}
void PrtCnvApplication::shutdownApp()
{
stop();
deinit();
destroy();
for (const auto &ti : m_threadInfo)
ti.thread->quit();
for (const auto &ti : m_threadInfo)
ti.thread->wait();
for (auto &ti : m_threadInfo)
{
delete ti.thread;
ti.thread = nullptr;
}
}
void PrtCnvApplication::setCleanDb(bool bCleanDb)
{
PrtCnvThread::m_DoCleanDb = bCleanDb;
}
void PrtCnvApplication::setExerciseId(int exerciseId)
{
m_exerciseId = exerciseId;
}
void PrtCnvApplication::setConfigFile(const QString &configFile)
{
m_ConfigFileName = configFile;
}
// ---------------------------------------------------------------
// Creates the childs
// ---------------------------------------------------------------
int32_t PrtCnvApplication::createApplication()
{
setName(QStringLiteral("Common"));
if (m_ConfigFileName.isEmpty())
{
m_ConfigFileName = QCoreApplication::applicationDirPath();
if (m_exerciseId > 0)
m_ConfigFileName += QStringLiteral("/PrtCnv_ex_") + QString::number(m_exerciseId) + QStringLiteral(".dat");
else
m_ConfigFileName += QStringLiteral("/PrtCnv.dat");
}
m_Config = new NGL::Settings(m_ConfigFileName);
std::cout << "Use config-file: " << qPrintable(m_ConfigFileName) << std::endl;
for (int i = 0; i < e_ThreadIdMax; i++)
m_threadInfo[i].thread = new PrtCnvThreadStarter((E_ThreadId)i, m_ConfigFileName, this);
N_AppComponent::AppComponent::create(m_Config);
return 0;
}
// ---------------------------------------------------------------
// Starts the childs and then the main application
// ---------------------------------------------------------------
int32_t PrtCnvApplication::start()
{
// start and create threads
for (const auto &ti : m_threadInfo)
ti.thread->start();
N_AppComponent::AppComponent::start();
return 0;
}
// ---------------------------------------------------------------
// Returns the desired thread-starter
// ---------------------------------------------------------------
PrtCnv::PrtCnvThreadStarter *PrtCnvApplication::getThreadStarter(const E_ThreadId a_threadId)
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return nullptr;
QMutexLocker l(&m_accessMutex);
return m_threadInfo[a_threadId].thread;
}
void PrtCnvApplication::setDatabaseConnectionParams(const QString &driver,
const QString &hostName,
const QString &database,
const QString &schemaName,
const QString &userName,
const QString &password)
{
m_dbDriver = driver;
m_dbHostName = hostName;
m_dbDatabaseName = database;
m_dbSchemaName = schemaName;
m_dbUserName = userName;
m_dbPassword = password;
}
void PrtCnvApplication::logWlsTelegram(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele,
bool bIsSend)
{
const auto threadHandler = m_threadInfo[e_Log].thread;
PrtCnvTelegramLogThread *t = static_cast<PrtCnvTelegramLogThread*>(threadHandler ? threadHandler->getThread() : nullptr);
if (t)
t->writeWlsTelegramLog(tele, bIsSend, 0);
}
// ---------------------------------------------------------------
bool PrtCnvApplication::isConstructed(const E_ThreadId a_threadId) const
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return false;
QMutexLocker l(&m_accessMutex);
return m_threadInfo[a_threadId].isConstructed;
}
// ---------------------------------------------------------------
void PrtCnvApplication::setConstructed(const E_ThreadId a_threadId)
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return;
QMutexLocker l(&m_accessMutex);
m_threadInfo[a_threadId].isConstructed = true;
}
// ---------------------------------------------------------------
bool PrtCnvApplication::isInitialized(const E_ThreadId a_threadId) const
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return false;
QMutexLocker l(&m_accessMutex);
return m_threadInfo[a_threadId].isInitialized;
}
// ---------------------------------------------------------------
void PrtCnvApplication::setInitialized(const E_ThreadId a_threadId)
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return;
QMutexLocker l(&m_accessMutex);
m_threadInfo[a_threadId].isInitialized = true;
}
// ---------------------------------------------------------------
bool PrtCnvApplication::isCreated(const E_ThreadId a_threadId) const
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return false;
QMutexLocker l(&m_accessMutex);
return m_threadInfo[a_threadId].isCreated;
}
// ---------------------------------------------------------------
void PrtCnvApplication::setCreated(const E_ThreadId a_threadId)
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return;
QMutexLocker l(&m_accessMutex);
m_threadInfo[a_threadId].isCreated = true;
}
// ---------------------------------------------------------------
bool PrtCnvApplication::isStarted(const E_ThreadId a_threadId) const
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return false;
QMutexLocker l(&m_accessMutex);
return m_threadInfo[a_threadId].isStarted;
}
// ---------------------------------------------------------------
void PrtCnvApplication::setStarted(const E_ThreadId a_threadId)
{
if (a_threadId < e_Central || a_threadId >= e_ThreadIdMax)
return;
QMutexLocker l(&m_accessMutex);
m_threadInfo[a_threadId].isStarted = true;
}
} // namespace PrtCnv
+228
View File
@@ -0,0 +1,228 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvApplication
// **************************************************************************
#pragma once
#include <QObject>
#include "AppComponent.hpp"
#include "logger.h"
#include <nglsettings/nglsettings_settings.h>
#include <QtCore/QMutex>
#include <PrtCnv/prtcnvlib_export.h>
#include <TrainingAreaBase/training_area_telegrams.h>
/**
* Here are all project specific classes of protocol-converter implemented.
*/
namespace PrtCnv
{
class PrtCnvThreadStarter;
/**
* The main-application of protocol-converter. It is responsible for starting and
* controlling the threads of this application.
*
* @author alois.wismer
* @version 1.0
* @created 23-Jun-2009 17:48:47
*/
class PRTCNVLIB_EXPORT PrtCnvApplication : public QObject, public N_AppComponent::AppComponent
{
Q_OBJECT
public:
/**
* This enum will be used to unique identify the threads
*
* @author alois.wismer
* @version 1.0
* @created 19-Jan-2010 17:09:00
*/
enum E_ThreadId
{
e_Central,
e_Radio,
e_Db,
e_Log,
e_Injection,
e_RadioTraffic,
e_ThreadIdMax
};
PrtCnvApplication();
~PrtCnvApplication();
/**
* Starts the main-application
*/
void startApp();
void shutdownApp();
static void setCleanDb(bool bCleanDb);
void setExerciseId(int exerciseId);
int exerciseId() const { return m_exerciseId; }
void setConfigFile(const QString &configFile);
/**
* Starts the childs and then the main application
*/
int32_t start() override;
/**
* Synchronisation of threads before init().
*
* @param a_threadId id of the calling thread
*/
void waitForInit(E_ThreadId a_threadId);
/**
* Synchronisation of threads before start().
*
* @param a_threadId id of the calling thread
*/
void waitForStart(E_ThreadId a_threadId);
/**
* Synchronisation of threads before create().
*
* @param a_threadId Id of the calling thread
*/
void waitForCreate(const E_ThreadId a_threadId);
/**
* Synchronisation of threads before execution of thread.
*
* @param a_threadId id of the calling thread
*/
void waitForExec(E_ThreadId a_threadId);
/**
* Returns the desired thread-starter
*
* @param a_threadId Id of the desired thread-started (corresponding thread has
* the same id)
*/
PrtCnvThreadStarter* getThreadStarter(const E_ThreadId a_threadId);
/**
* @brief Set the database connection parameter when used as library
*/
void setDatabaseConnectionParams(const QString &driver,
const QString &hostName,
const QString &database,
const QString &schemaName,
const QString &userName,
const QString &password);
QString databaseDriver() const { return m_dbDriver; }
QString databaseHostName() const { return m_dbHostName; }
QString databaseName() const { return m_dbDatabaseName; }
QString databaseSchemaName() const { return m_dbSchemaName; }
QString databaseUserName() const { return m_dbUserName; }
QString databasePassword() const { return m_dbPassword; }
public slots:
void logWlsTelegram(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool bIsSend);
signals:
void textMessageFromPrtCnvToCentral(const QByteArray &ba);
void textMessageFromCentralToPrtCnv(const QByteArray &ba);
void outgoingBroadcastTelegramsRequest(int equipmentNumber, int broadcastIndex, int resetCounter);
void warehouseMessageFromPtrCnv(const QByteArray &jsonData);
protected:
/**
* Creates the childs
*/
int32_t createApplication();
/**
* Returns true when the specified thread is
* already constructed
*/
bool isConstructed(const E_ThreadId a_threadId) const;
/**
* The specifed thread is now constructed
*/
void setConstructed(const E_ThreadId a_threadId);
/**
* Returns true when the specified thread is
* already initialized
*/
bool isInitialized(const E_ThreadId a_threadId) const;
/**
* The specifed thread is now initialized
*/
void setInitialized(const E_ThreadId a_threadId);
/**
* Returns true when the specified thread is
* already created
*/
bool isCreated(const E_ThreadId a_threadId) const;
/**
* The specifed thread is now created
*/
void setCreated(const E_ThreadId a_threadId);
/**
* Returns true when the specified thread is
* already started
*/
bool isStarted(const E_ThreadId a_threadId) const;
/**
* The specifed thread is now started
*/
void setStarted(const E_ThreadId a_threadId);
protected:
/**
* Used to get configuration-parameter
*/
NGL::Settings *m_Config;
struct ThreadInfo
{
PrtCnvThreadStarter *thread = nullptr;
bool isConstructed = false;
bool isCreated = false;
bool isInitialized = false;
bool isStarted = false;
} m_threadInfo[e_ThreadIdMax];
/**
* The name of the config-file
*/
QString m_ConfigFileName;
/**
* The exerciseId (or zero if not set)
*/
int m_exerciseId;
/**
* Access Mutex
*/
mutable QMutex m_accessMutex;
QString m_dbDriver;
QString m_dbHostName;
QString m_dbDatabaseName;
QString m_dbSchemaName;
QString m_dbUserName;
QString m_dbPassword;
NGL::INGLLogger &m_logger;
};
}
File diff suppressed because it is too large. Load diff
+574
View File
@@ -0,0 +1,574 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#pragma once
#include "logger.h"
#include <CoPrtCnv/tetra_telegram_ids.h>
#include <nglsettings/nglsettings_settings.h>
#include <TrainingAreaBase/training_area_telegrams.h>
#include <QObject>
#include <QHash>
#include <QMap>
#include <QSet>
#include <map>
#include <memory>
#include <vector>
namespace N_CoPrtCnv
{
class CoPrtCnvRadioTelegram;
}
#define MAX_BROADCAST_INDEX 65535
namespace PrtCnv
{
class PrtCnvDbThread;
/**
* This class is responsible for handling all the broadcast stuff. This includes:
* - sending the broadcast 3 times
* - sending Broadcast-Nachforderungen to TNE
* - store and manage the broadcast-list
* - measure the Broadcast-Nachforderung-statistic
* @author wia
* @version 1.0
* @updated 30-Apr-2010 11:21:05
*/
class PrtCnvBroadcastMgr final : public QObject
{
Q_OBJECT
public:
/**
* The different states the broadcast-manager can be.
* @author wia
* @version 1.0
* @updated 30-Apr-2010 11:21:05
*/
enum E_State
{
e_ST_UNDEFINED,
e_ST_COLLECT,
e_ST_SEND_NACHFORDERUNG
};
PrtCnvBroadcastMgr();
/**
* returns true when the telegram should be sent to central
*
* @param a_radioTelegram
*/
bool handleRadioTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
/**
* Creates all subcomponents
*/
int32_t create(NGL::Settings *settings);
/**
* Initializes the variables.
*/
int32_t init();
/**
* returns the next normal broadcast to send.
* a_doDelete is true, when the caller has to delete the telegram when no longer
* using it.
* Use the same a_actualTime in one tick-loop.
*
* @param a_beginSearchWithBroadcastIndex
* @param a_actualTime
* @param a_doDelete true if telegram must be deleted after using it
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextBroadcastTelegramToSend(const uint16_t a_beginSearchWithBroadcastIndex,
const time_t a_currentTime);
/**
* sets the DbThread used for get the stored broadcast-telegrams
*
* @param a_dbThread
*/
void setDbThread(PrtCnvDbThread *a_dbThread);
/**
* Initializes the broadcast-manager. If a_deleteTelegrams is true, all stored
* telegrams and maps will be deleted.
*
* @param a_deleteTelegrams
*/
void cleanAndInit(const bool a_deleteTelegrams);
/**
* Removes all Nop out of broadcast-list and numerates the broadcast-index of kept
* broadcast-telegrams new.
*
* @param a_doRestore
*/
void handleOverrunOfBroadcastCounter(const bool a_doRestore = false);
/**
* returns the next broadcast to send for nachforderungen
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextNachforderungTelegramToSend();
/**
* dumps important values, only for debugging
*/
void dump();
/**
* Restores data after crash for seamless proceeding of work.
*/
void restoreAfterCrash();
/**
* Returns the actual broadcast-index for normal broadcasts.
*/
uint16_t getBroadcastIndex() const;
/**
* Returns the broadcastindex for the next broadcast
*/
uint16_t getNextBroadcastIndex();
/**
* Adds the telegram a_broadcastOnlyTelegram (which must have set already the
* broadcast-index) to the broadcast-map. The telegram will be attached to an
* already existing broadcast-telegram with same broadcast-index in the map,
* otherwise it will be marked as broadcast-only.
* If the there is already an broadcast-only-telegram attached, nothing will be
* done.
*
* @param a_broadcastOnlyTelegram
*/
void addBroadcastOnlyTelegramToMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_broadcastOnlyTelegram);
/**
* Increments the tick-counter of broadcast-manager and informs the broadcast-
* manager about the traffic-load.
*
* @param a_isHighLoad
* @param a_isFullLoad
*/
void incTickAndSetTrafficLoad(const bool a_isHighLoad, const bool a_isFullLoad);
/**
* Checks if the given id is in m_deactivatedAesMine2bcIndex/m_deactivatedDusMine2bcIndex/m_deactivatedFuncAreaMine2bcIndex
*
* @brief isEffectSimulationIdDeactivated
* @param effectsimulationId
* @return
*/
bool isEffectSimulationIdDeactivated(const uint16_t effectsimulationId) const;
/**
* Adds the TNE to the nachforderungs-list
*
* @param a_radioTelegram
*/
void handleNachforderungBroadcastTelegram(int identity, int broadcastIndex, int resetCounter);
protected:
struct EquipNoBcIndex
{
int equipNo = 0;
int bcIndex = 0;
EquipNoBcIndex() = default;
EquipNoBcIndex(int e, int b) : equipNo(e), bcIndex(b) {}
};
/**
* Map of TNE, that sent a NachforderungBroadcast-Telegram.
* Key: identity of TNE; Value: broadcast-counter
*/
std::vector<EquipNoBcIndex> m_claimerArray;
/**
* Time (in SendGap-ticks) spent for collection NachforderungeBroadcast, before
* sending broadcasts begins
* This value will be used for normal load.
*/
uint16_t m_ClaimWindow;
/**
* the factor for widening claim-window before sending broadcasts when we have
* high load traffic
*/
uint16_t m_HighLoadFactor;
/**
* the number of TPUs needed for sending the broadcasts as broadcast instead
* directed
*/
uint16_t m_BroadcastLimit;
/**
* Used to get configuration-parameter.
*/
NGL::Settings *m_Config;
/**
* Actual time (in SendGap-ticks) spent for collection NachforderungeBroadcast,
* before sending broadcasts begins. This value changes depending on downlink-load.
*/
uint16_t m_ActClaimWindow;
/**
* The state of broadcast-manager.
*/
PrtCnvBroadcastMgr::E_State m_State;
uint16_t m_TickCounter;
/**
* The actual broadcast-index for sending nachforderungen
*/
PrtCnvDbThread *m_DbThread;
/**
* sorted by broadcast-index
*/
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_BroadcastMap;
std::vector<std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_broadcastTelegamsToSend;
/**
* Contains all telegrams which must only be stored once for nachforderungen
*/
std::map<gef_telegramm_id_et, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_LastTelegramMap;
/**
* Contains all wirkfeld-telegrams which must be stored for nachforderungen
* ordered by wirkfeldkennung.
*/
std::multimap<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_WirkfeldTelegramMapByKennung;
/**
* Contains all wirkfeld-telegrams which must be stored for nachforderungen
* ordered by expiration-time.
*/
std::multimap<time_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_WirkfeldTelegramMapByTime;
/**
* Contains all funktionsraum-telegrams which must be stored for nachforderungen
* ordered by funktionsraumkennung.
*/
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_FunktionsraumTelegramMap;
/**
* Contains all technik-funktionsraum-telegrams which must be stored for nachforderungen
* ordered by funktionsraumkennung.
*/
std::map<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>> m_TechnikFunktionsraumTelegramMap;
/**
* The actual broadcast-index for normal broadcast
*/
uint16_t m_BroadcastIndex;
/**
* True if send-traffic is on full-load => no nachforderungen will be sent
*/
bool m_IsFullLoad;
/**
* The highest broadcast-index of 3rd time sent broadcast-telegram (for optimize
* by time)
*/
uint16_t m_FinishedBroadcastIndex;
/**
* The broadcast-index of the 1st broadcast-telegram which has been sent 1 time
* only (for optimize by time)
*/
uint16_t m_FirstBroadcastIndexOfBc1;
/**
* adds the radio-telegram to the broadcast-map, or generates the sammeltelegramme
*
* @param a_radioTelegram
*/
void addToBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
/**
* returns the Nachforderungs-telegram with this broadcast-index from the
* broadcast-map with sammeltelegrams. If the telegram with a_broadcastIndex does
* not exist, a NOP-telegram will be returned, which must be consumed immediately
* and must never be deleted.
* The parameter a_broadcastIndex will be updated to the value of found telegram.
* ATTENTION: the returned a_broadcastIndex could be different from the value
* returned by getBroadcastIndex(), for example in NOP-telegrams
* getBroadcastIndex() returns the Anfang, but a_broadcastIndex will be the Ende.
*
* @param a_broadcastIndex
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNachforderungsTelegram(int curBcIndex, int startBcIndex, int &nextBcIndex);
/**
* searches the kennung within sammeltelegram.
* returns -1 when kennung not found
*
* @param a_sammelTelegram
* @param a_kennung
*/
int getSammeltelegramIndex(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_sammelTelegram,
const uint16_t a_kennung) const;
/**
* Moves a_radioTelegram to a_newBroadcastIndex in broadcast-map.
* If a_radioTelegram has an attached broadcast-only-telegram, the broadcastOnly-
* telegram will be detached and kept on the old broadcast-index position.
* If a_attachTelegram in not NULL, this telegram will be attached to
* a_radioTelegram.
*
* @param a_radioTelegram The radio-telegram to be moved
* @param a_newBroadcastIndex The new broadcast-index
* @param a_attachTelegram The telegram to be attached
*/
void moveTelegramInBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram,
const uint16_t a_newBroadcastIndex,
const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_attachTelegram = {});
/**
* Removes the telegram a_radioTelegram from the broadcast-map. If the
* a_radioTelegram has an attached telegram, the attached telegram will be
* detached and added to broadcast-map as broadcast-only telegram.
* If the telegram is an attached BC-only-telegram, it will be detached.
* The telegram itself will be deleted when a_doDeleteTelegram is true.
* ATTENTION: iterators to m_BroadcastMap could be invalid after calling this
* function
*
* @param a_radioTelegram
* @param a_doDeleteTelegram
*/
void removeFromBroadcastMap(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
void removeFromBroadcastMapFunc(const std::function<bool(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &)> &pred);
void removeDusMineDeactivationFromBroadcastMap(int dusMineId);
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccumulationTelegram(int curBcIndex,
int startBcIndex,
gef_telegramm_id_et telId,
const QMap<int, int> &bcIndex2Type,
int maxIdsPerTelegram,
int sizeOfSumField) const;
bool removeFromDeactivatedAesArea(int aesAreaId);
bool addToDeactivatedAesArea(int aesAreaId, int bcIndex);
// QHash<mineId, broadcastIndexDeactivated>
QHash<int, int> m_deactivatedAesArea2bcIndex;
// QMap<broadcastIndexDeactivated, mineId>, keep sorted!
QMap<int, int> m_deactivatedBcIndex2AesArea;
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>createAccuTeleDeactivatedAesAreas(int curBcIndex, int startBcIndex) const;
bool removeFromDeactivatedFuncArea(int aesAreaId);
bool addToDeactivatedFuncArea(int aesAreaId, int bcIndex);
// QHash<mineId, broadcastIndexDeactivated>
QHash<int, int> m_deactivatedFuncArea2bcIndex;
// QHash<broadcastIndexDeactivated, mineId>, keep sorted!
QMap<int, int> m_deactivatedBcIndex2FuncArea;
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>createAccuTeleDeactivatedFuncAreas(int curBcIndex, int startBcIndex) const;
bool removeFromDeactivatedDusMine(int dusMineId);
bool addToDeactivatedDusMine(int dusMineId, int bcIndex);
// QHash<mineId, broadcastIndexDeactivated>
QHash<int, int> m_deactivatedDusMine2bcIndex;
// QHash<broadcastIndexDeactivated, mineId>, keep sorted!
QMap<int, int> m_deactivatedBcIndex2DusMine;
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccuTeleDeactivatedDusMines(int curBcIndex, int startBcIndex) const;
bool removeFromActivatedDusMine(int dusMineId);
void addToActivatedDusMine(int dusMineId);
QSet<int> m_activatedDusMines;
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> createAccuTeleActivatedDusMines(int curBcIndex);
/**
* Removes the kennung from the sammeltelegram.
* If the sammeltelegram is empty after remove of kennung, it will be taken out of
* broadcast-map (but not deleted) and a_sammelTelRemoved will be true. A attached
* braodcast-only-telegram will be deattached and inserted into broadcast-map.
* Returns true if kennung was successful removed, otherwise the kennung was not
* found within sammeltelegram
*
* @param a_sammelTelegram
* @param a_kennung
* @param a_anzahlPos
* @param a_anzahlSize
* @param a_sammelTelRemoved
*/
bool removeFromSammeltelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_sammelTelegram,
const uint16_t a_kennung,
const uint16_t a_anzahlPos,
const uint8_t a_anzahlSize,
bool &a_sammelTelRemoved);
/**
* used for telegrams that must only exists once in BC-list
*
* @param a_newRadioTelegram
*/
void replaceLastTelegramAndAddAtEnd(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_newRadioTelegram);
/**
* Small helper to dump all wf-ids from a sammeltelegramm
*/
std::string dumpSammeldeaktivierung(const N_CoPrtCnv::CoPrtCnvRadioTelegram *sammelTelegram);
/**
* Gets the braodcast-telegrams from Db, which were sent 1st time and adds them to
* the maps. Expired telegrams will be removed.
*
* @param a_doRestore
*/
void getAllFinishedBroadcastsFromDb(const bool a_doRestore = false);
/**
* Takes expired wirkfeld-telegrams out of maps.
*/
void removeExpiredTelegrams();
/**
* Removes the wirkfeld-telegrams with this kennung from the by time and by
* kennung maps.
* Replace this telegrams with Nop in BC-list.
* Finally the telegram will be deleted.
*
* @param a_kennung
*/
void removeWirkfeldTelegrams(const uint16_t a_kennung);
/**
* adds the wirkfeld-telegram to the by time and by kennung maps
*
* @param a_radioTelegram
* @param a_wirkfeldKennung
* @param a_telId
* @param a_minenKennung
*/
void addWirkfeldTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram,
const uint16_t a_wirkfeldKennung, const gef_telegramm_id_et a_telId = GEF_BESTAETIGUNG,
const uint16_t a_minenKennung = 0);
/**
* Deactivates the mine in the corresponding "WirkfeldstatusMinensperre"-telegram
* and moves the "WirkfeldstatusMinensperre"-telegram to the position of
* "DeactiviereEinzelmine"-telegram.
* Returns true if the "WirkfeldstatusMinensperre"-telegram was already sent 3
* times as normal broadcast.
*
* @param a_wirkfeldKennung
* @param a_minenKennung
* @param a_broadcastIndex
*/
bool deactivateEinzelmineFromMinensperre(const uint16_t a_wirkfeldKennung, const uint16_t a_minenKennung, const uint16_t a_broadcastIndex);
/**
* Returns a NOP-telegramm filled with the desired values.
*
* @param a_from
* @param a_to
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNopTelegram(const uint16_t a_from, const uint16_t a_to);
/**
* Removes the telegram a_radioTelegram from all maps (broadcast-, wirkfeld-,
* funktionsraum- and lastTelegram-map).
* If the telegram is a attached BC-only-telegram, it will be detached.
* The telegram itself will be deleted when a_doDeleteTelegram is true.
* ATTENTION: iterators to all maps could be invalid after calling this function
*
* @param a_radioTelegram
* @param a_doDeleteTelegram
*/
void removeFromAllMaps(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
/**
* Detaches an existing radio-telegram and adds it to the broadcast-map
*
* @param a_radioTelegram The telegram which could contain an attached telegram
*/
void detachTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
/**
* Removes the telegram with a_funktionsraumKennung from funktionsraum-map and
* from the broadcast-map.
* If the telegram is a attached BC-only-telegram, it will be detached.
* The telegram itself will be deleted.
* ATTENTION: iterators to m_FunktionsraumTelegramMap could be invalid after
* calling this function
*
* @param a_funktionsraumKennung
*/
void removeFromFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung);
void removeFromTechnikFunktionsAndBroadcastMaps(const uint16_t a_funktionsraumKennung);
/**
* Removes the telegram where wirkfeldTelegramMapByKennungIterator points to from
* wirkfeld-maps and from the broadcast-map.
* If the telegram is a attached BC-only-telegram, it will be detached.
* The telegram itself will be deleted.
* ATTENTION: iterators to m_WirkfeldTelegramMapByKennung and
* m_WirkfeldTelegramMapByTime could be invalid after calling this function
*
* @param a_wirkfeldTelegramMapByKennungIterator
*/
void removeFromWirkfeldAndBroadcastMaps(const std::multimap<uint16_t, std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>>::iterator &a_wirkfeldTelegramMapByKennungIterator);
/**
* Handles the REAKTIVIERUNG_DUS_MINE-telegram for normal-broadcasts and
* nachforderungen.
*
* @param a_radioTelegram The GEF_REAKTIVIERUNG_DUS_MINE-telegram
*/
void reactivateDusMine(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
/**
* Handles the DEAKTIVIERUNG_EINZELMINE-telegram for normal-broadcasts and
* nachforderungen. This includes deactivation of mines from minen-sperre and DUS-
* mines.
*
* @param a_radioTelegram The GEF_REAKTIVIERUNG_DUS_MINE-telegram
*/
void deactivateSingleMine(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram);
/**
* Prepares values for start sending nachforderungen
*/
void prepareForSendNachforderungen();
/**
* returns the next nachforderung to send and prepares collecting window if no
* more nachforderungen are to be sent
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> evaluateNextNachforderungToSend();
Q_SIGNALS:
void areaEffectSimulationActivated(int wirkfeldKennung);
void areaEffectSimulationDeactivated(int wirkfeldKennung);
private:
/**
* The logger object
*/
NGL::INGLLogger &m_logger;
NGL::INGLLogger &m_loggerBcRequest;
};
}
+855
View File
@@ -0,0 +1,855 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvCentralThread
// **************************************************************************
#include "PrtCnvCentralThread.hpp"
#include "PrtCnvRadioThread.hpp"
#include "PrtCnvDbThread.hpp"
#include "PrtCnvStoredCentralTelegram.hpp"
#include "PrtCnvTelegramLogThread.hpp"
#include "PrtCnvInjectionThread.hpp"
#include "PrtCnvThreadStarter.hpp"
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include <LtsUtilsCore/dds_bitarray.h>
#include <QTimer>
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <time.h>
#include <TrainingAreaBase/training_area_telegrams.h>
namespace PrtCnv
{
// ---------------------------------------------------------------
PrtCnvCentralThread::PrtCnvCentralThread(PrtCnv::PrtCnvThreadStarter* a_parent)
: PrtCnvThread(a_parent, "PrtCnvCentralThread")
, m_webSocket(nullptr)
, m_SendCounter(0)
, m_RadioThread(nullptr)
, m_RadioStopped(false)
, m_CentralStopped(false)
, m_LastSendCounter(0)
, m_VirtualTeilnehmerId(0)
, m_maxEqID(50000)
, m_aliveTimer(nullptr)
, m_sendAckTimer(nullptr)
, m_reconnectTimer(nullptr)
, m_DbThread(nullptr)
, m_LogThread(nullptr)
, m_InjectionThread(nullptr)
, m_ReceiveCounter(0)
, m_ReceiveAckCounter(0)
, m_State(PrtCnvSocketThread::e_INITIAL)
, m_bDirectConnection(false)
, m_bRadioThreadInitDone(false)
{
m_Name = QStringLiteral("Central");
}
PrtCnvCentralThread::~PrtCnvCentralThread()
{
destroy();
}
// ---------------------------------------------------------------
// Creates all subcomponents and connects signals to slots
// ---------------------------------------------------------------
int32_t PrtCnvCentralThread::create(NGL::Settings *settings)
{
PrtCnvThread::create(settings);
INFO_LOG(m_logger) << "Create";
m_RadioThread = qobject_cast<PrtCnvRadioThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Radio));
m_DbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
m_LogThread = qobject_cast<PrtCnvTelegramLogThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Log));
m_InjectionThread = qobject_cast<PrtCnvInjectionThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Injection));
connect(m_DbThread, &PrtCnvDbThread::playerLicencedChanged,
this, &PrtCnvCentralThread::onPlayerLicencedChanged, Qt::QueuedConnection);
connect(this, &PrtCnvCentralThread::checkSendToCentral, this, &PrtCnvCentralThread::checkSend, Qt::QueuedConnection);
connect(this, &PrtCnvCentralThread::checkSendToRadio, m_RadioThread, &PrtCnvRadioThread::checkSend, Qt::QueuedConnection);
connect(this, &PrtCnvCentralThread::stopRadio, m_RadioThread, &PrtCnvRadioThread::doStop, Qt::QueuedConnection);
connect(this, &PrtCnvCentralThread::cleanRadio, m_RadioThread, &PrtCnvRadioThread::doClean, Qt::QueuedConnection);
connect(this, &PrtCnvCentralThread::startRadio, m_RadioThread, &PrtCnvRadioThread::doStart, Qt::QueuedConnection);
connect(m_RadioThread, &PrtCnvRadioThread::initDone,
this, &PrtCnvCentralThread::onRadioThreadInitDone);
connect(m_RadioThread, &PrtCnvRadioThread::areaEffectSimulationActivated,
this, &PrtCnvCentralThread::onAreaEffectSimulationActivated, Qt::QueuedConnection);
connect(m_RadioThread, &PrtCnvRadioThread::areaEffectSimulationDeactivated,
this, &PrtCnvCentralThread::onAreaEffectSimulationDeactivated, Qt::QueuedConnection);
auto mainApp = m_Parent->getMainApp();
connect(this, &PrtCnvCentralThread::directTextMessage,
mainApp, &PrtCnvApplication::textMessageFromPrtCnvToCentral);
connect(mainApp, &PrtCnvApplication::textMessageFromCentralToPrtCnv,
this, &PrtCnvCentralThread::onDirectTextMessageReceived);
m_aliveTimer = new QTimer(this);
connect(m_aliveTimer, &QTimer::timeout, this, &PrtCnvCentralThread::timeoutAliveAndConnection);
m_sendAckTimer = new QTimer(this);
connect(m_sendAckTimer, &QTimer::timeout, this, &PrtCnvCentralThread::timeoutTick);
const int disconnectTimeout = m_Config->getInteger(getName(), QStringLiteral("DisconnectTimeout"), 10000);
m_reconnectTimer = new QTimer(this);
m_reconnectTimer->setInterval(disconnectTimeout);
m_reconnectTimer->setSingleShot(true);
connect(m_reconnectTimer, &QTimer::timeout, this, &PrtCnvCentralThread::connectSocket);
return 0;
}
// ---------------------------------------------------------------
// Deallocates subcomponents.
// ---------------------------------------------------------------
int32_t PrtCnvCentralThread::destroy()
{
INFO_LOG(m_logger) << "Destroy";
PrtCnvThread::destroy();
delete m_webSocket; m_webSocket = nullptr;
delete m_aliveTimer; m_aliveTimer = nullptr;
delete m_sendAckTimer; m_sendAckTimer = nullptr;
delete m_reconnectTimer; m_reconnectTimer = nullptr;
return 0;
}
// ---------------------------------------------------------------
// Initializes the variables.
// ---------------------------------------------------------------
int32_t PrtCnvCentralThread::init()
{
INFO_LOG(m_logger) << "Init";
PrtCnvThread::init();
m_CentralStopped = false;
m_RadioStopped = false;
m_aliveTimer->setInterval(m_Config->getInteger(getName(), QStringLiteral("AliveTimeout"), 3000));
m_sendAckTimer->setInterval(m_Config->getInteger(getName(), QStringLiteral("AckTimeout"), 900));
m_VirtualTeilnehmerId = m_Config->getInteger(QStringLiteral("Common"), QStringLiteral("VirtualIdPrtCnv"), 90);
m_maxEqID = m_Config->getInteger(QStringLiteral("Common"), QStringLiteral("MaxEqID"), 50000); //or max unsigned short 65535
return 0;
}
int32_t PrtCnvCentralThread::deinit()
{
INFO_LOG(m_logger) << "Deinit";
PrtCnvThread::deinit();
return 0;
}
// ---------------------------------------------------------------
// Starts the thread and connects to TCP-server
// ---------------------------------------------------------------
int32_t PrtCnvCentralThread::start()
{
INFO_LOG(m_logger) << "Start";
PrtCnvThread::start();
if (m_DbThread->mustProceedCleanup())
{
INFO_LOG(m_logger) << "Proceed cleanup DB after crash...";
m_CentralStopped = true;
emit stopRadio();
}
else
{
connectSocket();
}
return 0;
}
// ---------------------------------------------------------------
// Stops the thread
// ---------------------------------------------------------------
int32_t PrtCnvCentralThread::stop()
{
INFO_LOG(m_logger) << "Stop";
PrtCnvThread::stop();
return 0;
}
// ---------------------------------------------------------------
// see base-class
// ---------------------------------------------------------------
void PrtCnvCentralThread::connected()
{
INFO_LOG(m_logger) << "Central-Socket connected m_Socket=" << m_webSocket;
m_SendCounter = 0; // we reset this counter after every connect to 0 => next telegram to send has counter 1 (preincrement)
m_LastSendCounter = 0;
m_ReceiveCounter = 0; // send 0 until we get a telegram, we reset this counter after every connect to 0 => the next telegram from central must be 1
m_ReceiveAckCounter = 0;
m_aliveTimer->start();
if (m_bRadioThreadInitDone)
m_sendAckTimer->start();
m_State = PrtCnvSocketThread::e_WORKING;
emit checkSendToCentral(); // start sending telegrams for central still in list (reconnection)
}
// ---------------------------------------------------------------
// see base-class
// ---------------------------------------------------------------
void PrtCnvCentralThread::disconnected()
{
INFO_LOG(m_logger) << "disconnected m_Socket=" << m_webSocket;
// for the case central is disconnecting, we have to disconnect also, otherwise
// we could send rubish (still resisting in buffers) after a reconnection
if (PrtCnvSocketThread::e_WORKING == m_State && m_webSocket)
m_webSocket->close();
m_State = PrtCnvSocketThread::e_DISCONNECTING;
if (m_DoCleanup)
{
INFO_LOG(m_logger) << "cleanup while disconnection";
m_sendAckTimer->stop();
m_CentralStopped = true;
if (m_RadioStopped)
cleanAndReinit();
}
else
{
m_DbThread->prepareCentralTelegramsForReconnection();
connectSocket();
}
}
// ---------------------------------------------------------------
// Stores that radio-thread has stopped. Cleans the Db and
// reinitializes when central-thread has also stopped.
// ---------------------------------------------------------------
void PrtCnvCentralThread::radioStopped()
{
m_RadioStopped = true;
if (m_CentralStopped)
cleanAndReinit();
}
// ---------------------------------------------------------------
// Calls clean of radio-thread and Db-thread and initializes
// the variables for rework.
// ---------------------------------------------------------------
void PrtCnvCentralThread::cleanAndReinit()
{
INFO_LOG(m_logger) << "cleanAndReinit";
emit cleanRadio();
// reinit mit sicherung und löschung der datenbestände
m_ReceiveAckCounter = 0;
m_DbThread->cleanAndInit();
m_DoCleanup = false;
}
// ---------------------------------------------------------------
// Starts radio-thread and itself for proceeding work after
// cleaning the Db.
// ---------------------------------------------------------------
void PrtCnvCentralThread::radioCleant()
{
if (!m_DoCleanup)
{
m_DbThread->setMachineState(0);
m_RadioStopped = false;
m_CentralStopped = false;
emit startRadio();
connectSocket();
}
else
{
ERROR_LOG(m_logger) << "Internal error: radioCleant, but central still cleaning up.";
}
}
// ---------------------------------------------------------------
// see base-class
// ---------------------------------------------------------------
void PrtCnvCentralThread::onTextMessageReceived(const QString &msg)
{
// eat all pending readyReadyRead
if (m_DoCleanup)
return;
if (PrtCnvSocketThread::e_WORKING != m_State)
return; // do nothing when we are not in working state
onDirectTextMessageReceived(msg.toUtf8());
}
void PrtCnvCentralThread::onDirectTextMessageReceived(const QByteArray &baJson)
{
try
{
if (baJson.isEmpty())
throw std::exception(); // FIXME: use own exception
const QJsonObject obj = QJsonDocument::fromJson(baJson).object();
const QJsonValue typeId = obj.value(QStringLiteral("typeId"));
if (!typeId.isDouble())
throw TrainingAreaBase::TetraIcd::JsonException(obj, QStringLiteral("typeId"));
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(baJson, obj, static_cast<TrainingAreaBase::TetraIcd::TelegramId>(typeId.toInt()));
m_LogThread->writeCentralTelegramLog(&telegram, false, 1);
m_aliveTimer->start();
// which kind of telegram is it?
if (telegram.telegramId() == TrainingAreaBase::TetraIcd::BESTAETIGUNG)
{
// it is an ack-telegram
handleAck(&telegram);
}
else
{
DEBUG_LOG(m_logger) << "Received telegram '" << baJson << "'";
// a payload-telegram
handleTelegram(&telegram);
}
}
catch (const std::exception &e)
{
ERROR_LOG(m_logger) << "JSon conversion error happenend. Error:"
<< e.what() << ", msg: " << baJson;
// inform central about the failure
TrainingAreaBase::TetraIcd::CtaDataError tele;
tele.header.equipmentNumber = m_VirtualTeilnehmerId;
tele.header.timeStamp = time(nullptr) * 1000LL;
tele.reason = TrainingAreaBase::TetraIcd::CtaDataError::Reason::JsonDecodeFailure;
tele.originTelegramId = 0;
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(tele);
sendTelegram(&telegram);
}
}
// ---------------------------------------------------------------
void PrtCnvCentralThread::stateChanged(QAbstractSocket::SocketState newState)
{
INFO_LOG(m_logger) << "Websocket changed to state " << newState;
}
// ---------------------------------------------------------------
// Stores a telegram to be sent to radio in volatile radio-
// list and permanent in DB.
// ---------------------------------------------------------------
void PrtCnvCentralThread::handleTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
// => check with receive-counter if we lost a telegram or we get a telegram twice.
// => in this cases neither store the telegram nor send an ack (just ignore and read the next telegram)
// => the receive-counter will be hold on the last correct received telegram
DEBUG_LOG(m_logger) << "Receive Central-Telegram with telId=" << a_telegram->telegramId()
<< "; rcounter=" << a_telegram->counter();
if (checkAndUpdateReceiveCounter(a_telegram->counter()))
{
if (handleSpecialTelegram(a_telegram))
{
using namespace TrainingAreaBase::TetraIcd;
if (a_telegram->telegramId() == TelegramId::DEAKTIVIERUNG_WIRKFELD)
{
const DeactivateEffectSimulation des(DeactivateEffectSimulation::fromJson(a_telegram->jsonData()));
switch (des.aesType)
{
case AreaEffectSimulationType::MineArea:
case AreaEffectSimulationType::ExplosionArea:
case AreaEffectSimulationType::ExplosionSingle:
case AreaEffectSimulationType::NContamination:
case AreaEffectSimulationType::CContamination:
case AreaEffectSimulationType::FunctionalArea:
case AreaEffectSimulationType::DusSingleMineSimulator:
if (m_deactivatedAreaEffectSimulations.contains(des.aesDdsId))
{
INFO_LOG(m_logger) << "AES with id " << des.aesDdsId << " already deactivated - ignoring new deactivation telegram";
return;
}
break;
case AreaEffectSimulationType::TechnicalArea:
case AreaEffectSimulationType::GasPipeDetonation:
case AreaEffectSimulationType::SpecialArea:
case AreaEffectSimulationType::Unknown:
Q_ASSERT(false); // should not happen but who knows
break;
case AreaEffectSimulationType::BeStillWarning:
break;
}
}
else if (a_telegram->telegramId() == TelegramId::WIRKFELD_STATUS_MINENSPERRE)
{
const MineAreaSingleMineState msms(MineAreaSingleMineState::fromJson(a_telegram->jsonData()));
QSet<int> &oldTriggeredMines = m_alreadyTriggeredMines[msms.aesDdsId];
QSet<int> newTriggeredMines;
newTriggeredMines.reserve(oldTriggeredMines.size() + 5);
bool bSomethingChanged = false;
for (int i = 0; i < msms.mineCount; ++i)
{
if (!LtsUtils::BitArray::testBit(msms.mineState, i)) // 0 = triggered
{
INFO_LOG(m_logger) << "Testing bit " << i << " ... not set = triggered";
if (!oldTriggeredMines.contains(i))
{
INFO_LOG(m_logger) << "not in old one";
bSomethingChanged = true;
}
newTriggeredMines.insert(i);
}
}
if (!bSomethingChanged)
bSomethingChanged = newTriggeredMines.size() != oldTriggeredMines.size();
if (!bSomethingChanged)
{
INFO_LOG(m_logger) << "MineState telegram contains no new informations - skipping " << msms.aesDdsId;
return;
}
INFO_LOG(m_logger) << "MineState telegram contains new informations... " << msms.aesDdsId;
oldTriggeredMines = newTriggeredMines; // update in m_alreadyTriggeredMines[]
}
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram>
radioTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(a_telegram));
if (radioTelegram)
{
if (radioTelegram->getIdentity() <= m_maxEqID) {
// save data in persistent database
m_DbThread->storeRadioTelegram(radioTelegram, a_telegram->counter());
} else {
WARNING_LOG(m_logger) << "Central telegram with telId=" << a_telegram->telegramId()
<< " has invalid identity:" << radioTelegram->getIdentity();
}
}
else
{
m_DbThread->centralCounterReadyForAck(a_telegram->counter());
WARNING_LOG(m_logger) << "Central telegram with telId=" << a_telegram->telegramId()
<< " could not be converted";
}
}
}
emit checkSendToRadio();
}
// ---------------------------------------------------------------
// The counter will be set to a_startCounter if a_maxCounter
// is reached
// returns true if receive-counter is ok
// returns false if a telegram is repeated or lost
// ---------------------------------------------------------------
bool PrtCnvCentralThread::checkAndUpdateReceiveCounter(const int32_t a_counter)
{
TRACE_LOG(m_logger) << "checkAndUpdateReceiveCounter: " << a_counter << ", " << m_ReceiveCounter;
// check for wrap
if (std::numeric_limits<int32_t>::max() == ++m_ReceiveCounter)
m_ReceiveCounter = 0;
// has the counter the expected value?
if (m_ReceiveCounter != a_counter)
{
if (m_ReceiveCounter > a_counter)
{
ERROR_LOG(m_logger) << "It seems, the telegram with counter " << a_counter << " was resent from "
<< getName() << "! Expected: " << m_ReceiveCounter;
}
else
{
ERROR_LOG(m_logger) << "It seems, we lost one or several telegrams from " << getName()
<< ": receive-counter of received telegram: " << a_counter << "; Expected: " << m_ReceiveCounter;
}
#if 0
m_ReceiveCounter--;
return false; // failure
#else
m_ReceiveCounter = a_counter;
return true;
#endif
}
return true; // all ok
}
// ---------------------------------------------------------------
// used to send periodically an ack to central
// ---------------------------------------------------------------
void PrtCnvCentralThread::timeoutTick()
{
//NOTICE_LOG(m_logger) << "timeoutTick, " << m_DoCleanup << ", " << m_State;
if (m_DoCleanup)
return; // eat all pending timeouts
if (PrtCnvSocketThread::e_WORKING != m_State)
return; // do nothing when we are not in working state
/*
// Das bringt nur noch mehr Traffic wenn zskskp die gesendeten Daten
// nicht schnell genug abarbeiten kann - deshalb ersteinmal auskommentiert
// do we have to resend unacked telegrams
if (m_DbThread->resendCentralTelegrams(m_LastSendCounter))
{
emit checkSendToCentral(); // resend
}
else
{
// nothing has to be resent => update m_LastSendCounter
m_LastSendCounter = m_SendCounter;
}
*/
// do we have to inject a central-telegram?
try
{
N_CoPrtCnv::CoPrtCnvCentralTelegram *centralInjectTelegram;
while ((centralInjectTelegram = m_InjectionThread->getNextCentralInjectionTelegram()))
{
sendTelegram(centralInjectTelegram);
delete centralInjectTelegram;
}
}
catch (std::exception& e)
{
ERROR_LOG(m_logger) << "Catched an unexpected exception: " << e.what()
<< " => could not inject radio-telegram, but we proceed...";
}
// send the periodically ack-telegram to central
sendAck();
}
// ---------------------------------------------------------------
// see base-class
// ---------------------------------------------------------------
void PrtCnvCentralThread::timeoutAliveAndConnection()
{
INFO_LOG(m_logger) << "timeoutAliveAndConnection socketState="
<< int(m_webSocket ? m_webSocket->state() : -1)
<< " m_State=" << m_State << " m_Socket=" << m_webSocket;
switch (m_State)
{
case PrtCnvSocketThread::e_CONNECTING:
connectSocket();
break;
case PrtCnvSocketThread::e_WORKING:
if (!m_bDirectConnection)
{
disconnectSocket();
m_reconnectTimer->start();
}
break;
case PrtCnvSocketThread::e_DISCONNECTING:
break;
default:
ERROR_LOG(m_logger) << "timeoutAlive for m_State=" << m_State << " not implemented";
}
}
// ---------------------------------------------------------------
// Sends a ack-telegram to central.
// ---------------------------------------------------------------
void PrtCnvCentralThread::sendAck()
{
TrainingAreaBase::TetraIcd::Acknowledge ack;
ack.timeStamp = time(nullptr) * 1000;
ack.ackCounter = m_ReceiveAckCounter;
N_CoPrtCnv::CoPrtCnvCentralTelegram tele(ack);
// send to socket
if (m_webSocket)
m_webSocket->sendTextMessage(QString::fromUtf8(tele.data()));
else
emit directTextMessage(tele.data());
TRACE_LOG(m_logger) << "Wrote ack: " << tele.data();
}
// ---------------------------------------------------------------
// Sends a central-telegram to socket or file, depending on
// configuration. Before sending, the send-counter will be set.
// ---------------------------------------------------------------
void PrtCnvCentralThread::sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
PrtCnvStoredCentralTelegram *handle = nullptr;
sendTelegram(a_telegram, handle);
}
// ---------------------------------------------------------------
// Sends a central-telegram to socket or file, depending on
// configuration. Before sending, the send-counter will be set.
// After sending the Db will be updated.
// ---------------------------------------------------------------
void PrtCnvCentralThread::sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram,
PrtCnvStoredCentralTelegram *a_handle)
{
using namespace TrainingAreaBase::TetraIcd;
if (a_telegram->telegramId() == TelegramId::DEAKTIVIERUNG_EINZELMINE)
{
const MineTriggered tele(MineTriggered::fromJson(a_telegram->jsonData()));
m_alreadyTriggeredMines[tele.mineAreaId].insert(tele.mineIndex - 1); // starts at 1 ...
INFO_LOG(m_logger) << "Adding triggered mine with id " << tele.mineIndex << " to area id " << tele.mineAreaId;
}
// set the counter, but keep old counter if we resend the telegram
const int writeCounter = a_handle ? a_handle->writeCounter() : 0;
if (writeCounter != 0)
a_telegram->setCounter(writeCounter);
else
a_telegram->setCounter(++m_SendCounter);
// send telegram to central
DEBUG_LOG(m_logger) << "Send telegram to central: telId=" << a_telegram->telegramId()
<< " wcounter=" << a_telegram->counter();
// send to socket
if (m_webSocket)
m_webSocket->sendTextMessage(QString::fromUtf8(a_telegram->data()));
else
emit directTextMessage(a_telegram->data());
// mark in Db telegram as sent
if (a_handle)
m_DbThread->centralTelegramSent(a_telegram->counter(), a_handle);
m_LogThread->writeCentralTelegramLog(a_telegram, true, 1);
}
// ---------------------------------------------------------------
// Processes the telegram
// AnforderungStatistikBroadcastNachforderugen. The function
// fills the answer telegram StatistikBroadNachforderungen
// with the actual statistic.
// ---------------------------------------------------------------
void PrtCnvCentralThread::handleAnforderungStatistikBroadcastNachforderungen()
{
const auto nachforderungsStat = m_DbThread->getClaimStatistics();
// inform central about the failure
TrainingAreaBase::TetraIcd::BroadcastStatistics tele;
tele.header.equipmentNumber = m_VirtualTeilnehmerId;
tele.header.timeStamp = time(nullptr) * 1000LL;
tele.information.reserve(nachforderungsStat.size());
for (const auto &it : nachforderungsStat)
{
TrainingAreaBase::TetraIcd::BroadcastInformation bi;
bi.equipmentNumber = it.first;
bi.broadcastRequests = it.second;
tele.information.push_back(std::move(bi));
}
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(tele);
sendTelegram(&telegram);
}
// ---------------------------------------------------------------
// Checks whether a telegram must be sent and sends it.
// ---------------------------------------------------------------
void PrtCnvCentralThread::checkSend()
{
// eat all pending checkSend-signals, if we have to cleanup
if (m_DoCleanup) return;
if (PrtCnvSocketThread::e_WORKING != m_State) return; // do nothing when we are not in working state
PrtCnvStoredCentralTelegram *handle = nullptr;
auto *centralTelegram = m_DbThread->getNextCentralTelegram(handle);
if (centralTelegram)
{
sendTelegram(centralTelegram, handle);
delete centralTelegram;
emit checkSendToCentral();
}
}
// ---------------------------------------------------------------
// Removes telegrams from ToCentral-table, if the central
// acknowledges their reception
// ---------------------------------------------------------------
void PrtCnvCentralThread::handleAck(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
TrainingAreaBase::TetraIcd::Acknowledge ack(TrainingAreaBase::TetraIcd::Acknowledge::fromJson(a_telegram->jsonData()));
// INFO_LOG(m_logger) << "Got Central-Ack with wcounter=" << a_ackTelegram.zaehler();
m_DbThread->ackCentralTelegrams(ack.ackCounter);
}
// ---------------------------------------------------------------
// Will be called from Db-thread when telegrams were stored
// permanent. This function will then send the ack to central
// (socket-based) or delete the telegramfiles (file-based)
// ---------------------------------------------------------------
void PrtCnvCentralThread::permanentStored(int32_t a_centralCounter)
{
if (m_DoCleanup) return; // eat all pending permanentStored
if (PrtCnvSocketThread::e_WORKING != m_State) return; // do nothing when we are not in working state
DEBUG_LOG(m_logger) << "got permanentStored a_centralCounter=" << a_centralCounter;
if (0 != a_centralCounter && a_centralCounter > m_ReceiveAckCounter)
{
m_ReceiveAckCounter = a_centralCounter;
sendAck();
}
}
void PrtCnvCentralThread::onTetraConnectionState(bool bConnected)
{
TrainingAreaBase::TetraIcd::ProxyConnectionState connState;
connState.connectedToSystem = bConnected;
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(connState);
sendTelegram(&telegram);
}
void PrtCnvCentralThread::onPlayerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind)
{
TrainingAreaBase::TetraIcd::ParticipantLicenseChange lc;
lc.equipmentNumber = equipNo;
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unknown;
switch (kind)
{
case N_CoPrtCnv::E_LicensingKind::Success:
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Licensed;
break;
case N_CoPrtCnv::E_LicensingKind::Rejected:
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Rejected;
break;
case N_CoPrtCnv::E_LicensingKind::Unlicense:
lc.licenseState = TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unlicense;
break;
}
Q_ASSERT(lc.licenseState != TrainingAreaBase::TetraIcd::ParticipantLicenseState::Unknown);
N_CoPrtCnv::CoPrtCnvCentralTelegram telegram(lc);
sendTelegram(&telegram);
}
void PrtCnvCentralThread::onAreaEffectSimulationDeactivated(int wirkfeldKennung)
{
m_deactivatedAreaEffectSimulations.insert(wirkfeldKennung);
m_alreadyTriggeredMines.remove(wirkfeldKennung);
}
void PrtCnvCentralThread::onAreaEffectSimulationActivated(int wirkfeldKennung)
{
m_deactivatedAreaEffectSimulations.remove(wirkfeldKennung);
}
void PrtCnvCentralThread::onRadioThreadInitDone()
{
m_bRadioThreadInitDone = true;
if (!m_sendAckTimer->isActive())
m_sendAckTimer->start();
}
// ---------------------------------------------------------------
// returns true if telegram should be converted, stored and
// sent to radio
// ---------------------------------------------------------------
bool PrtCnvCentralThread::handleSpecialTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
using namespace TrainingAreaBase::TetraIcd;
switch (a_telegram->telegramId())
{
case Constraints<ExerciseStateData>::id():
{
ExerciseStateData tele(ExerciseStateData::fromJson(a_telegram->jsonData()));
switch(tele.exerciseState)
{
case ExerciseStateData::ExerciseState::Terminated:
{
INFO_LOG(m_logger) << "Got Betriebszustandsmeldung Uebung_Aufraeumen => disconnect, cleanup and reconnect";
m_DbThread->setMachineState(1); // store permanent that we cleanup now, so we will proceed cleanup, after a crash
// send ack before disconnecting
permanentStored(a_telegram->counter());
m_DoCleanup = true;
m_CentralStopped = false;
m_RadioStopped = false;
// INFO_LOG(m_logger) << "stop timer";
m_sendAckTimer->stop();
m_DbThread->waitUntilDbWritten();
disconnectSocket();
m_RadioThread->prepareCleanUp(); // this makes empty the message-queue of radio-thread faster
emit stopRadio();
m_reconnectTimer->start();
break;
}
case ExerciseStateData::ExerciseState::Started:
m_DbThread->setPause(false);
m_RadioThread->sendEnvironmentTelegramTimerNow();
break;
case ExerciseStateData::ExerciseState::Paused:
m_DbThread->setPause(true);
m_RadioThread->sendEnvironmentTelegramTimerNow();
break;
default:
break;
}
return false;
}
case Constraints<RequestBroadcastStatistics>::id():
// here we must not use sendAck(), because it could be that we ack central-telegrams which are not yet stored permanent
m_DbThread->centralCounterReadyForAck(a_telegram->counter());
handleAnforderungStatistikBroadcastNachforderungen();
return false;
case Constraints<ExerciseEquipmentAssignmentComplete>::id():
{
const ExerciseEquipmentAssignmentComplete tele(ExerciseEquipmentAssignmentComplete::fromJson(a_telegram->jsonData()));
m_RadioThread->exerciseEquipmentChanged(tele);
return false;
}
default:
break;
}
return true;
}
// ---------------------------------------------------------------
void PrtCnvCentralThread::connectSocket()
{
const QString host = m_Config->getString(getName(), QStringLiteral("HostName"), QStringLiteral("EXCTDS01"));
if (host == QLatin1String("directConnection"))
{
m_bDirectConnection = true;
connected();
return;
}
m_bDirectConnection = false;
disconnectSocket();
m_webSocket = new QWebSocket;
connect(m_webSocket, &QWebSocket::stateChanged,
this, &PrtCnvCentralThread::stateChanged);
connect(m_webSocket, &QWebSocket::textMessageReceived,
this, &PrtCnvCentralThread::onTextMessageReceived);
connect(m_webSocket, &QWebSocket::connected,
this, &PrtCnvCentralThread::connected);
connect(m_webSocket, &QWebSocket::disconnected,
this, &PrtCnvCentralThread::disconnected);
const uint16_t port = static_cast<uint16_t>(m_Config->getInteger(getName(), QStringLiteral("Port"), 4934));
const QUrl scmUrl(QStringLiteral("ws://%1:%2").arg(host).arg(port));
m_webSocket->open(scmUrl);
m_State = PrtCnvSocketThread::e_CONNECTING;
}
// ---------------------------------------------------------------
void PrtCnvCentralThread::disconnectSocket()
{
m_State = PrtCnvSocketThread::e_INITIAL;
if (!m_webSocket)
return;
m_State = PrtCnvSocketThread::e_DISCONNECTING;
m_webSocket->disconnect(this);
m_webSocket->close();
m_webSocket->deleteLater();
m_webSocket = nullptr;
}
} // namespace PrtCnv
+316
View File
@@ -0,0 +1,316 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvCentralThread
// **************************************************************************
#pragma once
#include "PrtCnvSocketThread.hpp"
#include "PrtCnvDbThread.hpp"
#include <QWebSocket>
namespace N_CoPrtCnv
{
class CoPrtCnvRadioTelegram;
}
namespace PrtCnv
{
class PrtCnvRadioThread;
class PrtCnvThreadStarter;
/**
* This class handles the protocoll to central. This includes:
* - connection with files instead sockets (configurable)
* - send and receive telegrams
*
* @author
* @version 1.0
* @created 05-Jan-2006 13:34:28
*/
class PrtCnvCentralThread : public PrtCnv::PrtCnvThread
{
Q_OBJECT
private:
public:
/**
* @author wia
* @version 1.0
* @updated 02-Jul-2009 14:32:58
*/
~PrtCnvCentralThread();
/**
* @param a_parent The pointer to the starter-thread
*/
explicit PrtCnvCentralThread(PrtCnvThreadStarter* a_parent);
/**
* Creates all subcomponents and connects signals to slots
*/
int32_t create(NGL::Settings *settings) override;
int32_t deinit() override;
/**
* Deallocates subcomponents.
*/
int32_t destroy() override;
/**
* Initializes the variables.
*/
int32_t init() override;
/**
* Starts the thread and connects to TCP-server
*/
int32_t start() override;
/**
* Stops the thread
*/
int32_t stop() override;
// for direct communication
void onDirectTextMessageReceived(const QByteArray &baJson);
signals:
// for direct communication
void directTextMessage(const QByteArray &baJson);
/**
* Signal to start sending a telegram to central
*/
void checkSendToCentral();
/**
* Signal to start sending a telegram to radio
*/
void checkSendToRadio();
/**
* Signal to stop radio
*/
void stopRadio();
/**
* Signal to start cleaning-process of radio-thread
*/
void cleanRadio();
/**
* Signal to restart work of radio-thread
*/
void startRadio();
protected:
/**
* For reading/writing json telegrams to socket
*/
QWebSocket *m_webSocket;
/**
* The send-counter (only for sockets)
*/
int32_t m_SendCounter;
public slots:
/**
* Checks whether a telegram must be sent and sends it.
*/
void checkSend();
/**
* Stores that radio-thread has stopped. Cleans the Db and reinitializes when
* central-thread has also stopped.
*/
void radioStopped();
/**
* Starts radio-thread and itself for proceeding work after cleaning the Db.
*/
void radioCleant();
/**
* Will be called from Db-thread when telegrams were stored permanent. This
* function will then send the ack to central (socket-based) or delete the
* telegramfiles (file-based)
*
* @param a_centralCounter
*/
void permanentStored(int32_t a_centralCounter);
// called from Radio thread to indicate current connection state
void onTetraConnectionState(bool bConnected);
// called from Radio thread to indicate the license state of an equipment
void onPlayerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
protected slots:
// an effect siumlation was (de)activated
void onAreaEffectSimulationDeactivated(int wirkfeldKennung);
void onAreaEffectSimulationActivated(int wirkfeldKennung);
// radio thread completely initialized
void onRadioThreadInitDone();
/**
* called when websocket is connected
*/
void connected();
/**
* called when websocket is disconnected
*/
void disconnected();
/**
* used to send periodically an ack to central
*/
void timeoutTick();
/**
* see base-class
*/
void timeoutAliveAndConnection();
void onTextMessageReceived(const QString &msg);
void stateChanged(QAbstractSocket::SocketState newState);
protected:
/**
* Sends a ack-telegram to central.
*/
void sendAck();
/**
* Stores a telegram to be sent to radio in volatile radio-list and permanent in
* DB.
*
* @param a_telegram
*/
void handleTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
bool checkAndUpdateReceiveCounter(const int32_t a_counter);
/**
* Sends a central-telegram to socket or file, depending on configuration. Before
* sending, the send-counter will be set. After sending the Db will be updated.
*
* @param a_telegram The central-telegram to be sent.
* @param a_handle The handle assigned to telegram to be sent, we got from Db.
*/
void sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram,
PrtCnvStoredCentralTelegram *a_handle);
/**
* Processes the telegram AnforderungStatistikBroadcastNachforderugen. The
* function fills the answer telegram StatistikBroadNachforderungen with the
* actual statistic.
*
* @param a_umgMldTelegram
*/
void handleAnforderungStatistikBroadcastNachforderungen();
/**
* Removes telegrams from ToCentral-table, if the central acknowledges their
* reception
*
* @param a_ackTelegram
*/
void handleAck(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
/**
* Calls clean of radio-thread and Db-thread and initializes the variables for
* rework.
*/
void cleanAndReinit();
/**
* returns true if telegram should be converted, stored and sent to radio
*
* @param a_telegram
*/
bool handleSpecialTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
/**
* Sends a central-telegram to socket or file, depending on configuration. Before
* sending, the send-counter will be set.
*
* @param a_telegram The central-telegram to be sent.
*/
void sendTelegram(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
/**
* Connect as client to a TCP-server on ip-address configured as HostName and Port
* configured as Port
*/
void connectSocket();
/**
* Disconnects from TCP-server
*/
void disconnectSocket();
protected:
/**
* used for periodically send an ack to central
*/
PrtCnvRadioThread *m_RadioThread;
/**
* True if radio has stopped work
*/
bool m_RadioStopped;
/**
* True if central has stopped work
*/
bool m_CentralStopped;
/**
* Used for resending unacked telegrams
*/
int32_t m_LastSendCounter;
/**
* The (virtual) identify of the protocol-converter
*/
uint16_t m_VirtualTeilnehmerId;
/**
* The maximum equipment-ID for tetra - all telegrams with an higher ID will
* be silently ignored
*/
uint16_t m_maxEqID;
// timer to reconnect when no ack from central is coming anymore
QTimer *m_aliveTimer;
// timer to send ack to central
QTimer *m_sendAckTimer;
// timer to reconnect to central
QTimer *m_reconnectTimer;
/**
* True if the database has to be cleant up (used for fast empty of signal buffers)
*/
QAtomicInt m_DoCleanup;
PrtCnvDbThread *m_DbThread;
// log telegrams in file
PrtCnvTelegramLogThread *m_LogThread;
// get injected telegrams
PrtCnvInjectionThread *m_InjectionThread;
/**
* Counter of received telegrams (only for sockets)
*/
int32_t m_ReceiveCounter;
/**
* Acknowledged telegrams counter (only for sockets)
*/
int32_t m_ReceiveAckCounter;
/**
* The state-event-machine state
*/
PrtCnvSocketThread::E_State m_State;
// set with all already deactivated effect simulations to not send deactivate telegram twice
QSet<int> m_deactivatedAreaEffectSimulations;
// multimap with all mine areas and it's already triggered mines
QHash<int, QSet<int>> m_alreadyTriggeredMines;
// true when we don't use socket but direct connections (through onDirectTextMessageReceived)
bool m_bDirectConnection;
bool m_bRadioThreadInitDone;
};
}
File diff suppressed because it is too large. Load diff
+559
View File
@@ -0,0 +1,559 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvDbThread
// **************************************************************************
#pragma once
#include <QtCore/QAtomicInt>
#include <QtCore/QMutex>
#include <QtCore/QString>
#include <QtSql/QSqlDatabase>
#include "PrtCnvThread.hpp"
#include "PrtCnvRadioThread.hpp"
#include <memory>
#include <unordered_map>
#include <vector>
namespace N_CoPrtCnv
{
class CoPrtCnvCentralTelegram;
class CoPrtCnvRadioTelegram;
}
namespace PrtCnv
{
class PrtCnvThreadStarter;
class PrtCnvStoredCentralTelegram;
class PrtCnvStoredRadioTelegram;
class PrtCnvMember;
/**
* Handles all data which must be stored permanent:
* - central-telegrams
* - radio-telegrams
* - radio-members (TNE)
* - reset-counter
* - states
*
* @author wia
* @version 1.0
* @created 28-Jul-2009 16:08:53
*/
class PrtCnvDbThread : public PrtCnvThread
{
Q_OBJECT
public:
/**
* Constructor with thread-starter as argument
*
* @param a_parent
*/
PrtCnvDbThread(const QString &dbDriver,
const QString &dbHostName,
const QString &dbDatabaseName,
const QString &schemaName,
const QString &dbUserName,
const QString &dbPassword,
PrtCnvThreadStarter* a_parent);
~PrtCnvDbThread();
/**
* starts this thread and its childs
*/
int32_t start() override;
/**
* stops this thread and its childs
*/
int32_t stop() override;
/**
* creates this thread and its childs
*/
int32_t create(NGL::Settings *settings) override;
/**
* initializes this thread and its childs
*/
int32_t init() override;
/**
* Indicates the Db, which telegrams were successful received by central. This
* will delete this telegrams from Db.
*
* @param a_counter
*/
void ackCentralTelegrams(int32_t a_counter);
/**
* returns the next telegram to send to central.
* returns NULL if no telegram found.
* ATTENTION: The caller is responsible to deallocate the returned
* centralTelegram!
*
* @param a_handle
*/
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextCentralTelegram(PrtCnvStoredCentralTelegram *& a_handle);
/**
* Marks the central-telegram as to be sent
*
* @param a_counter The counter the telegram has while sent
* @param a_handle The telegram-handle gotten from getNextCentralTelegram()
*/
void centralTelegramSent(const int32_t a_counter, PrtCnvStoredCentralTelegram *a_handle);
/**
* Updates the counter for unsuccessful sending the telegram. If a maximal value
* of this counter is reached, the central-telegram will be deleted.
* a_handle could be deleted after calling this function!
* This function could block some milliseconds, because it must wait until all
* changes are written to DB.
*
* @param a_handle The telegram-handle gotten from getNextCentralTelegram()
*/
void centralTelegramCouldNotBeSent(PrtCnvStoredCentralTelegram *a_handle);
/**
* Marks the radio-telegram as to be sent
*
* @param a_sendCounter
* @param a_handle
* @param a_broadcastIndex
*/
void radioTelegramSent(const uint32_t a_sendCounter, PrtCnvStoredRadioTelegram *a_handle, const uint32_t a_broadcastIndex);
/**
* returns the next telegram to send to radio, respecting the priority.
* returns NULL if no radio-telegram found.
*
* @param a_handle
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextRadioTelegram(PrtCnvStoredRadioTelegram *&a_handle);
/**
* returns the next broadcast-telegram from list.
* Deletes the telegram from this list.
* returns NULL if list empty.
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getNextBroadcastTelegram();
/**
* stores the telegram to be sent to central in working-list and also in Db.
* Because storing data in XDR_Wrapper_it-format is complicated (has pointer to
* data), we store them in the radio-telegram-format).
* Attention: Be aware that centralTelegram and radioTelegram contain the same
* data.
* The DbThread takes control over centralTelegram and radioTelegram and deletes
* them when no longer needed.
*
* @param a_centralTelegram
* @param a_radioTelegram Must be the same telegram like the central-telegram
* (used for storing the telegram in Db)
*/
void storeCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
/**
* Stores the telegram to be sent to radio in working-list and also in Db.
* The DbThread takes control over a_telegram and deletes it when no longer needed.
*
*
* @param a_telegram
* @param a_readCounter
*/
void storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram, const int32_t a_readCounter);
/**
* This function causes acking a central-telegram, which must not be stored
* permanent in Db, because it will be handled immediately (i.e.
* AnforderungStatistikBroadcastNachforderungen).
*
* @param a_centralCounter The read-counter of the central-telegram
*/
void centralCounterReadyForAck(const int32_t a_centralCounter);
/**
* Adds an sql-command in the sql-command-list. On next iteration of
* storePermanent() all command in list will be executed.
*
* @param a_sqlCommand
*/
void addSqlCommand(const QString &sql);
/**
* erases and initializes the database
*/
void cleanAndInit();
/**
* Checks whether central-telegrams have to be resent. If yes, all actions will be
* done, that getNextCentralTelegram() delivers the telegram(s) to be resent.
* Returns true if there must telegrams be resent.
*
* @param a_lastSendCounter The counter of the telegram we expect a ack-
* telegram has been received
*/
bool resendCentralTelegrams(const int32_t a_lastSendCounter);
/**
* Prepares the telegrams not yet acked from central for reconnection. This will
* be done by setting the write-counter to 0.
*/
void prepareCentralTelegramsForReconnection();
/**
* Blocks until all SQL-commands were written to DB.
*/
void waitUntilDbWritten();
/**
* returns NULL if no member found
*
* @param a_ISSI
*/
PrtCnvMember* getMemberByISSI(const uint32_t a_ISSI) const;
/**
* returns NULL if member not found
*
* @param a_id
*/
PrtCnvMember* getMemberById(const uint16_t a_id);
/**
* Licenses the member
*
* @param a_member
* @param a_doLicense
*/
bool licenseMember(PrtCnvMember *a_member, const bool a_doLicense = true);
/**
* Updates the current exercise for this member
*/
void updateExerciseIdForMember(PrtCnvMember *a_member, int newExerciseId);
/**
* adds a member in the 2 maps, sorted by id and ISSI
* returns the new member
*
* @param a_identity
* @param a_ISSI
*/
PrtCnvMember* addMember(const int a_identity, const int a_ISSI);
/**
* removes the member in the 2 maps, sorted by id and ISSI
*
* @param a_member
*/
void removeMember(PrtCnvMember* a_member);
/**
* Returns the actual reset-counter
*/
uint8_t getResetCounter() const;
/**
* Sets the actual reset-counter
*
* @param newVal
*/
void setResetCounter(uint8_t newVal);
/**
* The actual 'Betriebszustaende Gefechtsfeld' which will be sent to TNEs
* periodically
*/
uint8_t getBattleFieldOperatingState() const;
/**
* Sets or unsets pause, depending on a_doSet.
* (Bit 0 in UMGEBUNGS_MELDUNG(2))
* @param a_doSet
*/
void setPause(const bool a_doSet = true);
/**
* Set the inhibit time for position telegrams (1 .. 31 Seconds)
* (Bit 1-5 in UMGEBUNGS_MELDUNG(2))
* @param a_doSet
*/
void setPositionSendTime(unsigned int sendTime);
/**
* Set the current air load to be sent with the Umgebungsmeldung
* (Bit 6-7 in UMGEBUNGS_MELDUNG(2))
* @param airLoad The current air-load received from Tetra
*/
void setAirLoad(PrtCnvRadioThread::E_TrafficLoad airLoad);
/**
* Returns true if the resetCounter itself is different than the reset-counter of
* the environment-telegram
*/
bool isEnvironmentResetCounterOld() const;
/**
* Returns the actual Umgebungsmeldung
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> getEnvironmentTelegram();
/**
* Sets the resetcounter of the environment-telegram to the actual reset-counter.
*/
void setResetCounterOnEnvironment();
/**
* Increments the counter for nachforderungen of the TNE with id a_identity.
*
* @param a_identity The identity of the TNE, which asked for nachforderung
*/
void incClaimCount(int a_identity);
/**
* Fills the map with nachforderungsstatistic.
* Returns the number of elements in map.
*
* @param a_statisticMap this map will be filled with pairs of
* ausstattungsnummer and amount of nachforderungen
*/
std::map<int32_t, int32_t> getClaimStatistics() const;
/**
* Sets the actual machine-state permanent, used to proceed cleaning db after a
* crash.
*
* @param a_newMachineState The new machine-state. 0 => working; 1 => cleaning
* DB
*/
void setMachineState(const int a_newMachineState);
/**
* Returns true if protocol-converter was crashed while cleanup DB.
*/
bool mustProceedCleanup() const;
protected:
void closeDb();
/**
* List of telegrams to be sent to central
*/
std::vector<PrtCnvStoredCentralTelegram*> m_CentralList;
/**
* Synchronizes access to m_BattleFieldOperatingState
*/
mutable QMutex m_BattleFieldOpStateMutex;
/**
* Synchronizes access to m_BattleFieldOperatingState
*/
mutable QMutex m_MemberListByIdMutex;
/**
* Synchronizes access to m_CentralList
*/
mutable QMutex m_CentralListMutex;
/**
* The permanent DB
*/
QSqlDatabase m_db;
/**
* The last counter received from central as ack. Used for deleting no longer used
* central-telegrams and for resending of central-telegrams.
*/
QAtomicInt m_CentralAckCounter;
/**
* Used for optimizing deletion of no longer needed central-telegrams.
*/
unsigned int m_OldCentralAckCounter;
/**
* List of telegrams to be sent to radio
*/
std::list<PrtCnvStoredRadioTelegram*> m_RadioList;
/**
* Synchronizes access to m_RadioList
*/
mutable QMutex m_RadioListMutex;
/**
* Holds the sql-command, which have to be executed in next physical storing cycle
*/
std::vector<QString> m_SqlCommandList;
/**
* Synchronizes access to m_SqlCommandList
*/
mutable QMutex m_SqlCmdListMutex;
/**
* Synchronizes access to m_BroadcastList
*/
mutable QMutex m_BroadcastMapMutex;
/**
* List of broadcast-telegrams. The broadcast-manager will get them periodically
* out of this list.
*/
std::list<PrtCnvStoredRadioTelegram*> m_BroadcastList;
/**
* Used to wait until cleaning is finished and to suppress storing data within
* database while cleaning
*/
bool m_DoClean;
/**
* Used for waiting data written to DB
*/
bool m_DoWaitUntilSqlWritten;
/**
* Holds all logged in members ordered by Id.
*/
std::unordered_map<int,PrtCnvMember*> m_MemberListById;
/**
* Holds all logged in members ordered by ISSI
*/
std::unordered_map<int,PrtCnvMember*> m_MemberListByISSI;
/**
* Used to prevent data storing within DB while restoring after crash
*/
bool m_DoStorePermanent;
/**
* the actual reset-counter
*/
uint8_t m_ResetCounter;
/**
* The actual 'Betriebszustaende Gefechtsfeld' which will be sent to TNEs
* periodically
*/
uint8_t m_BattleFieldOperatingState;
/**
* The actual telegram "Umgebungsmeldung"
*/
PrtCnvStoredRadioTelegram* m_EnvironmentTelegram;
/**
* True if cleanup has to be proceed after crash.
*/
bool m_DoProceedCleanup;
/**
* Used to optimize send the signal permanentStored
*/
int32_t m_OldCentralCounter;
/**
* For ensure that not too much storePermanent-signals will be queued
*/
QBasicAtomicInt m_StorePermanentPending;
/**
* nbr of unsuccessful send-attempts, before telegram will be deleted without
* sending it
*/
uint32_t m_NbrOfAttempts;
/**
* creates or restores the permanent database
*/
bool createDatabase();
/**
* Sends a signal storePermanent if not already such a signal is pending
*/
void sendStorePermanentSignal();
/**
* Helper for storePermanent()
*/
void storeCentralTelegrams();
/**
* Helper for storePermanent()
*/
int32_t storeRadioTelegrams();
/**
* Helper for storePermanent()
*/
void handleSqlCommandList();
/**
* Delete all telegrams with the ids in telegramsToDelete
* from a given table
*/
void deleteTelegramsFromDbById(const QString &table);
/**
* Update the write counter for the telegrams in
* telegramsToUpdateCounter for the given table
*/
void updateTelegramsWriteCounter(const QString &table);
/**
* Update the broadcast counter for the telegrams in
* telegramsToUpdateCounter for the given table
*/
void updateTelegramsBroadcast(const QString &table);
/**
* Reinitialize the database
*/
void reinitDatabase();
QString toCentralTableName() const { return tableName(QStringLiteral("prtcnv_to_central")); }
QString toRadioTableName() const { return tableName(QStringLiteral("prtcnv_to_radio")); }
QString memberTableName() const { return tableName(QStringLiteral("prtcnv_member")); }
QString statusTableName() const { return tableName(QStringLiteral("prtcnv_status")); }
QString versionTableName() const { return tableName(QStringLiteral("prtcnv_version")); }
QString tableName(const QString &table) const { return m_dbSchemaName.isEmpty() ? table : m_dbSchemaName + QLatin1Char('.') + table; }
protected slots:
/**
* Stores the radio-telegrams, central-telegrams in Db and executes the sql-
* command-list.
*/
void storePermanent();
/**
* cleans up this thread and its childs
*/
void clean();
public:
signals:
/**
* Signal to call storePermanent()
*/
void doStorePermanent();
/**
* Signal to inform central-thread about stored telegrams
*
* @param a_centralCounter
*/
void permanentStored(int32_t a_centralCounter);
/**
* a signal to erase the permanent database
*/
void doClean();
void playerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
private:
// local caches to memory (re)allocation all the time
// note: std::vector.clear() does not release memory and therefore
// a reallocation in the next turn is not neccessary
std::vector<int64_t> m_telegramsToDelete;
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateCounter;
std::vector<std::pair<int64_t, int32_t> > m_telegramsToUpdateBC;
std::vector<std::pair<PrtCnvStoredCentralTelegram*, int64_t> > m_telegramsToInsert;
QString m_dbDriver;
QString m_dbHostName;
QString m_dbDatabaseName;
QString m_dbSchemaName;
QString m_dbUserName;
QString m_dbPassword;
};
}
+127
View File
@@ -0,0 +1,127 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "PrtCnvInjectionSocket.hpp"
#include <CoPrtCnv/CoPrtCnvInjectionHeader.hpp>
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include "PrtCnvInjectionThread.hpp"
#include <QtCore/QByteArray>
#include <QTcpSocket>
namespace PrtCnv
{
PrtCnvInjectionSocket::PrtCnvInjectionSocket()
: m_Socket(nullptr)
, m_Server(nullptr)
, m_logger(getLogger("PrtCnvInjectionSocket"))
{}
PrtCnvInjectionSocket::~PrtCnvInjectionSocket()
{
delete m_Socket;
}
// ---------------------------------------------------------------
// Disconnects a TCP-client
// ---------------------------------------------------------------
void PrtCnvInjectionSocket::disconnectClient()
{
INFO_LOG(m_logger) << "A client has disconnected from the injection-server";
emit disconnected(this);
}
// ---------------------------------------------------------------
// Reads in TCP-data and handles them
// ---------------------------------------------------------------
void PrtCnvInjectionSocket::processInputData()
{
INFO_LOG(m_logger) << "Injection-server has data received";
N_CoPrtCnv::CoPrtCnvInjectionHeader injectionHeader;
qint64 bytesRead = m_Socket->peek((char*)&injectionHeader, sizeof(injectionHeader));
if (sizeof(injectionHeader) == bytesRead)
{
if (injectionHeader.isIdCorrect())
{
// check whether all bytes are available
qint64 available = m_Socket->bytesAvailable();
if (available < (injectionHeader.m_Size+sizeof(injectionHeader)))
{
INFO_LOG(m_logger) << "not enough bytes available - waiting for more data: "
<< "headersize=" << sizeof(injectionHeader) << " id1="
<< injectionHeader.m_Id1 << " id2=" << injectionHeader.m_Id2
<< " kind=" << (int)injectionHeader.m_Kind << " size="
<< injectionHeader.m_Size;
return; // try it later, when (maybe) enough bytes are available...
}
// enough bytes => read now all in
m_Socket->read((char*)&injectionHeader, sizeof(injectionHeader));
if (injectionHeader.isIdCorrect())
{
//INFO_LOG(m_logger) << "kind=" << (int)injectionHeader.m_Kind << " size=" << injectionHeader.m_Size;
// read the data
QByteArray data(injectionHeader.m_Size, Qt::Uninitialized);
m_Socket->read(data.data(), injectionHeader.m_Size);
switch (injectionHeader.m_Kind)
{
case N_CoPrtCnv::CoPrtCnvInjectionHeader::eCentral:
case N_CoPrtCnv::CoPrtCnvInjectionHeader::eRadio:
{
N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = new N_CoPrtCnv::CoPrtCnvCentralTelegram(data);
if (N_CoPrtCnv::CoPrtCnvInjectionHeader::eCentral == injectionHeader.m_Kind &&
tele->telegramId() != TrainingAreaBase::TetraIcd::TelegramId::DATENVERKEHR_TDS_SRT)
{
INFO_LOG(m_logger) << "Got an injection-central-telegram with telId=" << tele->telegramId();
m_Server->addToCentralList(tele);
}
else
{
INFO_LOG(m_logger) << "Got an injection-radio-telegram with telId=" << tele->telegramId();
m_Server->addToRadioList(tele);
}
}
break;
default:
ERROR_LOG(m_logger) << "Unknown kind (" << injectionHeader.m_Kind << ") of injection-telegram";
break;
}
}
}
else
{
ERROR_LOG(m_logger) << "Corrupt injection-telegram received => reconnect";
m_Socket->disconnectFromHost();
}
}
}
// ---------------------------------------------------------------
void PrtCnvInjectionSocket::setSocket(QTcpSocket* a_socket, PrtCnvInjectionThread* a_server)
{
m_Socket = a_socket;
m_Server = a_server;
connect(this, &PrtCnvInjectionSocket::disconnected, m_Server, &PrtCnvInjectionThread::disconnectClient, Qt::QueuedConnection);
if (m_Socket)
{
connect(m_Socket, &QTcpSocket::readyRead, this, &PrtCnvInjectionSocket::processInputData, Qt::QueuedConnection);
connect(m_Socket, &QTcpSocket::disconnected, this, &PrtCnvInjectionSocket::disconnectClient, Qt::QueuedConnection);
}
}
} // namespace PrtCnv
+79
View File
@@ -0,0 +1,79 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#pragma once
#include <QtCore/QObject>
#include "logger.h"
class QTcpSocket;
namespace PrtCnv
{
class PrtCnvInjectionThread;
/**
* Used for receiving the socket signals
*
* @author alois.wismer
* @version 1.0
* @created 06-Aug-2010 16:41:44
*/
class PrtCnvInjectionSocket : public QObject
{
Q_OBJECT
public:
PrtCnvInjectionSocket();
~PrtCnvInjectionSocket();
/**
*
* @param a_socket
* @param a_server
*/
void setSocket(QTcpSocket* a_socket, PrtCnvInjectionThread* a_server);
protected:
QTcpSocket *m_Socket;
PrtCnvInjectionThread *m_Server;
/**
* The logger object
*/
NGL::INGLLogger &m_logger;
protected slots:
/**
* Disconnects a TCP-client
*/
void disconnectClient();
/**
* Reads in TCP-data and handles them
*/
void processInputData();
public:
signals:
/**
*
* @param a_injectSocket
*/
void disconnected(PrtCnvInjectionSocket* a_injectSocket);
};
}
+182
View File
@@ -0,0 +1,182 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "PrtCnvInjectionThread.hpp"
#include "PrtCnvInjectionSocket.hpp"
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QMutex>
#include <nglsettings/nglsettings_settings.h>
namespace PrtCnv
{
// ---------------------------------------------------------------
PrtCnvInjectionThread::PrtCnvInjectionThread(PrtCnvThreadStarter* a_parent)
: PrtCnvThread(a_parent, "PrtCnvInjectionThread")
, m_Port(46666)
, m_Enabled(false)
, m_Server(nullptr)
, m_CentralListMutex(nullptr)
, m_RadioListMutex(nullptr)
{
m_Name = QStringLiteral("Injection");
}
PrtCnvInjectionThread::~PrtCnvInjectionThread()
{
delete m_CentralListMutex;
delete m_RadioListMutex;
}
// ---------------------------------------------------------------
int32_t PrtCnvInjectionThread::create(NGL::Settings *settings)
{
INFO_LOG(m_logger) << "create";
PrtCnvThread::create(settings);
m_CentralListMutex = new QMutex;
m_RadioListMutex = new QMutex;
m_Enabled = m_Config->getBoolean(getName(), QStringLiteral("Enable"), false);
// prepare a tcp-server for simulating central
if (m_Enabled)
{
m_Server = new QTcpServer(this);
if (m_Server)
connect(m_Server, &QTcpServer::newConnection, this, &PrtCnvInjectionThread::connectClient, Qt::QueuedConnection);
}
return 0;
}
// ---------------------------------------------------------------
int32_t PrtCnvInjectionThread::init()
{
INFO_LOG(m_logger) << "init";
PrtCnvThread::init();
m_Port = m_Config->getInteger(getName(), QStringLiteral("Port"), 46666);
return 0;
}
// ---------------------------------------------------------------
// Starts the thread and listen for TCP-clients
// ---------------------------------------------------------------
int32_t PrtCnvInjectionThread::start()
{
INFO_LOG(m_logger) << "Start";
PrtCnvThread::start();
// start the TCP-server
if (m_Enabled)
{
m_Server->setMaxPendingConnections(1);
m_Server->listen(QHostAddress::Any, m_Port);
INFO_LOG(m_logger) << "Injection-Server is listening on port " << m_Port;
}
return 0;
}
// ---------------------------------------------------------------
// Connects a TCP-client
// ---------------------------------------------------------------
void PrtCnvInjectionThread::connectClient()
{
INFO_LOG(m_logger) << "A client has connected to the injection-server";
PrtCnvInjectionSocket* injectSocket = new PrtCnvInjectionSocket;
m_SocketList.insert(injectSocket);
INFO_LOG(m_logger) << "Now are " << m_SocketList.size() << " injection-client(s) connected";
injectSocket->setSocket(m_Server->nextPendingConnection(), this);
}
// ---------------------------------------------------------------
// Disconnects a TCP-client
// ---------------------------------------------------------------
void PrtCnvInjectionThread::disconnectClient(PrtCnvInjectionSocket *a_injectionSocket)
{
auto it = m_SocketList.find(a_injectionSocket);
if (it != m_SocketList.end())
m_SocketList.erase(it);
INFO_LOG(m_logger) << "Now are " << m_SocketList.size() << " injection-client(s) connected";
delete a_injectionSocket;
}
// ---------------------------------------------------------------
// Returns the next central-telegram to be injected. It will
// automatically be taken out of the list.
// The caller is responsible to delete the telegram, if no
// longer used.
// Returns NULL if no central-telegram available
// ---------------------------------------------------------------
N_CoPrtCnv::CoPrtCnvCentralTelegram *PrtCnvInjectionThread::getNextCentralInjectionTelegram()
{
QMutexLocker l(m_CentralListMutex);
if (m_CentralTelegramList.empty())
return nullptr;
auto centralTelegram = m_CentralTelegramList.front();
m_CentralTelegramList.pop();
return centralTelegram;
}
// ---------------------------------------------------------------
// Returns the next radio-telegram to be injected. It will
// automatically be taken out of the list.
// The caller is responsible to delete the telegram, if no
// longer used.
// Returns NULL if no radio-telegram available
// ---------------------------------------------------------------
N_CoPrtCnv::CoPrtCnvCentralTelegram *PrtCnvInjectionThread::getNextRadioInjectionTelegram()
{
QMutexLocker l(m_RadioListMutex);
if (m_RadioTelegramList.empty())
return nullptr;
auto radioTelegram = m_RadioTelegramList.front();
m_RadioTelegramList.pop();
return radioTelegram;
}
// ---------------------------------------------------------------
void PrtCnvInjectionThread::addToCentralList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
QMutexLocker l(m_CentralListMutex);
m_CentralTelegramList.push(a_telegram);
}
// ---------------------------------------------------------------
void PrtCnvInjectionThread::addToRadioList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram)
{
QMutexLocker l(m_RadioListMutex);
m_RadioTelegramList.push(a_telegram);
}
} // namespace PrtCnv
+132
View File
@@ -0,0 +1,132 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#pragma once
#include "PrtCnvThread.hpp"
#include <queue>
#include <set>
class QTcpServer;
class QMutex;
namespace N_CoPrtCnv
{
class CoPrtCnvCentralTelegram;
}
namespace PrtCnv
{
class PrtCnvInjectionSocket;
/**
* This class represents a thread, which acts as TCP-Server. The LogView-
* application can connect to it and send radio- or central-telegrams. The
* telegrams will be stored within this class and can be taken by another thread.
* This mechanism will be used for inject telegrams to protocol-converter.
*
* @author alois.wismer
* @version 1.0
* @created 05-Aug-2010 11:17:06
*/
class PrtCnvInjectionThread : public PrtCnvThread
{
Q_OBJECT
public:
~PrtCnvInjectionThread();
/**
*
* @param a_parent
*/
explicit PrtCnvInjectionThread(PrtCnvThreadStarter* a_parent);
int32_t create(NGL::Settings *settings) override;
int32_t init() override;
/**
* Starts the thread and listen for TCP-clients
*/
int32_t start() override;
/**
* Returns the next central-telegram to be injected. It will automatically be
* taken out of the list.
* The caller is responsible to delete the telegram, if no longer used.
* Returns NULL if no central-telegram available
*/
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextCentralInjectionTelegram();
/**
* Returns the next radio-telegram to be injected. It will automatically be taken
* out of the list.
* The caller is responsible to delete the telegram, if no longer used.
* Returns NULL if no radio-telegram available
*/
N_CoPrtCnv::CoPrtCnvCentralTelegram *getNextRadioInjectionTelegram();
/**
*
* @param a_telegram
*/
void addToCentralList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
/**
*
* @param a_telegram
*/
void addToRadioList(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_telegram);
protected: /**
* The port of the injection-server (configurable with key 'Port' in group
* [Injection])
*/
uint16_t m_Port;
/**
* true if injection-server is enabled (configurable with key 'Enable' in group
* [Injection])
*/
bool m_Enabled;
QTcpServer *m_Server;
std::queue<N_CoPrtCnv::CoPrtCnvCentralTelegram *> m_CentralTelegramList;
QMutex *m_CentralListMutex;
std::queue<N_CoPrtCnv::CoPrtCnvCentralTelegram *> m_RadioTelegramList;
QMutex *m_RadioListMutex;
std::set<PrtCnvInjectionSocket*> m_SocketList;
public slots:
/**
* Connects a TCP-client
*/
void connectClient();
/**
* Disconnects a TCP-client
*
* @param a_injectionSocket
*/
void disconnectClient(PrtCnvInjectionSocket* a_injectionSocket);
};
}
+130
View File
@@ -0,0 +1,130 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
/*!
*
* \page Tetra-Interface Tetra-Interface
*
* This component is needed to communicate between the Server software and the players in
* the field via Tetra. It's an extra component due to historical reasons only.
*
*/
#include "PrtCnvApplication.hpp"
#include <NGLLogger/INGLLogger.h>
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QNetworkProxy>
#ifndef WIN32
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
// ---------------------------------------------------------------
// Become a daemon
// ---------------------------------------------------------------
static void daemonize()
{
#ifndef WIN32
pid_t pid, sid;
/* already a daemon */
if ( getppid() == 1 )
return;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* At this point we are executing as the child process */
/* Change the file mode mask */
umask(0);
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}
/* Change the current working directory. This prevents the current
directory from being locked; hence not being able to remove it. */
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}
/* Redirect standard files to /dev/null */
freopen( "/dev/null", "r", stdin);
freopen( "/dev/null", "w", stdout);
freopen( "/dev/null", "w", stderr);
#endif
}
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(ExerciseId|CustomConfigFile))
{
addOption( { QStringList() << QStringLiteral("d") << QStringLiteral("daemonize"),
QStringLiteral("Start in background") } );
addOption( { QStringList() << QStringLiteral("c") << QStringLiteral("cleandb"),
QStringLiteral("Start over with clean database") } );
}
bool daemonize() const
{
return isSet(QStringLiteral("d"));
}
bool cleanDb() const
{
return isSet(QStringLiteral("c"));
}
};
int main(int argc, char** argv)
{
QNetworkProxy::setApplicationProxy(QNetworkProxy());
NGLCoreApplication app(argc, argv);
CmdLineParser options;
if (!options.init())
return -1;
app.createLogger("PrtCnv", options.exerciseId());
if (options.daemonize())
daemonize();
PrtCnv::PrtCnvApplication prtCnvApp;
prtCnvApp.setCleanDb(options.cleanDb());
prtCnvApp.setExerciseId(options.exerciseId());
if (options.iniFileSet())
prtCnvApp.setConfigFile(options.iniFile());
STARTUP_LOG(app.baseLogger());
prtCnvApp.startApp();
int ret = app.exec();
prtCnvApp.shutdownApp();
SHUTDOWN_LOG(app.baseLogger());
return ret;
}
+98
View File
@@ -0,0 +1,98 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "PrtCnvMember.hpp"
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
namespace PrtCnv
{
// ---------------------------------------------------------------
// Constructor with id and ISSI as parameter
// ---------------------------------------------------------------
PrtCnvMember::PrtCnvMember(int a_id, int a_issi)
: m_ISSI(a_issi)
, m_ClaimCount(0)
, m_loggedInExerciseId(0)
, m_Id(a_id)
, m_Licensed(false)
{}
PrtCnvMember::~PrtCnvMember()
{
clearFragmentedList();
}
// ---------------------------------------------------------------
// Adds a radio-telegram-fragment to this member.
// ---------------------------------------------------------------
void PrtCnvMember::addFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram)
{
m_FragmentedTelegramList.insert(std::make_pair(a_radioTelegram.getFragmentCounter(),
new N_CoPrtCnv::CoPrtCnvRadioTelegram(a_radioTelegram)));
}
// ---------------------------------------------------------------
// Returns the telegram-Id of the fragmented telegram assiged
// to this member.
// If not evaluable: 0 will be returned.
// ---------------------------------------------------------------
gef_telegramm_id_et PrtCnvMember::getFragmentedTelegramId() const
{
// the fragment with the highest counter is the first fragment
for (auto rit = m_FragmentedTelegramList.rbegin(); rit != m_FragmentedTelegramList.rend(); ++rit)
{
N_CoPrtCnv::CoPrtCnvRadioTelegram *firstFragment = rit->second;
if (N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment == firstFragment->getFragmentStatus())
return firstFragment->getTelID();
}
return GEF_BESTAETIGUNG; // no telegram found => return 0 (= GEF_BESTAETIGUNG)
}
// ---------------------------------------------------------------
// Erases the radio-telegram-fragments assiged to this member.
// The fragments itself will be deleted.
// ---------------------------------------------------------------
void PrtCnvMember::clearFragmentedList()
{
for (auto it : m_FragmentedTelegramList)
delete it.second;
m_FragmentedTelegramList.clear();
}
// ---------------------------------------------------------------
// Returns the radio-telegram-fragments assembled to a only
// big radio-telegram. The fragments will not be deleted.
// Returns NULL if it was impossible to assemble the telegram.
// ---------------------------------------------------------------
QByteArray PrtCnvMember::getUnfragmentedTelegramData() const
{
return N_CoPrtCnv::CoPrtCnvRadioTelegram::getUnfragmentedData(m_FragmentedTelegramList);
}
// ---------------------------------------------------------------
// Returns true if the fragment-list is empty
// ---------------------------------------------------------------
bool PrtCnvMember::isFragmentedListEmpty() const
{
return m_FragmentedTelegramList.empty();
}
} // namespace PrtCnv
+108
View File
@@ -0,0 +1,108 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#pragma once
#include <CoPrtCnv/tetra_telegram_ids.h>
#include <map>
#include <QByteArray>
// manual class imports
namespace N_CoPrtCnv
{
class CoPrtCnvRadioTelegram;
}
namespace PrtCnv
{
/**
* Hold the data of a TNE (Client on radio-side) needed for login, licensing,
* nachforderungs-statistic and reading fragmented telegrams from TNE.
* @author wia
* @version 1.0
* @updated 16-Mrz-2010 11:20:11
*/
class PrtCnvMember final
{
public:
/**
* The status of the member
* @author wia
* @version 1.0
* @updated 16-Mrz-2010 11:20:11
*/
enum E_MemberStatus
{
e_Ok,
e_AlreadyLoggedIn,
e_MasteringLogIn,
e_NotLoggedIn
};
~PrtCnvMember();
/**
* Constructor with id and ISSI as parameter
*
* @param a_id
* @param a_issi
*/
PrtCnvMember(int a_id, int a_issi);
/**
* Adds a radio-telegram-fragment to this member.
*
* @param a_radioTelegram
*/
void addFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
/**
* Returns the telegram-Id of the fragmented telegram assiged to this member.
* If not evaluable: 0 will be returned.
*/
gef_telegramm_id_et getFragmentedTelegramId() const;
/**
* Erases the radio-telegram-fragments assiged to this member. The fragments
* itself will be deleted.
*/
void clearFragmentedList();
/**
* Returns the radio-telegram-fragments assembled to a only big radio-telegram.
* The fragments will not be deleted.
* Returns NULL if it was impossible to assemble the telegram.
*/
QByteArray getUnfragmentedTelegramData() const;
/**
* Returns true if the fragment-list is empty
*/
bool isFragmentedListEmpty() const;
public:
// The ISSI of this TNE (0..0xFFFFFF)
int m_ISSI;
// Counts the Nachforderungen asked from this TNE
int m_ClaimCount;
// the exercise where the member is currently logged in
int m_loggedInExerciseId;
// The identification (also called Ausstattungs-Nummer) of this TNE (0..10000)
int m_Id;
// true if TNE is licensed
bool m_Licensed;
private:
// Map of radio-telegram-fragments ordered by fragment-number
std::multimap<uint16_t,N_CoPrtCnv::CoPrtCnvRadioTelegram*> m_FragmentedTelegramList;
};
}
File diff suppressed because it is too large. Load diff
+583
View File
@@ -0,0 +1,583 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvRadioThread
// **************************************************************************
#pragma once
#include "PrtCnvSocketThread.hpp"
#include "PrtCnvBroadcastMgr.hpp"
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <set>
#include <QList>
#include <QMutex>
#include <TrainingAreaBase/training_area_telegrams.h>
#define MAX_IDENTITY 65535
namespace PrtCnv
{
struct BiteError
{
int errorNumber = 0;
int canDeviceNumber = 0;
int subdeviceNumber = 0;
int uniqueId = 0;
};
class PrtCnvCentralThread;
class PrtCnvMember;
class PrtCnvRadioTrafficThread;
class PrtCnvStoredRadioTelegram;
/**
* This class handles the protocol to radio. This includes:
* - handle login and licensing of TNEs
* - send and receive normal telegrams
* - send normal broadcasts and nachforderungen
* - optimize bandwith usage
*
* @author
* @version 1.0
* @created 05-Jan-2006 13:34:41
*/
class PrtCnvRadioThread : public PrtCnv::PrtCnvSocketThread
{
Q_OBJECT
public:
PrtCnvRadioThread(int exerciseId, PrtCnvThreadStarter* a_parent);
/**
* Creates all subcomponents and connects signals to slots
*/
int32_t create(NGL::Settings *settings) override;
/**
* Deallocates subcomponents.
*/
int32_t destroy() override;
/**
* Initializes the variables.
*/
int32_t init() override;
/**
* Starts the thread and connects to TCP-server
*/
int32_t start() override;
/**
* Makes empty the message-queue faster, because the messages will be consumed
* without action.
*/
void prepareCleanUp();
/**
* Immediatelly send the environment telegramm to inform the players
* about the new state
*/
void sendEnvironmentTelegramTimerNow();
void exerciseEquipmentChanged(const TrainingAreaBase::TetraIcd::ExerciseEquipmentAssignmentComplete &data);
protected:
void onCheckMemberForValidLogin(int identity, int issi);
void sendLoginAccept(int identity, int issi, int exerciseId, bool bWithMastering);
bool isIdentityInCurrentExercise(int identity) const;
int exerciseIdForIdentity(int identity) const;
void removeMasteringMember(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram, PrtCnvMember *member,
PrtCnvMember *issiMember,
int masterIdentity,
int masterIssi);
protected:
/**
* Pointer to central-thread
*/
PrtCnvCentralThread *m_CentralThread;
/**
* Pointer to radio traffic counter thread
*/
PrtCnvRadioTrafficThread *m_radioTrafficThread;
public:
Q_SIGNALS:
/**
* Signal to start sending a telegram to central
*/
void checkSendToCentral();
/**
* Signal to start sending a telegram to radio
*/
void checkSendToRadio();
/**
* Signal to indicate thread has finished the cleaning-phase
*/
void cleant();
/**
* Signal to indicate thread has stopped work
*/
void stopped();
/**
* a new equipment exercise mapping was received, check if member (all when identiy = -1)
* is still in this exercise
*/
void checkMemberForValidLogin(int identity, int issi);
// emitted when the tetra connection state changes
void tetraConnectionState(bool connected);
// an effect siumlation was (de)activated
void areaEffectSimulationActivated(int wirkfeldKennung);
void areaEffectSimulationDeactivated(int wirkfeldKennung);
// initialization of the thread is finished
void initDone();
// a player was (un)licensed
void playerLicencedChanged(int equipNo, N_CoPrtCnv::E_LicensingKind kind);
void warehouseMessageFromPtrCnv(const QByteArray &jsonData);
protected:
/**
* Counts the sended telegrams
*/
uint32_t m_SendCounter;
/**
* The ISSI of the protocol-converter
*/
uint32_t m_HostISSI;
/**
* The lowest vaild value for the counter
*/
const uint32_t START_COUNTER;
/**
* One more than the highest vaild value for the counter
*/
const uint32_t END_COUNTER;
/**
* This timer will be used for periodically send the Umgebungsmeldung to TNEs
*/
QTimer *m_EnvironmentTelegramTimer;
/**
* The ISSI used for broadcast-telegrams
*/
uint32_t m_BroadcastISSI;
/**
* The directory and prefix for the login-log-file
*/
QString m_LoginPathWithPrefix;
/**
* Übungskennung (used for login-logfile name)
*/
int m_ExerciseId;
/**
* Bit-map for evaluating whether a playerid is valid
*/
std::set<uint16_t> m_AllowedIdentities;
/**
* true => TPU will be licensed whether selftest and configtest fails or not
*/
bool m_DoLicenseDespiteSelftestOrConfigFailure;
/**
* true => identify of TPU will be checked, whether allowed
*/
bool m_DoCheckIdentity;
PrtCnvBroadcastMgr m_BroadcastManager;
/**
* True => received telegrams will be dumped to common log-file
*/
bool m_DoDumpReceivedTelegrams;
/**
* True => sent telegrams will be dumped to common log-file
*/
bool m_DoDumpSentTelegrams;
/**
* True => radio-telegrams will be sent in SDS-TL-format
*/
bool m_DoUseSDSTL;
/**
* Identities that need an active position response after licensing
*/
std::set<uint16_t> m_DoNeedActPosResp;
/**
* The ISSI used for emergency call-telegrams
*/
uint32_t m_EmergencyCallISSI;
/**
* Sends a telegram to radio. It will be fragmented if needed. It will be checked,
* whether the receiver is logged in, licensed, the content is valid and the
* telegram is not expired and an error-message will be sent to central if needed.
* It will be tested, whether the maximal broadcast-counter is reached and if it
* is, the needed action will be done.
*
* @param a_telegram
* @param a_handle
* @param a_doCheckWhetherExpired
*/
void sendTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
PrtCnvStoredRadioTelegram *a_handle,
const bool a_doCheckWhetherExpired = true);
/**
* Sends the register-indication to TETRA
*/
void doRegister();
/**
* Send a register group message to TETRA
*/
void registerGroups();
/**
* Processes the register-response-telegram
*
* @param a_regsitrationStatus
*/
void handleRegisterRsp(const uint8_t a_regsitrationStatus);
public:
/**
* @author alois.wismer
* @version 1.0
* @updated 06-Apr-2010 16:01:59
*/
enum class E_TrafficLoad
{
Unknown = -1,
NormalLoad = 0,
HighLoad,
FullLoad
};
protected:
/**
* Holds the radio-telegram for sending Umgebungsmeldung periodically
*/
N_CoPrtCnv::CoPrtCnvRadioTelegram *m_EnvironmentTelegram;
/**
* load of traffic from Protocolconverter to TETRA
*/
E_TrafficLoad m_SendLoad;
uint32_t m_SendQueueFullLoad;
uint32_t m_SendQueueHighLoad;
uint32_t m_SendTelegramsNormalLoad;
uint32_t m_SendTelegramsHighLoad;
uint32_t m_SendTelegramsFullLoad;
/**
* The number of telegrams sent within the last second
*/
uint32_t m_SentTelegrams;
/**
* The number of bytes sent within last second
*/
uint32_t m_SentBytes;
/**
* how many times tick must be called until 1 second has elapsed
*/
uint32_t m_TickCounter;
/**
* how many times tick was called (used for knowing when 1 second has elapsed,
* which we use for sent-bytes and receive-bytes statistics)
*/
uint32_t m_TicksPerSecond;
/**
* How many values we use for computing average
*/
static const int AVERAGE_COUNT = 10;
/**
* The last few number of bytes sent, used for calculating the average
*/
uint32_t m_SentBytesAverage[AVERAGE_COUNT];
/**
* index for writing into aveage-array
*/
uint8_t m_AverageIndex;
/**
* The number of telegrams sent within the last second
*/
uint32_t m_ReceivedTelegrams;
/**
* The number of bytes sent within last second
*/
uint32_t m_ReceivedBytes;
/**
* The last few number of bytes sent, used for calculating the average
*/
uint32_t m_ReceivedBytesAverage[AVERAGE_COUNT];
/**
* The groups which should be listened to
*/
std::set<uint32_t> m_ListenGroups;
/**
* @brief use scrambling yes/no (Radio/UseScrambling)
*/
bool m_bUseScrambling;
/**
* The logger object for statistics
*/
NGL::INGLLogger &m_loggerStats;
// mapping which equipNos are in which exercise
std::unordered_map<int, std::unordered_set<int>> m_exerciseEquipmentAssignments;
std::unordered_map<int, TrainingAreaBase::TetraIcd::GroupIssiInformation> m_groupIssiInformation;
mutable QMutex m_exerciseEquipmentAssignmentMutex;
private:
/**
* generates an answer to TNE from protocoll-converter for some special radio-
* telegrams (i.e. NachforderungBroadcast, BITE-Meldung)
* returns true when the telegram must be sent to central
*
* @param a_centralTelegram
* @param a_radioTelegram
*/
bool sendAnswer(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram);
/**
* After calling this function, the caller must no longer use a_data, because this
* function 'consumes' a_data and will delete a_data, when no longer needed.
*
* @param a_data A pointer to the telegram-data, including frame-header.
* Control over a_data will be lost (see description of function itself)
*/
void handleReceivedTelegram(const QByteArray &data);
/**
* Processes the data-indication-telegram
*
* @param a_data
* @param bDoDeleteData is set to true when the caller can properly delete the data
*/
void handleDataIndReq(const QByteArray &data, bool bIsMaybeScrambled);
/**
* Processes the login-telegram
*
* @param a_loginTelegram
*/
void handleLoginTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_loginTelegram);
/**
* Processes a fragment of a telegram
*
* @param a_fragmentedTelegram
*/
void handleFragmentedTelegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_fragmentedTelegram);
/**
* Sends a telegram "FehlermeldungGefechtsfeld" to central
*
* @param a_identity
* @param a_errorCause
* @param a_telegramId
*/
void sendFehlermeldungGefechtsfeldToCentral(const uint16_t a_identity,
const TrainingAreaBase::TetraIcd::CtaDataError::Reason a_errorCause,
const gef_telegramm_id_et a_telegramId = GEF_BESTAETIGUNG);
/**
* Returns the ISSI of TNE with a_TNE_Id
*
* @param a_TNE_Id
*/
uint32_t getDestinationISSI(const uint16_t a_TNE_Id) const;
/**
* Processes the BITE/BITE extended-telegram
*
* @param a_BITE_CentralTelegram
*/
bool handleBITEEXT_Telegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_BITE_RadioTelegram);
bool handleBITE_Telegram(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_BITE_RadioTelegram,
TrainingAreaBase::TetraIcd::BiteReport::LoginType anmeldeart,
uint32_t ergebnisCAN,
uint32_t ergebnisAusstattung,
uint32_t aktuelleKonfiguration,
uint32_t konfigurationsvergleich,
const QList<PrtCnv::BiteError> &fehlerListe
);
/**
* Sends a login-telegram to radio
*
* @param a_loginTelegramStatus
* @param a_identity
* @param a_ISSI
*/
void sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus, const uint16_t a_identity, const uint32_t a_ISSI);
void sendLoginResponse(const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_LoginStatus a_loginTelegramStatus,
const uint16_t a_identity, const uint32_t a_ISSI, const TrainingAreaBase::TetraIcd::GroupIssiInformation &group);
/**
* Sends a telegram "LizenzierungTNE" to radio
*
* @param a_identity AusstattungsId
* @param a_issi
* @param a_licensingKind
* @param a_errorCause
*/
void sendLizenzierungTneToRadio(const uint16_t a_identity, const uint32_t a_issi,
const N_CoPrtCnv::E_LicensingKind a_licensingKind,
const N_CoPrtCnv::E_LicensingErrorCode a_errorCause);
/**
* Writes the login of a TNE to the login-logfile
*
* @param a_radioTelegram
* @param a_prosaText
*/
void writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram &a_radioTelegram, const QString &a_prosaText);
void writeLoginLog(const N_CoPrtCnv::CoPrtCnvRadioTelegram *a_radioTelegram, const QString &a_prosaText);
void writeLoginLog(uint32_t identity, uint32_t issi, const QString &a_prosaText);
/**
* Receives the file with allowed playerId, or the lowest and hightest allowed
* playerIds
*/
void initIdentityArray();
/**
* Reads one line of the playerId-file.
* Aborts when not successful
*
* @param a_idAsText
* @param a_line
* @param a_fileName
*/
uint16_t readIdentity(const QByteArray& a_idAsText, const uint32_t a_line, const QString& a_fileName);
/**
* Checks whether the playerId is allowd.
*
* @param a_identity
*/
bool isIdentityValid(uint16_t a_identity) const;
/**
* Allows this playerId.
*
* @param a_identity
*/
void setIdentityVaild(uint16_t a_identity);
/**
* Fragment a telegram and sends the fragments to radio.
*
* @param a_telegram
*/
void sendFragmentedTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram);
/**
* sends the telegram to radio-socket
* returns the broadcastIndex (0 if it was no broadcast)
*
* @param a_telegram
*/
uint16_t sendToSocket(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram);
/**
* Processes the report-indication-telegram
*
* @param a_data
*/
void handleReportIndReq(const QByteArray &data);
void prepareWareHouseMessage(qint32 equipNr,
bool doLicence,
qint64 ergebnisCAN,
qint64 ergebnisAusstattung,
qint64 aktuelleKonfiguration,
qint64 konfigurationsvergleich,
const QList<PrtCnv::BiteError> &fehlerListe);
protected Q_SLOTS:
/**
* @brief oslot to handle call to sendEnvironmentTelegramTimerNow in correct thread
*/
void onSendEnvironmentTelegramTimerNow();
/**
* see base-class
*/
void timeoutAliveAndConnection() override;
/**
* Processes the periodical stuff, like broadcasts and nachforderungen
*/
void timeoutTick() override;
/**
* Checks whether a normal telegram must be sent and sends it.
*/
void checkSend();
/**
* see base-class
*/
void readyRead() override;
/**
* see base-class
*/
void disconnected() override;
/**
* see base-class
*/
void connected() override;
/**
* Processes the periodical sending of Umgebungsmeldung
*/
void timeoutEnvironmentTelegram();
/**
* Clean the DB.
*/
void doClean();
/**
* Restart work after cleaning Db
*/
void doStart();
/**
* Stops work for cleaning DB
*/
void doStop();
// a broadcastTelegrams request was received from EXCON (e.g. through Wls)
void onBroadcastTelegramsRequestFromCentral(int equipmentNumber, int broadcastIndex, int resetCounter);
private:
/**
* Processes the status-telegram
*
* @param data
*/
void handleStatusIndReq(const QByteArray &data);
/**
* Processes the air-interface-report-telegram
*
* @param a_data
*/
void handleAirInterfaceLoadReq(const QByteArray &data);
/**
* Computes and logs the sent- and receive-statistics
*/
void computeAndLogTrafficStatistics();
/**
* returns true, if at the moment no more telegrams should be sent
*/
bool isSendLimitReached() const;
void sendScrambledContent(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram) const;
bool unscrambleContent(N_CoPrtCnv::CoPrtCnvRadioTelegram &a_telegram) const;
friend class PrtCnvCentralThread;
};
std::ostream &operator<<(std::ostream &os, const PrtCnvRadioThread::E_TrafficLoad tl);
}
+208
View File
@@ -0,0 +1,208 @@
// **************************************************************************
//
// @RDE_HEADER@
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvRadioTrafficThread
// **************************************************************************
#include "PrtCnvRadioTrafficThread.hpp"
#include "PrtCnvDbThread.hpp"
#include "PrtCnvThreadStarter.hpp"
#include <QtCore/QTimer>
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <time.h>
namespace PrtCnv
{
PrtCnvRadioTrafficThread::PrtCnvRadioTrafficThread(PrtCnvThreadStarter* a_parent)
: PrtCnvThread(a_parent, "PrtCnvRadioTrafficThread")
, m_receiveLoad(PrtCnvRadioThread::E_TrafficLoad::Unknown)
, m_fullLoad(100)
, m_highLoad(50)
, m_hystLoad(5)
, m_calcTimeout(1000)
, m_cleanupTimeout(5000)
, m_mutex(QMutex::Recursive)
{}
void PrtCnvRadioTrafficThread::newTelegramArrived(unsigned int size)
{
time_t curTime = time(nullptr);
QMutexLocker l(&m_mutex);
DEBUG_LOG(m_logger) << "newTelegramArrived with size " << size << " at " << curTime;
m_data[curTime] += size;
m_telegrams[curTime]++;
}
int32_t PrtCnvRadioTrafficThread::start()
{
// read data from m_Config
const QString trafficControlGroup(QStringLiteral("TrafficControl"));
int highLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueHighLoad"), 60);
int fullLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueFullLoad"), 80);
int hystLoad = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveQueueHysteresis"), 5);
int avgTime = m_Config->getInteger(trafficControlGroup, QStringLiteral("ReceiveAveragingTime"), 5);
float trafficPerReceiverKB = m_Config->getFloat(trafficControlGroup, QStringLiteral("ReceiveThroughput"), 3.6f);
int cntTransceiver = m_Config->getInteger(trafficControlGroup, QStringLiteral("TransceiverCount"), 6);
// sanity checks
if (highLoad < 0 || highLoad > 100)
highLoad = 60;
if (fullLoad < highLoad || fullLoad > 100)
fullLoad = 80;
if (hystLoad < 0 || hystLoad > 10)
hystLoad = 5;
if (avgTime <= 0 || avgTime > 60)
avgTime = 5;
if (trafficPerReceiverKB < 1 || trafficPerReceiverKB > 1000)
trafficPerReceiverKB = 3.6f;
if (cntTransceiver < 1 || cntTransceiver > 100)
cntTransceiver = 6;
float maxTrafficBytes = trafficPerReceiverKB * cntTransceiver * 1024;
m_fullLoad = maxTrafficBytes * fullLoad / 100.f;
m_highLoad = maxTrafficBytes * highLoad / 100.f;
m_hystLoad = maxTrafficBytes * hystLoad / 100.f;
m_cleanupTimeout = avgTime * 1000; // ms
DEBUG_LOG(m_logger) << "Read config ReceiveQueueHighLoad: " << highLoad;
DEBUG_LOG(m_logger) << "Read config ReceiveQueueFullLoad: " << fullLoad;
DEBUG_LOG(m_logger) << "Read config ReceiveQueueHysteresis: " << hystLoad;
DEBUG_LOG(m_logger) << "Read config ReceiveAveragingTime: " << avgTime;
DEBUG_LOG(m_logger) << "Read config ReceiveThroughput: " << trafficPerReceiverKB;
DEBUG_LOG(m_logger) << "Read config TransceiverCount: " << cntTransceiver;
INFO_LOG(m_logger) << "Using to following limits: high - " << m_highLoad
<< " full - " << m_fullLoad
<< " hyst - " << m_hystLoad
<< " avg. time - " << m_cleanupTimeout / 1000;
QTimer::singleShot(m_calcTimeout, this, &PrtCnvRadioTrafficThread::onCalculateTrafficLoad);
QTimer::singleShot(m_cleanupTimeout, this, &PrtCnvRadioTrafficThread::onCleanupTimeout);
return 0;
}
void PrtCnvRadioTrafficThread::onCalculateTrafficLoad()
{
const time_t curTime = time(nullptr);
unsigned int payload = 0;
{
QMutexLocker l(&m_mutex);
// fuer aktuelle Sekunde haben wir noch nicht alle Daten
for (int i = 1; i <= m_cleanupTimeout / 1000; ++i)
payload += m_data.value(curTime - i);
}
// Durchschnitt ...
payload /= (m_cleanupTimeout / 1000);
INFO_LOG(m_logger) << "Average Payload in the last "
<< m_cleanupTimeout << " Milliseconds: "
<< payload << " Bytes/sec, last sec: "
<< m_data[curTime - 1] << " Bytes, #telegrams: " << m_telegrams[curTime - 1];
PrtCnvRadioThread::E_TrafficLoad newReceiveLoad;
if (payload >= m_fullLoad)
{
// full load
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::FullLoad;
// Hysterese nach oben
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::HighLoad)
{
// wenn cnt in Bereich m_fullLoad ... m_fullLoad + m_hystLoad
// -> noch warten
if(payload < m_fullLoad + m_hystLoad)
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
}
}
else if (payload >= m_highLoad)
{
// high load
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
// Hysterese nach oben
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::NormalLoad)
{
// wenn cnt in Bereich m_highLoad ... m_highLoad + m_hystLoad
// -> noch warten
if (payload < m_highLoad + m_hystLoad)
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::NormalLoad;
}
// Hysterese nach unten
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::FullLoad)
{
// wenn cnt in Bereich m_fullLoad ... m_fullLoad - m_hystLoad
// -> noch warten
if (payload >= m_fullLoad - m_hystLoad)
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::FullLoad;
}
}
else
{
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::NormalLoad;
// Hysterese nach unten
if (m_receiveLoad == PrtCnvRadioThread::E_TrafficLoad::HighLoad)
{
// wenn cnt in Bereich m_highLoad ... m_highLoad - m_hystLoad
// -> noch warten
if(payload >= m_highLoad - m_hystLoad)
newReceiveLoad = PrtCnvRadioThread::E_TrafficLoad::HighLoad;
}
}
if (m_receiveLoad != newReceiveLoad)
{
m_receiveLoad = newReceiveLoad;
// Achtung: Nicht mehr als 31 Sekunden erlaubt
unsigned int wartezeit_posmldg = (static_cast<unsigned int>(m_receiveLoad) * 10) + 1;
INFO_LOG(m_logger) << "New traffic load: " << m_receiveLoad;
PrtCnvDbThread *dbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
if (dbThread)
{
dbThread->setAirLoad(m_receiveLoad);
dbThread->setPositionSendTime(wartezeit_posmldg);
Q_ASSERT(QThread::currentThreadId() == m_currentThreadId);
}
else
ERROR_LOG(m_logger) << "Could not get PrtCnvDbThread!";
}
QTimer::singleShot(m_calcTimeout, this, &PrtCnvRadioTrafficThread::onCalculateTrafficLoad);
}
void PrtCnvRadioTrafficThread::onCleanupTimeout()
{
const time_t curTime = time(nullptr);
{
QMutexLocker l(&m_mutex);
QMap<time_t, unsigned int>::iterator it = m_data.begin();
while (it != m_data.end())
{
const time_t key = it.key();
// 10 als Puffer (siehe auch onCalculateTrafficLoad())
if (key < curTime - m_cleanupTimeout - 10)
{
m_data.erase(it++);
m_telegrams.remove(key);
continue;
}
break; // arbitrarily ordered
}
}
QTimer::singleShot(m_cleanupTimeout, this, &PrtCnvRadioTrafficThread::onCleanupTimeout);
}
} // namespace PrtCnv
@@ -0,0 +1,96 @@
// **************************************************************************
//
// @RDE_HEADER@
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvRadioTrafficThread
// **************************************************************************
#pragma once
#include "PrtCnvThread.hpp"
#include "PrtCnvRadioThread.hpp"
#include <QtCore/QMap>
#include <QtCore/QMutex>
namespace PrtCnv
{
/**
* This class handles the traffic counting for the
* incoming radio data
*/
class PrtCnvRadioTrafficThread final : public PrtCnvThread
{
Q_OBJECT
public:
explicit PrtCnvRadioTrafficThread(PrtCnvThreadStarter* a_parent);
/**
* A new telegram arrived.
*/
void newTelegramArrived(unsigned int size);
/**
* Start the thread handling
*/
int32_t start() override;
protected Q_SLOTS:
/**
* Timeout to recalculate traffic load
*/
void onCalculateTrafficLoad();
/**
* Timeout to throw away old data
*/
void onCleanupTimeout();
protected:
/**
* The current traffic load
*/
PrtCnvRadioThread::E_TrafficLoad m_receiveLoad;
/**
* Full load
*/
unsigned int m_fullLoad;
/**
* High load
*/
unsigned int m_highLoad;
/**
* Hysterese
*/
unsigned int m_hystLoad;
/**
* Timeout for recalculation
*/
int m_calcTimeout;
/**
* Timeout until data is thrown away
*/
int m_cleanupTimeout;
/**
* Data access mutex
*/
QMutex m_mutex;
/**
* map<time, nTelegrams>
*/
QMap<time_t, unsigned int> m_data;
QMap<time_t, unsigned int> m_telegrams;
};
} // namespace PrtCnv
+382
View File
@@ -0,0 +1,382 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvSocketThread
// **************************************************************************
#include "PrtCnvSocketThread.hpp"
#include "PrtCnvDbThread.hpp"
#include "PrtCnvTelegramLogThread.hpp"
#include "PrtCnvThreadStarter.hpp"
#include "PrtCnvInjectionThread.hpp"
#include <QtNetwork/QTcpSocket>
#include <QtCore/QTimer>
namespace PrtCnv
{
// ---------------------------------------------------------------
// Constructor with pointer to thread-starter as parameter
// ---------------------------------------------------------------
PrtCnvSocketThread::PrtCnvSocketThread(PrtCnv::PrtCnvThreadStarter* a_parent, const char *loggerName)
: PrtCnvThread(a_parent, loggerName)
, m_ConnectionTimer(nullptr)
, m_Socket(nullptr)
, m_TickTimer(nullptr)
, m_ReceiveCounter(0)
, m_ReceiveAckCounter(0)
, m_State(PrtCnvSocketThread::e_INITIAL)
, m_DbThread(nullptr)
, m_ConnectTimeout(2000)
, m_DisconnectTimeout(10000)
, m_AliveTimeout(3000)
, m_TickTimeout(900)
, m_DoCleanup(false)
, m_LogThread(nullptr)
, m_InjectionThread(nullptr)
, m_currentHostAdressIndex(0)
{}
PrtCnvSocketThread::~PrtCnvSocketThread()
{
cleanup();
}
// ---------------------------------------------------------------
// Creates all subcomponents and connects signals to slots
// ---------------------------------------------------------------
int32_t PrtCnvSocketThread::create(NGL::Settings *settings)
{
PrtCnvThread::create(settings);
createNewSocket();
m_ConnectionTimer = new QTimer(this);
connect(m_ConnectionTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutAliveAndConnection);
m_TickTimer = new QTimer(this);
connect(m_TickTimer, &QTimer::timeout, this, &PrtCnvSocketThread::timeoutTick);
m_DbThread = qobject_cast<PrtCnvDbThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Db));
m_LogThread = qobject_cast<PrtCnvTelegramLogThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Log));
m_InjectionThread = qobject_cast<PrtCnvInjectionThread*>(m_Parent->getOtherThread(PrtCnvApplication::e_Injection));
return 0;
}
// ---------------------------------------------------------------
// Creates a new socket to avoid getting old stuff from the
// former socket when reconnecting
// ---------------------------------------------------------------
void PrtCnvSocketThread::createNewSocket()
{
INFO_LOG(m_logger) << "createNewSocket m_oldSocket=" << m_Socket;
Q_ASSERT(QThread::currentThreadId() == m_currentThreadId);
if (m_Socket)
{
m_Socket->disconnectFromHost(); // Falls noch connected
m_Socket->disconnect(); // Alle Signals/Slots aufräumen
if (m_Socket->state() != QAbstractSocket::UnconnectedState)
m_Socket->waitForDisconnected();
m_Socket->deleteLater(); // und weg damit
}
m_Socket = new QTcpSocket(this);
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
connect(m_Socket, &QTcpSocket::connected, this, &PrtCnvSocketThread::connected, Qt::QueuedConnection);
connect(m_Socket, &QTcpSocket::disconnected, this, &PrtCnvSocketThread::disconnected, Qt::QueuedConnection);
connect(m_Socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &PrtCnvSocketThread::error, Qt::QueuedConnection);
connect(m_Socket, &QTcpSocket::hostFound, this, &PrtCnvSocketThread::hostFound, Qt::QueuedConnection);
connect(m_Socket, &QTcpSocket::stateChanged, this, &PrtCnvSocketThread::stateChanged);
connect(m_Socket, &QTcpSocket::readyRead, this, &PrtCnvSocketThread::readyRead, Qt::QueuedConnection);
}
// ---------------------------------------------------------------
// Deallocates subcomponents.
// ---------------------------------------------------------------
int32_t PrtCnvSocketThread::destroy()
{
PrtCnvThread::destroy();
cleanup();
return 0;
}
void PrtCnvSocketThread::cleanup()
{
delete m_Socket; m_Socket = nullptr;
delete m_ConnectionTimer; m_ConnectionTimer = nullptr;
delete m_TickTimer; m_TickTimer = nullptr;
}
// ---------------------------------------------------------------
// Initializes the variables.
// ---------------------------------------------------------------
int32_t PrtCnvSocketThread::init()
{
INFO_LOG(m_logger) << "PrtCnvSocketThread::init()";
PrtCnvThread::init();
m_State = e_INITIAL;
m_ConnectTimeout = m_Config->getInteger(getName(), QStringLiteral("ConnectTimeout"), 2000);
m_DisconnectTimeout = m_Config->getInteger(getName(), QStringLiteral("DisconnectTimeout"), 10000);
m_AliveTimeout = m_Config->getInteger(getName(), QStringLiteral("AliveTimeout"), 3000);
return 0;
}
// ---------------------------------------------------------------
// Connect as client to a TCP-server on ip-address configured
// as HostName and Port configured as Port
// ---------------------------------------------------------------
void PrtCnvSocketThread::connectSocket()
{
int sockState = m_Socket ? m_Socket->state() : -1;
INFO_LOG(m_logger) << "connectSocket m_Socket=" << m_Socket << " state=" << m_State << " socketState=" << sockState;
if (e_WORKING != m_State && e_REGISTERING != m_State)
{
if (m_Socket)
{
m_Socket->disconnectFromHost(); // Falls noch connected
if (m_Socket->state() != QAbstractSocket::UnconnectedState)
m_Socket->waitForDisconnected();
}
createNewSocket();
if(m_hostAdresses.isEmpty())
readHostAdresses();
const QString hostname = m_hostAdresses.at(m_currentHostAdressIndex).first;
const uint16_t port = m_hostAdresses.at(m_currentHostAdressIndex).second;
m_currentHostAdressIndex = ((m_currentHostAdressIndex + 1) % m_hostAdresses.size());
INFO_LOG(m_logger) << "Connecting to " << hostname << ":" << port << " m_Socket=" << m_Socket;
m_Socket->connectToHost(hostname, port);
m_State = e_CONNECTING;
startConnectionTimer();
}
else
{
WARNING_LOG(m_logger) << getName() << ": connectSocket called, but m_State is " << m_State;
}
}
// ---------------------------------------------------------------
// Disconnects from TCP-server
// ---------------------------------------------------------------
void PrtCnvSocketThread::disconnectSocket()
{
m_Socket->disconnectFromHost();
startDisconnectionTimer();
m_State = e_DISCONNECTING;
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when we have connected to
// TCP-server
// ---------------------------------------------------------------
void PrtCnvSocketThread::connected()
{
INFO_LOG(m_logger) << "connected (implement connected() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when we have disconnected
// to TCP-server
// ---------------------------------------------------------------
void PrtCnvSocketThread::disconnected()
{
INFO_LOG(m_logger) << "got signal 'disconnected' (implement disconnected() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when we have have read
// data over socket
// ---------------------------------------------------------------
void PrtCnvSocketThread::readyRead()
{
INFO_LOG(m_logger) << "got signal 'newData' (implement newData() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// this slot is used for connection, disconnection and alive
// timeouts
// ---------------------------------------------------------------
void PrtCnvSocketThread::timeoutAliveAndConnection()
{
INFO_LOG(m_logger) << "got signal 'reconnectTimeout' (implement reconnectTimeout() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when we have a socket-error
// ---------------------------------------------------------------
void PrtCnvSocketThread::error(QAbstractSocket::SocketError a_socketError)
{
switch (a_socketError)
{
case QAbstractSocket::ConnectionRefusedError:
ERROR_LOG(m_logger) << getName() << ": ConnectionRefusedError m_State=" << m_State;
break;
case QAbstractSocket::RemoteHostClosedError:
ERROR_LOG(m_logger) << getName() << ": RemoteHostClosedError";
break;
case QAbstractSocket::HostNotFoundError:
//ERROR_LOG(m_logger) << getName() << ": HostNotFoundError";
break;
default:
ERROR_LOG(m_logger) << getName() << ": got signal 'error(QAbstractSocket::SocketError=" << a_socketError << ")'";
}
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when the host was found
// ---------------------------------------------------------------
void PrtCnvSocketThread::hostFound()
{
// INFO_LOG(m_logger) << "got signal 'hostFound' (implement hostFound() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// Will be called from Qt-Framework when socket-state has
// changed
// ---------------------------------------------------------------
void PrtCnvSocketThread::stateChanged(QAbstractSocket::SocketState a_socketState)
{
Q_UNUSED(a_socketState);
// INFO_LOG(m_logger) << "got signal 'stateChanged(" << ao_socketState << ")' (implement stateChanged() in inherited class, if you want use this signal...)";
}
// ---------------------------------------------------------------
// calls storeRadioTelegram of DbThread, see there
// ---------------------------------------------------------------
void PrtCnvSocketThread::storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram,
const uint32_t a_readCounter)
{
m_DbThread->storeRadioTelegram(a_telegram, a_readCounter);
}
// ---------------------------------------------------------------
// used to check whether the other side is still alive
// ---------------------------------------------------------------
void PrtCnvSocketThread::startAliveTimer()
{
m_ConnectionTimer->setInterval(m_AliveTimeout);
m_ConnectionTimer->start();
}
// ---------------------------------------------------------------
// used to retry connection
// ---------------------------------------------------------------
void PrtCnvSocketThread::startConnectionTimer()
{
m_ConnectionTimer->setInterval(m_ConnectTimeout);
m_ConnectionTimer->start();
}
// ---------------------------------------------------------------
// used to abort connection, when normal disconnection is not
// successful
// ---------------------------------------------------------------
void PrtCnvSocketThread::startDisconnectionTimer()
{
m_ConnectionTimer->setInterval(m_DisconnectTimeout);
m_ConnectionTimer->start();
}
// ---------------------------------------------------------------
// used for periodically ticks (i.e. for acks or sending
// nachforderungen)
// ---------------------------------------------------------------
void PrtCnvSocketThread::startTickTimer()
{
// INFO_LOG(m_logger) << "start timer";
m_TickTimer->setInterval(m_TickTimeout);
m_TickTimer->start();
}
// ---------------------------------------------------------------
// The counter will be set to a_startCounter if a_maxCounter
// is reached
// returns true if receive-counter is ok
// returns false if a telegram is repeated or lost
// ---------------------------------------------------------------
bool PrtCnvSocketThread::checkAndUpdateReceiveCounter(const uint32_t a_counter, const uint32_t a_maxCounter, const uint32_t a_startCounter)
{
TRACE_LOG(m_logger) << "checkAndUpdateReceiveCounter: " << a_counter << ", " << m_ReceiveCounter;
// check for wrap
if (a_maxCounter == ++m_ReceiveCounter)
{
m_ReceiveCounter = a_startCounter;
}
// has the counter the expected value?
if (m_ReceiveCounter != a_counter)
{
//if (m_ReceiveCounter != 1)
{
if (m_ReceiveCounter > a_counter)
{
ERROR_LOG(m_logger) << "It seems, the telegram with counter " << a_counter << " was resent from "
<< getName() << "! Expected: " << m_ReceiveCounter;
}
else
{
ERROR_LOG(m_logger) << "It seems, we lost one or several telegrams from " << getName()
<< ": receive-counter of received telegram: " << a_counter << "; Expected: " << m_ReceiveCounter;
}
}
#if 0
m_ReceiveCounter--;
return false; // failure
#else
m_ReceiveCounter = a_counter;
return true;
#endif
}
return true; // all ok
}
void PrtCnvSocketThread::readHostAdresses()
{
QString host = m_Config->getString(getName(), QStringLiteral("HostName"), QStringLiteral("127.0.0.1"));
uint16_t port = m_Config->getInteger(getName(), QStringLiteral("Port"), 4934);
m_hostAdresses.push_back(qMakePair(host, port));
for (int i = 1; i < 10; ++i) {
const QString hostName = QLatin1String("HostName") + QString::number(i);
const QString portName = QLatin1String("Port") + QString::number(i);
host = m_Config->getString(getName(), hostName, QString());
port = m_Config->getInteger(getName(), portName, 0);
if (host.isEmpty() || port == 0)
continue;
m_hostAdresses.push_back(qMakePair(host, port));
}
for (const auto &p : qAsConst(m_hostAdresses)) {
INFO_LOG(m_logger) << "Read Host Adress " << p.first
<< ":" << p.second
<< " from settings";
}
}
} // namespace PrtCnv
+261
View File
@@ -0,0 +1,261 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvSocketThread
// **************************************************************************
#pragma once
#include "PrtCnvThread.hpp"
#include <QtNetwork/QAbstractSocket>
#include <memory>
class QTimer;
class QTcpSocket;
namespace N_CoPrtCnv
{
class CoPrtCnvRadioTelegram;
}
namespace PrtCnv
{
class PrtCnvThreadStarter;
class PrtCnvDbThread;
class PrtCnvTelegramLogThread;
class PrtCnvInjectionThread;
/**
* The father-class of radio- and central-thread. It implements common-parts like
* - socket-connection and -disconnection
* - telegram-counter
* - timers
* - telegram-log
* - thread-state
*
* @author wia
* @version 1.0
* @created 26-Jun-2009 07:39:05
*/
class PrtCnvSocketThread : public PrtCnvThread
{
Q_OBJECT
public:
/**
* The different states for the state-event-machine
*
* @author wia
* @version 1.0
* @created 02-Jul-2009 14:16:49
*/
enum E_State
{
e_INITIAL,
e_CONNECTING,
e_REGISTERING,
e_WORKING,
e_DISCONNECTING,
e_REINIT
};
/**
* Constructor with pointer to thread-starter as parameter
*
* @param a_parent
*/
PrtCnvSocketThread(PrtCnvThreadStarter* a_parent, const char *loggerName);
~PrtCnvSocketThread();
/**
* Creates all subcomponents and connects signals to slots
*/
int32_t create(NGL::Settings *settings) override;
/**
* Creates a new socket to avoid getting old stuff from the
* former socket when reconnecting
*/
void createNewSocket();
/**
* Initializes the variables.
*/
int32_t init() override;
/**
* Deallocates subcomponents.
*/
int32_t destroy() override;
void cleanup();
protected:
/**
* this timer is used for controlling connection and disconnection
*/
QTimer *m_ConnectionTimer;
/**
* The TCP-socket
*/
QTcpSocket *m_Socket;
/**
* Return the hostname to connect to
*/
void readHostAdresses();
protected slots:
/**
* Will be called from Qt-Framework when we have connected to TCP-server
*/
virtual void connected();
/**
* Will be called from Qt-Framework when we have have read data over socket
*/
virtual void readyRead();
/**
* this slot is used for connection, disconnection and alive timeouts
*/
virtual void timeoutAliveAndConnection();
/**
* @brief Processes the periodical stuff, like broadcasts, acks
*/
virtual void timeoutTick() {}
/**
* Will be called from Qt-Framework when we have a socket-error
*
* @param a_socketError
*/
virtual void error(QAbstractSocket::SocketError a_socketError);
/**
* Will be called from Qt-Framework when the host was found
*/
virtual void hostFound();
/**
* Will be called from Qt-Framework when socket-state has changed
*
* @param a_socketState
*/
virtual void stateChanged(QAbstractSocket::SocketState a_socketState);
protected:
/**
* used for periodically send an ack to central
*/
QTimer *m_TickTimer;
/**
* Counter of received telegrams (only for sockets)
*/
uint32_t m_ReceiveCounter;
/**
* Acknowledged telegrams counter (only for sockets)
*/
uint32_t m_ReceiveAckCounter;
/**
* The state-event-machine state
*/
PrtCnvSocketThread::E_State m_State;
/**
* Pointer to DbThread
*/
PrtCnvDbThread *m_DbThread;
/**
* The time to wait until connection has happenend [ms]. After this time
* connection will be retryed.
*/
int32_t m_ConnectTimeout;
/**
* The time to wait until disconnection has happenend [ms]. After this time has
* elapsed it will be aborted.
*/
int32_t m_DisconnectTimeout;
/**
* The period to wait until timeout of connection is detected [ms]
*/
int32_t m_AliveTimeout;
/**
* The period of m_TickTimer [ms]
*/
int32_t m_TickTimeout;
/**
* True if the database has to be cleant up (used for fast empty of signal buffers)
*/
QAtomicInt m_DoCleanup;
/**
* for independent logging of telegrams
*/
PrtCnvTelegramLogThread *m_LogThread;
PrtCnvInjectionThread *m_InjectionThread;
/**
* A vector with hostname - port pairs to connect to
*/
QVector<QPair<QString, uint16_t> > m_hostAdresses;
/**
* The current index to connect to in m_hostAdresses
*/
int m_currentHostAdressIndex;
/**
* Will be called from Qt-Framework when we have disconnected to TCP-server
*/
virtual void disconnected();
/**
* used to check whether the other side is still alive
*/
void startAliveTimer();
/**
* used to retry connection
*/
void startConnectionTimer();
/**
* used to abort connection, when normal disconnection is not successful
*/
void startDisconnectionTimer();
/**
* Connect as client to a TCP-server on ip-address configured as HostName and Port
* configured as Port
*/
void connectSocket();
/**
* Disconnects from TCP-server
*/
void disconnectSocket();
/**
* used for periodically ticks (i.e. for acks or sending nachforderungen)
*/
void startTickTimer();
/**
* The counter will be set to a_startCounter if a_maxCounter is reached
* returns true if receive-counter is ok
* returns false if a telegram is repeated or lost
*
* @param a_counter
* @param a_maxCounter
* @param a_startCounter
*/
bool checkAndUpdateReceiveCounter(const uint32_t a_counter, const uint32_t a_maxCounter, const uint32_t a_startCounter);
/**
* calls storeRadioTelegram of DbThread, see there
*
* @param a_telegram
* @param a_readCounter
*/
void storeRadioTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram, const uint32_t a_readCounter);
};
}
@@ -0,0 +1,51 @@
/**
* Helper to store/fetch a central telegram into/from database
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-22
*/
#pragma once
#include "PrtCnvStoredTelegram.hpp"
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
namespace PrtCnv
{
class PrtCnvStoredCentralTelegram : public PrtCnv::PrtCnvStoredTelegram
{
public:
PrtCnvStoredCentralTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> &a_centralTelegram,
const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_radioTelegram)
: PrtCnvStoredTelegram(a_radioTelegram)
, m_CentralTelegram(a_centralTelegram)
{}
int writeCounter() const { return m_WriteCounter; }
/**
* Assigned central-telegram.
*/
std::shared_ptr<N_CoPrtCnv::CoPrtCnvCentralTelegram> m_CentralTelegram;
/**
* The number of unsuccessful attempts to send the telegram to central
*/
uint32_t m_Attempts = 0;
/**
* The counter while sending this telegram to central
*/
int m_WriteCounter = 0;
/**
* True if the telegram must be resent.
*/
bool m_DoResend = false;
/**
* True if the telegram must be deleted.
* Use this boolean only if you want delete a telegram as an exception (i.e.
* corrupt telegram, which cannot be sent)
*/
bool m_DoDelete = false;
};
}
@@ -0,0 +1,43 @@
/**
* Helper to store/fetch a radio telegram into/from database
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-22
*/
#pragma once
#include "PrtCnvStoredTelegram.hpp"
namespace PrtCnv
{
class PrtCnvStoredRadioTelegram : public PrtCnv::PrtCnvStoredTelegram
{
public:
using PrtCnv::PrtCnvStoredTelegram::PrtCnvStoredTelegram;
/**
* The counter while reading this telegram from central
*/
int32_t m_ReadCounter = 0;
/**
* The broadcast index if it is a broadcast-telegram and it was sent first time,
* otherwise 0.
*/
uint16_t m_BroadcastIndex = 0;
/**
* The frame-counter while sending this telegram to radio.
*/
uint8_t m_WriteCounter = 0;
/**
* True if telegram must be deleted.
*/
bool m_DoDelete = false;
/**
* True if telegram was sent.
*/
bool m_IsSent = false;
};
}
+28
View File
@@ -0,0 +1,28 @@
/**
* Helper to store/fetch a telegram into/from database
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-22
*/
#pragma once
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <memory>
namespace PrtCnv
{
class PrtCnvStoredTelegram
{
public:
explicit PrtCnvStoredTelegram(const std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> &a_telegram)
: m_RadioTelegram(a_telegram)
{}
std::shared_ptr<N_CoPrtCnv::CoPrtCnvRadioTelegram> m_RadioTelegram;
int64_t m_Id = 0;
bool m_WriteCounterChanged = false;
};
}
+182
View File
@@ -0,0 +1,182 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "PrtCnvTelegramLogThread.hpp"
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvLogHeader.hpp>
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QDateTime>
#include <nglsettings/nglsettings_settings.h>
#include <QtCore/QCoreApplication>
#include <TrainingAreaBase/training_area_telegrams.h>
#ifdef Q_OS_WIN
#include <windows.h>
static int gettimeofday(struct timeval *__p, void *__t)
{
union {
unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
FILETIME ft;
} now;
constexpr long long SECONDS_SINCE_1601 = 11644473600LL;
constexpr long long USEC_IN_SEC = 1000000LL;
GetSystemTimeAsFileTime(&now.ft);
__p->tv_usec = (long)((now.ns100 / 10LL) % USEC_IN_SEC);
__p->tv_sec = (long)(((now.ns100 / 10LL) / USEC_IN_SEC) - SECONDS_SINCE_1601);
return (0);
}
#else
#include <sys/time.h>
#endif
namespace PrtCnv
{
// ---------------------------------------------------------------
PrtCnvTelegramLogThread::PrtCnvTelegramLogThread(PrtCnvThreadStarter* a_parent)
: PrtCnvThread(a_parent, "PrtCnvTelegramLogThread")
, m_DoLogCentralTelegram(true)
, m_DoLogWlsTelegram(true)
, m_DoLogRadioTelegram(true)
{
m_Name = QStringLiteral("TelegramLog");
}
// ---------------------------------------------------------------
int32_t PrtCnvTelegramLogThread::create(NGL::Settings *settings)
{
PrtCnvThread::create(settings);
connect(this, &PrtCnvTelegramLogThread::telegramLogReceived,
this, &PrtCnvTelegramLogThread::writeTelegramLog,
Qt::QueuedConnection);
return 0;
}
// ---------------------------------------------------------------
int32_t PrtCnvTelegramLogThread::init()
{
PrtCnvThread::init();
m_DoLogCentralTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogCentralTelegrams"), true);
m_DoLogWlsTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogWlsTelegrams"), true);
m_DoLogRadioTelegram = m_Config->getBoolean(QStringLiteral("Common"), QStringLiteral("LogRadioTelegrams"), true);
QString path = m_Config->getString(QStringLiteral("Common"), QStringLiteral("LogTelegramPath"), QStringLiteral("."));
if (!QDir::isAbsolutePath(path))
path = QCoreApplication::applicationDirPath() + QStringLiteral("/../") + path;
m_LogTelegramPathWithPrefix = path + QLatin1Char('/') + m_Config->getString(QStringLiteral("Common"), QStringLiteral("LogTelegramPrefix"), QStringLiteral("PrtCnv_"));
if (!m_LogTelegramPathWithPrefix.endsWith(QLatin1Char('_')))
m_LogTelegramPathWithPrefix += QLatin1Char('_');
// create the telegram-log-path when it not yet exists
QFileInfo fileInfo(m_LogTelegramPathWithPrefix);
QDir dir;
dir.mkpath(fileInfo.absolutePath());
INFO_LOG(m_logger);
return 0;
}
static inline int setSend(int protocol, bool bIsSend)
{
return (protocol | (bIsSend ? N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend : N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive));
}
// ---------------------------------------------------------------
// Writes a received or sent central-telegram into log-file
// ---------------------------------------------------------------
void PrtCnvTelegramLogThread::writeCentralTelegramLog(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_centralTelegram, bool a_isSend, int a_priority)
{
if (!m_DoLogCentralTelegram)
return;
emit telegramLogReceived(a_centralTelegram->data(),
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral, a_isSend),
a_priority);
}
// ---------------------------------------------------------------
// Writes a received or sent wls-telegram into log-file
// ---------------------------------------------------------------
void PrtCnvTelegramLogThread::writeWlsTelegramLog(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool a_isSend, int a_priority)
{
if (!m_DoLogWlsTelegram)
return;
N_CoPrtCnv::CoPrtCnvCentralTelegram central(*tele);
emit telegramLogReceived(central.data(),
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls, a_isSend),
a_priority);
}
// ---------------------------------------------------------------
// Writes a received or sent radio-telegram into log-file
// ---------------------------------------------------------------
void PrtCnvTelegramLogThread::writeRadioTelegramLog(const QByteArray &tele, bool a_isSend, int a_priority)
{
if (!m_DoLogRadioTelegram)
return;
emit telegramLogReceived(tele,
setSend(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra, a_isSend),
a_priority);
}
// ---------------------------------------------------------------
// Writes the telegram to the file
// ---------------------------------------------------------------
void PrtCnvTelegramLogThread::writeTelegramLog(const QByteArray &a_data, int a_protocolType, int a_priority)
{
// get time with microseconds
timeval tv;
gettimeofday(&tv, nullptr);
uint64_t exactTime = tv.tv_sec;
exactTime = exactTime << 32;
exactTime += tv.tv_usec;
// generate the filename of the telegram-file: "PrefixYYMMDD_HH.krlog"
QString fileName = QStringLiteral("%1%2.krlog")
.arg(m_LogTelegramPathWithPrefix, QDateTime::fromSecsSinceEpoch(tv.tv_sec).toString(QStringLiteral("yyMMdd_hh")));
// open the log-file
QFile telegramFile(fileName);
telegramFile.open(QIODevice::WriteOnly | QIODevice::Append);
// write header
N_CoPrtCnv::CoPrtCnvLogHeader lh;
lh.m_Length = a_data.size() + sizeof(N_CoPrtCnv::CoPrtCnvLogHeader);
lh.m_Version = 3;
lh.m_Priority = a_priority;
lh.m_Protocol = a_protocolType;
lh.m_Time = exactTime;
lh.m_Reserved = 0;
telegramFile.write(reinterpret_cast<const char*>(&lh), sizeof(N_CoPrtCnv::CoPrtCnvLogHeader));
// write telegram-data
telegramFile.write(a_data);
telegramFile.close();
}
} // namespace PrtCnv
+117
View File
@@ -0,0 +1,117 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#pragma once
#include "PrtCnvThread.hpp"
namespace N_CoPrtCnv
{
class CoPrtCnvCentralTelegram;
}
namespace TrainingAreaBase
{
namespace TetraIcd
{
struct TrainingAreaTelegram;
}
}
namespace PrtCnv
{
/**
* This thread is used to log telegrams in a log-file. This action is separated
* from central/radio-thead because of 2 reasons:
*
* 1. An unwanted synchronisation of radio- and central thread will be avoided
* 2. Storing to a file is very slow and would thwart the radio and central-thread
* @author alois.wismer
* @version 1.0
* @updated 03-Mai-2010 17:57:34
*/
class PrtCnvTelegramLogThread final : public PrtCnvThread
{
Q_OBJECT
public:
/**
*
* @param a_parent
*/
explicit PrtCnvTelegramLogThread(PrtCnvThreadStarter* a_parent);
int32_t create(NGL::Settings *settings) override;
int32_t init() override;
public slots:
/**
* Writes a received or sent central-telegram into log-file
*/
void writeCentralTelegramLog(N_CoPrtCnv::CoPrtCnvCentralTelegram *a_centralTelegram, bool a_isSend, int a_priority);
/**
* Writes a received or sent central-telegram into log-file
*/
void writeWlsTelegramLog(const TrainingAreaBase::TetraIcd::TrainingAreaTelegram *tele, bool a_isSend, int a_priority);
/**
* Writes a received or sent radio-telegram into log-file
*/
void writeRadioTelegramLog(const QByteArray &tele, bool a_isSend, int a_priority);
private:
signals:
/**
* This signal will be emitted when this thread receives a new telegram to log
*
* @param a_data
* @param a_protocolType
* @param a_isSend
* @param a_priority
*/
void telegramLogReceived(const QByteArray &a_data, int a_protocolType, int a_priority);
private slots:
/**
* Writes the telegram to the file
*
* @param a_data
* @param a_protocolType
* @param a_isSend
* @param a_priority
*/
void writeTelegramLog(const QByteArray &a_data, int a_protocolType, int a_priority);
protected:
/**
* The directory where we want to log the telegrams (configureable over parameter
* file)
*/
QString m_LogTelegramPathWithPrefix;
/**
* True if we want to log central-telegrams (configureable over parameter file)
*/
bool m_DoLogCentralTelegram;
/**
* True if we want to log wls-telegrams (configureable over parameter file)
*/
bool m_DoLogWlsTelegram;
/**
* True if we want to log TETRA-telegrams (configureable over parameter file)
*/
bool m_DoLogRadioTelegram;
};
}
+74
View File
@@ -0,0 +1,74 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvThread
// **************************************************************************
#include "PrtCnvThread.hpp"
#include <QThread>
namespace PrtCnv
{
bool PrtCnvThread::m_DoCleanDb = false;
// ---------------------------------------------------------------
// Constructor with thread-starter as parameter
// ---------------------------------------------------------------
PrtCnvThread::PrtCnvThread(PrtCnv::PrtCnvThreadStarter* a_parent, const char *loggerName)
: m_Parent(a_parent)
, m_currentThreadId(nullptr)
, m_logger(getLogger(loggerName))
{}
int32_t PrtCnvThread::create(NGL::Settings *settings)
{
m_currentThreadId = QThread::currentThreadId();
m_Config = settings;
INFO_LOG(m_logger) << "create " << m_Name
<< " - current thread id is " << m_currentThreadId;
return 0;
}
int32_t PrtCnvThread::init()
{
INFO_LOG(m_logger) << "init " << m_Name;
return 0;
}
int32_t PrtCnvThread::start()
{
INFO_LOG(m_logger) << "start " << m_Name;
return 0;
}
int32_t PrtCnvThread::stop()
{
return 0;
}
int32_t PrtCnvThread::deinit()
{
return 0;
}
int32_t PrtCnvThread::destroy()
{
return 0;
}
} // namespace PrtCnv
+87
View File
@@ -0,0 +1,87 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvThread
// **************************************************************************
#pragma once
#include "IAppComponent.hpp"
#include "logger.h"
#include <QObject>
namespace PrtCnv
{
class PrtCnvThreadStarter;
/**
* The father-class of all threads in protocol-converter. It implements access to
* config-system and knows whether we have a restart after crash or a normal start
* (from scratch).
* @author wia
* @version 1.0
* @updated 21-Jan-2010 08:38:23
*/
class PrtCnvThread : public QObject, public N_AppComponent::IAppComponent
{
Q_OBJECT
protected:
/**
* Used to get configuration-parameter
*/
NGL::Settings* m_Config;
/**
* Pointer to ThreadStarter
*/
PrtCnvThreadStarter *m_Parent;
/**
* @brief The Id of the current thread (for debug purposes)
*/
Qt::HANDLE m_currentThreadId;
/**
* The logger object
*/
NGL::INGLLogger &m_logger;
public:
/**
* Indicates what happens with the DB while startup. True => it will be cleant;
* False => it will be restored (after crash). This variable will be True at
* startup when you use the parameter --cleandb when starting PrtCnv.
*/
static bool m_DoCleanDb;
/**
* Constructor with thread-starter as parameter
*
* @param a_parent
*/
PrtCnvThread(PrtCnvThreadStarter* a_parent, const char *loggerName);
int32_t create(NGL::Settings *settings) override;
int32_t init() override;
int32_t start() override;
int32_t stop() override;
int32_t deinit() override;
int32_t destroy() override;
};
}
+125
View File
@@ -0,0 +1,125 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvThreadStarter
// **************************************************************************
#include "PrtCnvThreadStarter.hpp"
#include "PrtCnvCentralThread.hpp"
#include "PrtCnvRadioThread.hpp"
#include "PrtCnvDbThread.hpp"
#include "PrtCnvTelegramLogThread.hpp"
#include "PrtCnvInjectionThread.hpp"
#include "PrtCnvRadioTrafficThread.hpp"
#include "PrtCnvApplication.hpp"
namespace PrtCnv
{
// ---------------------------------------------------------------
// Constructor with id and ptr to main-app as parameters.
// ---------------------------------------------------------------
PrtCnvThreadStarter::PrtCnvThreadStarter(const PrtCnv::PrtCnvApplication::E_ThreadId a_id,
const QString &configFileName,
PrtCnv::PrtCnvApplication* a_mainApp)
: m_Thread(nullptr)
, m_Application(a_mainApp)
, m_Id(a_id)
, m_settings(new NGL::Settings(configFileName))
, m_logger(getLogger("PrtCnvThreadStarter"))
{
DEBUG_LOG(m_logger) << "Using Config file '" << configFileName << "'";
}
PrtCnvThreadStarter::~PrtCnvThreadStarter()
{
delete m_settings;
}
// ---------------------------------------------------------------
// The run()-function, which will be called by Qt-Framework,
// when starting the thread.
// ---------------------------------------------------------------
void PrtCnvThreadStarter::run()
{
INFO_LOG(m_logger) << "Entering event loop for thread " << this << ", id: " << m_Id;
// allocate the thread
switch (m_Id)
{
case PrtCnvApplication::e_Central:
m_Thread = new PrtCnvCentralThread(this);
break;
case PrtCnvApplication::e_Radio:
m_Thread = new PrtCnvRadioThread(m_Application->exerciseId(), this);
break;
case PrtCnvApplication::e_Db:
m_Thread = new PrtCnvDbThread(m_Application->databaseDriver(),
m_Application->databaseHostName(),
m_Application->databaseName(),
m_Application->databaseSchemaName(),
m_Application->databaseUserName(),
m_Application->databasePassword(),
this);
break;
case PrtCnvApplication::e_Log:
m_Thread = new PrtCnvTelegramLogThread(this);
break;
case PrtCnvApplication::e_Injection:
m_Thread = new PrtCnvInjectionThread(this);
break;
case PrtCnvApplication::e_RadioTraffic:
m_Thread = new PrtCnvRadioTrafficThread(this);
break;
case PrtCnvApplication::e_ThreadIdMax:
break;
}
if (!m_Thread)
{
ERROR_LOG(m_logger) << "Unknown threadId: " << m_Id;
return;
}
while (!m_Application)
sleep(1);
m_Application->waitForCreate(m_Id);
m_Thread->create(m_settings);
m_Application->waitForInit(m_Id);
m_Thread->init();
m_Application->waitForStart(m_Id);
m_Thread->start();
m_Application->waitForExec(m_Id);
exec();
INFO_LOG(m_logger) << "Leaving event loop for thread " << this << ", id: " << m_Id;
delete m_Thread;
m_Thread = nullptr;
}
// ---------------------------------------------------------------
// Returns the desired thread.
// ---------------------------------------------------------------
PrtCnvThread *PrtCnvThreadStarter::getOtherThread(PrtCnvApplication::E_ThreadId a_threadId) const
{
return m_Application->getThreadStarter(a_threadId)->m_Thread;
}
} // namespace PrtCnv
+87
View File
@@ -0,0 +1,87 @@
// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// Implementation of the Class PrtCnvThreadStarter
// **************************************************************************
#pragma once
#include "PrtCnvApplication.hpp"
#include <QtCore/QThread>
namespace NGL {
class Settings;
}
namespace PrtCnv
{
class PrtCnvThread;
/**
* This class is responsible for starting threads. It also impelements an access
* to other threads.
* @author wia
* @version 1.0
* @updated 21-Jan-2010 08:33:58
*/
class PrtCnvThreadStarter : public QThread
{
Q_OBJECT
protected:
/**
* Pointer to the started thread
*/
PrtCnvThread *m_Thread;
/**
* Pointer to main-application
*/
PrtCnvApplication *m_Application;
/**
* The id of the thread
*/
PrtCnvApplication::E_ThreadId m_Id;
/**
* The settings object
*/
NGL::Settings *m_settings;
/**
* The logger object
*/
NGL::INGLLogger &m_logger;
public:
/**
* Constructor with id and ptr to main-app as parameters.
*
* @param a_id
* @param a_mainApp The pointer to the main-app
*/
PrtCnvThreadStarter(const PrtCnvApplication::E_ThreadId a_id,
const QString &settingsFile,
PrtCnvApplication *a_mainApp);
~PrtCnvThreadStarter();
PrtCnvThread *getThread() const { return m_Thread; }
PrtCnvApplication *getMainApp() const { return m_Application; }
/**
* Returns the desired thread.
*
* @param a_threadId The id of the desired thread.
*/
PrtCnvThread *getOtherThread(PrtCnvApplication::E_ThreadId a_threadId) const;
protected:
/**
* The run()-function, which will be called by Qt-Framework, when starting the
* thread.
*/
void run() override;
};
}
@@ -0,0 +1,29 @@
SET(tts_SRCS
main.cpp
server.cpp
worker.cpp
log4cxx_tetratestserver.props
)
SET(tts_HDRS
server.h
worker.h
)
NGL_ADD_EXECUTABLE(AAR SV tts ${tts_SRCS} ${tts_HDRS})
NGL_TARGET_LINK_LIBRARIES(tts
CoPrtCnv
NGLLogger
LtsCommandLineParser
nglsettings
nglcoreapplication
Qt5::Network
)
NGL_INSTALL_TARGET(tts)
SET(LOG4CXX_PROPS_FILE log4cxx_tetratestserver.props)
NGL_INSTALL(FILES SV ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} DESTINATION "bin")
@@ -0,0 +1,81 @@
log4j.reset=true
log4j.rootLogger=WARN, stdout
#log4j.rootLogger=WARN, file, stdout
log4j.logger.TTWorker=INFO
log4j.logger.TTServer=INFO
# Appnd_File
###########################################################
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_TetraTestServer.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n
#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=FATAL
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
+193
View File
@@ -0,0 +1,193 @@
/*
* @RDE_HEADER@
* Main for Tetra Simulation
* $Id$
* $HeadURL$
* $Author$
*/
#include <QtCore/QCoreApplication>
#include <QtCore/QPointF>
#include <QtCore/QStringList>
#include <NGLLogger/INGLLogger.h>
#include <nglapplication/nglcoreapplication.h>
#include <nglsettings/nglsettings_settings.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include "server.h"
int main(int argc, char **argv)
{
QCoreApplication::setApplicationName(QLatin1String("TTS"));
QCoreApplication::setOrganizationName(QLatin1String("RME"));
NGLCoreApplication app(argc, argv);
app.createLogger("tts", "log4cxx_tetratestserver.props");
NGL::INGLLogger &logger = app.baseLogger();
QScopedPointer<QSettings> settings(new QSettings(app.organizationName(), app.applicationName()));
ERROR_LOG(logger) << "Settings Directory: " << settings->fileName();
LtsCommandLineParser::LtsCommandLineParser options(LtsCommandLineParser::LtsCommandLineParser::CustomConfigFile);
options.addOption({ QStringList() << QLatin1String("p") << QLatin1String("player")
, QLatin1String("number of players to be simulated.")
, QLatin1String("Default value")
, QLatin1String("5")
});
options.addOption({ QStringList() << QLatin1String("n") << QLatin1String("northing")
, QLatin1String("initial position UTM north.")
, QLatin1String("Default value")
, QLatin1String("77777")
});
options.addOption({ QStringList() << QLatin1String("e") << QLatin1String("easting")
, QLatin1String("initial position UTM east.")
, QLatin1String("Default value")
, QLatin1String("99999")
});
options.addOption({ QStringList() << QLatin1String("f") << QLatin1String("first-id")
, QLatin1String("first equipment ID to use.")
, QLatin1String("Default value")
, QLatin1String("1101")
});
options.addOption({ QStringList() << QLatin1String("r") << QLatin1String("pprow")
, QLatin1String("players per row.")
, QLatin1String("Default value")
, QLatin1String("25")
});
options.addOption({ QStringList() << QLatin1String("s") << QLatin1String("pspacing")
, QLatin1String("utm based spacing between the players.")
, QLatin1String("Default value")
, QLatin1String("100")
});
options.addOption({ QStringList() << QLatin1String("z") << QLatin1String("issi")
, QLatin1String("DestinationIssi / HostISSI")
, QLatin1String("Default value")
, QLatin1String("2")
});
options.addOption({ QStringList() << QLatin1String("m") << QLatin1String("movements")
, QLatin1String("how many movements per milliseconds to make.")
, QLatin1String("Default value")
, QLatin1String("2000")
});
options.addOption({ QStringList() << QLatin1String("d") << QLatin1String("death")
, QLatin1String("minimum times, after the movements timer is triggered, a dead player can revive.")
, QLatin1String("Default value")
, QLatin1String("100")
});
options.addOption({ QStringList() << QLatin1String("o") << QLatin1String("death-offset")
, QLatin1String("maximum times, after the movements timer is triggered, a dead player can revive.")
, QLatin1String("(0 -> identical to min)")
, QLatin1String("0")
});
options.addOption({ QStringList() << QLatin1String("t") << QLatin1String("pattern")
, QLatin1String("pattern the player has to walk [enter pattern by hand or pattern alias-name from settings file]\r\nfor more info, enter '--pattern-help'.")
, QLatin1String("Default value")
, QLatin1String("3,30,20#3,30,-20#3,-30,-20#3,-30,20")
});
options.addOption({ QStringList() << QLatin1String("demand-old-broadcast-telegrams")
, QLatin1String("player demand old broadcast telegrams starting at BCI <default value>")
, QLatin1String("Default value")
, QLatin1String("0")
});
options.addOption({ QStringList() << QLatin1String("deactivate-single-mines")
, QLatin1String("the first player deactivates single mines with ids from minimum-mine-id to maximum-mine-id")
});
options.addOption({ QStringList() << QLatin1String("minimum-mine-id")
, QLatin1String("sets the value for minimum-mine-id used within deactivate-single-mines command")
, QLatin1String("Default value")
, QLatin1String("minimum-mine-id 2048")
});
options.addOption({ QStringList() << QLatin1String("maximum-mine-id")
, QLatin1String("sets the value for maximum-mine-Id used within deactivate-single-mines command")
, QLatin1String("Default value")
, QLatin1String("maximum-mine-id 2100")
});
options.addOption({ QStringList() << QLatin1String("pattern-help")
, QLatin1String("help for the pattern interpreter.")
});
if (!options.init())
{
options.showHelp();
return -1;
}
QSettings serverSettings(app.applicationDirPath() + QLatin1String("/") + options.iniFile(), QSettings::IniFormat);
TTSArgs ttsArgs;
// args.destIssi = serverSettings.value(QLatin1String("Radio/HostISSI"), 2).toInt();
ttsArgs.destIssi = options.isSet(QLatin1String("issi")) ? options.value(QLatin1String("issi")).toInt() : 2;
ttsArgs.cntPlayer = options.isSet(QLatin1String("player")) ? options.value(QLatin1String("player")).toInt() : 0;
int northPos_arg = options.isSet(QLatin1String("northing")) ? options.value(QLatin1String("northing")).toInt() : 77777;
int eastPos_arg = options.isSet(QLatin1String("easting")) ? options.value(QLatin1String("easting")).toInt() : 90000;
ttsArgs.position = QPointF(eastPos_arg, northPos_arg);
ttsArgs.eqIdStart = options.isSet(QLatin1String("first-id")) ? options.value(QLatin1String("first-id")).toInt() : 1101;
ttsArgs.pprow = options.isSet(QLatin1String("pprow")) ? options.value(QLatin1String("pprow")).toInt() : 25;
ttsArgs.pspacing = options.isSet(QLatin1String("pspacing")) ? options.value(QLatin1String("pspacing")).toInt() : 100;
ttsArgs.movs_per_msec = options.isSet(QLatin1String("movements")) ? options.value(QLatin1String("movements")).toInt() : 2000;
ttsArgs.minimumDeathTicks = options.isSet(QLatin1String("death")) ? options.value(QLatin1String("death")).toInt() : 10;
ttsArgs.maximumDeathTicks = options.isSet(QLatin1String("death-offset")) ? options.value(QLatin1String("death-offset")).toInt() : 20;
ttsArgs.demandOldBraodcastTelegrams = options.isSet(QLatin1String("demand-old-broadcast-telegrams")) ? true : false;
if (ttsArgs.demandOldBraodcastTelegrams)
ttsArgs.demandOldBraodcastTelegramsCounter = options.value(QLatin1String("demand-old-broadcast-telegrams")).toInt();
ttsArgs.deactivationSingleMines = options.isSet(QLatin1String("deactivate-single-mines")) ? true : false;
ttsArgs.minimumMineId = options.isSet(QLatin1String("minimum-mine-id")) ? options.value(QLatin1String("minimum-mine-id")).toInt() : 2048;
ttsArgs.maximumMineId = options.isSet(QLatin1String("maximum-mine-id")) ? options.value(QLatin1String("maximum-mine-id")).toInt() : 2100;
ttsArgs.pattern = options.isSet(QLatin1String("pattern")) ? options.value(QLatin1String("pattern")) : QLatin1String("3,30,20#3,30,-20#3,-30,-20#3,-30,20");
if (options.isSet(QLatin1String("pattern-help")))
{
options.showHelp();
return -1;
}
if (ttsArgs.maximumDeathTicks == 0)
ttsArgs.maximumDeathTicks = ttsArgs.minimumDeathTicks;
WARNING_LOG(logger) << "Simulating " << ttsArgs.cntPlayer << " players";
WARNING_LOG(logger) << " ... DestinationIssi/HostIssi: " << ttsArgs.destIssi;
WARNING_LOG(logger) << " ... on Position n/e: " << ttsArgs.position.y() << "/" << ttsArgs.position.x();
WARNING_LOG(logger) << " ... first equipmentID is: " << ttsArgs.eqIdStart;
WARNING_LOG(logger) << " ... player is reborn randomly after minimum " << ttsArgs.minimumDeathTicks
<< " ticks and maximum " << ttsArgs.maximumDeathTicks << " ticks";
WARNING_LOG(logger) << " ... pattern = " << ttsArgs.pattern;
WARNING_LOG(logger) << " ... max. " << ttsArgs.pprow << " players per row";
WARNING_LOG(logger) << " ... spacing of '" << ttsArgs.pspacing << " UTM east' between players";
WARNING_LOG(logger) << " ... one player will be moved after " << ttsArgs.movs_per_msec << " msecs";
TTServer *s = new TTServer(ttsArgs);
if (!s->listen(QHostAddress::Any, serverSettings.value(QLatin1String("Radio/Port"), 4200).toInt()))
{
ERROR_LOG(logger) << "Error listening on localhost:" << serverSettings.value(QLatin1String("Radio/Port"), 52010).toInt() << ": "
<< s->errorString();
delete s;
return -1;
}
DEBUG_LOG(logger) << "Starting Server";
int ret = app.exec();
delete s;
return ret;
}
+43
View File
@@ -0,0 +1,43 @@
/*
* @RDE_HEADER@
* Listen Server for Tetra Simulation
* $Id$
* $HeadURL$
* $Author$
*/
#include "server.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QtNetwork/QTcpSocket>
#include "worker.h"
TTServer::TTServer(const TTSArgs &ttsArgs)
: m_ttsArgs(ttsArgs)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTServer"))
{
connect(this, &TTServer::newConnection,
this, &TTServer::onNewConnection);
}
void TTServer::onNewConnection()
{
QTcpSocket *sock = nextPendingConnection();
if (!sock)
return;
NOTICE_LOG(m_logger) << "New incoming connection from "
<< sock->peerName() << ":"
<< sock->peerPort();
TTWorker *ttw = new TTWorker(sock, m_ttsArgs);
if (!ttw->init())
exit(5);
connect(sock, &QTcpSocket::disconnected,
this, &TTServer::onDisconnect);
}
void TTServer::onDisconnect()
{
NOTICE_LOG(m_logger) << "Disconnected";
}
+59
View File
@@ -0,0 +1,59 @@
/*
* @RDE_HEADER@
* Listen Server for Tetra Simulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QtCore/QPointF>
#include <QtNetwork/QTcpServer>
#include <QtCore/QSettings>
#include <nglsettings/nglsettings_settings.h>
#include <NGLLogger/INGLLogger.h>
struct runningPattern
{
int steps = 0;
int ethering = 0;
int northering = 0;
};
struct TTSArgs
{
QPointF position;
QString pattern;
QSettings *settings = nullptr;
int destIssi = 0;
unsigned int cntPlayer = 0;
unsigned int eqIdStart = 0;
unsigned int minimumDeathTicks = 0;
unsigned int maximumDeathTicks = 0;
unsigned int pprow = 0;
unsigned int pspacing = 0;
unsigned int movs_per_msec = ~0U;
unsigned int minimumMineId = 0;
unsigned int maximumMineId = 0;
bool demandOldBraodcastTelegrams = false;
int demandOldBraodcastTelegramsCounter = 0;
bool deactivationSingleMines = false;
};
class TTServer : public QTcpServer
{
Q_OBJECT
public:
explicit TTServer(const TTSArgs &m_ttsArgs);
protected Q_SLOTS:
void onNewConnection();
void onDisconnect();
protected:
TTSArgs m_ttsArgs;
NGL::INGLLogger &m_logger;
};
+111
View File
@@ -0,0 +1,111 @@
TTS Tetra Test Server:
Das Testprogramm tts wird als Ersatz des Tetra-Server gestartet. Es dient dazu, Lasttests im Gesamtsystem zu starten.
-----------------------------------------------
Teilnehmer anlegen:
In der Opperator-App in die Übung einloggen
Vorplanung->Teilnehmer Truppe->Bearbeite Teilnehmer
Einen Zug selektieren und Erstellen
Eine Anfangs-Ausstattungsnummer wählen und mit Enter jede Menge Teilnehmer mit aufsteigender Ausstattungsnummererzeugen.
-----------------------------------------------
Verschlüsselung abschalten:
auf dem Server EXCTDS01 die Datei /etc/lts-cc.conf öffnen
[TrainingAreaBase]
UseTetraScrambling=false
-----------------------------------------------
Tabelle pdm.pdm_radio_group aendern:
Dazu vorher den Namen der Gruppe herausfinden:
Als Administrator in der Operator App Vorplanung->Übungen->Übungen den Namen der Tetra Gruppe von der aktuellen Übung nehmen.
Die Einstellungen, die geändert werden vorher aufschreiben, um wieder zum Betrieb mit dem Gefechtsfeld zurückzukommen.
In der Tabelle pdm.pdm_radio_group den Namen "radiohost" auf localhost und issi auf 2 einstellen
In der Tabelle pdm.pdm_radio_group den radio_port auf 4200 einstellen
-----------------------------------------------
Servernamen in PrtCnv.dat ändern
Auf dem Server (EXCTDS01) ala user ctc_sv im Verzeichnis bin die Datei PrtCnv.dat öffnen
[Central]
HostName=localhost
-----------------------------------------------
Serversoftware neu starten
-----------------------------------------------
tts starten:
Auf dem Server (EXCTDS01) ala user ctc_sv im Verzeichnis bin
./tts -p 800 -f 1200
Bedeutung:
-p 800 Positionsmeldungen für 800 Teilnehmer erzeugen
-f 1200 Anfangsausstattungsnummer vom Teilnehmer anlegen hier eingeben.
-----------------------------------------------
-----------------------------------------------
Relevante Tabellen sind:
1) tab_prtcnv_member
2) ex_nn.prtcnv_to_radio
Hier werden die Telegramme (Rtg Gefechtsfeld) zwischengespeichert, bis sie quittiert sind.
Bei Problemen hat es sich als sinnvoll erwiesen, diese Tabellen zu löschen
-----------------------------------------------
-----------------------------------------------
-----------------------------------------------
Optionen mit tts --help anzeigen lassen:
./tts --help
Could not find log4cxx configuration file tetra_log4cxx.props
Using default logging to stdout
2020-09-03 13:19:15.300 ERROR tts |#| Settings Directory: /shd/CTC/TOOLS/.config/RME/TTS.conf (TDS-source/TetraIF/PrtCnv/TetraTestServer/main.cpp:30)
Usage: ./tts [options]
Options:
-i, --ini <filename> Default: /etc/lts-cc.conf
-h, --help Displays this help.
-v, --version Displays version
information.
-p, --player <Default value> number of players to be
simulated.
-n, --northing <Default value> initial position UTM north.
-e, --easting <Default value> initial position UTM east.
-f, --first-id <Default value> first equipment ID to use.
-r, --pprow <Default value> players per row.
-s, --pspacing <Default value> utm based spacing between
the players.
-m, --movements <Default value> how many movements per
milliseconds to make.
-d, --death <Default value> minimum times, after the
movements timer is
triggered, a dead player can
revive.
-o, --death-offset <(0 -> identical to min)> maximum times, after the
movements timer is
triggered, a dead player can
revive.
-t, --pattern <Default value> pattern the player has to
walk [enter pattern by hand
or pattern alias-name from
settings file]
for more info, enter
'--pattern-help'.
--demand-old-broadcast-telegrams <Default value> player demand old broadcast
telegrams starting at BCI
<default value>
--deactivate-single-mines the first player
deactivates single mines
with ids from
minimum-mine-id to
maximum-mine-id
--minimum-mine-id <Default value> sets the value for
minimum-mine-id used within
deactivate-single-mines
command
--maximum-mine-id <Default value> sets the value for
maximum-mine-Id used within
deactivate-single-mines
command
--pattern-help help for the pattern
interpreter.
+654
View File
@@ -0,0 +1,654 @@
/*
* @RDE_HEADER@
* Worker for Tetra Simulation
* $Id$
* $HeadURL$
* $Author$
*/
#include "worker.h"
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <time.h>
#include <random>
#include <iostream>
#include <QStringList>
#include <QJsonDocument>
TTWorker::TTWorker(QTcpSocket *socket, const TTSArgs &ttsArgs)
: m_socket(socket)
, m_timer(new QTimer(this))
, m_counter(0)
, m_toogle(true)
, m_ttsArgs(ttsArgs)
, m_patternList(interpretPattern(ttsArgs.pattern))
, m_generalPosition(ttsArgs.position)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("TTWorker"))
{
qsrand(time(nullptr)); // randomize for later use in class "Player"
}
bool TTWorker::init()
{
connect(m_socket, &QTcpSocket::disconnected,
this, &TTWorker::deleteLater);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
this, &TTWorker::onSocketError);
connect(m_socket, &QTcpSocket::readyRead,
this, &TTWorker::onReadyRead);
connect(m_timer, &QTimer::timeout,
this, &TTWorker::onSendPlayerPositions);
if (m_ttsArgs.movs_per_msec < 200)
{
ERROR_LOG(m_logger) << "Invalid value for 'movements per milliseconds'! Please choose a number greater than 200 msecs or equal. Program stopped.";
return false;
}
return true;
}
void TTWorker::startExecution()
{
//mTimer->start(mTtsargs.movs_per_msec);
m_timer->start(m_ttsArgs.movs_per_msec);
NOTICE_LOG(m_logger) << "Start Exe login all Player";
// create all players & add them to the players-list, send logins
for (unsigned int i = 0; i < m_ttsArgs.cntPlayer; i++)
{
int eqId = m_ttsArgs.eqIdStart + i;
int issi = m_ttsArgs.eqIdStart + i + 2000; // = Ausstattungsnummer + 2000
QPointF posUTM((m_generalPosition.x() + (i / m_ttsArgs.pprow) * m_ttsArgs.pspacing),
(m_generalPosition.y() + (i % m_ttsArgs.pprow) * m_ttsArgs.pspacing)); // initial position for a player
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);
// QTimer::singleShot(25000, this , &TTWorker::onSendNlosSimulatorDirection);
// QTimer::singleShot(120000, this , &TTWorker::onSendNlosFireResult);
}
QVector<runningPattern> TTWorker::interpretPattern(QString pattern) const
{
if (pattern.isEmpty())
{
ERROR_LOG(m_logger) << "INVALID PATTERN - pattern-size < 1";
exit(8);
}
if (pattern.at(0) == QLatin1Char('%')) // this pattern is a predefined pattern from the settings file
{
pattern.remove(0, 1); // delete '%'-character as it disturbes the interpreter
QString patternAlias = pattern;
pattern = m_ttsArgs.settings->value(QLatin1String("pattern/") + patternAlias).toString();
if (pattern.isEmpty()) // there is no pattern with this alias!
{
ERROR_LOG(m_logger) << "INVALID PATTERN - alias '" << patternAlias << "' is not available in settings file! Program halted.";
exit(9);
}
}
// now the pattern (if it was only an alias) is refered, continue..:
if (pattern.at(pattern.count() - 1) == QLatin1Char('#')) // delete the character ';' if there is one at the end of string - because of strtok/split
pattern.remove(pattern.count() - 1, 1);
QVector<runningPattern> newPatternList; // list we give back to replace the patternList in this class
runningPattern rp; // pattern we use (and reuse) to add a new runningPattern instance to the patternList
QStringList strings = pattern.split(QLatin1Char('#')); // list of pattern (f.g. "1,2,3", "1,2,3");
QStringList stringy; // list of pattern-ingredients ;) (f.g. "1", "2", "3")
int i; // counter, global because of error message at the end of this function (see variable "strings")
for (i = 0; i < strings.count(); i++)
{
stringy = strings.at(i).split(QLatin1Char(',')); // split one pattern to the "ingredients"
if (stringy.count() != 3) // pattern consists of 3 numbers, if there are more or less, then the pattern is wrong
break;
bool ok = true;
rp.steps = stringy.at(0).toInt(&ok); if (!ok) break; // is not a number, so throw an error and exit this application
rp.ethering = stringy.at(1).toInt(&ok); if (!ok) break; // ^
rp.northering = stringy.at(2).toInt(&ok); if (!ok) break; // ^
newPatternList.append(rp); // add a new instance to the list (i hope this will create an instance and does not use a reference !!1)
if (i == strings.count() - 1) // are we at the end of the strings.count ? if yes, then return the list - important to NOT throw a error message
return newPatternList; // IMPORTANT! Or an wrong error message will be thrown
}
ERROR_LOG(m_logger) << "INVALID PATTERN: '" << strings.at(i) << "'. Cannot start without correct pattern!";
exit(5); // we can't work - exit!
return QVector<runningPattern>(); // for security & compiler issues..
}
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.";
}
// prepare data-bucket for entire 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);
// read the telegram with header
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();
// evaluate PDU-Type
switch (data.at(4))
{
// response to register indication
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd:
handleRegisterInd();
startExecution();
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
handleDataIndReq(data); // tele->getDestISSI() == ISSI an des angesprochenen Teilnehmers
// data is deleted inside handleDataIndReq()
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);
//INFO_LOG(mLogger) << "BITE message sent for Player (" << eqId << ") with ISSI: " << 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); // gps_zeit
tel.setPayload1Byte(0); // ruecksetzzaehler
tel.setPayload2Byte(m_ttsArgs.demandOldBraodcastTelegramsCounter); // broadcast index
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);
}
/*!
\brief Send sample NlosFireResult Telegram every three minutes (FOR TESTING NLOS)
\author Niklas Schmidt
*/
// void TTWorker::onSendNlosFireResult()
// {
// if (m_playerList.isEmpty())
// {
// ERROR_LOG(m_logger) << "No player for onSendNlosFireResult...";
// return;
// }
// Player *p = m_playerList.at(0);
// TrainingAreaBase::TetraIcd::NlosDirectionOrShootingResult fireResult;
// fireResult.header.equipmentNumber = p->eqId;
// fireResult.header.timeStamp = time(nullptr) * 1000;
// fireResult.messageId = 0xE401;
// fireResult.version = 4;
// fireResult.shootingRes = TrainingAreaBase::TetraIcd::NlosDirectionOrShootingResult::ShootingResult::Hit;
// fireResult.targetEquipmentNumber = 1205;
// INFO_LOG(m_logger) << "Send NlosFireResult";
// sendTelegram(fireResult, p->issi);
// QTimer::singleShot(120000, this, &TTWorker::onSendNlosFireResult);
// }
/*!
\brief Send sample NlosSimulatorDirection Telegram(FOR TESTING NLOS)
\author Niklas Schmidt
*/
// void TTWorker::onSendNlosSimulatorDirection()
// {
// if (m_playerList.isEmpty())
// {
// ERROR_LOG(m_logger) << "No player for onSendNlosFireResult...";
// return;
// }
// Player *p = m_playerList.at(0);
// TrainingAreaBase::TetraIcd::NlosDirectionOrShootingResult simulatorDirection;
// simulatorDirection.header.equipmentNumber = 7150;
// simulatorDirection.header.timeStamp = time(nullptr) * 1000;
// simulatorDirection.messageId = 0xE400;
// std::random_device rd; //Will be used to obtain a seed for the random number engine
// std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
// std::uniform_int_distribution<long long> distrib(1, 34999);
// simulatorDirection.version = 4;
// simulatorDirection.directionInDegree = 0/*distrib(gen)*/;
// simulatorDirection.attachmentInDegree = 0;
// INFO_LOG(m_logger) << "Send NlosSimulatorDirection";
// sendTelegram(simulatorDirection, p->issi);
// QTimer::singleShot(100000, this, &TTWorker::onSendNlosSimulatorDirection);
// }
void TTWorker::sendTaktischerStatus(uint32_t eqId, uint32_t issi, TrainingAreaBase::TetraIcd::VulnerabilityState vbg)
{
TrainingAreaBase::TetraIcd::StateChange tele;
tele.header.equipmentNumber = eqId;
tele.header.timeStamp = time(nullptr) * 1000;
tele.tasVitality = 90;
tele.tasVulnerabilityKindId = 459008;
tele.vulnerabilityBits = 0;
for (Player *p : qAsConst(m_playerList))
{
if (p->eqId == eqId)
{
p->vbg = vbg;
tele.vulnerabilityState = vbg;
DEBUG_LOG(m_logger) << "Player (" << p->eqId << ") - VBG: " << int(vbg);
break;
}
}
sendTelegram(tele, issi);
}
void TTWorker::sendSanitationReport(uint32_t eqId, uint32_t issi)
{
m_toogle = false;
return; //NOTE: Will never be executed
TrainingAreaBase::TetraIcd::SanitaryMaterialStockReport tele;
tele.header.equipmentNumber = eqId;
tele.header.timeStamp = time(nullptr) * 1000;
int matCount = 14;
tele.sanitaryMaterialCount = matCount;
std::vector<int> mat;
mat.reserve(matCount);
for (int i = 1; i <= matCount; i++)
mat.emplace_back(i);
tele.sanitaryMaterialStock = mat;
sendTelegram(tele, issi);
}
// void TTWorker::sendSanitation(uint32_t eqId, uint32_t issi)
// {
// m_toogle = true;
// return; //NOTE: Will never be executed
// TrainingAreaBase::TetraIcd::Sanitation tele;
// tele.header.equipmentNumber = eqId;
// tele.header.timeStamp = time(nullptr) * 1000;
// tele.proceduresTaken = 1117;
// tele.uniqeId = 2777777;
// tele.originatorEquipmentNumber = 1001;
// tele.qualification = TrainingAreaBase::TetraIcd::Qualification::FAA;
// tele.sanitationResult = TrainingAreaBase::TetraIcd::SanitationResult::Needed;
// 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); // rebirth for old Players, who are still dead
}
void TTWorker::handleDataIndReq(const QByteArray &data)
{
QScopedPointer<N_CoPrtCnv::CoPrtCnvRadioTelegram> radioTelegram(new N_CoPrtCnv::CoPrtCnvRadioTelegram(data));
int telID = radioTelegram->getTelID(); // get the Telegram ID, to switch through it
switch (telID)
{
#if 0
TODO
case GEF_AUSLOESUNG_DUS_TREFFER: // player hit! send a taktischer status that we are dead
{
/* TODO: only receive damage, do not die instantly - rt_data unused */
DEBUG_LOG(mLogger) << "handleAusloeserDUSTreffer: eqId=" << radioTelegram->getIdentity() <<
" issi=" << radioTelegram->getDestISSI();
XDR_Wrapper_it *wrapper = nullptr;
if (!N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(wrapper, radioTelegram.data()))
if (!wrapper)
return;
const ausloesung_dus_treffer_rt &rt_data = static_cast<XDR_AUSLOESUNG_DUS_TREFFER_Wrapper *>(wrapper)->get_rt();
if (rt_data.bek_wirkung > 90)
sendTaktischerStatus(radioTelegram->getIdentity(), radioTelegram->getDestISSI(), TOTALAUSFALL); // kill Player
else
sendTaktischerStatus(radioTelegram->getIdentity(), radioTelegram->getDestISSI(), ANNAEHENDER_TREFFER);
delete wrapper;
return;
}
#endif
case GEF_TAKTISCHER_STATUS: // just resend this one to answer the station's question
{
N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(radioTelegram.data());
TrainingAreaBase::TetraIcd::StateChange sc = TrainingAreaBase::TetraIcd::StateChange::fromJson(tele->jsonData());
delete tele;
DEBUG_LOG(m_logger) << " Try to Resetnd State " << sc.header.equipmentNumber << " we got state: " << static_cast<int>(sc.vulnerabilityState) << " and this bits here: " << sc.vulnerabilityBits;
sc.header.timeStamp = time(nullptr) * 1000;
uint32_t issi = 0;
for (int i = 0; i < m_playerList.count(); i++) // get issi from player's eqId
{
if (radioTelegram->getIdentity() == m_playerList.at(i)->eqId)
{
issi = m_playerList.at(i)->issi;
break;
}
}
ERROR_LOG(m_logger) << " Try to Resetnd S2tate " << sc.header.equipmentNumber << " we got state: " << static_cast<int>(sc.vulnerabilityState) << " and this bits here: " << sc.vulnerabilityBits;
sendTelegram(sc, issi); // resend
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++) // get issi from player's eqId
{
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); // resend
return;
}
case GEF_TREFFER_AUSLOESUNG: // we have got a new status (f.g. wounded lying) and send back our new status
case GEF_INIT: // ?? .. or just act like this is a treffer ausloesung ....... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
TrainingAreaBase::TetraIcd::StateChange tele;
uint32_t issi = 0;
for (int i = 0; i < m_playerList.count(); i++) // get issi from player's eqId
{
if (radioTelegram->getIdentity() == m_playerList.at(i)->eqId)
{
issi = m_playerList.at(i)->issi;
break;
}
}
TrainingAreaBase::TetraIcd::VulnerabilityState state = (telID == GEF_TREFFER_AUSLOESUNG)
? static_cast<TrainingAreaBase::TetraIcd::VulnerabilityState>(radioTelegram->getPayloadDataUInt8(7))
: TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady; //get vbg from radio
tele.originatorType = TrainingAreaBase::TetraIcd::OriginatorType::Initialisation; //set origin
tele.header.equipmentNumber = radioTelegram->getIdentity(); //set header
tele.header.timeStamp = time(nullptr) * 1000;
uint32_t vulcomplete = radioTelegram->getPayloadDataUInt32(8, 2);
uint32_t vitality = vulcomplete &= 0xF000;
vitality >>= 12;
tele.tasVulnerabilityKindId = vulcomplete &= 0x0FFF;
//including vitality
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); // send new status
return;
}
// case GEF_NLOS_DATENVERKEHR_TDS_GEF:
// {
// ERROR_LOG(m_logger) << "NlosTargetData empfangen";
// N_CoPrtCnv::CoPrtCnvCentralTelegram *tele = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(radioTelegram.data());
// TrainingAreaBase::TetraIcd::NlosTargetData sc = TrainingAreaBase::TetraIcd::NlosTargetData::fromJson(tele->jsonData());
// delete tele;
// QJsonDocument doc(sc.toJson());
// ERROR_LOG(m_logger) << "Receive NlosTargetData Size: " << radioTelegram.data()->getSize() << "\n Structure: " << doc.toJson(QJsonDocument::Indented);
// return;
// }
}
// miscellaneous messages..
if (radioTelegram->isLoginTelegram())
{
// fixme: login accepted ?
DEBUG_LOG(m_logger) << "player cont: " << m_playerList.count();
for (int i = 0; i < m_playerList.count(); i++) // set player isLogin true
{
Player *p = m_playerList.at(i);
if (radioTelegram->getDestISSI() == p->issi) // is this the player we look for?
{
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); // send BITE message to log us in, though
return;
}
}
// player not found
ERROR_LOG(m_logger) << "Invalid player was logged in. issi: '" << radioTelegram->getDestISSI() << "'";
return;
}
// none of our swicthes & conditions wanted this received message! we have got a unused or invalid message.. throw it away and give, at least, a DEBUG message
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);
//mTimer->start(1000);
}
void TTWorker::movePlayer(Player *p) // move the player like the pattern says
{
// use all variables from the Player POINTER (do not use new instances)
if (p->patternInList > m_patternList.count() - 1) // if patternInList is HIGHER than the list-count is, then jump back to position 0 to start again
p->patternInList = 0; // set to list-start
else
{
if (p->stepInPattern > m_patternList.at(p->patternInList).steps) // if we have less steps than the stepInPattern says, then we have obsolved all wnted steps, so we can jump to the new pattern in the List
{
p->stepInPattern = 1; // reset steps for the new pattern
if (p->patternInList == m_patternList.count() - 1) // if there is NOT a next pattern, then reset pattern counter to 0
p->patternInList = 0;
else
p->patternInList++; // jump to next pattern IF there is a next pattern
}
}
p->posUTM.setX(p->posUTM.x() +
m_patternList.at(p->patternInList).northering); // increase northering as the pattern says
p->posUTM.setY(p->posUTM.y() +
m_patternList.at(p->patternInList).ethering); // ^^^^^^^^ ethering ^^^^^^^^^^^^^^^^^^^
p->stepInPattern++; // increase the step counter before we leave, so the player can be moved the next time!!
}
void TTWorker::onSendPlayerPositions()
{
// return;
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) // do not send position data if one player is not logged in
{
INFO_LOG(m_logger) << "Player with eqId=" << p->eqId << " and issi=" << p->issi << " is not logged in.";
break;
}
playerCount++;
// respawn :
if (p->vbg != TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady && p->isEndOfTicks()) // only count to zero (isEndOfTicks = are the ticks running out ?) for respawn, if Player is NOT alive!
sendTaktischerStatus(p->eqId, p->issi, TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady); // only give rebirth, do not kill //(unsigned short)!p->isAlive); // kill Player if he is alive, give rebirth to Player if he is dead
tele.header.equipmentNumber = p->eqId;
if (m_toogle)
sendSanitationReport(p->eqId, p->issi);
// else
// sendSanitation(p->eqId, p->issi);
movePlayer(p); // move players
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();
}
+133
View File
@@ -0,0 +1,133 @@
/*
* @RDE_HEADER@
* Worker for Tetra Simulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QtCore/QByteArray>
#include <QtCore/QMap>
#include <QtCore/QObject>
#include <QtCore/QPointF>
#include <QtCore/QMap>
#include <NGLLogger/INGLLogger.h>
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <TrainingAreaBase/training_area_telegrams.h>
#include "server.h"
class QTcpSocket;
class QTimer;
class Player
{
private:
void randomizeDeathTicks()
{
deathTicks = ((qrand() % (maximumDeathTicks - minimumDeathTicks + 1)) + minimumDeathTicks);
}
public:
// PU simulation vars:
uint32_t eqId;
uint32_t issi;
QPointF posUTM;
// PU login-state vars:
bool isLogin;
bool isValidBITE;
TrainingAreaBase::TetraIcd::VulnerabilityState vbg;
// pattern vars:
int patternInList; // >-| which pattern to take from the pattern list
int stepInPattern; // <-| which step we have in the current pattern (see "patternInList" above)
// x seconds, for a Player to respawn [maximum / mininum]
uint32_t deathTicks;
uint32_t minimumDeathTicks;
uint32_t maximumDeathTicks;
Player(int eqId_, int issi_, uint32_t minimumDeathTicks_, uint32_t maximumDeathTicks_, const QPointF &posUTM_)
: eqId(eqId_)
, issi(issi_)
, posUTM(posUTM_)
, isLogin(false)
, isValidBITE(false)
, vbg(TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady)
, patternInList(0)
, stepInPattern(1)
, deathTicks(0)
, minimumDeathTicks(minimumDeathTicks_)
, maximumDeathTicks(maximumDeathTicks_)
{
randomizeDeathTicks();
}
bool isEndOfTicks() // gives TRUE if deathTicks is lower than 1 and randomizes deathTicks again, so this function can be reused everytime
{
deathTicks--;
if (deathTicks < 1)
{
randomizeDeathTicks(); // randomize again
return true;
}
return false;
}
};
class TTWorker : public QObject
{
Q_OBJECT
public:
explicit TTWorker(QTcpSocket *socket, const TTSArgs &ttsArgs);
bool init();
private Q_SLOTS:
void onReadyRead();
void onSendPlayerPositions(); // -> nur senden, wenn isLogin & isValidBite für tln ok
// wenn LoginOk von PrtCnv -> onSendBite(eqId)
void onSocketError();
void onSendNachforderungBroadcastTelegramm();
void onSendDeaktivierungEinzelminen();
protected:
void startExecution();
QVector<runningPattern> interpretPattern(QString pattern) const;
void movePlayer(Player *p);
void handleReceivedTelegram(const QByteArray &data);
void handleRegisterInd();
void handleDataIndReq(const QByteArray &data);
void sendTelegram(N_CoPrtCnv::CoPrtCnvRadioTelegram *radioTelegram, uint32_t issi);
template <typename T>
void sendTelegram(const T &tele, uint32_t issi)
{
N_CoPrtCnv::CoPrtCnvCentralTelegram ct(tele);
sendTelegram(N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToRadio(&ct), issi);
}
void sendLogin(uint32_t eqId, uint32_t issi);
void sendBITE(uint32_t eqId, uint32_t issi);
void sendTaktischerStatus(uint32_t eqId, uint32_t issi, TrainingAreaBase::TetraIcd::VulnerabilityState vbg);
void sendSanitationReport(uint32_t eqId, uint32_t issi);
//void sendSanitation(uint32_t eqId, uint32_t issi);
private:
QTcpSocket *m_socket;
QTimer *m_timer;
QByteArray m_buffer;
unsigned int m_counter;
bool m_toogle;
TTSArgs m_ttsArgs;
QVector<Player *> m_playerList;
QVector<runningPattern> m_patternList;
QPointF m_generalPosition;
NGL::INGLLogger &m_logger;
};
+49
View File
@@ -0,0 +1,49 @@
#
# Logger-Config für PrtCnv
#
# $Id$
# $HeadURL$
# $Author$
#
# Aus: grep getNGLLogger * | cut -d \" -f 2 | sort -u
#
log4j.reset=true
#log4j.rootLogger=DEBUG, stdout
log4j.rootLogger=WARN, file
#log4j.logger.CoPrtCnvCentralTelegram=WARN
#log4j.logger.CoPrtCnvRadioTelegram=WARN
#log4j.logger.CoPrtCnvTelegramConverter=WARN
#log4j.logger.PrtCnvApplication=WARN
#log4j.logger.PrtCnvBroadcastMgr=WARN
#log4j.logger.PrtCnvCentralThread=WARN
#log4j.logger.PrtCnvDbThread=WARN
#log4j.logger.PrtCnvInjectionSocket=WARN
#log4j.logger.PrtCnvInjectionThread=WARN
#log4j.logger.PrtCnvMain=WARN
#log4j.logger.PrtCnvMember=WARN
#log4j.logger.PrtCnvRadioThread=WARN
#log4j.logger.PrtCnvRadioThread.statistic=WARN
#log4j.logger.PrtCnvRadioTrafficThread=WARN
#log4j.logger.PrtCnvStoredCentralTelegram=WARN
#log4j.logger.PrtCnvStoredRadioTelegram=WARN
#log4j.logger.PrtCnvStoredRadioTelegram=WARN
#log4j.logger.PrtCnvTelegramLogThread=WARN
#log4j.logger.PrtCnvThreadStarter=WARN
# Appender
############
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=9
#will be replace by ngllogger
#log4j.appender.file.file=${HOME}/log/PrtCnv.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
+35
View File
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace PrtCnv
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "PRT.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try {
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger( loggerName );
} catch ( const std::exception &ex )
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace PrtCnv
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for AereaEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace PrtCnv
{
typedef NGL::INGLLogger& LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace PrtCnv