Major Changes:
- Merged latest Qt5 devel content from dlbo (292 files changed, 7 new dirs)
- Consolidated forward-declarations and include folders
- Removed unneeded modules per Niklas' repo check list
Build System Fixes:
- Fixed CMake VERSION_GREATER syntax in ParticipantManagement/CMakeLists.txt
- Added VLC packages (libvlc-dev, vlc-plugin-base) to docker_build.sh
- Added PostgreSQL dev packages (libpq-dev, postgresql-server-dev-all) to Dockerfile
- Fixed locale warnings (ANSI_X3.4-1968 → C.UTF-8)
- Fixed ArcGIS SDK path warnings when BUILD_ARCGIS=OFF
Code Generation Fixes:
- Fixed IDL enum collisions (PortType→CorePortType/DdsutPortType, PdmEventType)
- Fixed Grantlee template svc_translation.cpp.grantlee:
- Changed {{ idl_enum.name }}_def:: to {{ idl_enum.name }}::
- Fixed .underlying() calls for C++11 scoped enums (enum class)
- Removed generated files from repo (moc_*, *_dtc.h, *_ftc.h)
Validation:
- BUILD_RTI=ON: SUCCESS
- BUILD_RTI=OFF: SUCCESS
- C++17 standard maintained for RTI 7.7 compatibility
- Ubuntu 26.04 Docker environment verified
Remaining Warnings (expected):
- BgViewer, KDChart, KDReports, Poppler-Qt6 not found (optional features)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
368 lines
11 KiB
C++
368 lines
11 KiB
C++
/**
|
|
* Handler definition and state IED
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Philipp Dassow, SLEA, 2017-07-20
|
|
*/
|
|
|
|
#include "handler_ied.h"
|
|
#include "object_ied.h"
|
|
#include "main_handler.h"
|
|
#include "gim_db.h"
|
|
|
|
#include <Proxies/TrainingAreaBase/tab_dtc.h>
|
|
|
|
#include <QCoreApplication>
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
#include <ngldb/dbtransaction.h>
|
|
#include <ngldb/nsqlquery.h>
|
|
|
|
#include <GuezIedManagement/gim_record_replay_plugin.h>
|
|
|
|
namespace GuezIedManagement
|
|
{
|
|
|
|
HandlerIed::HandlerIed(MainHandler *mainHandler,
|
|
const std::shared_ptr<DdsHandler> &ddsHandler,
|
|
const std::shared_ptr<DatabaseInterface> &db,
|
|
QObject *parent)
|
|
: QObject(parent)
|
|
, Interface::Fts::PairIedRequestHandler(ddsHandler->fts())
|
|
, m_mainHandler(mainHandler)
|
|
, m_ddsHandler(ddsHandler)
|
|
, m_tabDtc(nullptr)
|
|
, m_ppmDtc(nullptr)
|
|
, m_db(db)
|
|
, m_logger(getLogger("HandlerIed"))
|
|
{}
|
|
|
|
HandlerIed::~HandlerIed()
|
|
{
|
|
qDeleteAll(m_iedObjMap);
|
|
m_db = nullptr;
|
|
m_tabDtc = nullptr;
|
|
m_ppmDtc = nullptr;
|
|
m_ddsHandler = nullptr;
|
|
}
|
|
|
|
bool HandlerIed::init()
|
|
{
|
|
// initialize PPM DTC
|
|
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsHandler->dds());
|
|
if (!m_ppmDtc->enableParticipantPosition())
|
|
{
|
|
ERROR_LOG(m_logger) << "PPM DTC enableParticipantPosition() failed.";
|
|
return false;
|
|
}
|
|
if (!m_ppmDtc->enableParticipant())
|
|
{
|
|
ERROR_LOG(m_logger) << "PPM DTC enableParticipant() failed.";
|
|
return false;
|
|
}
|
|
if (!m_ppmDtc->enableParty())
|
|
{
|
|
ERROR_LOG(m_logger) << "PPM DTC enableParty() failed.";
|
|
return false;
|
|
}
|
|
|
|
QVector<Database::GimIed> dataVec;
|
|
// read all Ieds from database
|
|
if (!m_db->select(dataVec))
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading data from database!";
|
|
return false;
|
|
}
|
|
|
|
// create Ied objects
|
|
for (const Database::GimIed &data : qAsConst(dataVec))
|
|
{
|
|
ObjectIed *ifs(new ObjectIed(m_ddsHandler, m_db, m_ppmDtc, this));
|
|
Core::FunctionReplyObject ret = ifs->createObject(data);
|
|
if (ret.error())
|
|
{
|
|
delete ifs;
|
|
ERROR_LOG(m_logger) << ret();
|
|
return false;
|
|
}
|
|
m_iedObjMap.insert(ifs->getId(), ifs);
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "m_exeObjMap.size() = " << m_iedObjMap.size();
|
|
|
|
// Initialize RecordReplay to get initial IedPosition
|
|
RecordReplayPlugin recordReplay;
|
|
// no ddsconnector - we don't want to publish data here
|
|
recordReplay.initDtc(nullptr, m_mainHandler->exerciseId());
|
|
|
|
NSqlQuery q(m_db->database());
|
|
recordReplay.readIedPositionAt(q, QDateTime::currentMSecsSinceEpoch());
|
|
|
|
// key is sampleTime -> multiMap
|
|
QMultiMap<qint64, GuezIedManagement::IedPosition> latestPositions = recordReplay.getIedPositionMap();
|
|
|
|
DEBUG_LOG(m_logger) << "Starting to read IED positions from record replay. Size: "
|
|
<< latestPositions.size();
|
|
|
|
for (auto it = latestPositions.begin(); it != latestPositions.end(); ++it)
|
|
{
|
|
const auto &pos = it.value();
|
|
ObjectIed *currentIed = m_iedObjMap.value(pos.id());
|
|
|
|
if (currentIed == nullptr)
|
|
{
|
|
INFO_LOG(m_logger) << "Got position for non existing IED-Id: " << pos.id();
|
|
continue;
|
|
}
|
|
DEBUG_LOG(m_logger) << "Setting position for IED-Id " << pos.id();
|
|
currentIed->setPosition(it.value());
|
|
}
|
|
|
|
m_tabDtc = TrainingAreaBase::Interface::Dtc::create(m_ddsHandler->dds());
|
|
|
|
if (!m_tabDtc->enableDefinitionIed() ||
|
|
!m_tabDtc->enableStateIed())
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not initialize TraininAreaBase data topic client";
|
|
return false;
|
|
}
|
|
|
|
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::definitionIedChanged,
|
|
this, &HandlerIed::onDefinitionIed);
|
|
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::stateIedChanged,
|
|
this, &HandlerIed::onStateIed);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline IedTacticalState toTacticalState(int tacticalState)
|
|
{
|
|
switch (tacticalState)
|
|
{
|
|
case 0:
|
|
return IedTacticalState::NotActive;
|
|
case 1:
|
|
return IedTacticalState::Active;
|
|
case 2:
|
|
return IedTacticalState::Defused;
|
|
case 3:
|
|
return IedTacticalState::TriggeredBySwitch;
|
|
case 4:
|
|
return IedTacticalState::TriggeredByRemote;
|
|
case 5:
|
|
return IedTacticalState::TriggeredByLightBarrier;
|
|
case 6:
|
|
return IedTacticalState::TriggeredByTautWire;
|
|
case 7:
|
|
return IedTacticalState::TriggeredByControlDevice;
|
|
case 255:
|
|
break;
|
|
default:
|
|
Q_ASSERT(false);
|
|
break;
|
|
}
|
|
return IedTacticalState::Unknown;
|
|
}
|
|
|
|
static void assignBaseData(const TrainingAreaBase::IedBaseData &inData, Database::GimIed &outData)
|
|
{
|
|
outData.equipmentNumber = inData.iedId();
|
|
outData.tacticalStateId = toTacticalState(inData.tacticalState());
|
|
outData.pyrotechnics = inData.statePyrotechnics();
|
|
outData.sampleTime = inData.sampleTime();
|
|
}
|
|
|
|
void HandlerIed::onDefinitionIed(const TrainingAreaBase::DefinitionIed &data, DataModification modType)
|
|
{
|
|
if (modType != DataModification::eAdded && modType != DataModification::eModified)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onDefinitionIed: " << data;
|
|
|
|
Database::GimIed newData;
|
|
|
|
assignBaseData(data, newData);
|
|
newData.typeId = static_cast<IedType::inner_enum>(data.type());
|
|
newData.remoteTrigger = data.remoteTrigger();
|
|
newData.switchTrigger = data.switchTrigger();
|
|
newData.lightBarrierTrigger = data.lightBarrierTrigger();
|
|
newData.tautWireTrigger = data.tautWireTrigger();
|
|
newData.explosiveForce = data.explosiveForce();
|
|
|
|
if (m_ppmDtc->party(data.partyId()).id() > 0)
|
|
newData.partyId = data.partyId(); // use party from telegram
|
|
else
|
|
newData.partyId = getPartyIdFromParticipantEquipmentNumber(data.equipmentNumber()); // use party from equipmentNumber
|
|
|
|
newData.pairingStateId = static_cast<IedPairingState::inner_enum>(data.pairingState());
|
|
switch (newData.pairingStateId.underlying())
|
|
{
|
|
case IedPairingState_def::PairedToParticipant:
|
|
newData.pairedParticipantId.setValue(data.pairedId().get());
|
|
break;
|
|
case IedPairingState_def::PairedToOps:
|
|
newData.pairedOpsId.setValue(data.pairedId().get());
|
|
break;
|
|
case IedPairingState_def::NotPaired:
|
|
break;
|
|
}
|
|
|
|
// set symbol / sidc
|
|
// Note:
|
|
// Special: triggered Ied<Small|Big> has seperate SIDC:
|
|
// "M25D|CMPL|10014000001103000000"); // SIDC for IED (OHNE ZickZack)
|
|
// "M25D|CMPL|10014000001103010000"); // SIDC for IED (MIT ZickZack)
|
|
// ^
|
|
// | -> Therefore suppress Injury
|
|
// handled at guez_ied_to_map.cpp and gim_info_dialog.cpp
|
|
switch (newData.typeId.underlying())
|
|
{
|
|
case IedType_def::SuicideIed:
|
|
newData.symbol = QStringLiteral("AP6D|CMPL|10014000001103040000"); //SIDC for suicide bomber
|
|
break;
|
|
case IedType_def::IedSmallPyro:
|
|
case IedType_def::IedBigPyro:
|
|
newData.symbol = QStringLiteral("AP6D|CMPL|10014000001103000000"); // SIDC for IED (OHNE ZickZack)
|
|
break;
|
|
} // switch
|
|
|
|
int id = getIdByEquipmentnumber(newData.equipmentNumber);
|
|
|
|
if (m_iedObjMap.contains(id))
|
|
m_iedObjMap[id]->updateDefinition(newData);
|
|
else
|
|
{
|
|
ObjectIed *ifs(new ObjectIed(m_ddsHandler, m_db, m_ppmDtc, this));
|
|
Core::FunctionReplyObject ret = ifs->createObject(newData);
|
|
if (ret.error())
|
|
{
|
|
delete ifs;
|
|
ERROR_LOG(m_logger) << ret();
|
|
return;
|
|
}
|
|
m_iedObjMap.insert(ifs->getId(), ifs);
|
|
auto it = m_orphanedStateIed.find(data.iedId());
|
|
if (it != m_orphanedStateIed.end())
|
|
{
|
|
auto defState = it.value();
|
|
m_orphanedStateIed.erase(it);
|
|
|
|
defState.tacticalState(data.tacticalState());
|
|
defState.statePyrotechnics(data.statePyrotechnics());
|
|
defState.sampleTime(data.sampleTime());
|
|
onStateIed(defState, DataModification::eAdded);
|
|
}
|
|
id = ifs->getId();
|
|
}
|
|
|
|
m_iedObjMap[id]->setPosition(newData.sampleTime, Core::GeoPosition(data.position().lon(), data.position().lat(), 0));
|
|
}
|
|
|
|
static inline IedError toIedError(int err)
|
|
{
|
|
switch (err)
|
|
{
|
|
case 0:
|
|
return IedError::NoError;
|
|
case 1:
|
|
return IedError::DeviceMissing;
|
|
}
|
|
Q_ASSERT(false);
|
|
return IedError::DeviceMissing;
|
|
}
|
|
|
|
void HandlerIed::onStateIed(const TrainingAreaBase::StateIed &data, DataModification modType)
|
|
{
|
|
if (modType != DataModification::eAdded && modType != DataModification::eModified)
|
|
return;
|
|
|
|
DEBUG_LOG(m_logger) << "onStateIed: " << data;
|
|
|
|
// only overwrite state for existing IEDs
|
|
if (m_iedObjMap.contains(getIdByEquipmentnumber(data.iedId())))
|
|
{
|
|
Database::GimIed newData;
|
|
|
|
assignBaseData(data, newData);
|
|
newData.batterywarnings = data.batteryWarnings();
|
|
newData.electronicsErrorStateId = toIedError(data.errorStateElectronics());
|
|
newData.pyrotechnics1ErrorStateId = toIedError(data.errorStatePyrotechnics1());
|
|
newData.pyrotechnics2ErrorStateId = toIedError(data.errorStatePyrotechnics2());
|
|
newData.pyrotechnics3ErrorStateId = toIedError(data.errorStatePyrotechnics3());
|
|
|
|
m_iedObjMap[getIdByEquipmentnumber(newData.equipmentNumber)]->updateState(newData);
|
|
}
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "StateIed without an existing IED - got no DefinitionIED? " << data;
|
|
m_orphanedStateIed.insert(data.iedId(), data);
|
|
}
|
|
}
|
|
|
|
void HandlerIed::onPairIedRequest(const rti::core::SampleIdentity &cookie,
|
|
const PairIedRequest &request)
|
|
{
|
|
Core::FunctionReplyObject ret;
|
|
|
|
INFO_LOG(m_logger) << "onPairIedRequest id = " << request.id();
|
|
|
|
ObjectIed *ifs = m_iedObjMap.value(request.id());
|
|
if (ifs == nullptr)
|
|
{
|
|
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "IED with id %1 not found")).arg(request.id());
|
|
m_ddsHandler->fts()->pairIedReply(cookie, ret);
|
|
return;
|
|
}
|
|
|
|
Database::GimIed data;
|
|
data.pairingStateId = request.pairingState();
|
|
|
|
switch (request.pairingState().underlying())
|
|
{
|
|
case IedPairingState_def::PairedToParticipant:
|
|
{
|
|
data.pairedParticipantId.setValue(request.pairedParticipantId().get());
|
|
data.partyId = m_ppmDtc->participant(request.pairedParticipantId().get()).partyId();
|
|
break;
|
|
}
|
|
case IedPairingState_def::PairedToOps:
|
|
{
|
|
data.pairedOpsId.setValue(request.pairedOpsId().get());
|
|
break;
|
|
}
|
|
case IedPairingState_def::NotPaired:
|
|
break;
|
|
}
|
|
|
|
ret = ifs->updatePairingState(data);
|
|
if (ret.error())
|
|
ERROR_LOG(m_logger) << ret();
|
|
|
|
m_ddsHandler->fts()->pairIedReply(cookie, ret);
|
|
}
|
|
|
|
int HandlerIed::getIdByEquipmentnumber(const int number) const
|
|
{
|
|
for (const auto &obj : m_iedObjMap)
|
|
{
|
|
if (obj->getEquipmentnumber() == number)
|
|
return obj->getId();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int HandlerIed::getPartyIdFromParticipantEquipmentNumber(const int equipmentNumber) const
|
|
{
|
|
const QMap<int, ParticipantManagement::Participant> participants = m_ppmDtc->participants();
|
|
|
|
for (const auto &participant : participants)
|
|
{
|
|
if (participant.equipmentNumber() == equipmentNumber)
|
|
return participant.partyId();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // Namespace GuezIedManagement
|