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

634 lines
20 KiB
C++
Executable File

/**
* Handler for all oc data at the server
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 14-02-2018
*/
#include "handler_oc_data.h"
#include <QMutexLocker>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include "main_handler.h"
#include "oc_situation.h"
#include "handler_oc_aes_data.h"
#include "handler_oc_situation_data.h"
#include "handler_oc_central_aes_data.h"
#include "settings.h"
#include "logger.h"
namespace OcSituationServer
{
HandlerOcData::HandlerOcData(MainHandler *mainHandler,
QObject *parent)
: QObject(parent)
, Interface::Fts::SendEquipmentMessageOcRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EquipmentErrorRequestHandler(mainHandler->dds()->fts())
, m_mainHandler(mainHandler)
, m_handlerOcAesData(new HandlerOcAesData(mainHandler, this))
, m_dts(mainHandler->dds()->dts())
, m_fts(mainHandler->dds()->fts())
, m_evlDtc(mainHandler->evlDtc())
, m_evlFtc(mainHandler->evlFtc())
, m_msgFtc(mainHandler->msgFtc())
, m_ppmDtc(mainHandler->ppmDtc())
, m_ppmFtc(mainHandler->ppmFtc())
, m_tabDtc(mainHandler->tabDtc())
, m_tabFtc(mainHandler->tabFtc())
, m_currentActiveEvaluationSchemaId(0)
, m_stateInformationTimer(new QTimer(this))
, m_logger(getLogger("HandlerOcData"))
{}
HandlerOcData::~HandlerOcData()
{}
bool HandlerOcData::init()
{
// EVL DTC enable
if (!m_evlDtc->enableEvaluationSchema())
{
ERROR_LOG(m_logger) << "Enabling EVL Data-Topics failed.";
return false;
}
connect(m_evlDtc.get(), &Evaluations::Interface::Dtc::evaluationSchemaChanged, this, &HandlerOcData::onEvaluationSchemaChanged);
auto cb = [this](const Evaluations::EvaluationSchema & schema)
{
onEvaluationSchemaChanged(schema, DataModification::eAdded);
};
m_evlDtc->evaluationSchemas(cb);
// EVL FTC enable
if (!m_evlFtc->enableEvlCommentModify())
{
ERROR_LOG(m_logger) << "Enabling EVL Function-Topics failed.";
return false;
}
// Msg FTC enable
if (!m_msgFtc->enableCreateOperatorMessage())
{
ERROR_LOG(m_logger) << "Enabling MSG Function-Topics failed.";
return false;
}
if (!m_ppmDtc->enableParticipant())
{
ERROR_LOG(m_logger) << "Enabling PPM Data-Topics failed.";
return false;
}
if (!m_ppmFtc->enableRegisterParticipant())
{
ERROR_LOG(m_logger) << "Enabling PPM FTC-Topics failed.";
return false;
}
// TAB DTC enable
if (!m_tabDtc->enableOcData())
{
ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed.";
return false;
}
// TAB DTC enable
if (!m_tabDtc->enableConnections())
{
ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed.";
return false;
}
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::ocDataChanged, this, &HandlerOcData::onOcDataChanged);
// TAB FTC enable
if (!m_tabFtc->enableSendOcData())
{
ERROR_LOG(m_logger) << "Enabling TAB Function-Topics failed.";
return false;
}
// Handler aes data
if (!m_handlerOcAesData->init())
{
ERROR_LOG(m_logger) << "Could not initialise HandlerOcAesData.";
return false;
}
connect(m_stateInformationTimer, &QTimer::timeout, this, &HandlerOcData::sendStateInformation);
m_stateInformationTimer->setInterval(Settings::stateInformationCycleTime());
INFO_LOG(m_logger) << "Starting StateInformation timer with interval: " << m_stateInformationTimer->interval();
m_stateInformationTimer->start();
INFO_LOG(m_logger) << "Initialisation HandlerOcData succesful.";
return true;
}
void HandlerOcData::onEvlCommentModifyReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Evaluation could not be transmitted to EVL: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Evaluation successfully transmitted to EVL!";
}
void HandlerOcData::onCreateOperatorMessageReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Message could not be created by MSG: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Message created successfully by MSG!";
}
void HandlerOcData::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 HandlerOcData::onRegisterParticipantReply(const ParticipantManagement::RegisterReply &reply)
{
if (reply.errorString().size())
ERROR_LOG(m_logger) << "Modify Participant data could not be send: " << reply.errorString();
else
DEBUG_LOG(m_logger) << "Modify Participant data send successfully to ppm!";
}
void HandlerOcData::onEvaluationSchemaChanged(const Evaluations::EvaluationSchema &data, DataModification modType)
{
if ((modType != DataModification::eAdded) && (modType != DataModification::eModified))
return;
if (data.sendToTa())
m_currentActiveEvaluationSchemaId = data.id();
}
void HandlerOcData::onOcDataChanged(const TrainingAreaBase::OcData &data, DataModification modType)
{
if ((modType != DataModification::eAdded) && (modType != DataModification::eModified))
return;
if (data.sampleTime() < QDateTime::currentMSecsSinceEpoch() - 1000 * 60 * 10) // older than 10 mins -> ignore
return;
const QByteArray payload = LtsUtils::toQByteArray(data.payload());
unsigned int telegramId;
LtsUtils::get1Byte(payload, telegramId);
DEBUG_LOG(m_logger) << "New OcData telegramId: " << telegramId;
switch (telegramId)
{
case OcSituation::TelegramId::Message:
{
handleMessage(payload);
break;
}
case OcSituation::TelegramId::DemandEvaluationSchema:
{
handleDemandEvaluationSchema(payload, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::Evaluation:
{
handleEvaluation(payload);
break;
}
case OcSituation::TelegramId::DemandSituation:
{
handleDemandSituation(payload, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::DemandAesItems:
{
OcSituation::AesType aesType;
size_t pos = 1;
LtsUtils::get1Byte(payload, pos, aesType);
m_handlerOcAesData->demandAesItems(aesType, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::EquipmentErrorRegistration:
{
handleEquipmentErrorRegistration(payload);
break;
}
case OcSituation::TelegramId::EquipmentRegistration:
{
handleEquipmentRegistration(payload);
break;
}
default:
{
INFO_LOG(m_logger) << "OC-data with telegram ID: " << telegramId << " is not by HandlerOcData OSS!";
break;
}
}
}
void HandlerOcData::onSendOcData(const TrainingAreaBase::SendOcDataRequest &req)
{
m_tabFtc->sendOcDataRequest(req, this);
}
void HandlerOcData::sendStateInformation()
{
QByteArray payload;
LtsUtils::set1Byte(OcSituation::TelegramId::StateInformation, payload);
LtsUtils::set2Byte(m_currentActiveEvaluationSchemaId, payload);
LtsUtils::set2Byte(m_handlerOcAesData->dangerAreaResetCnt(), payload);
LtsUtils::set2Byte(m_handlerOcAesData->iedResetCnt(), payload);
TrainingAreaBase::SendOcDataRequest out;
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
/*!
* \details Only if Tetra is connected, the OcData telegram will be send
* If we not check = A bunch of telegrams will be queued/stored until Tetra is connected, than Tetra is in Overload
*/
QMapIterator<qint32, TrainingAreaBase::Connections> conIt(m_tabDtc->connectionss());
while (conIt.hasNext())
{
conIt.next();
for (TrainingAreaBase::ConnectionState conState : conIt.value().connections())
{
if (conState.name() == "TETRA" && conState.state() == false)
return;
}
}
onSendOcData(out);
}
void HandlerOcData::onSituationThreadExit()
{
ThreadOcSituationData *t = qobject_cast<ThreadOcSituationData *>(sender());
INFO_LOG(m_logger) << "Exit O/C-Thread for O/C-Laptop-Id: " << m_situationThreads.key(t);
QMutexLocker l(&m_accessMutex);
m_situationThreads.remove(m_situationThreads.key(t));
t->deleteLater();
}
void HandlerOcData::onCentralAesThreadExit()
{
ThreadOcCentralAesData *t = qobject_cast<ThreadOcCentralAesData *>(sender());
INFO_LOG(m_logger) << "Exit CentralAesData-Thread for O/C-Laptop-Id: " << m_centralAesDataThreads.key(t);
QMutexLocker l(&m_accessMutex);
m_centralAesDataThreads.remove(m_centralAesDataThreads.key(t));
t->deleteLater();
}
void HandlerOcData::onSendEquipmentMessageOcRequest(const rti::core::SampleIdentity &cookie, const SendEquipmentMessageRequest &request)
{
QByteArray preparedPayload;
LtsUtils::set1Byte(OcSituation::TelegramId::Message, preparedPayload);
LtsUtils::set2Byte(0, preparedPayload); // dummy receiverId is individually set for each eqId in list
LtsUtils::set1Byte(Messaging::AddressType::ObserverController, preparedPayload);
LtsUtils::set2Byte(request.sender(), preparedPayload);
LtsUtils::set1Byte(request.senderType().underlying(), preparedPayload);
LtsUtils::set2Byte(m_mainHandler->exerciseId(), preparedPayload);
preparedPayload.append(QByteArray::fromStdString(request.subject()));
preparedPayload.append('\n'); // separating subject from message
preparedPayload.append(QByteArray::fromStdString(request.message()));
for (const auto equipmentNumber : request.equipmentNumbers())
{
TrainingAreaBase::SendOcDataRequest out;
out.equipmentNumber(equipmentNumber);
// replace 2 bytes with current receiver
QByteArray receiverId;
LtsUtils::set2Byte(equipmentNumber, receiverId);
QByteArray payload = preparedPayload.replace(1, 2, receiverId);
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
onSendOcData(out);
}
m_fts->sendEquipmentMessageOcReply(cookie, Core::FunctionReplyObject(request.requestId()));
}
void HandlerOcData::onEquipmentErrorRequest(const rti::core::SampleIdentity &cookie, const EquipmentErrorRequest &request)
{
ERROR_LOG(m_logger) << "onEquipmentErrorRequest" << request.equipmentNumber();
EquipmentErrorRegistration out;
// out.id(request.requestId());
out.sampleTime(QDateTime::currentMSecsSinceEpoch());
out.participant(QString::number(request.equipmentNumber()).toStdString());
out.device(request.device());
out.text(request.text());
m_dts->publish(out);
m_fts->equipmentErrorReply(cookie, Core::FunctionReplyObject(request.requestId()));
}
void HandlerOcData::handleDemandSituation(const QByteArray &data, int equipmentId)
{
OcSituation::SituationCenterType centerType;
int centerEquipmentId = 0, radius = 0;
Core::GeoPosition2D position;
position.lon(std::nan(""));
position.lat(std::nan(""));
bool initial = false;
size_t pos = 1;
LtsUtils::get1Byte(data, pos, centerType);
if (centerType == OcSituation::SituationCenterType::Equipment)
{
LtsUtils::get2Byte(data, pos, centerEquipmentId);
LtsUtils::get2Byte(data, pos, radius);
DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId
<< " centerEquipmentId: " << centerEquipmentId << " radius: " << radius;
}
else
{
double tmp;
LtsUtils::get8Byte(data, pos, tmp);
position.lon(tmp);
LtsUtils::get8Byte(data, pos, tmp);
position.lat(tmp);
LtsUtils::get2Byte(data, pos, radius);
DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId
<< " center: " << position << " radius: " << radius;
}
bool filterActive = false;
LtsUtils::get1Byte(data, pos, initial);
LtsUtils::get1Byte(data, pos, filterActive);
QSet<ParticipantManagement::TypeClass::type> typeClassList{};
QSet<int> partyList{};
if (filterActive)
{
// get partyTypes
int pCount = 0;
LtsUtils::get1Byte(data, pos, pCount);
for (int i = 0; i < pCount; i++)
{
int partyId;
LtsUtils::get2Byte(data, pos, partyId);
partyList.insert(partyId);
}
//get types
int typeCount = 0;
LtsUtils::get1Byte(data, pos, typeCount);
for (int i = 0; i < typeCount; i++)
{
int typClass;
LtsUtils::get1Byte(data, pos, typClass);
typeClassList.insert((ParticipantManagement::TypeClass::type)typClass);
}
}
QMutexLocker l(&m_accessMutex);
// Situation Thread
ThreadOcSituationData *ocThread = m_situationThreads.value(equipmentId);
bool bInitialSituation;
if (!ocThread)
{
ocThread = new ThreadOcSituationData(m_mainHandler, equipmentId, centerEquipmentId, radius, filterActive, partyList, typeClassList);
m_situationThreads.insert(equipmentId, ocThread);
connect(ocThread, &QThread::finished, this, &HandlerOcData::onSituationThreadExit);
connect(ocThread, &ThreadOcSituationData::sendOcData, this, &HandlerOcData::onSendOcData);
ocThread->start();
bInitialSituation = true;
}
else
bInitialSituation = initial;
ocThread->requestOcSituationData(centerType, centerEquipmentId, position, radius, filterActive, partyList, typeClassList, bInitialSituation);
// Central AES Data Thread
bool bInitialAes;
ThreadOcCentralAesData *centralAesDataThread = m_centralAesDataThreads.value(equipmentId);
if (!centralAesDataThread)
{
centralAesDataThread = new ThreadOcCentralAesData(m_mainHandler, equipmentId, centerEquipmentId, radius);
m_centralAesDataThreads.insert(equipmentId, centralAesDataThread);
connect(centralAesDataThread, &QThread::finished, this, &HandlerOcData::onCentralAesThreadExit);
connect(centralAesDataThread, &ThreadOcCentralAesData::sendOcData, this, &HandlerOcData::onSendOcData);
centralAesDataThread->start();
bInitialAes = true;
}
else
bInitialAes = initial;
centralAesDataThread->requestOcCentralAesData(centerType, centerEquipmentId, position, radius, bInitialAes);
}
void HandlerOcData::handleDemandEvaluationSchema(const QByteArray &data, int equipmentNumber)
{
Q_UNUSED(data);
// ignoring requested schema ID, as we only have one active schema to be send to TA
Evaluations::EvaluationSchema schema = m_evlDtc->evaluationSchema(m_currentActiveEvaluationSchemaId);
DEBUG_LOG(m_logger) << "Sending to equipmentnumber: " << equipmentNumber << " evaluation schmema: " << m_currentActiveEvaluationSchemaId;
TrainingAreaBase::SendOcDataRequest out;
out.equipmentNumber(equipmentNumber);
QByteArray payload;
LtsUtils::set1Byte(OcSituation::TelegramId::EvaluationSchema, payload);
LtsUtils::set2Byte(schema.id(), payload);
QByteArray compressed = qCompress(QByteArray::fromStdString(schema.schemadata()));
payload.append(compressed);
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
onSendOcData(out);
}
void HandlerOcData::handleEvaluation(const QByteArray &data)
{
Evaluations::EvlCommentModifyRequest out;
size_t pos = 1;
unsigned int tmp;
// get schema id
LtsUtils::get2Byte(data, pos, tmp);
out.schemaId(tmp);
// get sender operator id
LtsUtils::get4Byte(data, pos, tmp);
out.senderOperatorId(tmp);
// get receiver operator id
LtsUtils::get4Byte(data, pos, tmp);
if (tmp > 0)
out.receiverOperatorId(dds::core::optional<int32_t>(tmp));
// get timeStamp
int64_t timeStamp;
LtsUtils::get8Byte(data, pos, timeStamp);
out.ts(timeStamp);
// get hierarchy string
unsigned int hierarchyLength;
LtsUtils::get2Byte(data, pos, hierarchyLength);
out.hierarchy(QByteArray(data.mid(pos, hierarchyLength)).toStdString());
pos += hierarchyLength;
// get text string
unsigned int textLength;
LtsUtils::get2Byte(data, pos, textLength);
out.text(QByteArray(data.mid(pos, textLength)).toStdString());
DEBUG_LOG(m_logger) << "Got new evl comment: " << out;
m_evlFtc->evlCommentModifyRequest(out, this);
}
void HandlerOcData::handleMessage(const QByteArray &data)
{
Messaging::CreateOperatorMessageRequest out;
size_t pos = 1;
unsigned int tmp = 0;
unsigned int addressType;
// get receiver
LtsUtils::get2Byte(data, pos, tmp);
std::vector<int32_t> recipientIds;
recipientIds.push_back(tmp);
// get address type
LtsUtils::get1Byte(data, pos, addressType);
out.recipientType(static_cast<Messaging::AddressType::inner_enum>(addressType));
if (addressType == Messaging::AddressType::ObserverController)
{
int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp);
if (participantId > 0)
recipientIds[0] = participantId;
else
{
ERROR_LOG(m_logger) << "Recipient equipment number: " << tmp << " is unknown. Aborting creating message.";
return;
}
}
out.recipientIds(recipientIds);
// get sender
LtsUtils::get2Byte(data, pos, tmp);
LtsUtils::get1Byte(data, pos, addressType);
out.senderType(static_cast<Messaging::AddressType::inner_enum>(addressType));
if (addressType == Messaging::AddressType::ObserverController)
{
int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp);
if (participantId > 0)
out.senderId(participantId);
else
{
WARNING_LOG(m_logger) << "Sender equipment number: " << tmp << " is unknown. Setting senderId to 0.";
out.senderId(0);
out.senderType(Messaging::AddressType::ObserverController);
}
}
else
{
// no need to check if operator still exists, there is no harm if not
out.senderId(tmp);
}
// get exerciseId
LtsUtils::get2Byte(data, pos, tmp);
if (tmp == 0)
WARNING_LOG(m_logger) << "Could'n set ExerciseId" << " " << tmp << " set to 0";
out.exerciseId(tmp);
// get address type
const QByteArray msg(data.right(data.size() - pos));
int idx = msg.indexOf('\n');
if (idx < 0)
{
out.subject((tr("Message from %1").arg(out.senderId())).toStdString());
out.message(msg.constData());
}
else
{
out.subject(msg.left(idx).constData());
out.message(msg.mid(idx + 1).constData());
}
DEBUG_LOG(m_logger) << "Got new message: " << out;
m_msgFtc->createOperatorMessageRequest(out, this);
}
void HandlerOcData::handleEquipmentErrorRegistration(const QByteArray &data)
{
EquipmentErrorRegistration out;
size_t pos = 1;
unsigned int tmp;
// equipmentNumber
LtsUtils::get2Byte(data, pos, tmp);
out.equipmentNumber(tmp);
// participant
out.participant(data.mid(pos, 36).toStdString());
pos += 36;
// device
out.device(data.mid(pos, 36).toStdString());
pos += 36;
// length
LtsUtils::get1Byte(data, pos, tmp);
// text
out.text(data.mid(pos, tmp).toStdString());
out.sampleTime(QDateTime::currentMSecsSinceEpoch());
DEBUG_LOG(m_logger) << "Got new error registration: " << out;
m_dts->publish(out);
}
void HandlerOcData::handleEquipmentRegistration(const QByteArray &data)
{
size_t pos = 1;
unsigned int tmp;
// participantID
LtsUtils::get2Byte(data, pos, tmp);
int participantId = tmp;
// equipmentNumber
LtsUtils::get2Byte(data, pos, tmp);
int equipmentNumber = tmp;
ParticipantManagement::Participant part = m_ppmDtc->participant(participantId);
if (part.equipmentNumber().is_set() && equipmentNumber > 0)
{
// we need a deregistration first!
ParticipantManagement::RegisterParticipantRequest dereq;
dereq.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
dereq.equipmentNumber(0);
dereq.participantId(participantId);
dereq.info(part.info());
dereq.identification(part.identification());
m_ppmFtc->registerParticipantRequest(dereq, this);
}
ParticipantManagement::RegisterParticipantRequest req;
if (equipmentNumber > 0)
req.assignState(ParticipantManagement::ParticipantAssignState::HandoutExcon);
else
req.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
req.equipmentNumber(equipmentNumber);
req.participantId(participantId);
req.info(part.info());
req.identification(part.identification());
DEBUG_LOG(m_logger) << "Try to register part with id: " << req.participantId() << " and eqNumeber " << req.equipmentNumber();
m_ppmFtc->registerParticipantRequest(req, this);
}
} // Namespace OcSituationServer