/** * Handler for player data * $Id$ * $HeadURL$ * $Author$ * Created by hjl, SLSE, 2017-02-20 */ #include "handler_building_ownership.h" #include #include #include #include namespace CesBuildings { HandlerBuildingOwnership::HandlerBuildingOwnership(const std::shared_ptr &ddsHandler, const std::shared_ptr &participantHandler, const std::shared_ptr &buildingDataHandler, const std::shared_ptr &db, QObject *parent) : QObject(parent) , SetOwnershipRequestHandler(ddsHandler->ftsCes()) , m_ddsHandler(ddsHandler) , m_handlerParticipant(participantHandler) , m_handlerBuildingData(buildingDataHandler) , m_db(db) , m_logger(CentralEffectSimulation::getLogger("Plugin.Buildings.HandlerBuildingOwnership")) { INFO_LOG(m_logger) << "HandlerBuildingOwnership::HandlerBuildingOwnership"; } // end of ctor() //----------------------------------------------------------------------------- HandlerBuildingOwnership::~HandlerBuildingOwnership() { } // end of dtor() //----------------------------------------------------------------------------- bool HandlerBuildingOwnership::init() { INFO_LOG(m_logger) << "HandlerBuildingOwnership::init"; auto dtcIsm = m_ddsHandler->dtcIsm(); DEBUG_LOG(m_logger) << "init"; loadOwnershipsFromDb(); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newOutdoorPosition, this, &HandlerBuildingOwnership::onParticipantPositionChanged); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newIndoorPosition, this, &HandlerBuildingOwnership::onParticipantPositionChanged); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newNotfightMode, this, &HandlerBuildingOwnership::onParticipantPositionChanged); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::participantLeavesBuilding, this, &HandlerBuildingOwnership::onParticipantLeavesBuilding); connect(m_handlerParticipant.get(), &CentralEffectSimulation::HandlerParticipant::newParticipantState, this, &HandlerBuildingOwnership::onParticipantPositionChanged); dtcIsm->waitForHistoricalDataMainStructureElement(1000); connect(dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this, &HandlerBuildingOwnership::onMainStructureElementChanged); // Get 'static' data for all MainStructureElements { auto cb = [this](const InfrastructureManagement::MainStructureElement & mse) { onMainStructureElementChanged(mse, DataModification::eAdded); }; if (!dtcIsm->mainStructureElements(cb)) return false; } return true; } // end of init() //----------------------------------------------------------------------------- // /////////////////////////////////// // B L D (Buildings) // /////////////////////////////////// void HandlerBuildingOwnership::onSetOwnershipRequest(const rti::core::SampleIdentity &cookie, const CentralEffectSimulation::SetOwnershipRequest &request) { INFO_LOG(m_logger) << "onSetOwnershipRequest: " << request; // remove Ownership, if partyId == 0 if (request.partyId() == 0) removeFromDbBuildingId(request.buildingId()); const QVector floors = m_handlerBuildingData->getFloorsFromBuilding(request.buildingId()); for (int floorId : floors) saveBuildingOwnership(floorId, request.partyId(), request.buildingId()); if (!floors.isEmpty()) sendBuildingOwnership(request.buildingId()); m_ddsHandler->ftsCes()->setOwnershipReply(cookie, Core::FunctionReplyObject(request.buildingId())); } void HandlerBuildingOwnership::onParticipantPositionChanged(const CentralEffectSimulation::HandlerParticipant::CesParticipantData &partPos) { const int participantId = partPos.participantId(); INFO_LOG(m_logger) << "BuildingOwnership onParticipantPositionChanged participantId=" << participantId << " vulnerabilityState=" << partPos.vulnerabilityState << " buildingId=" << partPos.buildingId() << " notfightMode=" << partPos.notfightMode; // [GUEZE-2256] 3D Wirksim - Funktionsraum Nicht Kampffaehig // Teilnehmer, die den Status Nicht Kampffaehig haben, düfen nicht bei der Berechnung des Gebaeudebesitzes beruecksichtigt werden. if (partPos.notfightMode) return; int floorId = m_handlerBuildingData->getFloorIdFromRoomId(partPos.roomId()); if (partPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill) m_participantFloor.remove(participantId); else m_participantFloor.insert(participantId, floorId); int newOwnership = calculateOwnershipByParticipantId(participantId); checkAndSendOwnership(newOwnership, floorId); INFO_LOG(m_logger) << "END BuildingOwnership onParticipantPositionChanged participantId=" << participantId << " vulnerabilityState=" << partPos.vulnerabilityState; } // end of onParticipantPositionChanged() // /////////////////////////////////// // I S M (InfrastructureManagement) // /////////////////////////////////// void HandlerBuildingOwnership::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType) { INFO_LOG(m_logger) << "onMainStructureElementChanged id=" << data.id() << " modelId =" << data.modelId(); if (modType == DataModification::eRemoved || data.modelId() <= 0) { m_buildingOwnership.remove(data.id()); return; } QJsonArray jsonArray = QJsonDocument::fromJson(QByteArray::fromStdString(data.properties())).object().value(QLatin1String("available_in_exercise")).toArray(); DEBUG_LOG(m_logger) << "available_in_exercise jsonDocument.size()=" << jsonArray.size(); QVector result; result.reserve(jsonArray.size()); // Check if present in the current exercise and remove if (m_buildingOwnership.contains(data.id())) { int id = data.id(); m_buildingOwnership.remove(id); m_ddsHandler->dtsCes()->removeBuildingOwnershipByKey(id); } for (auto it = jsonArray.constBegin(); it != jsonArray.constEnd(); ++it) { DEBUG_LOG(m_logger) << "available_in_exercise jsonDocument=" << it->toInt(); if (m_ddsHandler->getExerciseId() != it->toInt()) continue; DEBUG_LOG(m_logger) << "Got MainStructureElement" << " data.id()=" << data.id() << " data.modelId=" << data.modelId(); if (!m_buildingOwnership.contains(data.id())) { // insert in m_buildingOwnership and publish to dds CentralEffectSimulation::BuildingOwnership bo; bo.buildingId(data.id()); bo.sampleTime(QDateTime::currentMSecsSinceEpoch()); updateOwnerships(&bo); m_buildingOwnership.insert(data.id(), bo); m_ddsHandler->dtsCes()->publish(bo); } // if } // for } void HandlerBuildingOwnership::onParticipantLeavesBuilding(const int participantId) { INFO_LOG(m_logger) << "BuildingOwnership onParticipantLeavesBuilding participantId=" << participantId; auto it = m_participantFloor.find(participantId); if (it == m_participantFloor.end()) return; m_participantFloor.erase(it); const int floorId = it.value(); const int newOwnership = calculateOwnershipByFloorId(floorId); DEBUG_LOG(m_logger) << "newOwnership=" << newOwnership; if (newOwnership > 0) checkAndSendOwnership(newOwnership, floorId); } // end of onParticipantLeavesBuilding() int HandlerBuildingOwnership::calculateOwnershipByParticipantId(const int participantId) { INFO_LOG(m_logger) << "BuildingOwnership calculateOwnershipByParticipantId participantId=" << participantId; CentralEffectSimulation::HandlerParticipant::CesParticipantData partPos = m_handlerParticipant->position(participantId); return calculateOwnershipByFloorId(m_handlerBuildingData->getFloorIdFromRoomId(partPos.roomId())); } int HandlerBuildingOwnership::calculateOwnershipByFloorId(const int floorId) { INFO_LOG(m_logger) << "BuildingOwnership calculateOwnershipByFloorId floorId=" << floorId; int ownership = -1; if (floorId == -1) return ownership; DEBUG_LOG(m_logger) << "calculateOwnership floorId = " << floorId; QHash partyCount; // key = Party, value= Count of Participants; // Suche alle Participants in der Etage for (auto it = m_participantFloor.cbegin(); it != m_participantFloor.cend(); ++it) { int iteratorFloor = it.value(); int iteratorParticipant = it.key(); if (floorId == iteratorFloor) { const CentralEffectSimulation::HandlerParticipant::CesParticipantData &localPartPos = m_handlerParticipant->position(iteratorParticipant); if (localPartPos.vulnerabilityState == ProjectDataManagement::VulnerabilityState::CatastrophicKill) continue; // Don't count participants with catastrophic Kill const int partyId = localPartPos.partyId(); ++partyCount[partyId]; } } // berechne daraus die Farbe der Partei int countParty = 0; for (auto it = partyCount.begin(); it != partyCount.end(); ++it) { if (it.value() > countParty) { countParty = it.value(); ownership = it.key(); } } return ownership; } int HandlerBuildingOwnership::getOwnership(int buildingId, int floorId) const { INFO_LOG(m_logger) << "BuildingOwnership getOwnership buildingId=" << buildingId << " floorId=" << floorId; auto itBldg = m_buildingOwnership.find(buildingId); if (itBldg == m_buildingOwnership.end()) return -1; const CentralEffectSimulation::BuildingOwnership &bo = itBldg.value(); for (const CentralEffectSimulation::Ownership &ownership : bo.ownerships()) { if (ownership.floorId() == floorId) return ownership.partyId(); } return -1; } void HandlerBuildingOwnership::checkAndSendOwnership(const int newOwnership, const int floorId) { INFO_LOG(m_logger) << "BuildingOwnership checkAndSendOwnership"; if (floorId < 0) // outside building return; const int buildingId = m_handlerBuildingData->getBuildingIdFromFloorId(floorId); if (buildingId < 0) { WARNING_LOG(m_logger) << "Could not find building for floor with id " << floorId << " - ignoring"; return; } // prüfe, ob sich die Farbe geändert hat. const int oldOwnership = getOwnership(buildingId, floorId); DEBUG_LOG(m_logger) << "checkAndSendOwnership buildingId = " << buildingId << " floorId=" << floorId << " oldOwnership=" << oldOwnership << " newOwnership=" << newOwnership; if (oldOwnership != newOwnership) // Kriterium zum Senden { // Wenn ja, Eintrag in der Verwaltung saveBuildingOwnership(floorId, newOwnership, buildingId); // und Meldung verschicken. sendBuildingOwnership(buildingId); DEBUG_LOG(m_logger) << "new Building Ownership"; } } void HandlerBuildingOwnership::updateOwnerships(CentralEffectSimulation::BuildingOwnership *bo) { INFO_LOG(m_logger) << "BuildingOwnership updateOwnerships"; QMap ownershipMap; const QVector floors = m_handlerBuildingData->getFloorsFromBuilding(bo->buildingId()); DEBUG_LOG(m_logger) << "updateOwnerships floors.size= " << floors.size(); for (const int floorId : floors) ownershipMap[floorId] = 0; for (const CentralEffectSimulation::Ownership &ownership : bo->ownerships()) { DEBUG_LOG(m_logger) << "updateOwnerships floorId=" << ownership.floorId() << ": " << ownership.partyId(); ownershipMap[ownership.floorId()] = ownership.partyId(); } bo->ownerships().resize(0); bo->ownerships().reserve(ownershipMap.size()); CentralEffectSimulation::Ownership ownership; for (auto it = ownershipMap.cbegin(); it != ownershipMap.cend(); ++it) { DEBUG_LOG(m_logger) << it.key() << ": " << it.value(); ownership.floorId(it.key()); ownership.partyId(it.value()); bo->ownerships().push_back(ownership); } } void HandlerBuildingOwnership::saveBuildingOwnership(int floorId, int partyId, int buildingId) { INFO_LOG(m_logger) << "saveBuildingOwnership floorId: " << floorId << ", partyId: " << partyId << ", buildingId: " << buildingId; // Vorhandenen Eintrag für das Gebäude suchen auto it = m_buildingOwnership.find(buildingId); if (it != m_buildingOwnership.end()) { CentralEffectSimulation::BuildingOwnership &bo = *it; // Vorhanden Eintrag für das Stockwerk suchen: for (CentralEffectSimulation::Ownership &ownership : bo.ownerships()) { if (ownership.floorId() == floorId) { ownership.partyId(partyId); return; } } // Neuen Eintrag für das Stockwerk anlegen CentralEffectSimulation::Ownership ownership; ownership.floorId(floorId); ownership.partyId(partyId); bo.ownerships().push_back(std::move(ownership)); m_buildingOwnership.insert(buildingId, bo); return; } // Neuen Eintrag für das Gebäude anlegen CentralEffectSimulation::BuildingOwnership bo; CentralEffectSimulation::Ownership ownership; bo.buildingId(buildingId); ownership.floorId(floorId); ownership.partyId(partyId); bo.ownerships().resize(1, ownership); m_buildingOwnership.insert(buildingId, bo); } void HandlerBuildingOwnership::sendBuildingOwnership(int buildingId) { INFO_LOG(m_logger) << "sendBuildingOwnership for building: " << buildingId; CentralEffectSimulation::BuildingOwnership bo = m_buildingOwnership.value(buildingId); bo.buildingId(buildingId); bo.sampleTime(QDateTime::currentMSecsSinceEpoch()); updateOwnerships(&bo); m_ddsHandler->dtsCes()->publish(bo); insertInDb(bo); } // /////////////////////////////////////////////////// /// \brief HandlerConfigureAlarms::loadOwnershipsFromDb /// \return /// bool HandlerBuildingOwnership::loadOwnershipsFromDb() { INFO_LOG(m_logger) << "HandlerBuildingOwnership::loadOwnershipsFromDb "; m_buildingOwnership.clear(); auto cbBo = [this](const CentralEffectSimulation::Database::CesOwnership & bo) { saveBuildingOwnership(bo.floorId, bo.partyId, bo.buildingId); }; if (!m_db->select(cbBo)) { ERROR_LOG(m_logger) << "Error reading data from database 'CesOwnership'"; return false; } return true; } void HandlerBuildingOwnership::removeFromDbBuildingId(const int buildingId) { INFO_LOG(m_logger) << "HandlerBuildingOwnership::removeFromDbBuildingId buildingId: " << buildingId; m_db->removeCesOwnershipByBuildingId(buildingId); } void HandlerBuildingOwnership::insertInDb(const CentralEffectSimulation::BuildingOwnership &bo) { DBTransaction trans(m_db->database()); removeFromDbBuildingId(bo.buildingId()); INFO_LOG(m_logger) << "insertInDb buildingId: " << bo.buildingId() << ", bo.ownerships().size(): " << bo.ownerships().size(); CentralEffectSimulation::Database::CesOwnership newOwnership; newOwnership.buildingId = bo.buildingId(); for (const CentralEffectSimulation::Ownership &ownership : bo.ownerships()) { newOwnership.id = 0; newOwnership.floorId = ownership.floorId(); newOwnership.partyId = ownership.partyId(); DEBUG_LOG(m_logger) << "ownership: floorId = " << newOwnership.floorId << " partyId = " << newOwnership.partyId; if (!m_db->insert(newOwnership)) ERROR_LOG(m_logger) << "Error adding 'CesOwnership' to database " << " buildingId =" << newOwnership.buildingId << " floorId = " << newOwnership.floorId << " partyId = " << newOwnership.partyId; } trans.commit(); } // end of insertInDb } // namespace CesBuildings