/** * @RDE_HEADER@ * Api for VWM * $Id$ * $HeadURL$ * $Author$ * Created by Gerlinde Rudolphus, SLSA, 2013-03-05 */ #include "vwm_api.h" #include "vwm_deposition.h" #include "vwm_xml_reader.h" #include "vwm_state_store.h" #include "vwm_math.h" #include #include #include #include #ifdef LTS_DEBUG // For ShotResult.cvs # include # include #endif // LTS_DEBUG #include #include #include #include #include namespace BuildingControlUnit { namespace VWM { Api::Api(QObject *parent) : QObject(parent) , m_lastHitResult(HitResult()) , m_logger(BuildingControlUnit::getLogger("VWM.Api")) {} 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; // Fake: 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 << "'"; // return false; } 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) { // Reale Position berechnen (Ablage) // 1. zugehöriges BSM suchen const Bsm bsm = m_bsmMap.value(sensorId); // 2. ist BSM gültig if (!bsm.isValid()) { WARNING_LOG(m_logger) << "Given bsm id " << sensorId << " is not in our internal bsm list!"; return; } // 3. Ablage berechnen (wenn deposition1 != 0 || deposition2 != 0) 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; // bsm looks away from building sendHitToVwm(hit); } void Api::onSetEffectStatesRequest(const QString &hostname, const QVector &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) { // write ShotResult.cvs 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/* |QFile::Truncate*/ )) { 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"; } { // some values, more or less 'const': 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 : qAsConst(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; // bsm looks away from building } 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 &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 &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)) { //used in qt >5.14 #if QT_VERSION < QT_VERSION_CHECK(5,10,0) //satisfy 42.3 Build QSet roomsSet; for (const auto i: qAsConst(newcs.rooms)) { roomsSet.insert(i); } #else QSet roomsSet = QSet(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(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); // intention: data could be added to struct 'Hit' and passed to the user Hit hit; hit.effectTimeMSec = effectTimeMSec; hit.chargeTypeId = chargeTypeId; hit.destructiveFactor = destructiveFactor; hit.penetrationFactor = penetrationFactor; hit.originatorId = aesId; hit.originatorType = OriginatorType::CesHit; hit.position = posImpact; // use "1" meter here to find rooms ABOVE ground: hit.position.setZ(1); INFO_LOG(m_logger) << "XXXXX CES Hit = " << hit; // first we have to check if the building was hit directly // -> check every wall if it would be hit with bsm direction 0/0/1 BuildingControlUnit::VWM::HitPosition hitPos; hitPos.distance = +10000; LtsUtils::QPoint3D posTmp(posImpact); posTmp.setZ(10000); const LtsUtils::QPoint3D direction(0,0,1); // fake bsm dir -> looking upwards 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()) // should not happen but who knows... { // At the beginning, only on-ground-bulding were considered. // Starting with U-Bahn-Tunnel and Kanalisation, below-ground-building have to be considerd. // In that cases the distance is like 10003 meters... // so skip this additional check // // if (hitPosTmp.distance < hitPos.distance) { hitPos = hitPosTmp; hit.direction = direction * -1; // look downwards hit.position = posTmp; // hit from above, use this pos with z == 10000; } } // not really relevant, but who knows: hit.typeOfHit = TypeOfHit::InsideBuilding; } // if not inside building -> find nearest wall point to impact position LtsUtils::QPoint3D posImpactOnGround(posImpact); posImpactOnGround.setZ(1); // use "1" meter here to find rooms ABOVE ground: // struct NearestWall // { // int roomId{0}; // WallElement wallElement; // }; 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; // will reduce charge (effect) } } 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 : qAsConst(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::max(); INFO_LOG(m_logger) << "XXXXX searching nearest wall ..."; for (const Room &room : qAsConst(m_building.rooms())) { for (const OuterWall &wall : qAsConst(room.outerWalls())) { for (const WallElement &we : qAsConst(wall.elements())) { for (const LtsUtils::QPoint3D &p : qAsConst(we)) { const double dist = (detonationLocation - p).length(); DEBUG_LOG(m_logger) << "Room[" << room.id() << "]: " << p << ", dist = " << dist; if (dist >= distanceOut) continue; // hitPos.state = BuildingControlUnit::VWM::HitPosition::State::OuterWall; distanceOut = dist; INFO_LOG(m_logger) << "New min distance " << dist << " Room[" << room.id() << "]: " << p; directionWallElementPointToDetonationOut = p - detonationLocation; nearestWall.roomId = room.id(); nearestWall.wallElement = we; // hit.typeOfHit = TypeOfHit::OutsideBuilding; // will reduce charge (effect) } } } } 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); // intention: data could be added to struct 'Hit' and passed to the user Hit hit; hit.effectTimeMSec = effectTimeMSec; hit.chargeTypeId = chargeTypeId; hit.originatorId = aesId; hit.originatorType = OriginatorType::CesHit3D; hit.position = detonationLocation; // hit.direction // hit.detonationLocation INFO_LOG(m_logger) << "XXXXXXXXXX CES Hit 3D = " << hit << ", detonationLocation = " << detonationLocation << ", detonationDistance = " << detonationDistance << ", chargeTypeId = " << chargeTypeId << ", destructiveFactor = " << destructiveFactor << ", penetrationFactor = " << penetrationFactor; // Aufschlagzünder == 0 ? // Verzögerungszünder > 0 ? // Annäherungszünder < 0 ? // ProjectDataManagement::FuseType::FtProximity -> position = (1.91756,10.9597,9.94583), direction = (0,0,0) // ProjectDataManagement::FuseType::FtImpact -> position = (1.91756,10.9597,9.94583), direction = (0,0,0) // ProjectDataManagement::FuseType::FtDelay -> QVector roomsWithDetonation; const bool bIsDelayed = detonationDistance > 0; HitPosition hitPosPreCalculated; hitPosPreCalculated.state = HitPosition::State::OuterWall; hitPosPreCalculated.outerWallIdx = 0; // will point to the dummyWall !? 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 if (!bIsDelayed || roomsWithDetonation.isEmpty()) // fallback { // handle similar as before: findNearestWall 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; // will reduce charge (effect) } } // if HitResult result; result.damagePercentOld = m_building.damagePercent(); result.effectTimeMSec = effectTimeMSec; // von hier ueber a) sendHitToVwm, b) hitResult // Oder direkt hitResult?? 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(), // pos and dir: default values are okay 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(); //add Values from function: result.chargeTypeId = chargeTypeId; result.originatorType = OriginatorType::CesHit; result.originatorId = aesId; #ifdef LTS_DEBUG // write ShotResult.cvs dumpHitResult(hit, result, chargeOrg, charge, context); #endif // LTS_DEBUG emit hitResult(result, m_contaminationStates, true); } void Api::onFortifyFactorRequest(int originatorId, const QVector &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 &relayStateInfos) { DEBUG_LOG(m_logger) << "onRelayStateRequest hostname=" << hostname << " only hanlde in svcBuildings" << " relayStateInfos.size()=" << relayStateInfos.size(); } void Api::sendOverrides() { const QMap &segmentPropertyMap = m_building.segmentPropertyMap(); QVector 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; ///////////////////////////////////////// /// \brief BCU-Flash /// { DEBUG_LOG(m_logger) << "sendOverrides hostname=" << hostname << " properties=" << properties << "BCU-Flash"; BcuEffectState bcuEffectState; bcuEffectState.id = 1; // Id aus effect_config.xml 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(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(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; // Id aus effect_config.xml bcuEffectState.type = BcuEffectType::BET_BackgroundSound; BcuEffectOverride eo = BcuEffectOverride::BEO_Auto; if (overridesObj.contains(QStringLiteral("sound"))) eo = static_cast(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); // we know the correct charge id if ( ( hit.originatorType == OriginatorType::CesHit) || ( hit.originatorType == OriginatorType::CesHit3D)) { // [GUEZE-2821] Building protects buildings 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 // no effect on the building && !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 // write ShotResult.cvs dumpHitResult(hit, result, chargeOrg, charge, QStringLiteral("default")); #endif // LTS_DEBUG m_lastHitResult = result; break; } case OriginatorType::ECC: case OriginatorType::OC: { if (!hit.affectedRoomIds.isEmpty()) { // destroy/init a special room from ECC 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 // write ShotResult.cvs dumpHitResult(hit, result, Charge(), Charge(), QStringLiteral("Reset by ECC/OC")); #endif // LTS_DEBUG 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 // write ShotResult.cvs dumpHitResult(hit, result, Charge(), Charge(), QStringLiteral("Destroyed by ECC/OC")); #endif // LTS_DEBUG } 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); } } // namespace VWM } // namespace BuildingControlUnit