934 lines
30 KiB
C++
Executable File
934 lines
30 KiB
C++
Executable File
|
|
|
|
|
|
#include "vwm_api.h"
|
|
#include <TrainingAreaBase/logger.h>
|
|
#include <utility>
|
|
#include "vwm_deposition.h"
|
|
#include "vwm_xml_reader.h"
|
|
#include "vwm_state_store.h"
|
|
#include "vwm_math.h"
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QMetaType>
|
|
#include <BuildingControlUnit/settings.h>
|
|
#include <QtCore/QFile>
|
|
#include <initializer_list>
|
|
#ifdef LTS_DEBUG
|
|
|
|
# include <QTextStream>
|
|
# include <sstream>
|
|
#endif
|
|
|
|
#include <QtCore/QJsonDocument>
|
|
#include <QtCore/QJsonObject>
|
|
#include <QtCore/QJsonArray>
|
|
#include <QJsonParseError>
|
|
#include <QtCore/QDebug>
|
|
|
|
namespace BuildingControlUnit
|
|
{
|
|
namespace VWM
|
|
{
|
|
|
|
namespace
|
|
{
|
|
// Ensure QPoint3D meta-type is registered for queued connections
|
|
// Qt6: Q_DECLARE_METATYPE is sufficient, no need for qRegisterMetaTypeStreamOperators
|
|
static const bool point3dRegistered = []() {
|
|
return true;
|
|
}();
|
|
}
|
|
|
|
Api::Api(QObject *parent)
|
|
: QObject(parent)
|
|
, m_logger(TrainingAreaBase::getLogger("VWM.Api"))
|
|
, m_lastHitResult(HitResult())
|
|
{
|
|
Q_UNUSED(point3dRegistered);
|
|
}
|
|
|
|
bool Api::init(const QString &chargesConfig,
|
|
const QString &vulBuildingConfig,
|
|
const BuildingBsmConfig &bsmConfig,
|
|
const bool initBuilding)
|
|
{
|
|
DEBUG_LOG(m_logger) << "chargesConfig=" << chargesConfig;
|
|
if (!XmlReader::readCharges(chargesConfig, m_chargeMap))
|
|
return false;
|
|
if (!XmlReader::readBuilding(vulBuildingConfig, m_building))
|
|
return false;
|
|
|
|
|
|
QString fltXml = QString(QStringLiteral("/opt/BuildingModels/StaticMeshes/%1.flt.xml")).arg(m_building.name());
|
|
if (!XmlReader::readBuildingGeometry(fltXml, m_buildingRoomGeometry ))
|
|
{
|
|
ERROR_LOG(m_logger) << "Error reading buildingRoomGeometry from file: " << fltXml << "'";
|
|
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "Building buildingRoomGeometry read successfully from file: " << fltXml << "'";
|
|
|
|
|
|
|
|
int numberOfRemoved = m_building.cleanupPolygons();
|
|
int numberOfDuplicates = m_building.checkPolygons();
|
|
DEBUG_LOG(m_logger) << "Building cleanupPolygons: " << m_building.name() << "' -> " << numberOfRemoved << " points removed";
|
|
if (numberOfDuplicates > 0)
|
|
ERROR_LOG(m_logger) << "Building checkPolygons: " << m_building.name() << "' -> " << numberOfDuplicates << " duplicates";
|
|
else
|
|
DEBUG_LOG(m_logger) << "Building checkPolygons: " << m_building.name() << "' -> " << numberOfDuplicates << " duplicates";
|
|
|
|
|
|
m_bsmMap = bsmConfig.bsmMap;
|
|
m_building.init();
|
|
if (!initBuilding)
|
|
StateStore::loadState(m_building, m_lastHitResult, m_contaminationStates);
|
|
else
|
|
StateStore::saveState(m_building, m_lastHitResult, m_contaminationStates);
|
|
m_lastHitResult.rooms = m_building.roomStates();
|
|
m_lastHitResult.damageState = m_building.damageState();
|
|
m_lastHitResult.damagePercentNew = m_building.damagePercent();
|
|
return true;
|
|
}
|
|
|
|
void Api::onHitOptical(qint64 timeMSec, int sensorId, int originatorId, int chargeTypeId, int deposition1, int deposition2)
|
|
{
|
|
|
|
|
|
const Bsm bsm = m_bsmMap.value(sensorId);
|
|
|
|
if (!bsm.isValid())
|
|
{
|
|
WARNING_LOG(m_logger) << "Given bsm id " << sensorId << " is not in our internal bsm list!";
|
|
return;
|
|
}
|
|
|
|
const LtsUtils::QPoint3D depositionRel = getDeposition(deposition1, deposition2, bsm.direction);
|
|
|
|
Hit hit;
|
|
hit.effectTimeMSec = timeMSec;
|
|
hit.sensorId = sensorId;
|
|
hit.originatorId = originatorId;
|
|
hit.originatorType = OriginatorType::DirectHit;
|
|
hit.chargeTypeId = chargeTypeId;
|
|
hit.position = bsm.position + depositionRel;
|
|
hit.direction = bsm.direction * -1;
|
|
|
|
sendHitToVwm(hit);
|
|
}
|
|
|
|
void Api::onSetEffectStatesRequest(const QString &hostname, const QVector<BcuEffectState> &effectStates)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onSetEffectStatesRequest:"
|
|
<< " hostname= " << hostname
|
|
<< " effectStes.zize=" << effectStates.size();
|
|
|
|
QJsonObject overridesObj;
|
|
|
|
if (hostname.isEmpty())
|
|
{
|
|
for (const BcuEffectState &es : effectStates)
|
|
{
|
|
if (es.type == BcuEffectType::BET_BsmFlash)
|
|
{
|
|
if (es.effectOverride == BcuEffectOverride::BEO_Off)
|
|
{
|
|
overridesObj.insert(QStringLiteral("flash"), 0);
|
|
DEBUG_LOG(m_logger) << "flash = 0";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
QJsonArray relayStateArray;
|
|
for (const BcuEffectState &es : effectStates)
|
|
{
|
|
if (es.effectOverride == BcuEffectOverride::BEO_Auto)
|
|
continue;
|
|
|
|
if (es.type == BcuEffectType::BET_Relay)
|
|
{
|
|
QJsonObject relayStateObj;
|
|
relayStateObj[QString::number(es.id)]=es.effectOverride;
|
|
DEBUG_LOG(m_logger) << "relay = " << QString::number(es.id)
|
|
<< " " << es.effectOverride;
|
|
relayStateArray.append(relayStateObj);
|
|
}
|
|
else if (es.type == BcuEffectType::BET_BackgroundSound)
|
|
{
|
|
overridesObj.insert(QStringLiteral("sound"), es.effectOverride);
|
|
DEBUG_LOG(m_logger) << "sound=" << es.effectOverride;
|
|
}
|
|
}
|
|
overridesObj.insert(QStringLiteral("relays"), relayStateArray);
|
|
}
|
|
|
|
QJsonObject propertiesObj;
|
|
propertiesObj[QStringLiteral("Overrides")] = overridesObj;
|
|
|
|
QString totalString = QString::fromLatin1(QJsonDocument(propertiesObj).toJson(QJsonDocument::Compact));
|
|
DEBUG_LOG(m_logger) << "totalString=" << totalString;
|
|
m_building.setSegmentProperties(hostname,totalString);
|
|
StateStore::saveState(m_building, m_lastHitResult, m_contaminationStates);
|
|
}
|
|
|
|
void Api::dumpHitResult(const Hit &hit, const HitResult &result, const Charge &chargeOrg, const Charge &charge, const QString &context)
|
|
{
|
|
|
|
static QString filename = QString(QLatin1String("/tmp/ShotResult_%1_%2.csv")).arg(m_building.name()).arg(result.effectTimeMSec);
|
|
QFile data(filename);
|
|
if(data.open(QFile::WriteOnly | QIODevice::Append ))
|
|
{
|
|
QTextStream output(&data);
|
|
|
|
output << "EffectTime at," << QDateTime::fromMSecsSinceEpoch(result.effectTimeMSec).toString(QStringLiteral("yyyy.MM.dd hh:mm::ss")) << "\n";
|
|
output << ",(" << result.effectTimeMSec << ")\n";
|
|
|
|
output << "Bldg-Name," << m_building.name() << "\n";
|
|
|
|
output << "Context," << context << "\n";
|
|
|
|
{
|
|
std::ostringstream stream;
|
|
stream << result.originatorType;
|
|
output << "OriginatorType," << QString::fromStdString(stream.str()) << "\n";
|
|
}
|
|
|
|
{
|
|
output << " [BCU]minimumRemainEffectThreshold," << Settings::minimumRemainEffectThreshold() << "\n";
|
|
output << " [BCU]transitionMultiplier," << Settings::transitionMultiplier() << "\n";
|
|
output << " buildingProtectionLevel," << m_building.buildingProtectionLevel() << "\n";
|
|
}
|
|
|
|
output << "ChargeTypeId," << result.chargeTypeId << "\n";
|
|
|
|
output << " Original dstrctvZF/pntrtnWF, "
|
|
<< QString::number(chargeOrg.destructiveFactor) << QStringLiteral("/") << QString::number(chargeOrg.penetrationEffect)<< "\n";
|
|
output << " Current dstrctvZF/pntrtnWF, "
|
|
<< QString::number(charge.destructiveFactor) << QStringLiteral("/") << QString::number(charge.penetrationEffect)<< "\n";
|
|
|
|
output << "OriginatorId," << result.originatorId << "\n";
|
|
|
|
{
|
|
std::ostringstream stream;
|
|
stream << hit.typeOfHit;
|
|
output << "TypeOfHit," << QString::fromStdString(stream.str()) << "\n";
|
|
output << "BelowGround(Hack)," << m_building.isLastHitPositionBelowGroundHack() << "\n";
|
|
}
|
|
|
|
output << "RoomId," << m_building.lastHitPosition().roomId << "\n";
|
|
|
|
{
|
|
std::ostringstream stream;
|
|
stream << result.damageState;
|
|
output << "DamageState," << QString::fromStdString(stream.str()) << "\n";
|
|
}
|
|
|
|
output << "DamagePercentNew," << result.damagePercentNew << "\n";
|
|
output << "DamagePercentOld," << result.damagePercentOld << "\n";
|
|
|
|
output << ",," << "\n";
|
|
|
|
output << "RoomId,damagePercent,remainingEffectPercent,directHit" << "\n";
|
|
for (const HitRoom &hitRoom : std::as_const(result.rooms))
|
|
{
|
|
output << hitRoom.roomId << ","
|
|
<< hitRoom.damagePercent << ","
|
|
<< hitRoom.remainingEffectPercent << ","
|
|
<< hitRoom.directHit << ","
|
|
<< "\n";
|
|
}
|
|
|
|
output << ",," << "\n";
|
|
output << ",," << "\n";
|
|
}
|
|
}
|
|
|
|
void Api::onHitOpticalOC(qint64 timeMSec, int sensorId, int originatorId, int chargeTypeId, DamageState damageState)
|
|
{
|
|
Hit hit;
|
|
const Bsm bsm = m_bsmMap.value(sensorId);
|
|
if (bsm.isValid())
|
|
{
|
|
hit.position = bsm.position;
|
|
hit.direction = bsm.direction * -1;
|
|
}
|
|
hit.sensorId = sensorId;
|
|
hit.effectTimeMSec = timeMSec;
|
|
hit.damageStateOverride = damageState;
|
|
hit.chargeTypeId = chargeTypeId;
|
|
hit.originatorId = originatorId;
|
|
hit.originatorType = OriginatorType::OC;
|
|
|
|
sendHitToVwm(hit);
|
|
}
|
|
|
|
void Api::onHitEcc(int originatorId, DamageState damageState, const QVector<int> &roomIds)
|
|
{
|
|
Hit hit;
|
|
hit.effectTimeMSec = QDateTime::currentMSecsSinceEpoch();
|
|
hit.damageStateOverride = damageState;
|
|
hit.originatorId = originatorId;
|
|
hit.originatorType = OriginatorType::ECC;
|
|
hit.affectedRoomIds = roomIds;
|
|
|
|
sendHitToVwm(hit);
|
|
}
|
|
|
|
void Api::onSetContaminationState(const std::vector<ContaminationState> &contaminationStates)
|
|
{
|
|
DEBUG_LOG(m_logger) << "contaminationStates.size=" << contaminationStates.size();
|
|
|
|
for (const auto &newcs : contaminationStates)
|
|
{
|
|
DEBUG_LOG(m_logger) << "newcs.damagePercentNew=" << newcs.damagePercentNew
|
|
<< " m_lastHitResult.damagePercentNew=" << m_lastHitResult.damagePercentNew;
|
|
if ( newcs.damagePercentNew > m_lastHitResult.damagePercentNew)
|
|
{
|
|
DEBUG_LOG(m_logger) << "newcs.damagePercentNew=" << newcs.damagePercentNew;
|
|
m_lastHitResult.damagePercentNew = newcs.damagePercentNew;
|
|
}
|
|
if (m_contaminationStates.contains(newcs.aesId))
|
|
{
|
|
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
|
|
|
|
QSet<int> roomsSet;
|
|
for (const auto i: std::as_const(newcs.rooms))
|
|
{
|
|
roomsSet.insert(i);
|
|
}
|
|
#else
|
|
QSet<int> roomsSet = QSet<int>(newcs.rooms.begin(), newcs.rooms.end());
|
|
#endif
|
|
const auto &rooms = m_contaminationStates.value(newcs.aesId).rooms;
|
|
for (const auto &oldRoom : rooms)
|
|
roomsSet.insert(oldRoom);
|
|
|
|
ContaminationState contaState;
|
|
contaState.aesId = newcs.aesId;
|
|
contaState.damagePercentNew = newcs.damagePercentNew;
|
|
contaState.rooms = std::vector<int>(roomsSet.begin(), roomsSet.end());
|
|
m_contaminationStates[newcs.aesId] = contaState;
|
|
}
|
|
else
|
|
m_contaminationStates.insert(newcs.aesId, newcs);
|
|
}
|
|
DEBUG_LOG(m_logger) << "onSetContaminationState emit hitResult "
|
|
<< " m_lastHitResult.damagePercentNew=" << m_lastHitResult.damagePercentNew;
|
|
emit hitResult(m_lastHitResult, m_contaminationStates, false);
|
|
StateStore::saveState(m_building, m_lastHitResult, m_contaminationStates);
|
|
}
|
|
|
|
void Api::onResetContaminations()
|
|
{
|
|
m_contaminationStates.clear();
|
|
DEBUG_LOG(m_logger) << "onResetContaminations emit hitResult";
|
|
m_lastHitResult.chargeTypeId = 0;
|
|
m_lastHitResult.originatorId = 0;
|
|
m_lastHitResult.originatorType = BuildingControlUnit::OriginatorType::ECC;
|
|
emit hitResult(m_lastHitResult, m_contaminationStates, false);
|
|
StateStore::saveState(m_building, m_lastHitResult, m_contaminationStates);
|
|
}
|
|
|
|
void Api::onHitCes(const qint64 effectTimeMSec, const int chargeTypeId,
|
|
const int destructiveFactor, const int penetrationFactor,
|
|
const int aesId, const LtsUtils::QPoint3D &posImpact,
|
|
const QString &properties)
|
|
{
|
|
Q_UNUSED(properties);
|
|
|
|
Hit hit;
|
|
hit.effectTimeMSec = effectTimeMSec;
|
|
hit.chargeTypeId = chargeTypeId;
|
|
hit.destructiveFactor = destructiveFactor;
|
|
hit.penetrationFactor = penetrationFactor;
|
|
hit.originatorId = aesId;
|
|
hit.originatorType = OriginatorType::CesHit;
|
|
hit.position = posImpact;
|
|
|
|
|
|
hit.position.setZ(1);
|
|
|
|
INFO_LOG(m_logger) << "XXXXX CES Hit = " << hit;
|
|
|
|
|
|
|
|
BuildingControlUnit::VWM::HitPosition hitPos;
|
|
hitPos.distance = +10000;
|
|
|
|
LtsUtils::QPoint3D posTmp(posImpact);
|
|
posTmp.setZ(10000);
|
|
const LtsUtils::QPoint3D direction(0,0,1);
|
|
const auto hitPosTmp = BuildingControlUnit::VWM::Math::checkBsmPosition(posTmp, direction, m_building.rooms());
|
|
INFO_LOG(m_logger) << "XXXXX hitPosTmp after checkBsmPosition = " << hitPosTmp;
|
|
if (hitPosTmp.state != BuildingControlUnit::VWM::HitPosition::State::NoHit)
|
|
{
|
|
auto room = m_building.rooms().value(hitPosTmp.roomId);
|
|
if (room.isValid())
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
hitPos = hitPosTmp;
|
|
hit.direction = direction * -1;
|
|
hit.position = posTmp;
|
|
}
|
|
}
|
|
|
|
hit.typeOfHit = TypeOfHit::InsideBuilding;
|
|
}
|
|
|
|
|
|
LtsUtils::QPoint3D posImpactOnGround(posImpact);
|
|
posImpactOnGround.setZ(1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NearestWall nearestWall;
|
|
if (hitPos.state == BuildingControlUnit::VWM::HitPosition::State::NoHit)
|
|
{
|
|
INFO_LOG(m_logger) << "XXXXX CES Hit outside building - searching nearest wall ...";
|
|
|
|
double distanceOut = 0;
|
|
LtsUtils::QPoint3D directionWallElementPointToDetonationOut;
|
|
nearestWall = findNearestWall(posImpactOnGround, distanceOut, directionWallElementPointToDetonationOut);
|
|
|
|
if (nearestWall.isValid())
|
|
{
|
|
hitPos.state = BuildingControlUnit::VWM::HitPosition::State::OuterWall;
|
|
hitPos.distance = distanceOut;
|
|
hit.direction = directionWallElementPointToDetonationOut;
|
|
hit.typeOfHit = TypeOfHit::OutsideBuilding;
|
|
}
|
|
}
|
|
|
|
if (hitPos.state == BuildingControlUnit::VWM::HitPosition::State::NoHit)
|
|
{
|
|
WARNING_LOG(m_logger) << "Internal error - could not find impact position on building "
|
|
<< m_building.name() << " with impact pos " << posImpactOnGround;
|
|
}
|
|
INFO_LOG(m_logger) << "XXXXX Aes hit - hit pos: " << posImpactOnGround << " direction: " << hit.direction
|
|
<< " (p=" << hit.direction+posImpactOnGround << ")"
|
|
<< " ; nearestWall.roomId=" << nearestWall.roomId;
|
|
for (auto item : std::as_const(nearestWall.wallElement))
|
|
DEBUG_LOG(m_logger) << "Aes hit - hit pos: " << posImpactOnGround << " direction: " << hit.direction
|
|
<< " (p=" << hit.direction+posImpactOnGround
|
|
<< " - " << item;
|
|
|
|
if (nearestWall.wallElement.isEmpty())
|
|
{
|
|
sendHitToVwm(hit);
|
|
} else {
|
|
|
|
const LtsUtils::QPoint3D ¢er = centerOfWallElement3D(nearestWall.wallElement);
|
|
|
|
INFO_LOG(m_logger) << "XXXXX Aes hit - hit pos: " << posImpactOnGround << " direction: " << hit.direction
|
|
<< ", m_building.name=" << m_building.name()
|
|
<< ", m_building.modelId=" << m_building.modelId()
|
|
<< ", nearestWall.roomId=" << nearestWall.roomId
|
|
<< ", hitPos.distance=" << hitPos.distance
|
|
<< ", center=" << center
|
|
<< ", direction would be " << center-posImpactOnGround;
|
|
|
|
hit.direction = center - posImpactOnGround;
|
|
|
|
sendHitToVwm(hit);
|
|
}
|
|
}
|
|
|
|
NearestWall Api::findNearestWall(const LtsUtils::QPoint3D &detonationLocation, double &distanceOut, LtsUtils::QPoint3D &directionWallElementPointToDetonationOut)
|
|
{
|
|
NearestWall nearestWall;
|
|
distanceOut = std::numeric_limits<double>::max();
|
|
|
|
INFO_LOG(m_logger) << "XXXXX searching nearest wall ...";
|
|
for (const Room &room : std::as_const(m_building.rooms()))
|
|
{
|
|
for (const OuterWall &wall : std::as_const(room.outerWalls()))
|
|
{
|
|
for (const WallElement &we : std::as_const(wall.elements()))
|
|
{
|
|
for (const LtsUtils::QPoint3D &p : std::as_const(we))
|
|
{
|
|
const double dist = (detonationLocation - p).length();
|
|
DEBUG_LOG(m_logger) << "Room[" << room.id() << "]: " << p << ", dist = " << dist;
|
|
if (dist >= distanceOut)
|
|
continue;
|
|
|
|
|
|
distanceOut = dist;
|
|
INFO_LOG(m_logger) << "New min distance " << dist << " Room[" << room.id() << "]: " << p;
|
|
directionWallElementPointToDetonationOut = p - detonationLocation;
|
|
|
|
nearestWall.roomId = room.id();
|
|
nearestWall.wallElement = we;
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nearestWall;
|
|
}
|
|
|
|
void Api::onHitCes3D_Evaluation(const qint64 effectTimeMSec,
|
|
const int chargeTypeId, const int destructiveFactor, const int penetrationFactor,
|
|
const int aesId,
|
|
const LtsUtils::QPoint3D &posImpact,
|
|
const LtsUtils::QPoint3D &detonationLocation,
|
|
const double detonationDistance,
|
|
const QString &properties)
|
|
{
|
|
Q_UNUSED(posImpact);
|
|
Q_UNUSED(properties);
|
|
|
|
Hit hit;
|
|
hit.effectTimeMSec = effectTimeMSec;
|
|
hit.chargeTypeId = chargeTypeId;
|
|
hit.originatorId = aesId;
|
|
hit.originatorType = OriginatorType::CesHit3D;
|
|
hit.position = detonationLocation;
|
|
|
|
|
|
|
|
INFO_LOG(m_logger) << "XXXXXXXXXX CES Hit 3D = " << hit
|
|
<< ", detonationLocation = " << detonationLocation
|
|
<< ", detonationDistance = " << detonationDistance
|
|
<< ", chargeTypeId = " << chargeTypeId
|
|
<< ", destructiveFactor = " << destructiveFactor
|
|
<< ", penetrationFactor = " << penetrationFactor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QVector<int> roomsWithDetonation;
|
|
const bool bIsDelayed = detonationDistance > 0;
|
|
|
|
HitPosition hitPosPreCalculated;
|
|
hitPosPreCalculated.state = HitPosition::State::OuterWall;
|
|
hitPosPreCalculated.outerWallIdx = 0;
|
|
|
|
|
|
if (bIsDelayed)
|
|
{
|
|
roomsWithDetonation = m_buildingRoomGeometry.pointIsInWhichRooms(detonationLocation);
|
|
if (roomsWithDetonation.isEmpty())
|
|
{
|
|
INFO_LOG(m_logger) << "XXXX Building '" << m_building.name() << "'"
|
|
<< ", detonationLocation = " << detonationLocation
|
|
<< " -> found no room";
|
|
} else {
|
|
if (roomsWithDetonation.size() == 1)
|
|
INFO_LOG(m_logger) << "XXXX Building '" << m_building.name() << "'"
|
|
<< ", detonationLocation = " << detonationLocation
|
|
<< " -> found just one room: " << roomsWithDetonation.first();
|
|
else
|
|
INFO_LOG(m_logger) << "XXXX Building '" << m_building.name() << "'"
|
|
<< ", detonationLocation = " << detonationLocation
|
|
<< " -> found several rooms, choose first: " << roomsWithDetonation.first();
|
|
|
|
hitPosPreCalculated.roomId = roomsWithDetonation.first();
|
|
hitPosPreCalculated.exclusionZoneIdx = -1;
|
|
hitPosPreCalculated.distance = 0;
|
|
}
|
|
}
|
|
|
|
if (!bIsDelayed ||
|
|
roomsWithDetonation.isEmpty())
|
|
{
|
|
|
|
double distanceOut = 0;
|
|
LtsUtils::QPoint3D directionWallElementPointToDetonationOut;
|
|
NearestWall nearestWall = findNearestWall(detonationLocation, distanceOut, directionWallElementPointToDetonationOut);
|
|
|
|
INFO_LOG(m_logger) << "XXXXX CES Hit outside building - searching nearest wall ..."
|
|
<< ", detonationDistance = " << detonationDistance
|
|
<< ", nearestWall.roomId = " << nearestWall.roomId;
|
|
|
|
if (nearestWall.isValid())
|
|
{
|
|
hitPosPreCalculated.roomId = nearestWall.roomId;
|
|
hitPosPreCalculated.distance = distanceOut;
|
|
hit.direction = directionWallElementPointToDetonationOut;
|
|
if (bIsDelayed)
|
|
hit.typeOfHit = TypeOfHit::InsideBuilding;
|
|
else
|
|
hit.typeOfHit = TypeOfHit::OutsideBuilding;
|
|
}
|
|
}
|
|
|
|
|
|
HitResult result;
|
|
result.damagePercentOld = m_building.damagePercent();
|
|
result.effectTimeMSec = effectTimeMSec;
|
|
|
|
|
|
|
|
|
|
QString context(QStringLiteral("onHitCes-3D - "));
|
|
|
|
const Charge &chargeOrg = chargeFromChargeTypeId(chargeTypeId);
|
|
Charge charge = chargeOrg;
|
|
charge.destructiveFactor = destructiveFactor;
|
|
charge.penetrationEffect = penetrationFactor;
|
|
if (detonationDistance>0)
|
|
{
|
|
charge.destructiveFactor *= 2.1;
|
|
charge.penetrationEffect *= 2.1;
|
|
context += QStringLiteral("FtDelay");
|
|
} else if (detonationDistance<0)
|
|
{
|
|
charge.destructiveFactor *= 0.55;
|
|
charge.penetrationEffect *= 0.55;
|
|
context += QStringLiteral("FtProximity");
|
|
} else {
|
|
context += QStringLiteral("FtImpact");
|
|
}
|
|
|
|
bool hitSuccess = m_building.hit(LtsUtils::QPoint3D(), LtsUtils::QPoint3D(),
|
|
charge, &hitPosPreCalculated);
|
|
|
|
INFO_LOG(m_logger) << "onHitCes3D_Evaluation hitSuccess=" << hitSuccess;
|
|
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = m_building.damageState();
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
result.chargeTypeId = chargeTypeId;
|
|
result.originatorType = OriginatorType::CesHit;
|
|
result.originatorId = aesId;
|
|
|
|
#ifdef LTS_DEBUG
|
|
|
|
dumpHitResult(hit, result, chargeOrg, charge, context);
|
|
#endif
|
|
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
}
|
|
|
|
void Api::onFortifyFactorRequest(int originatorId, const QVector<FortifyInfo> &foritfyInfos)
|
|
{
|
|
|
|
HitResult result;
|
|
result.effectTimeMSec = QDateTime::currentMSecsSinceEpoch();
|
|
result.originatorId = originatorId;
|
|
result.originatorType = OriginatorType::ECC;
|
|
result.damagePercentOld = m_building.damagePercent();
|
|
|
|
m_building.setFortifyInfo(foritfyInfos);
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = m_building.damageState();
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
|
|
m_lastHitResult = result;
|
|
}
|
|
|
|
void Api::onRelayStatesRequest(const QString &hostname, const QVector<RelayStateInfo> &relayStateInfos)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onRelayStateRequest hostname=" << hostname
|
|
<< " only hanlde in svcBuildings"
|
|
<< " relayStateInfos.size()=" << relayStateInfos.size();
|
|
}
|
|
|
|
void Api::sendOverrides()
|
|
{
|
|
const QMap<QString, QString> &segmentPropertyMap = m_building.segmentPropertyMap();
|
|
QVector<BcuEffectState> effectStates;
|
|
for (auto it = segmentPropertyMap.cbegin(); it != segmentPropertyMap.cend(); ++it)
|
|
{
|
|
QString hostname = it.key();
|
|
QString properties = it.value();
|
|
effectStates.clear();
|
|
QJsonParseError parserError;
|
|
const QJsonDocument doc(QJsonDocument::fromJson(properties.toUtf8(), &parserError));
|
|
|
|
if (parserError.error != QJsonParseError::NoError)
|
|
continue;
|
|
|
|
QJsonObject docObj = QJsonObject(doc.object());
|
|
|
|
DEBUG_LOG(m_logger) << "sendOverrides hostname=" << hostname
|
|
<< " properties=" << properties;
|
|
|
|
|
|
|
|
|
|
{
|
|
DEBUG_LOG(m_logger) << "sendOverrides hostname=" << hostname
|
|
<< " properties=" << properties << "BCU-Flash";
|
|
BcuEffectState bcuEffectState;
|
|
bcuEffectState.id = 1;
|
|
bcuEffectState.type = BcuEffectType::BET_BsmFlash;
|
|
BcuEffectOverride eo = BcuEffectOverride::BEO_Auto;
|
|
|
|
QJsonObject overrideObj;
|
|
QJsonObject flashObj;
|
|
if (docObj.contains(QStringLiteral("Overrides")))
|
|
{
|
|
overrideObj = docObj[QStringLiteral("Overrides")].toObject();
|
|
}
|
|
|
|
if (overrideObj.contains(QStringLiteral("flash")))
|
|
{
|
|
flashObj = docObj[QStringLiteral("flash")].toObject();
|
|
eo = static_cast<BuildingControlUnit::BcuEffectOverride>(flashObj.value(QStringLiteral("flash")).toInt());
|
|
}
|
|
if (eo != BcuEffectOverride::BEO_Auto)
|
|
{
|
|
DEBUG_LOG(m_logger) << "bcuEffectState.effectOverride = eo" << eo;
|
|
bcuEffectState.effectOverride = eo;
|
|
emit newOverrides({}, {bcuEffectState});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
QJsonObject overridesObj = docObj.value(QStringLiteral("Overrides")).toObject();
|
|
QJsonArray arrEgcus = overridesObj.value(QStringLiteral("relays")).toArray();
|
|
for (int i=0; i<arrEgcus.size(); ++i)
|
|
{
|
|
QJsonObject objState = arrEgcus[i].toObject();
|
|
QStringList keys = objState.keys();
|
|
QString relayIdString = keys.at(0);
|
|
int relayId = relayIdString.toInt();
|
|
if (!objState.contains(relayIdString))
|
|
continue;
|
|
BcuEffectOverride eo = static_cast<BuildingControlUnit::BcuEffectOverride>(objState.value(relayIdString).toInt());
|
|
if (eo == BcuEffectOverride::BEO_Auto)
|
|
continue;
|
|
BuildingControlUnit::BcuEffectState bcuEffectState;
|
|
bcuEffectState.id = relayId;
|
|
bcuEffectState.type = BcuEffectType::BET_Relay;
|
|
bcuEffectState.effectOverride = eo;
|
|
effectStates.append(bcuEffectState);
|
|
}
|
|
|
|
{
|
|
BuildingControlUnit::BcuEffectState bcuEffectState;
|
|
bcuEffectState.id = 1;
|
|
bcuEffectState.type = BcuEffectType::BET_BackgroundSound;
|
|
BcuEffectOverride eo = BcuEffectOverride::BEO_Auto;
|
|
if (overridesObj.contains(QStringLiteral("sound")))
|
|
eo = static_cast<BuildingControlUnit::BcuEffectOverride>(overridesObj.value(QStringLiteral("sound")).toInt());
|
|
if (eo != BcuEffectOverride::BEO_Auto)
|
|
{
|
|
bcuEffectState.effectOverride = eo;
|
|
effectStates.append(bcuEffectState);
|
|
}
|
|
}
|
|
emit newOverrides(hostname,effectStates);
|
|
}
|
|
}
|
|
|
|
void Api::sendHitToVwm(const Hit &hit)
|
|
{
|
|
m_hitsSinceLastInit += hit;
|
|
|
|
INFO_LOG(m_logger) << " XXXXX sendHitToVwm, Incoming hit=" << hit
|
|
<< " hit.chargeTypeId=" << hit.chargeTypeId
|
|
<< " originatorId=" << hit.originatorId
|
|
<< " typeOfHit=" << hit.typeOfHit;
|
|
|
|
HitResult result;
|
|
result.sensorId = hit.sensorId;
|
|
result.effectTimeMSec = hit.effectTimeMSec;
|
|
result.originatorId = hit.originatorId;
|
|
result.originatorType = hit.originatorType;
|
|
result.damagePercentOld = m_building.damagePercent();
|
|
switch (hit.originatorType)
|
|
{
|
|
case OriginatorType::CesHit:
|
|
case OriginatorType::CesHit3D:
|
|
case OriginatorType::DirectHit:
|
|
{
|
|
Charge charge, chargeOrg;
|
|
charge = chargeFromChargeTypeId(hit.chargeTypeId);
|
|
if ( ( hit.originatorType == OriginatorType::CesHit)
|
|
|| ( hit.originatorType == OriginatorType::CesHit3D))
|
|
{
|
|
|
|
charge.destructiveFactor = hit.destructiveFactor;
|
|
charge.penetrationEffect = hit.penetrationFactor;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "charge=" << charge.chargeTypeId
|
|
<< " destructiveFactor" << charge.destructiveFactor
|
|
<< " penetrationEffect" << charge.penetrationEffect;
|
|
chargeOrg = charge;
|
|
result.chargeTypeId = hit.chargeTypeId;
|
|
result.effectSoundId = charge.effectSoundId;
|
|
|
|
if ( charge.destructiveFactor == 0
|
|
&& !charge.showHitLine)
|
|
return;
|
|
|
|
if ((hit.originatorType == OriginatorType::CesHit) &&
|
|
(hit.typeOfHit == TypeOfHit::OutsideBuilding))
|
|
{
|
|
|
|
LtsUtils::QPoint3D dummyDist = hit.position - hit.direction;
|
|
|
|
INFO_LOG(m_logger) << " XXXX CES outside bulding, reduce charge: " << charge
|
|
<< ", hit.position " << hit.position
|
|
<< ", hit.direction " << hit.direction
|
|
<< ", dummyDist " << dummyDist.length();
|
|
charge.destructiveFactor *= 0.5;
|
|
charge.penetrationEffect *= 0.5;
|
|
INFO_LOG(m_logger) << " XXXX CES outside bulding, reduce charge: " << charge;
|
|
}
|
|
|
|
if (!charge.isValid())
|
|
{
|
|
if (hit.originatorType == OriginatorType::CesHit)
|
|
WARNING_LOG(m_logger) << "Charge with id " << hit.chargeTypeId
|
|
<< " not configured - can not caluclate impact effect for this charge";
|
|
else
|
|
{
|
|
WARNING_LOG(m_logger) << "Direct charge with id " << hit.chargeTypeId
|
|
<< " not configured - can not caluclate impact effect for this charge";
|
|
emit unknownDirectHit(hit.sensorId);
|
|
}
|
|
break;
|
|
}
|
|
if (!m_building.hit(hit.position, hit.direction, charge))
|
|
{
|
|
WARNING_LOG(m_logger) << "Detected hit was outside of the building - ignored";
|
|
break;
|
|
}
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = m_building.damageState();
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
|
|
#ifdef LTS_DEBUG
|
|
|
|
dumpHitResult(hit, result, chargeOrg, charge, QStringLiteral("default"));
|
|
#endif
|
|
|
|
m_lastHitResult = result;
|
|
break;
|
|
}
|
|
case OriginatorType::ECC:
|
|
case OriginatorType::OC:
|
|
{
|
|
if (!hit.affectedRoomIds.isEmpty())
|
|
{
|
|
|
|
m_building.setRoomState(hit.damageStateOverride, hit.affectedRoomIds);
|
|
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = m_building.damageState();
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
|
|
m_lastHitResult = result;
|
|
break;
|
|
}
|
|
switch (hit.damageStateOverride)
|
|
{
|
|
case DamageState::NoDamage:
|
|
{
|
|
DEBUG_LOG(m_logger) << "Hit OC/ECC with NoDamage";
|
|
m_building.init();
|
|
|
|
result.effectSoundId = Settings::effectSoundIdEccActivate();
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = DamageState::NoDamage;
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
|
|
#ifdef LTS_DEBUG
|
|
|
|
dumpHitResult(hit, result, Charge(), Charge(), QStringLiteral("Reset by ECC/OC"));
|
|
#endif
|
|
|
|
m_hitsSinceLastInit.clear();
|
|
m_hitsSinceLastInit += hit;
|
|
|
|
m_lastHitResult = result;
|
|
break;
|
|
}
|
|
case DamageState::Destroyed:
|
|
{
|
|
DEBUG_LOG(m_logger) << "Hit OC/ECC with Destroyed";
|
|
const DamageState dsOld = m_building.damageState();
|
|
|
|
m_building.destroyAllRooms();
|
|
|
|
result.effectSoundId = Settings::effectSoundIdEccDestroy();
|
|
result.rooms = m_building.roomStates();
|
|
result.damageState = DamageState::Destroyed;
|
|
result.damagePercentNew = m_building.damagePercent();
|
|
|
|
if (dsOld != DamageState::Destroyed)
|
|
{
|
|
emit hitResult(result, m_contaminationStates, true);
|
|
|
|
#ifdef LTS_DEBUG
|
|
|
|
dumpHitResult(hit, result, Charge(), Charge(), QStringLiteral("Destroyed by ECC/OC"));
|
|
#endif
|
|
}
|
|
|
|
m_lastHitResult = result;
|
|
break;
|
|
}
|
|
default:
|
|
WARNING_LOG(m_logger) << "Invalid damage state override given: " << hit.damageStateOverride;
|
|
result = HitResult();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case OriginatorType::Unknown:
|
|
WARNING_LOG(m_logger) << "Unknown originator type given: " << hit.originatorType;
|
|
result = HitResult();
|
|
break;
|
|
}
|
|
StateStore::saveState(m_building, m_lastHitResult, m_contaminationStates);
|
|
}
|
|
|
|
|
|
const Charge Api::chargeFromChargeTypeId(int chargeTypeId) const
|
|
{
|
|
|
|
if (chargeTypeId > 4000)
|
|
{
|
|
chargeTypeId %= 1000;
|
|
chargeTypeId += 2000;
|
|
}
|
|
|
|
for (auto i = m_chargeMap.cbegin(), end = m_chargeMap.cend(); i != end; ++i)
|
|
{
|
|
TRACE_LOG(m_logger) << "key=" << i.key() << " value=" << i.value();
|
|
if ( chargeTypeId == i.value().id)
|
|
return m_chargeMap.value(i.key());
|
|
}
|
|
|
|
return m_chargeMap.value(chargeTypeId);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|