Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Proxies/Buildings/BuildingControlUnit/VWM/vwm_api.cpp
T
2026-10-03 09:20:13 +02:00

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 &center = 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);
}
}
}