/** * Handler for infrastructure data * $Id$ * $HeadURL$ * $Author: * Created by hjl, SLSE, 2016-09-21 */ #include "handler_dynamic_participant_data.h" #include #include #include "web_socket_server.h" #include "settings.h" #include #include #include #include #include #include #include using namespace ExerciseEventManagement; //----------------------------------------------------------------------------- HandlerDynamicParticipantData::HandlerDynamicParticipantData(MainHandler *mainHandler, const std::shared_ptr &ddsHandler, const std::shared_ptr &db, const std::shared_ptr &eventServer) : m_ddsHandler(ddsHandler) , m_dbHandler(db) , m_eventServer(eventServer) , m_mainHandler(mainHandler) , m_logger(getLogger("HandlerDynamicParticipantData")) { } // end of ctor() //----------------------------------------------------------------------------- HandlerDynamicParticipantData::~HandlerDynamicParticipantData() { } // end of dtor() //----------------------------------------------------------------------------- bool HandlerDynamicParticipantData::init() { INFO_LOG(m_logger) << "start init() DTC ParticipantManagement "; // DTC ParticipantManagement m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsHandler->ddsConnector()); if (m_ppmDtc == nullptr) { ERROR_LOG(m_logger) << "Could not create DTC ParticipantManagement"; return false; } // Subscribe for DynamicParticipantData (for analyzing data) connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged, this, &HandlerDynamicParticipantData::onDynamicParticipantDataChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantGearEventChanged, this, &HandlerDynamicParticipantData::onParticipantGearEventChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged, this, &HandlerDynamicParticipantData::onParticipantChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::biteChanged, this, &HandlerDynamicParticipantData::onBiteChanged); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged, this, &HandlerDynamicParticipantData::onParticipantPositionChanged); if (!m_ppmDtc->enableDynamicParticipantData() || !m_ppmDtc->enableParticipantGearEvent() || !m_ppmDtc->enableParticipant() || !m_ppmDtc->enableBite() || !m_ppmDtc->enableParticipantPosition()) { ERROR_LOG(m_logger) << "init() DTC ParticipantManagement failed"; return false; } m_bldDtc = Buildings::Interface::Dtc::create(m_ddsHandler->ddsConnector()); if (m_bldDtc == nullptr) { ERROR_LOG(m_logger) << "Could not create DTC Buildings"; return false; } connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingStateChanged, this, &HandlerDynamicParticipantData::onBuildingStateChanged); if (!m_bldDtc->enableBuildingState()) { ERROR_LOG(m_logger) << "Enable buildingState failed"; return false; } m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsHandler->ddsConnector()); m_aesDtc->enableAesItemView(); connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged, this, &HandlerDynamicParticipantData::onAesItemViewChanged); // Infrastructure Management auto dtcIsm = m_ddsHandler->dtcIsm(); dtcIsm->enableMainStructureElement(); connect(dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this, &HandlerDynamicParticipantData::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() void HandlerDynamicParticipantData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: m_itemViews.insert(data.aesItemId(), data.aesTypeId()); break; case DataModification::eRemoved: m_itemViews.remove(data.aesItemId()); } } void HandlerDynamicParticipantData::tryCreateHitEvent(Database::EemEvent eData, const ParticipantManagement::StateInformation &stateInfo) { DEBUG_LOG(m_logger) << "tryCreateHitEvent"; // special values - ignore const ProjectDataManagement::VulnerabilityState vulnState = stateInfo.vulnerabilityState(); if (vulnState == ProjectDataManagement::VulnerabilityState_def::VulnerabilityBitsChanged || vulnState == ProjectDataManagement::VulnerabilityState_def::TemporarySeverance) return; // combat ready is no hit if (vulnState == ProjectDataManagement::VulnerabilityState_def::CombatReady) return; // only handle 'real' hit events switch (stateInfo.originatorType().underlying()) { case ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation: case ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice: case ProjectDataManagement::StateChangeOriginator::DusMine: eData.eventTypeId = ProjectDataManagement::EventType::HitEffectSimulation; break; case ProjectDataManagement::StateChangeOriginator::Participant: case ProjectDataManagement::StateChangeOriginator::ObserverController: case ProjectDataManagement::StateChangeOriginator::ExerciseControl: eData.eventTypeId = ProjectDataManagement::EventType::Hit; break; case ProjectDataManagement::StateChangeOriginator::Sanitation: eData.eventTypeId = ProjectDataManagement::EventType::Sanitation; break; case ProjectDataManagement::StateChangeOriginator::Unknown: case ProjectDataManagement::StateChangeOriginator::WoundTreatmentDevice: case ProjectDataManagement::StateChangeOriginator::MaintainanceDevice: case ProjectDataManagement::StateChangeOriginator::FunctionalArea: case ProjectDataManagement::StateChangeOriginator::Initialization: case ProjectDataManagement::StateChangeOriginator::MedicalTimeout: return; } QJsonObject obj; obj[QStringLiteral("VulnerabilityState")] = vulnState.underlying(); obj[QStringLiteral("OriginatorId")] = stateInfo.originatorId(); obj[QStringLiteral("OriginatorType")] = stateInfo.originatorType().underlying(); obj[QStringLiteral("OriginatorChargeTypeId")] = stateInfo.originatorChargeTypeId(); if (stateInfo.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant) { ParticipantManagement::Participant originData = m_partMap.value(stateInfo.originatorId()); if (originData.equipmentNumber().is_set()) obj[QStringLiteral("OriginatorEquipmentNumber")] = originData.equipmentNumber().get(); if (originData.partyId() == eData.partyId) obj[QStringLiteral("FriendlyFire")] = true; else obj[QStringLiteral("FriendlyFire")] = false; } if (eData.eventTypeId == ProjectDataManagement::EventType::HitEffectSimulation) { DEBUG_LOG(m_logger) << "Event HitEffectSimulation from originatorId = " << stateInfo.originatorId(); obj[QStringLiteral("AesId")] = stateInfo.originatorId(); obj[QStringLiteral("AesType")] = m_itemViews.value(stateInfo.originatorId()).underlying(); AreaEffectSimulation::AesItem aesItem; if (m_aesDtc->aesItemFromId(stateInfo.originatorId(), aesItem)) obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name())); } if (eData.eventTypeId == ProjectDataManagement::EventType::Sanitation) { // ignore these events if (stateInfo.sanitationProceduresTaken() == 0 && stateInfo.originatorChargeTypeId() == 0) //qualification return; DEBUG_LOG(m_logger) << "Event sanitation "; obj[QStringLiteral("Vitality")] = stateInfo.vitality(); obj[QStringLiteral("SanitationResult")] = stateInfo.sanitationResult().underlying(); obj[QStringLiteral("SanitationProceduresTaken")] = stateInfo.sanitationProceduresTaken(); } eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); eData.id = 0; if (eData.sampleTime <= 0) eData.sampleTime = stateInfo.stateTime(); if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << eData.participantId; else publishData(eData); } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType) { if (modType == DataModification::eRemoved) return; DEBUG_LOG(m_logger) << "onDynamicParticipantDataChanged()" << ", participantId: " << data.participantId() << ", size stateHistory: " << data.stateHistory().size(); auto itLastDynData = m_lastDynData.find(data.participantId()); // the first insert does not trigger any updates if (itLastDynData == m_lastDynData.end()) { itLastDynData = m_lastDynData.insert(data.participantId(), data); return; } ParticipantManagement::DynamicParticipantData &lastDynData = itLastDynData.value(); // get corresponding participant data auto itParticipant = m_partMap.find(data.participantId()); if (itParticipant == m_partMap.end()) { m_ppmDtc->waitForHistoricalDataParticipant(2000); itParticipant = m_partMap.find(data.participantId()); if (itParticipant == m_partMap.end()) { ERROR_LOG(m_logger) << "Participant does not exist: participantId: " << data.participantId(); return; } } static const ParticipantManagement::ParticipantPosition emptyPos; auto itParticipantPos = m_partPosMap.find(data.participantId()); const ParticipantManagement::ParticipantPosition &pPos = itParticipantPos == m_partPosMap.end() ? emptyPos : itParticipantPos.value(); const ParticipantManagement::Participant &pData = itParticipant.value(); Database::EemEvent eData; eData.participantId = data.participantId(); eData.equipmentNumber = pData.equipmentNumber().is_set() ? pData.equipmentNumber().get() : -1; eData.partyId = pData.partyId(); eData.sampleTime = data.sampleTime(); eData.eventTypeId = ProjectDataManagement::EventType_def::Unknown; eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z()); if (data.stateHistory() != lastDynData.stateHistory()) { // we assume that the order of the state information do *not* change (it's current true: january 2020) for (size_t i = 0; i < data.stateHistory().size(); ++i) { if (i < lastDynData.stateHistory().size() && data.stateHistory().at(i) == lastDynData.stateHistory().at(i)) continue; tryCreateHitEvent(eData, data.stateHistory().at(i)); } } const auto gearIsKnown = [ &lastDynData](const ParticipantManagement::GearInformation & gi) -> bool { return std::any_of(lastDynData.gear().begin(), lastDynData.gear().end(), [&gi](const ParticipantManagement::GearInformation & other) { return gi.gearTypeId() == other.gearTypeId() && gi.gearId() == other.gearId(); }); }; for (const ParticipantManagement::GearInformation &gi : data.gear()) { if (gearIsKnown(gi)) continue; DEBUG_LOG(m_logger) << "Found new gear " << gi << " for participant " << data.participantId(); QJsonObject obj; obj[QStringLiteral("GearId")] = QJsonValue(gi.gearId()); obj[QStringLiteral("GearTypeId")] = QJsonValue(gi.gearTypeId()); obj[QStringLiteral("DryFire")] = QJsonValue(gi.chargeCounting()); eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); eData.sampleTime = gi.lastUpdate(); eData.eventTypeId = ProjectDataManagement::EventType_def::WeaponAssignment; eData.id = 0; if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId(); else publishData(eData); } if (data.manipulation() != lastDynData.manipulation()) { eData.eventTypeId = ProjectDataManagement::EventType_def::Manipulation; const auto isOldManipulation = [&lastDynData](const ParticipantManagement::ManipulationInformation & mani) -> bool { auto it = std::find(lastDynData.manipulation().begin(), lastDynData.manipulation().end(), mani); return it != lastDynData.manipulation().end(); }; // we can *not* assume that the old and the new manipulation is in the same order for (const auto &mani : data.manipulation()) { if (isOldManipulation(mani)) continue; const QJsonObject obj({{QStringLiteral("DeviceType"), mani.equipmentFunctionId()}}); eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); eData.sampleTime = mani.manipulationTime(); eData.id = 0; if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId(); else publishData(eData); } } lastDynData = data; } // end of onDynamicParticipantDataChanged() //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onParticipantGearEventChanged(const ParticipantManagement::ParticipantGearEvent &data, DataModification modType) { if (modType == DataModification::eRemoved) return; // get corresponding participant data auto itParticipant = m_partMap.find(data.participantId()); if (itParticipant == m_partMap.end()) { m_ppmDtc->waitForHistoricalDataParticipant(2000); itParticipant = m_partMap.find(data.participantId()); if (itParticipant == m_partMap.end()) { ERROR_LOG(m_logger) << "Participant does not exist: participantId: " << data.participantId(); return; } } static const ParticipantManagement::ParticipantPosition emptyPos; auto itParticipantPos = m_partPosMap.find(data.participantId()); const ParticipantManagement::ParticipantPosition &pPos = itParticipantPos == m_partPosMap.end() ? emptyPos : itParticipantPos.value(); const ParticipantManagement::Participant &pData = itParticipant.value(); Database::EemEvent eData; eData.id = 0; eData.participantId = data.participantId(); eData.equipmentNumber = pData.equipmentNumber().is_set() ? pData.equipmentNumber().get() : -1; eData.partyId = pData.partyId(); eData.sampleTime = data.sampleTime(); eData.eventTypeId = ProjectDataManagement::EventType_def::FireEvent; eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z()); QJsonObject obj; obj[QStringLiteral("GearId")] = QJsonValue(data.charge().gearId()); obj[QStringLiteral("GearTypeId")] = QJsonValue(data.charge().gearTypeId()); obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.charge().chargeTypeId()); obj[QStringLiteral("Amount")] = QJsonValue(data.chargeAmount()); if (data.charge().remain() >= 0) obj[QStringLiteral("Remain")] = QJsonValue(data.charge().remain()); eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); eData.id = 0; DEBUG_LOG(m_logger) << "create fire event for participant " << data.participantId(); if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for participant: " << data.participantId(); else publishData(eData); } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: DEBUG_LOG(m_logger) << "added/modified"; m_partMap.insert(data.id(), data); DEBUG_LOG(m_logger) << "data.participantId: " << data.id() << ", data.partyId: " << data.partyId() << ". data.equipmentNumber: " << data.equipmentNumber(); break; case DataModification::eRemoved: m_partMap.remove(data.id()); break; } } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: m_partPosMap.insert(data.participantId(), data); DEBUG_LOG(m_logger) << "data.participantId: " << data.participantId() << ", position lat: " << data.pos().lat() << ", position lon: " << data.pos().lon(); break; case DataModification::eRemoved: m_partPosMap.remove(data.participantId()); break; } } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onBiteChanged(const ParticipantManagement::Bite &data, DataModification modType) { if (modType == DataModification::eRemoved) return; Database::EemEvent eData; eData.id = 0; eData.eventTypeId = ProjectDataManagement::EventType_def::BiteMessage; eData.equipmentNumber = data.equipmentNumber(); eData.participantId = data.participantId(); eData.sampleTime = data.sampleTime(); QJsonObject obj; obj[QStringLiteral("BatteryState")] = QJsonValue(data.batteryState()); obj[QStringLiteral("BiteState")] = QJsonValue(data.biteState()); obj[QStringLiteral("ConfigState")] = QJsonValue(data.configState()); QJsonArray array; for (const ParticipantManagement::BiteError &be : data.biteErrors()) { QJsonObject object; object[QStringLiteral("ErrorNo")] = QJsonValue(be.errorNumber()); object[QStringLiteral("CanDeviceNumber")] = QJsonValue(be.canDeviceNumber()); object[QStringLiteral("SubdeviceNumber")] = QJsonValue(be.subdeviceNumber()); object[QStringLiteral("UniqueId")] = QJsonValue(be.uniqueId()); array.append(object); } obj[QStringLiteral("BiteErrors")] = QJsonValue(array); eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); ParticipantManagement::Participant part = m_partMap.value(data.participantId()); if (part.id() > 0) { eData.partyId = part.partyId(); ParticipantManagement::ParticipantPosition pPos = m_partPosMap.value(eData.participantId); eData.position = LtsUtils::QPoint3D(pPos.pos().lon(), pPos.pos().lat(), pPos.pos().z()); } else { eData.participantId = -1; eData.partyId = -1; } if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for participantId: " << data.participantId(); else publishData(eData); } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::onBuildingStateChanged(const Buildings::BuildingState &data, DataModification modType) { if (modType == DataModification::eRemoved || data.damagePercent() <= 0) // only add hits return; Database::EemEvent eData; eData.id = 0; eData.eventTypeId = ProjectDataManagement::EventType_def::HitBuilding; eData.sampleTime = data.sampleTime(); QJsonObject obj; obj[QStringLiteral("ChargeTypeId")] = QJsonValue(data.chargeTypeId()); obj[QStringLiteral("damagePercent")] = QJsonValue(data.damagePercent()); obj[QStringLiteral("damagePercentOld")] = QJsonValue(data.damagePercentOld()); // obj[QStringLiteral("Name")] = QJsonValue(data.name()); obj[QStringLiteral("VulnerabilityState")] = QJsonValue(data.vulnerabilityState().underlying()); if (data.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant) { ParticipantManagement::Participant originData = m_partMap.value(data.originatorParticipantId()); if (originData.equipmentNumber().is_set()) obj[QStringLiteral("OriginatorEquipmentNumber")] = originData.equipmentNumber().get(); } if (data.originatorType() == ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation) { obj[QStringLiteral("AesId")] = data.aesId(); obj[QStringLiteral("AesType")] = m_itemViews.value(data.aesId()).underlying(); AreaEffectSimulation::AesItem aesItem; if (m_aesDtc->aesItemFromId(data.aesId(), aesItem)) obj[QStringLiteral("AesName")] = QJsonValue(QString::fromStdString(aesItem.name())); } //obj[QStringLiteral("StructureElementId")] = QJsonValue(data.structureElementId()); obj[QStringLiteral("MainStructureElementId")] = QJsonValue(data.id()); QJsonArray contaminationArr; for (const auto &cs: data.contaminationStates()) { QJsonArray roomArr; for (const auto &roomId : qAsConst(cs.rooms())) roomArr.append(QJsonValue(roomId)); contaminationArr.append(QJsonObject({{QStringLiteral("aesId") , cs.aesId()}, {QStringLiteral("rooms"), roomArr}})); } obj[QStringLiteral("contaminationStates")] = contaminationArr; int mainStructureElementId = data.id(); Core::GeoPosition geoPosition = m_mainStructureElements.value(mainStructureElementId).geoPosition(); LtsUtils::QPoint3D pos3D(geoPosition.lon(), geoPosition.lat(), geoPosition.z()); eData.position = pos3D; eData.properties = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact)); if (!m_dbHandler->db()->insert(eData)) ERROR_LOG(m_logger) << "Error while inserting event data for building "; else publishData(eData); } //----------------------------------------------------------------------------- void HandlerDynamicParticipantData::publishData(const Database::EemEvent &data) { m_eventServer->sendEvent(Database::toJson(data)); ExerciseEventManagement::Event ddsData = toDdsData(data); if (!m_ddsHandler->dts()->publish(ddsData)) ERROR_LOG(m_logger) << "Error publishing event-data to dds"; // take data from dds-bus after 10s, we need just to make sure the recording service got it m_mainHandler->addEventIdToRemove(ddsData.id()); } //----------------------------------------------------------------------------- ExerciseEventManagement::Event HandlerDynamicParticipantData::toDdsData(const Database::EemEvent &data) { ExerciseEventManagement::Event ddsData; ddsData.id(data.id); ddsData.sampleTime(data.sampleTime); ddsData.participantId(data.participantId); ddsData.partyId(data.partyId); ddsData.equipmentNumber(data.equipmentNumber); ddsData.eventTypeId(data.eventTypeId); ddsData.properties(data.properties.toStdString()); ddsData.position(Core::GeoPosition(data.position.x(), data.position.y(), data.position.z())); if (data.eventTypeId == ProjectDataManagement::EventType_def::HitBuilding) { int mainStructureElementId = LtsUtils::JsonObject::fromJson(ddsData.properties()).valueOr(QStringLiteral("MainStructureElementId"), 0); DEBUG_LOG(m_logger) << "MainStructureElementId=" << mainStructureElementId; ddsData.position(m_mainStructureElements.value(mainStructureElementId).geoPosition()); } return ddsData; } // /////////////////////////////////// // I S M (InfrastructureManagement) // /////////////////////////////////// void HandlerDynamicParticipantData::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType) { DEBUG_LOG(m_logger) << "onMainStructureElementChanged"; if (modType != DataModification::eAdded && modType != DataModification::eModified) { m_mainStructureElements.remove(data.id()); return; } m_mainStructureElements.insert(data.id(), data); DEBUG_LOG(m_logger) << "Got MainStructureElement: " << data; } //-----------------------------------------------------------------------------