Files
qt5-legatus-legacy/Lts/Services/OcSituationServer/handler_oc_aes_data.cpp
T
2026-08-13 18:33:10 +02:00

454 lines
14 KiB
C++
Executable File

/**
* Handler for aes data to be send to the oc-laptop transparent
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 18-04-2018
*/
#include "handler_oc_aes_data.h"
#include <functional>
#include <QByteArray>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include "settings.h"
namespace OcSituationServer
{
HandlerOcAesData::HandlerOcAesData(MainHandler *mainHandler,
QObject *parent)
: QObject(parent)
, m_aesDtc(mainHandler->aesDtc())
, m_gimDtc(mainHandler->gimDtc())
, m_tabFtc(mainHandler->tabFtc())
, m_dbHandler(mainHandler->db())
, m_positionConverter(mainHandler->positionConverter())
, m_dangerAreaResetCnt(0)
, m_iedResetCnt(0)
, m_collectionTimer(QMap<OcSituation::AesType, QTimer*>())
, m_demandingEquipments(QMap<OcSituation::AesType, QSet<int>>())
, m_iedPositionsToBeSend(QMap<int, GuezIedManagement::IedPosition>())
, m_iedPositionCycleTimer(new QTimer(this))
, m_logger(getLogger("HandlerOcAesData"))
{}
HandlerOcAesData::~HandlerOcAesData()
{}
bool HandlerOcAesData::init()
{
// create collectionTimer
m_collectionTimer[OcSituation::AesType::DangerArea] = new QTimer(this);
m_collectionTimer[OcSituation::AesType::DangerArea]->setSingleShot(true);
m_collectionTimer[OcSituation::AesType::DangerArea]->setInterval(Settings::aesDemandCollectionTime());
connect(m_collectionTimer[OcSituation::AesType::DangerArea], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandDangerAreas);
m_collectionTimer[OcSituation::AesType::Ied] = new QTimer(this);
m_collectionTimer[OcSituation::AesType::Ied]->setSingleShot(true);
m_collectionTimer[OcSituation::AesType::Ied]->setInterval(Settings::aesDemandCollectionTime());
connect(m_collectionTimer[OcSituation::AesType::Ied], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandIeds);
// set reset counter
QVector<Database::OssResetCounter> dataVec;
// read all Ieds from database
if (!m_dbHandler->select(dataVec))
{
ERROR_LOG(m_logger) << "Error reading reset counter from database!";
return false;
}
for (const auto &data : qAsConst(dataVec))
{
switch (data.aesTypeId)
{
case OcSituation::AesType::DangerArea:
{
m_dangerAreaResetCnt = data.resetCounter;
break;
}
case OcSituation::AesType::Ied:
{
m_iedResetCnt = data.resetCounter;
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << data.aesTypeId << " read from db is not handled.";
break;
}
}
}
// AES DTC enable
if (!m_aesDtc->enableDangerArea())
{
ERROR_LOG(m_logger) << "Enabling AES Data-Topics failed.";
return false;
}
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::dangerAreaChanged, this, &HandlerOcAesData::onDangerAreaChanged);
// GIM DTC enable
if (!m_gimDtc->enableIed() ||
!m_gimDtc->enableIedPosition())
{
ERROR_LOG(m_logger) << "Enabling GIM Data-Topics failed.";
return false;
}
connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedChanged, this, &HandlerOcAesData::onIedChanged);
connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedPositionChanged, this, &HandlerOcAesData::onIedPositionChanged);
connect(m_iedPositionCycleTimer, &QTimer::timeout, this, &HandlerOcAesData::sendIedPositions);
m_iedPositionCycleTimer->setInterval(Settings::iedPositionCycleTime());
DEBUG_LOG(m_logger) << "Starting ied position timer with interval: " << m_iedPositionCycleTimer->interval();
m_iedPositionCycleTimer->start();
return true;
}
void HandlerOcAesData::demandAesItems(OcSituation::AesType aesType, int eqNumber)
{
switch (aesType)
{
case OcSituation::AesType::DangerArea:
case OcSituation::AesType::Ied:
{
DEBUG_LOG(m_logger) << "Got demand for aes type: " << aesType << " from equipment: " << eqNumber;
if (!m_collectionTimer[aesType]->isActive())
m_collectionTimer[aesType]->start();
m_demandingEquipments[aesType].insert(eqNumber);
break;
}
default:
{
WARNING_LOG(m_logger) << "Aes type: " << aesType << " is currently not handled.";
}
}
}
void HandlerOcAesData::onSendOcDataReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Oc data could not be send: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Oc data send successfully to TAB!";
}
void HandlerOcAesData::onDangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification mod)
{
Q_UNUSED(data);
Q_UNUSED(mod);
incrementResetCnt(OcSituation::AesType::DangerArea);
}
void HandlerOcAesData::onIedChanged(const GuezIedManagement::Ied &data, DataModification mod)
{
Q_UNUSED(data);
Q_UNUSED(mod);
incrementResetCnt(OcSituation::AesType::Ied);
}
void HandlerOcAesData::onIedPositionChanged(const GuezIedManagement::IedPosition &data, DataModification mod)
{
if (mod == DataModification::eRemoved)
return;
GuezIedManagement::Ied ied = m_gimDtc->ied(data.id());
if (ied.id() > 0)
{
if (ied.pairingState().underlying() != GuezIedManagement::IedPairingState::NotPaired)
m_iedPositionsToBeSend.insert(data.id(), data);
}
else
ERROR_LOG(m_logger) << "Got ied position for not existing ID: " << data.id();
}
void HandlerOcAesData::onCollectionTimeOutDemandDangerAreas()
{
std::function<void (int)> f = std::bind(&HandlerOcAesData::sendDangerAreasToTab, this, std::placeholders::_1);
collectionTimeout(OcSituation::AesType::DangerArea, f);
}
void HandlerOcAesData::onCollectionTimeOutDemandIeds()
{
std::function<void (int)> f = std::bind(&HandlerOcAesData::sendIedsToTab, this, std::placeholders::_1);
collectionTimeout(OcSituation::AesType::Ied, f);
}
void HandlerOcAesData::sendIedPositions()
{
if (m_iedPositionsToBeSend.size() == 0)
{
DEBUG_LOG(m_logger) << "No new ied positions to be send.";
return;
}
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(0); // ied positions are always send broadcast
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &pos : qAsConst(m_iedPositionsToBeSend))
{
LtsUtils::set2Byte(m_gimDtc->ied(pos.id()).equipmentNumber(), data);
const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position()));
LtsUtils::set3Byte(qRound(relCoord.x()), data);
LtsUtils::set3Byte(qRound(relCoord.y()), data);
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added ied position " << pos
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data);
LtsUtils::set1Byte(cnt, data);
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
m_iedPositionsToBeSend.clear();
}
void HandlerOcAesData::collectionTimeout(OcSituation::AesType type, const std::function<void (int)> &sendFunction)
{
DEBUG_LOG(m_logger) << "There are " << m_demandingEquipments[type].size() << " requests for aes type: " << type
<< " sending Items as " << ((m_demandingEquipments[type].size() > 5) ? QStringLiteral("broadcast") : QStringLiteral("direct")) << " telegram(s)";
if (m_demandingEquipments[type].size() > 5)
sendFunction(0); // broadcast
else
for (int eqNumber : qAsConst(m_demandingEquipments[type]))
sendFunction(eqNumber);
//cleanup
m_demandingEquipments[type].clear();
}
void HandlerOcAesData::sendDangerAreasToTab(int eqNumber)
{
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(eqNumber);
const QMap<int, AreaEffectSimulation::DangerArea> dangerAreas = m_aesDtc->dangerAreas();
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &dangerArea : dangerAreas)
{
if (dangerArea.effectStateId() >= AreaEffectSimulation::AesEffectState::EsCreated) // do not transfer ended or deleted areas
{
LtsUtils::set2Byte(dangerArea.id(), data);
QByteArray name(QByteArray::fromStdString(dangerArea.name()));
name.resize(8);
data.append(name);
if (dangerArea.effectStart().is_set())
LtsUtils::set4Byte(dangerArea.effectStart().get() / 1000, data);
else
LtsUtils::set4Byte(0, data);
LtsUtils::set1Byte(dangerArea.segments().size(), data);
for (const auto &segment : dangerArea.segments())
{
const QPointF relCoord(m_positionConverter->relativeFromGeographic(segment));
LtsUtils::set3Byte(qRound(relCoord.y()), data);
LtsUtils::set3Byte(qRound(relCoord.x()), data);
}
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added danger area " << dangerArea
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data);
LtsUtils::set1Byte(cnt, data);
}
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
}
void HandlerOcAesData::sendIedsToTab(int eqNumber)
{
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(eqNumber);
const QMap<int, GuezIedManagement::Ied> ieds = m_gimDtc->ieds();
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::Ied, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &ied : ieds)
{
LtsUtils::set2Byte(ied.equipmentNumber(), data);
LtsUtils::set1Byte(ied.type().underlying(), data);
uint8_t trigger = (uint8_t(ied.tautWireTrigger()) << 3) |
(uint8_t(ied.lightBarrierTrigger()) << 2) |
(uint8_t(ied.switchTrigger()) << 1) |
(uint8_t(ied.remoteTrigger()));
LtsUtils::set1Byte(trigger, data);
LtsUtils::set1Byte(ied.explosiveForce(), data);
LtsUtils::set1Byte(ied.partyId(), data);
LtsUtils::set1Byte(ied.tacticalState().underlying(), data);
// set an initial position, as only paired ieds are updated concurrently
const auto pos = m_gimDtc->iedPosition(ied.id());
const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position()));
LtsUtils::set3Byte(qRound(relCoord.x()), data);
LtsUtils::set3Byte(qRound(relCoord.y()), data);
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added ied " << ied
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::Ied, data);
LtsUtils::set1Byte(cnt, data);
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
}
void HandlerOcAesData::incrementResetCnt(OcSituation::AesType aesType)
{
switch (aesType)
{
case OcSituation::AesType::DangerArea:
{
setResetCnt(aesType, m_dangerAreaResetCnt + 1);
break;
}
case OcSituation::AesType::Ied:
{
setResetCnt(aesType, m_iedResetCnt + 1);
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in incrementResetCnt.";
break;
}
}
}
void HandlerOcAesData::setResetCnt(OcSituation::AesType aesType, int resetCnt)
{
Database::OssResetCounter data;
data.aesTypeId = aesType;
data.resetCounter = resetCnt;
switch (aesType)
{
case OcSituation::AesType::DangerArea:
{
m_dangerAreaResetCnt = resetCnt;
break;
}
case OcSituation::AesType::Ied:
{
m_iedResetCnt = resetCnt;
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in setResetCnt.";
break;
}
}
// update db
if (!m_dbHandler->update(data))
ERROR_LOG(m_logger) << "Error updating reset counter in db.";
}
} // namespace OcSituationServer