Files
qt5-legatus-legacy/Lts/Services/CentralEffectSimulation/Plugins/Buildings/handler_contamination.cpp
T
2026-08-13 18:33:10 +02:00

520 lines
22 KiB
C++
Executable File

/**
* Handler for player data
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2017-02-20
*/
#include "handler_contamination.h"
#include <CentralEffectSimulation/handler_participant.h>
#include <CentralEffectSimulation/handler_building_data.h>
#include <QPainterPath>
#include <QPolygonF>
namespace CesBuildings
{
HandlerContamination::HandlerContamination(const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler,
QObject *parent)
: QObject(parent)
, m_ddsHandler(ddsHandler)
, m_logger(CentralEffectSimulation::getLogger("Plugin.Buildings.Buildings"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerContamination::~HandlerContamination()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerContamination::init()
{
auto dtcAes = m_ddsHandler->dtcAes();
auto ftcBld = m_ddsHandler->ftcBld();
auto dtcBld = m_ddsHandler->dtcBld();
ftcBld->enableSetBuildingContaminationState();
////////////////////////////
/// \brief Get 'static' data for all BCContaminations
///
dtcAes->waitForHistoricalDataBCContamination(1000);
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged,
this, &HandlerContamination::onBCContaminationChanged);
// Get 'static' data for all BCContaminations
{
auto cb = [this](const AreaEffectSimulation::BCContamination & bcc)
{
onBCContaminationChanged(bcc, DataModification::eAdded);
};
if (!dtcAes->bCContaminations(cb))
return false;
}
////////////////////////////
/// \brief Get 'static' data for all AesItemView
///
dtcAes->waitForHistoricalDataAesItemView(1000);
connect(dtcAes.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerContamination::onAesItemViewChanged);
// Get 'static' data for all AesItemView
{
auto cb = [this](const AreaEffectSimulation::AesItemView & aiv)
{
onAesItemViewChanged(aiv, DataModification::eAdded);
};
if (!dtcAes->aesItemViews(cb))
return false;
}
////////////////////////////
/// \brief Get data for all Building States
///
dtcBld->waitForHistoricalDataBuildingState(1000);
connect(dtcBld.get(), &Buildings::Interface::Dtc::buildingStateChanged,
this, &HandlerContamination::onBuildingStateChanged);
// Get 'static' data for all buildingStates
{
auto cb = [this](const Buildings::BuildingState & bs)
{
onBuildingStateChanged(bs, DataModification::eAdded);
};
if (!dtcBld->buildingStates(cb))
return false;
}
return true;
} // end of init()
//-----------------------------------------------------------------------------
// ///////////////////////////////////
// A E S (AreaEffectSimulation)
// ///////////////////////////////////
void HandlerContamination::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
if (modType != DataModification::eAdded &&
modType != DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "Got AesItemView: " << data.effectStateId()
<< " data.aesItemId()" << data.aesItemId()
<< " data.aesTypeId()" << data.aesTypeId();
switch (data.effectStateId().underlying())
{
case AreaEffectSimulation::AesEffectState_def::EsActivated:
{
double chargeHeight; // Alle Stockwerke beruecksichtigen
double chargeDepression; // Alle Kellerraeume beruecksichtigen
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtNContamination)
{
chargeHeight = 500.0; // Alle Stockwerke beruecksichtigen
chargeDepression = -1000.0; // Alle Kellerraeume beruecksichtigen
DEBUG_LOG(m_logger) << "AtNContamination: chargeHeight=" << chargeHeight
<< " chargeDepression=" << chargeDepression;
}
else
{
chargeHeight = m_aesBCContaminations.value(data.aesItemId()).upperOffset();
chargeDepression = m_aesBCContaminations.value(data.aesItemId()).lowerOffset();
DEBUG_LOG(m_logger) << "BCContaminations: chargeHeight=" << chargeHeight
<< " chargeDepression=" << chargeDepression;
}
if (data.aesTypeId() != AreaEffectSimulation::AesType_def::AtBCContamination
&& data.aesTypeId() != AreaEffectSimulation::AesType_def::AtNContamination)
{
DEBUG_LOG(m_logger) << "return1: AesItemView: " << data.effectStateId();
return;
}
// Gebaeude die von einem sesshaften Kampfstoff betroffen sind, müssen als kontaminiert gelten.
const ProjectDataManagement::VolatilityType volatilityType = m_ddsHandler->volatilityTypeId(data.chargeStruct());
if (volatilityType != ProjectDataManagement::VolatilityType_def::Resident
&& data.aesTypeId() != AreaEffectSimulation::AesType_def::AtNContamination)
{
DEBUG_LOG(m_logger) << "return2: AesItemView: " << data.effectStateId();
return;
}
DEBUG_LOG(m_logger) << "HandlerContamination::onAesItemViewChanged data.aesItemId=" << data.aesItemId();
auto dtcIsm = m_ddsHandler->dtcIsm();
// Form in UTM-Polygon wandeln
int exerciseId = m_ddsHandler->getExerciseId();
QPolygonF polygonGeo;
// Convert Form to UTM
for (unsigned int i = 0; i < data.form().size(); i++)
polygonGeo.append(QPointF(data.form()[i].lon(), data.form()[i].lat()));
bool completeEnclosed = true;
bool isGroundLevel = false;
const QVector<int> &buildingIds = dtcIsm.get()->getBuildingByPolygon(exerciseId, polygonGeo, completeEnclosed, isGroundLevel);
DEBUG_LOG(m_logger) << "buildingIds.size=" << buildingIds.size();
for (int buildingId : qAsConst(buildingIds))
{
calculateBuilding(buildingId, data, chargeHeight, chargeDepression);
}
//
break;
}
default:
break;
}
}
void HandlerContamination::calculateBuilding(int buildingId,
const AreaEffectSimulation::AesItemView &data,
double chargeHeight,
double chargeDepression)
{
auto dtcIsm = m_ddsHandler->dtcIsm();
QVector<int> effectedRooms;
QVector<int> effectedFloors;
if (data.aesTypeId() != AreaEffectSimulation::AesType_def::AtBCContamination
&& data.aesTypeId() != AreaEffectSimulation::AesType_def::AtNContamination)
{
DEBUG_LOG(m_logger) << "return: NO BC-Contamination AesItemView: " << data.effectStateId();
return;
}
const ProjectDataManagement::VolatilityType volatilityType = m_ddsHandler->volatilityTypeId(data.chargeStruct());
if ( volatilityType != ProjectDataManagement::VolatilityType_def::Resident
&& data.aesTypeId() != AreaEffectSimulation::AesType_def::AtNContamination)
return;
DEBUG_LOG(m_logger) << "buildingId=" << buildingId;
effectedRooms.clear();
effectedFloors = getEffectedFloors(buildingId, chargeHeight, chargeDepression);
for (int effectedFloor : qAsConst(effectedFloors))
effectedRooms += dtcIsm.get()->getRoomIdsFromFloorId(buildingId, effectedFloor);
if (!effectedRooms.empty())
{
DEBUG_LOG(m_logger) << "effectedRooms.size=" << effectedRooms.size();
ProjectDataManagement::VulnerabilityState vulnerabilityState = ProjectDataManagement::VulnerabilityState::CombatReady;
// [GUEZE-2752] N-Contamination: Only Contamionation inside Range1
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtNContamination
&& !isInsideRange1(buildingId, data.aesItemId()))
{
DEBUG_LOG(m_logger) << "!isInsideRange1 buildingId=" << buildingId;
return;
}
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtBCContamination
&& !isInsideBCContamination(buildingId, data.aesItemId()))
{
DEBUG_LOG(m_logger) << "AtBCContamination !isInsideRange buildingId=" << buildingId << "return";
return;
}
if (data.aesTypeId() == AreaEffectSimulation::AesType_def::AtNContamination
&& isInsideInnerCircle(buildingId, data.aesItemId()))
{
constexpr int TIME_2_SECONDS = 2000;
// Only actual explosion not old N-Fields
if ( m_ddsHandler->dtcCore()->partitionTime().timeStamp() < (data.effectStart() + TIME_2_SECONDS))
{
DEBUG_LOG(m_logger) << "VulnerabilityState=CatastrophicKill";
vulnerabilityState = ProjectDataManagement::VulnerabilityState::CatastrophicKill;
}
}
DEBUG_LOG(m_logger) << "setBuildingContaminationStateRequest buildingId=" << buildingId
<< " data.aesTypeId()=" << data.aesTypeId()
<< " isInsideInnerCircle(buildingId, data.aesItemId()=" << isInsideInnerCircle(buildingId, data.aesItemId());
{
DEBUG_LOG(m_logger) << "isInsideInnerCircle buildingId=" << buildingId;
setBuildingContaminationStateRequest(buildingId, data.aesItemId(), vulnerabilityState, effectedRooms);
}
}
}
// ///////////////////////////////////
// B L D (Buildings)
// ///////////////////////////////////
void HandlerContamination::onBuildingStateChanged(const Buildings::BuildingState &data, DataModification modType)
{
if (modType != DataModification::eAdded
&& modType != DataModification::eModified)
return;
INFO_LOG(m_logger) << "onBuildingStateChanged"
<< " id=" << data.id()
<< " aesId=" << data.aesId()
<< " contaminationStates().size=" << data.contaminationStates().size();
// Reset:
if (data.contaminationStates().empty())
m_contaminationState.remove(data.id());
// If a building is reset in an area where there is an active nuclear weapon, it will not be recontaminated -> in this case,
// regular checks should be carried out to see if the building needs to be contaminated
// Buildings that have been reset or decontaminated will be recontaminated as long as the nuclear weapon is still present
const auto aesItemViews = m_ddsHandler->dtcAes()->aesItemViews();
for (const auto &aesItemView : qAsConst(aesItemViews))
{
if (aesItemView.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated)
{
DEBUG_LOG(m_logger) << "Wirkfeld aesItemView.id()=" << aesItemView.id()
<< " aesItemView.aesItemId()=" << aesItemView.aesItemId()
<< " fuer Gebaeude " << data.id() << " neu berechnen";
double chargeHeight = 0.0;
double chargeDepression = 0.0;
if (aesItemView.aesTypeId() == AreaEffectSimulation::AesType_def::AtNContamination)
{
chargeHeight = 500.0; // Alle Stockwerke beruecksichtigen
chargeDepression = -1000.0; // Alle Kellerraeume beruecksichtigen
DEBUG_LOG(m_logger) << "AtNContamination: chargeHeight=" << chargeHeight
<< " chargeDepression=" << chargeDepression;
}
else
{
if ( m_aesBCContaminations.contains(aesItemView.aesItemId()))
{
chargeHeight = m_aesBCContaminations.value(aesItemView.aesItemId()).upperOffset();
chargeDepression = m_aesBCContaminations.value(aesItemView.aesItemId()).lowerOffset();
DEBUG_LOG(m_logger) << "BCContaminations: chargeHeight=" << chargeHeight
<< " chargeDepression=" << chargeDepression;
}
else
{
DEBUG_LOG(m_logger) << "BCContaminations: m_aesBCContaminations not contains:"<< aesItemView.aesItemId();
}
}
// [GUEZE-2527] Fehler bei A-Waffen und Dekontaminierung
auto dtcIsm = m_ddsHandler->dtcIsm();
int exerciseId = m_ddsHandler->getExerciseId();
QPolygonF polygonGeo;
polygonGeo.reserve(aesItemView.form().size());
// Convert Form to UTM
for (const auto point : aesItemView.form())
polygonGeo.append(QPointF(point.lon(), point.lat()));
bool completeEnclosed = true;
bool isGroundLevel = false;
const QVector<int> &buildingIds = dtcIsm.get()->getBuildingByPolygon(exerciseId, polygonGeo, completeEnclosed, isGroundLevel);
if (buildingIds.contains(data.id() ))
calculateBuilding(data.id(), aesItemView, chargeHeight, chargeDepression);
else
DEBUG_LOG(m_logger) << "Building not in Area BuildingId=" << data.id();
}
}
}
void HandlerContamination::setBuildingContaminationStateRequest(int buildingId,
int aesId,
ProjectDataManagement::VulnerabilityState vulnerabilityState,
QVector<int> rooms)
{
Buildings::ContaminationState contaminationState;
contaminationState.aesId(aesId);
contaminationState.vulnerabilityState(vulnerabilityState);
// contaminationState.vulne
DEBUG_LOG(m_logger) << "setBuildingContaminationStateRequest"
<< " buildingId=" << buildingId
<< " aesId=" << aesId
<< " vulnerabilityState=" << vulnerabilityState
<< " rooms.size=" << rooms.size();
contaminationState.rooms().resize(rooms.size());
for (int i = 0; i < rooms.size(); i++)
{
contaminationState.rooms()[i] = rooms[i];
contaminationState.vulnerabilityState(vulnerabilityState);
DEBUG_LOG(m_logger) << " room[" << i << "]=" << rooms[i];
}
Buildings::SetBuildingContaminationStateRequest requestContamination(
buildingId,
contaminationState
);
// Senden, wenn fuer das Gebaeude noch keine Contamination vorhanden ist.
if (!m_contaminationState.contains(buildingId))
{
DEBUG_LOG(m_logger) << "Neue Kontamination BuildingId=" << buildingId
<< " requestContamination aesId=" << requestContamination.contaminationState().aesId();
m_ddsHandler->ftcBld()->setBuildingContaminationStateRequest(requestContamination, this);
m_contaminationState.insert(buildingId, requestContamination);
return;
}
// Senden, wenn sich die Kontamination geaendert hat.
Buildings::SetBuildingContaminationStateRequest lastRequestContamination = m_contaminationState.value(buildingId);
if (lastRequestContamination != requestContamination)
{
DEBUG_LOG(m_logger) << "Änderung der Kontamination BuildingId=" << buildingId
<< " aesId=" << requestContamination.contaminationState().aesId();
m_ddsHandler->ftcBld()->setBuildingContaminationStateRequest(requestContamination, this);
m_contaminationState.insert(buildingId, requestContamination);
return;
}
DEBUG_LOG(m_logger) << "Keine Änderung der Kontamination BuildingId=" << buildingId;
}
QVector<int> HandlerContamination::getEffectedFloors(const int buildingId, const int chargeHeight, const int depression)
{
auto dtcIsm = m_ddsHandler->dtcIsm();
DEBUG_LOG(m_logger) << "Contaminated-Building=" << buildingId
<< " chargeHeight=" << chargeHeight
<< " depression=" << depression;
const QVector<int> &floorIds = dtcIsm->getFloorIdsFromBuilding(buildingId);
QVector<int> effectedFloorIds;
effectedFloorIds.reserve(floorIds.size());
for (int floorId : qAsConst(floorIds))
{
double halfFloorHeight = dtcIsm->getHalfFloorHeight(buildingId, floorId);
DEBUG_LOG(m_logger) << " floorId=" << floorId
<< " halfFloorHeight=" << halfFloorHeight
<< " chargeHeight=" << chargeHeight
<< " depression=" << depression;
if (depression < 0) // Kellerraeme beruecksichtigen
{
if (halfFloorHeight > depression)
DEBUG_LOG(m_logger) << "Checke Obergrenze";
else
{
DEBUG_LOG(m_logger) << "halfFloorHeight > depression floorId=" << floorId << " continue";
continue;
}
}
else
{
if (halfFloorHeight < 0) // Kellerraeume nicht beruecksichtigen
{
DEBUG_LOG(m_logger) << "depression ist nicht < 0"
<< " Kelleretagen nicht beruecksichtigen floorId=" << floorId << " continue";
continue;
}
}
if (halfFloorHeight < chargeHeight)
effectedFloorIds.append(floorId);
}
return effectedFloorIds;
}
void HandlerContamination::onSetBuildingContaminationStateReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
WARNING_LOG(m_logger) << "Can not send ContaminationStates on building to building service: " << replyObject();
else
DEBUG_LOG(m_logger) << "onSetBuildingContaminationStateReply for id " << replyObject.id() << " success\n"
<< " replyObject.str() = " << replyObject.str();
}
void HandlerContamination::onBCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType)
{
if ((modType == DataModification::eRemoved)
|| data.isDeleted())
m_aesBCContaminations.remove(data.id());
else
m_aesBCContaminations.insert(data.id(), data);
}
// Returns true if the building is in the inner circle at the time of impact and should fail as a result.
// Liefert true, wenn sich das Gebaeude beim Einschlag im inneren Kreis befindet und daraufhin ausfallen soll.
bool HandlerContamination::isInsideInnerCircle(int buildingId, int aesId)
{
InfrastructureManagement::MainStructureElement mse = m_ddsHandler->dtcIsm()->mainStructureElement(buildingId);
AreaEffectSimulation::NContamination nContamination = m_ddsHandler->dtcAes()->nContamination(aesId);
DEBUG_LOG(m_logger) << "isInsideInnerCircle borderLengthInner=" << nContamination.borderLengthInner()
<< " borderLengthOuter=" << nContamination.borderLengthOuter()
<< " impactRadius=" << nContamination.impactRadius();
int zone = -1;
const QPointF impactPositionUtm = CentralEffectSimulation::geographic2Utm(nContamination.impactPosition(), zone);
DEBUG_LOG(m_logger) << "impactPositionUtm=" << impactPositionUtm.x() << "/" << impactPositionUtm.y()
<< " zone=" << zone;
const QPointF buildingPositionUtm = CentralEffectSimulation::geographic2Utm(mse.geoPosition(), zone);
DEBUG_LOG(m_logger) << "buildingPositionUtm=" << buildingPositionUtm.x() << "/" << buildingPositionUtm.y();
double squareDistance = CentralEffectSimulation::getSquareDistance(impactPositionUtm, buildingPositionUtm);
if (squareDistance < nContamination.impactRadius() * nContamination.impactRadius())
{
DEBUG_LOG(m_logger) << "isInsideInnerCircle return true";
return true;
}
else
{
DEBUG_LOG(m_logger) << "isInsideInnerCircle return false";
return false;
}
}
// Returns true if the building is in the inner circle at the time of impact and should fail as a result.
// Liefert true, wenn sich das Gebaeude beim Einschlag im inneren Kreis befindet und daraufhin ausfallen soll.
bool HandlerContamination::isInsideBCContamination(int buildingId, int aesId)
{
InfrastructureManagement::MainStructureElement mse = m_ddsHandler->dtcIsm()->mainStructureElement(buildingId);
//AreaEffectSimulation::NContamination nContamination = m_ddsHandler->dtcAes()->nContamination(aesId);
AreaEffectSimulation::BCContamination bcContamination = m_ddsHandler->dtcAes()->bCContamination(aesId);
DEBUG_LOG(m_logger) << " impactRadius=" << bcContamination.impactRadius();
int zone = -1;
const QPointF impactPositionUtm = CentralEffectSimulation::geographic2Utm(bcContamination.impactPosition(), zone);
DEBUG_LOG(m_logger) << "impactPositionUtm=" << impactPositionUtm.x() << "/" << impactPositionUtm.y()
<< " zone=" << zone;
const QPointF buildingPositionUtm = CentralEffectSimulation::geographic2Utm(mse.geoPosition(), zone);
DEBUG_LOG(m_logger) << "buildingPositionUtm=" << buildingPositionUtm.x() << "/" << buildingPositionUtm.y();
double squareDistance = CentralEffectSimulation::getSquareDistance(impactPositionUtm, buildingPositionUtm);
if (squareDistance < bcContamination.impactRadius() * bcContamination.impactRadius())
{
DEBUG_LOG(m_logger) << "isInsideInnerCircle return true";
return true;
}
else
{
DEBUG_LOG(m_logger) << "isInsideInnerCircle return false";
return false;
}
}
// Returns true if the building is located in range 1 at the time of impact and is to be contaminated as a result.
// Liefert true, wenn sich das Gebaeude beim Einschlag im Bereich 1 befindet und daraufhin kontaminiert werden soll.
bool HandlerContamination::isInsideRange1(int buildingId, int aesId)
{
InfrastructureManagement::MainStructureElement mse = m_ddsHandler->dtcIsm()->mainStructureElement(buildingId);
AreaEffectSimulation::NContamination nContamination = m_ddsHandler->dtcAes()->nContamination(aesId);
DEBUG_LOG(m_logger) << "isInsideRange1 borderLengthInner=" << nContamination.borderLengthInner()
<< " borderLengthOuter=" << nContamination.borderLengthOuter()
<< " impactRadius=" << nContamination.impactRadius();
int zone = -1;
const QPointF impactPositionUtm = CentralEffectSimulation::geographic2Utm(nContamination.impactPosition(), zone);
DEBUG_LOG(m_logger) << "impactPositionUtm=" << impactPositionUtm.x() << "/" << impactPositionUtm.y()
<< " zone=" << zone;
const QPointF buildingPositionUtm = CentralEffectSimulation::geographic2Utm(mse.geoPosition(), zone);
DEBUG_LOG(m_logger) << "buildingPositionUtm=" << buildingPositionUtm.x() << "/" << buildingPositionUtm.y();
double squareDistance = CentralEffectSimulation::getSquareDistance(impactPositionUtm, buildingPositionUtm);
if (squareDistance < nContamination.borderLengthInner() * nContamination.borderLengthInner())
{
DEBUG_LOG(m_logger) << "isInsideRange1 return true";
return true;
}
else
{
DEBUG_LOG(m_logger) << "isInsideRange1 return false";
return false;
}
}
} // namespace CesBuildings