/** * The ParticipantHandler class manages all participant related function topics and holds classes for * further participant data. * $Id$ * $HeadURL$ * $Author$ * Created by Hannes Baeuerle, SLSE, 2016-05-12 */ #include "participant_handler_for_exercise.h" #include "convert_db_to_dds.h" #include "firing_unit_validator.h" #include "main_handler.h" #include "participant_for_exercise.h" #include "proxy_listener.h" #include "settings.h" #include "unit_position_calculator.h" #include #include #include #include #include #include #include #include #include #include namespace ParticipantManagement { namespace ParticipantHandling { static int s_tabRequestId = 1; static inline Database::PpmParticipant toDbParticipant(const ParticipantData ¤tData, bool withId = false) { // create db participant Database::PpmParticipant newDbParticipant; if (withId) newDbParticipant.id = currentData.id(); newDbParticipant.partyId = currentData.partyId(); newDbParticipant.typeId = currentData.typeId(); newDbParticipant.externalId = currentData.externalId(); newDbParticipant.name = QString::fromStdString(currentData.name()); newDbParticipant.identification = QString::fromStdString(currentData.identification()); newDbParticipant.symbol = QString::fromStdString(currentData.symbol()); newDbParticipant.info = QString::fromStdString(currentData.info()); newDbParticipant.designator = QString::fromStdString(currentData.designator()); newDbParticipant.tacticalIdentifier = QString::fromStdString(currentData.tacticalIdentifier()); newDbParticipant.isDeleted = currentData.isDeleted(); newDbParticipant.isSimulated = currentData.isSimulated(); newDbParticipant.assignState = currentData.assignState(); if (currentData.equipmentNumber().is_set()) newDbParticipant.equipmentNumber = currentData.equipmentNumber().get(); newDbParticipant.hierarchyNumber = currentData.hierarchyNumber(); newDbParticipant.properties = QString::fromStdString(currentData.properties()); return newDbParticipant; } static inline ParticipantOrganisationEx toDds(const Database::PpmParticipantOrganisation &db) { ParticipantOrganisationEx ret; ret.dbId = db.id; ret.participantId(db.participantId); ret.originPartyId(db.originPartyId); ret.currentPartyId(db.currentPartyId); ret.isAttached(db.isAttached); ret.isCooperate(db.isCooperate); return ret; } static inline Database::PpmParticipantOrganisation toDb(const ParticipantOrganisationEx &dds) { Database::PpmParticipantOrganisation ret; ret.id = dds.dbId; ret.participantId = dds.participantId(); ret.originPartyId = dds.originPartyId(); ret.currentPartyId = dds.currentPartyId(); ret.isAttached = dds.isAttached(); ret.isCooperate = dds.isCooperate(); return ret; } // // ParticipantHandlerForExercise // ParticipantHandlerForExercise::ParticipantHandlerForExercise(MainHandler *mainHandler) : ParticipantHandler(mainHandler, "ParticipantHandler") , Interface::Fts::ModifyStateRequestHandler(mainHandler->fts()) , Interface::Fts::ModifyParticipantDataRequestHandler(mainHandler->fts()) , Interface::Fts::InitParticipantRequestHandler(mainHandler->fts()) , Interface::Fts::SetParticipantModeRequestHandler(mainHandler->fts()) , Interface::Fts::SendMessageRequestHandler(mainHandler->fts()) , Interface::Fts::RegisterParticipantRequestHandler(mainHandler->fts()) , Interface::Fts::SetSimulatedPositionRequestHandler(mainHandler->fts()) , Interface::Fts::SetSimulatedPositionRelativeRequestHandler(mainHandler->fts()) , Interface::Fts::SetSimulatedParticipantFormationRequestHandler(mainHandler->fts()) , Interface::Fts::SetSanitationRequestHandler(mainHandler->fts()) , Interface::Fts::SelectiveReammunitionRequestHandler(mainHandler->fts()) , Interface::Fts::SetAsSimulatedRequestHandler(mainHandler->fts()) , Interface::Fts::OcCommandRequestHandler(mainHandler->fts()) , Interface::Fts::ModifyUasStateRequestHandler(mainHandler->fts()) , m_mainHandler(mainHandler) , m_hitLineInfoTimer(new QTimer(this)) , m_publishEquipmentExerciseMappingsTimer(new QTimer(this)) , m_publishEquipmentExerciseMappings(true) { m_publishEquipmentExerciseMappingsTimer->setSingleShot(true); connect(m_publishEquipmentExerciseMappingsTimer, &QTimer::timeout, this, &ParticipantHandlerForExercise::publishEquipmentExerciseMapping); } ParticipantHandlerForExercise::~ParticipantHandlerForExercise() { m_mainHandler->exAnyDts()->removeEquipmentExerciseMappingByKey(m_mainHandler->exerciseId()); m_proxyListener = nullptr; delete m_hitLineInfoTimer; } bool ParticipantHandlerForExercise::init() { ParticipantHandler::init(); if (m_firingUnitValidator) { delete m_firingUnitValidator; m_firingUnitValidator = nullptr; } if (m_mainHandler) m_firingUnitValidator = new FiringUnitValidator(m_mainHandler->participantHandler(), m_mainHandler->typeHandler(), this); std::shared_ptr dbEx0 = m_mainHandler->dbEx0(); auto cbEem = [this](const Database::EquipmentExerciseMapping &eem) { m_equipmentExerciseMappings[eem.equipmentNumber] = eem; }; dbEx0->selectByExerciseId(cbEem, m_mainHandler->exerciseId()); auto localEquipmentExerciseMappings = m_equipmentExerciseMappings; m_publishEquipmentExerciseMappings = !m_equipmentExerciseMappings.isEmpty(); m_proxyListener.reset(new ProxyListener(m_mainHandler)); if (!m_proxyListener->init()) { ERROR_LOG(m_logger) << "Could not initialise ProxyListener"; return false; } auto cbOrga = [this](const Database::PpmParticipantOrganisation &partOrga) { m_participantOrganisations.insert(partOrga.participantId, toDds(partOrga)); }; m_mainHandler->db()->select(cbOrga); DEBUG_LOG(m_logger) << "Reading participants from database"; // get participants from database / insert participant data into m_participants m_participants.reserve(100); auto cbParticipants = [this, &localEquipmentExerciseMappings](const Database::PpmParticipant ¤tParticipant) { ParticipantForExercise *newParticipant = new ParticipantForExercise(m_mainHandler, this, m_unitPositionCalculator.get(), currentParticipant); // update db only needed when not yet in there localEquipmentExerciseMappings.remove(newParticipant->equipmentNumber()); m_participants.insert(newParticipant->id(), newParticipant); }; if (!m_mainHandler->db()->select(cbParticipants)) { ERROR_LOG(m_logger) << "Error while reading participants from database"; return false; } // remove the 'unused' EquipmentExerciseMappings (for whatever reason there are some...) for (const auto &eem : qAsConst(localEquipmentExerciseMappings)) dbEx0->removeEquipmentExerciseMapping(eem); // after m_publishEquipmentExerciseMappings has been set and participants are created m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_mainHandler->dds()); connect(m_exmDtc.get(), &ExerciseManagement::Interface::Dtc::exerciseChanged, this, &ParticipantHandlerForExercise::onExerciseChanged); if (!m_exmDtc->enableExercise()) { ERROR_LOG(m_logger) << "Could not enable Topic 'Exercise' of EXM DTC"; return false; } m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_mainHandler->dds()); if (!m_ppmDtc->enableSetAsSimulated()) { ERROR_LOG(m_logger) << "Could not enable Topic 'Exercise' of PPM DTC"; return false; } bool isOwnPositionParticipant = false; // publish participants in dds for (Participant *p : qAsConst(m_participants)) { if (p->equipmentNumber() == 1) isOwnPositionParticipant = true; p->hasNewInferiorStructure(inferiorIdsForParticipant(p->id())); p->publishParticipant(); } auto dtc = m_mainHandler->ddsHandler()->dtc(); if (strcmp(PROJECTBUILD, "commaster") == 0) { if ((isOwnPositionParticipant == false) && (m_mainHandler->exerciseId() != 0) && (!m_equipmentExerciseMappings.contains(1))) // !m_equipmentExerciseMappings.keys().contains(1) // .keys() allocating an unneeded temporary container ^^^^^^ { ModifyParticipantDataRequest ownPositionParticipant; std::vector dataArray; ParticipantData data; data.assignState(ParticipantAssignState::HandoutExcon); data.partyId(1); data.hierarchyNumber(1); data.equipmentNumber(1); // data.info(QString(QStringLiteral("")).toStdString()); // a string is empty be default! // data.identification(QString(QStringLiteral("")).toStdString()); // a string is empty be default! data.typeId(60); data.symbol(QString(QStringLiteral("NONE|CVLX|227")).toStdString()); data.designator(QString(QStringLiteral("OC")).toStdString()); data.isSimulated(false); data.isDeleted(false); data.superiorModId(-1); dataArray.push_back(data); ownPositionParticipant.data(dataArray); ownPositionParticipant.forceAddWithId(false); modifyParticipantDataRequest(ownPositionParticipant); } } if (m_mainHandler->exerciseId() > 0) { // Initialize RecordReplay to get initial participantPosition RecordReplayPlugin recordReplay; // no ddsconnector - we don't want to publish data here recordReplay.initDtc(nullptr, m_mainHandler->exerciseId()); NSqlQuery q(m_mainHandler->db()->database()); recordReplay.readParticipantPositionAt(q, QDateTime::currentMSecsSinceEpoch()); // key is sampleTime -> multiMap const auto &latestPositions = recordReplay.getParticipantPositionMap(); DEBUG_LOG(m_logger) << "Starting to read participant positions from record replay. Size: " << latestPositions.size(); for (const auto &pos : latestPositions) { Participant *currentParticipant = m_participants.value(pos.participantId()); if (currentParticipant == nullptr) { INFO_LOG(m_logger) << "Got position for non existing participantId: " << pos.participantId(); continue; } DEBUG_LOG(m_logger) << "Setting position for participantId " << pos.participantId() << ": " << pos; currentParticipant->setPosition(pos, true); currentParticipant->publishPosition(); } recordReplay.readDynamicParticipantDataAt(q, QDateTime::currentMSecsSinceEpoch()); // key is sampleTime -> multiMap const auto &latestStates = recordReplay.getDynamicParticipantDataMap(); DEBUG_LOG(m_logger) << "Starting to read dynamic participant data from record replay. Size: " << latestStates.size(); for (const auto &dynData : latestStates) { Participant *currentParticipant = m_participants.value(dynData.participantId()); if (currentParticipant == nullptr) { INFO_LOG(m_logger) << "Got dynamic data for non existing participantId: " << dynData.participantId(); continue; } DEBUG_LOG(m_logger) << "Dynamic data for participantId " << dynData.participantId() << ": " << dynData; currentParticipant->setDynamicData(dynData); currentParticipant->publishDynamicData(); } recordReplay.readParticipantModeAt(q, QDateTime::currentMSecsSinceEpoch()); // key is sampleTime -> multiMap const auto &latestModes = recordReplay.getParticipantModeMap(); DEBUG_LOG(m_logger) << "Starting to read mode data from record replay. Size: " << latestStates.size(); for (const auto &mode : latestModes) { Participant *currentParticipant = m_participants.value(mode.participantId()); if (currentParticipant == nullptr) { INFO_LOG(m_logger) << "Got mode data for non existing participantId: " << mode.participantId(); continue; } DEBUG_LOG(m_logger) << "Mode data for participantId " << mode.participantId() << ": " << mode; currentParticipant->setMode(mode); currentParticipant->publishMode(); } recordReplay.readUASAt(q, QDateTime::currentMSecsSinceEpoch()); // key is sampleTime -> multiMap const auto &uasData = recordReplay.getUASMap(); DEBUG_LOG(m_logger) << "Starting to read uas data from record replay. Size: " << uasData.size(); for (const auto &uas : uasData) dts()->publish(uas); } connect(dtc.get(), &Interface::Dtc::equipmentExerciseMappingChanged, this, &ParticipantHandlerForExercise::onEquipmentExerciseMappingChanged); dtc->equipmentExerciseMappings([this](const EquipmentExerciseMapping &data) { onEquipmentExerciseMappingChanged(data, DataModification::eAdded); }); auto pub = dts(); for (const ParticipantOrganisation &partOrga : qAsConst(m_participantOrganisations)) pub->publish(partOrga); if (m_mainHandler->exerciseId() > 0) m_unitPositionCalculator->startCalculation(); connect(m_proxyListener.get(), &ProxyListener::newPosition, this, &ParticipantHandler::onNewPositionFromProxy); connect(m_proxyListener.get(), &ProxyListener::newState, this, &ParticipantHandler::onNewStateFromProxy); connect(m_proxyListener.get(), &ProxyListener::newGearAssignment, this, &ParticipantHandler::onNewGearAssignmentFromProxy); connect(m_proxyListener.get(), &ProxyListener::newGearChargeState, this, &ParticipantHandler::onNewGearChargeStateFromProxy); connect(m_proxyListener.get(), &ProxyListener::newGearEvent, this, &ParticipantHandler::onNewGearEvent); connect(m_proxyListener.get(), &ProxyListener::newEquipmentOperationalState, this, &ParticipantHandler::onNewEquipmentOperationalStateFromProxy); connect(m_proxyListener.get(), &ProxyListener::newEquipmentBite, this, &ParticipantHandler::onNewBiteFromProxy); connect(m_proxyListener.get(), &ProxyListener::newCtaDataError, this, &ParticipantHandler::onNewCtaDataErrorFromProxy); connect(m_proxyListener.get(), &ProxyListener::newParticipantModeReport, this, &ParticipantHandler::onNewParticipantModeReport); connect(m_proxyListener.get(), &ProxyListener::newSpecialSensorData, this, &ParticipantHandler::onNewSpecialSensorData); connect(m_proxyListener.get(), &ProxyListener::newFunctionalAreaEntryChanged, this, &ParticipantHandler::onNewFunctionalAreaEntryChanged); connect(m_hitLineInfoTimer, &QTimer::timeout, this, &ParticipantHandler::onHitLineInfoTimeout); connect(m_proxyListener.get(), &ProxyListener::newLaserDesignationData, this, &ParticipantHandlerForExercise::onNewLaserDesignation); connect(m_proxyListener.get(), &ProxyListener::laserDesignationRemoved, this, &ParticipantHandlerForExercise::onLaserDesignationRemoved); // PVSan connect(m_proxyListener.get(), &ProxyListener::newSanitaryMaterialStockReport, this, &ParticipantHandler::onSanitaryMaterialStockReport); connect(m_proxyListener.get(), &ProxyListener::newParticipantSanitation, this, &ParticipantHandler::onParticipantSanitation); connect(m_proxyListener.get(), &ProxyListener::newOperatorAction, this, &ParticipantHandler::onOperatorAction); connect(m_proxyListener.get(), &ProxyListener::newEquipmentEvent, this, &ParticipantHandler::onEquipmentEvent); // Vulcano / Building Laser Designation connect(m_proxyListener.get(), &ProxyListener::newBuildingHitLineInfo, this, &ParticipantHandler::onBuildingHitLineInfo); // for counting charges connect(m_mainHandler->participantHandler().get(), &ParticipantHandler::gearChargeChanged, this, &ParticipantHandlerForExercise::onGearChargeChanged); m_hitLineInfoTimer->setSingleShot(false); m_hitLineInfoTimer->start(15000); if (!m_pdmDtc) { m_pdmDtc = m_mainHandler->ddsHandler()->pdmDtc(); m_pdmDtc->waitForHistoricalDataGearType(); m_pdmDtc->waitForHistoricalDataGearTypeChargeType(); } // for(const auto &participant : qAsConst(m_participants)) // customedSetGearCharges(participant); return true; } void ParticipantHandlerForExercise::setEquipmentNumberForParticipant(int oldEqNo, int newEqNo, int participantId) { ParticipantHandler::setEquipmentNumberForParticipant(oldEqNo, newEqNo, participantId); if (m_publishEquipmentExerciseMappings && !m_publishEquipmentExerciseMappingsTimer->isActive()) m_publishEquipmentExerciseMappingsTimer->start(1000); } bool ParticipantHandlerForExercise::isUnitType(int typeId) const { return m_mainHandler->typeHandler()->isUnitType(typeId); } void ParticipantHandlerForExercise::createHitlineInfo(int originatorId, Participant *originParticipant, Participant *participantToBeChanged, const TrainingAreaBase::EquipmentPosition &pos, int originatorChargeTypeId, qint64 sampleTime) { if (!originParticipant) return; if (!m_pdmDtc) m_pdmDtc = m_mainHandler->ddsHandler()->pdmDtc(); // NOTE: igniterType/IgniterFunciton = 0 as long as HitLineInfo does not contain values if (!m_pdmDtc->showHitline(originatorChargeTypeId, 0, 0)) return; INFO_LOG(m_logger) << "createHitlineInfo: originParticipant " << originParticipant->position(); INFO_LOG(m_logger) << "createHitlineInfo: pos" << pos; // publish HitLineInfo HitLineInfo hli; hli.originatorParticipantId(originatorId); hli.originatorPartyId(originParticipant->partyId()); hli.originatorPos(originParticipant->position().pos()); hli.originatorBuildingId(originParticipant->position().buildingId()); hli.originatorRoomId(originParticipant->position().roomId()); hli.id(m_hitLineId); hli.injuryParticipantId(participantToBeChanged->id()); hli.injuryPos(pos.position().position()); hli.chargeTypeId(originatorChargeTypeId); hli.sampleTime(sampleTime); hli.injuryBuildingId(pos.position().moutObjectId().is_set() ? pos.position().moutObjectId().get() : 0); hli.injuryRoomId(pos.position().moutRoomId().is_set() ? pos.position().moutRoomId().get() : 0); if (!dts()->publish(hli)) return; m_hitLineInfoMap.insert(hli.id(), hli); m_hitLineId++; } void ParticipantHandlerForExercise::onModifyParticipantDataRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::ModifyParticipantDataRequest &request) { DEBUG_LOG(m_logger) << "onModifyParticipantDataRequest: " << request; const auto ret = modifyParticipantDataRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->modifyParticipantDataReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::modifyParticipantDataRequest(const ModifyParticipantDataRequest &request) { startLongAction(); QSet participantsWithChanges; ValidateParticipantData validateData; QSet partiesUsed; QSet partiesWithDeletedOrMovedOutParticipants; validateData.forceAddWithId = request.forceAddWithId(); validateData.participantsToBeCreatedOrModified.reserve(request.data().size()); bool permanentRemoveParticipants = !m_mainHandler->exerciseId() || m_exmDtc->exercise(m_mainHandler->exerciseId()).state() == ExerciseManagement::ExerciseState::Created; // check assign state - we can't remove participants handled by warehouse for (const ParticipantData ¤tParticipant : request.data()) { if (!currentParticipant.isDeleted()) { validateData.participantsToBeCreatedOrModified.append(currentParticipant); continue; } if (currentParticipant.id() == 0) return Core::FunctionReplyObject(request.requestId(), tr_lts("dds", "Not possible to remove participantData with modId %1, because it does not have participantId set")) .arg(currentParticipant.modId()); auto ret = checkDeleteParticipant(currentParticipant.id()); if (ret.error()) { ret.setId(request.requestId()); return ret; } } checkLongActionBreak(); for (const ParticipantManagement::ParticipantData ¤tParticipant : qAsConst(validateData.participantsToBeCreatedOrModified)) { auto ret = validateParticipantData(validateData, currentParticipant); if (ret.error()) { ret.setId(request.requestId()); return ret; } } DEBUG_LOG(m_logger) << " >>>>> ModifyParticipantDataRequest has valid data"; if (m_firingUnitValidator) { auto ret = m_firingUnitValidator->firingUnitError(validateData.modIdToParticipantId, validateData.participantsToBeCreatedOrModified); if (ret.error()) { ret.setId(request.requestId()); return ret; } DEBUG_LOG(m_logger) << "There are no firing unit errors."; } else WARNING_LOG(m_logger) << "There is no valid FiringUnitValidator"; checkLongActionBreak(); // first sort out participants to be marked as deleted DBTransaction removeTransaction(m_mainHandler->db()->database()); for (const ParticipantData ¤tParticipant : request.data()) { if (currentParticipant.isDeleted()) { if (currentParticipant.id() == 0) return Core::FunctionReplyObject(request.requestId(), tr_lts("dds", "Not possible to remove participantData with modId %1, because it does not have participantId set")) .arg(currentParticipant.modId()); auto ret = deleteParticipant(currentParticipant.id(), permanentRemoveParticipants, true); // only a real error if the participant exists (deleting an already deleted participant is fine if (ret.error() && m_participants.contains(currentParticipant.id())) { ret.setId(request.requestId()); return ret; } partiesWithDeletedOrMovedOutParticipants.insert(currentParticipant.partyId()); } } removeTransaction.commit(); checkLongActionBreak(); for (int i = 0; i < validateData.participantsToBeCreatedOrModified.size() && !validateData.participantsToResetEquipmentNumber.isEmpty(); ++i) { ParticipantData &pd = validateData.participantsToBeCreatedOrModified[i]; for (int j = 0; j < validateData.participantsToResetEquipmentNumber.size(); ++j) { if (pd.modId() == validateData.participantsToResetEquipmentNumber.at(j)) { pd.equipmentNumber().reset(); pd.assignState(ParticipantAssignState::NotAssigned); validateData.participantsToResetEquipmentNumber.removeAt(j); break; } } } DBTransaction trans(m_mainHandler->db()->database()); std::vector dbParticipantsToBeCreated; std::vector dbParticipantsToBeCreatedIndexes; dbParticipantsToBeCreated.reserve(validateData.participantsToBeCreatedOrModified.size()); dbParticipantsToBeCreatedIndexes.reserve(validateData.participantsToBeCreatedOrModified.size()); // create new and update existing participants DEBUG_LOG(m_logger) << "Creating new and updating existing participants ..."; for (int i = 0; i < validateData.participantsToBeCreatedOrModified.size(); ++i) { const ParticipantData ¤tData = validateData.participantsToBeCreatedOrModified.at(i); auto modIdIt = validateData.modIdToParticipantId.constFind(currentData.modId()); if (modIdIt == validateData.modIdToParticipantId.constEnd()) { auto part = toDbParticipant(currentData, request.forceAddWithId()); if (Settings::markVehiclesAsReservedFromExcon()) { if (part.assignState == ParticipantAssignState::NotAssigned && part.equipmentNumber == 0 && !isUnitType(part.typeId)) { const auto *type = m_mainHandler->typeHandler()->typeById(part.typeId); if (type && type->typeClass() != TypeClass::Human) { // reserve participants for excon since otherwise the guez warehouse will automatically // assign an equipment number for this participant... *argh* part.assignState = ParticipantAssignState::HandoutExcon; } } } dbParticipantsToBeCreated.push_back(std::move(part)); dbParticipantsToBeCreatedIndexes.push_back(i); DEBUG_LOG(m_logger) << "new participant, index: " << i << ", data: " << currentData; } else { // update existing participant Participant *participantToUpdate = participantById(modIdIt.value()); if (!participantToUpdate) { ERROR_LOG(m_logger) << "Could not find participant to update with modId " << modIdIt.value() << ", request: " << request; continue; } const int oldPartyId = participantToUpdate->partyId(); // check for nullptr not necessary. If the participant does not exist, modIdToParticipantId would not include the Id if (participantToUpdate->hasToBeChanged(currentData)) { const int newPartyId = participantToUpdate->partyId(); participantsWithChanges.insert(participantToUpdate); if (oldPartyId != newPartyId) partiesWithDeletedOrMovedOutParticipants.insert(oldPartyId); } participantToUpdate = nullptr; } } checkLongActionBreak(); // insert new participants auto ret = createNewParticipants(dbParticipantsToBeCreated, dbParticipantsToBeCreatedIndexes, validateData.participantsToBeCreatedOrModified, validateData.modIdToParticipantId, participantsWithChanges); if (ret.error()) { ret.setId(request.requestId()); return ret; } DEBUG_LOG(m_logger) << "New participants created and existing participants updated"; // build hierarchy for (const ParticipantData ¤tData : qAsConst(validateData.participantsToBeCreatedOrModified)) // first set superiorIds { Participant *participantToUpdate = participantById(validateData.modIdToParticipantId.value(currentData.modId())); if (currentData.superiorModId() >= 0) // superiorModId < 0 -> no change in superior { const int superiorId = validateData.modIdToParticipantId.value(currentData.superiorModId()); if (participantToUpdate->hasNewSuperiorId(superiorId)) participantsWithChanges.insert(participantToUpdate); } } // update inferiorIds, do not iterator over m_participants and use inferiorIdsForParticipant because its O² QHash> inferiors; inferiors.reserve(m_participants.size() / 2); // first collect all inferiors per superior for (const Participant *currentParticipant : qAsConst(m_participants)) { int superiorId = currentParticipant->superiorId(); inferiors[superiorId].push_back(currentParticipant->id()); if (!partiesWithDeletedOrMovedOutParticipants.isEmpty()) { // remove party ids which may be empty now since we found a participant with this party id auto it = partiesWithDeletedOrMovedOutParticipants.find(currentParticipant->partyId()); if (it != partiesWithDeletedOrMovedOutParticipants.end()) partiesWithDeletedOrMovedOutParticipants.erase(it); } } // now update all participants with newly collected inferiors for (Participant *currentParticipant : qAsConst(m_participants)) { if (currentParticipant->hasNewInferiorStructure(inferiors.value(currentParticipant->id()))) participantsWithChanges.insert(currentParticipant); } checkLongActionBreak(); // update in Database std::vector dbParticipantsToUpdate; dbParticipantsToUpdate.reserve(participantsWithChanges.size()); for (Participant *currentParticipant : qAsConst(participantsWithChanges)) { dbParticipantsToUpdate.push_back(currentParticipant->toDbParticipant()); partiesUsed.insert(currentParticipant->partyId()); } if (!m_mainHandler->db()->update(dbParticipantsToUpdate)) return Core::FunctionReplyObject(request.requestId(), tr_lts("dds", "Could not update participant data in database, participant data may be inconsistent")); trans.commit(); checkLongActionBreak(); DDSConnector::CoherentSet cs(m_mainHandler->dds()); // mark parties in use ChangePartyUsedStateRequest req; for (int partyId : qAsConst(partiesUsed)) { req.partyId(partyId); req.isUsedBy(true); m_mainHandler->partyHandler()->changePartyUsedStateRequest(req); } for (int partyId : qAsConst(partiesWithDeletedOrMovedOutParticipants)) { req.partyId(partyId); req.isUsedBy(false); m_mainHandler->partyHandler()->changePartyUsedStateRequest(req); } if (request.data().size() == 1 && request.data().at(0).isDeleted()) { // make sure to not save a participant orga for this participant const int removedParticipantId = request.data().at(0).id(); auto it = m_participantOrganisations.find(removedParticipantId); if (it != m_participantOrganisations.end()) { const int dbId = it.value().dbId; m_mainHandler->db()->removePpmParticipantOrganisation(dbId); dts()->removeParticipantOrganisationByKey(removedParticipantId); m_participantOrganisations.erase(it); } } if (request.participantOrganisation().is_set()) { const ParticipantOrganisation &newOrga = request.participantOrganisation().get(); auto it = m_participantOrganisations.find(newOrga.participantId()); const int originPartyId = (it == m_participantOrganisations.end() ? -1 : it.value().originPartyId()); // same party? -> returned to 'home' -> we can remove this if (newOrga.currentPartyId() == originPartyId) { if (it == m_participantOrganisations.end()) WARNING_LOG(m_logger) << "Could suprious party organisation - ignoring: " << newOrga; else { const int dbId = it.value().dbId; m_mainHandler->db()->removePpmParticipantOrganisation(dbId); dts()->removeParticipantOrganisationByKey(newOrga.participantId()); m_participantOrganisations.erase(it); } } else { ParticipantOrganisationEx orga; orga.copy(newOrga); if (it == m_participantOrganisations.end()) { // new Database::PpmParticipantOrganisation dbOrga(toDb(orga)); m_mainHandler->db()->insert(dbOrga); orga.dbId = dbOrga.id; m_participantOrganisations.insert(orga.participantId(), orga); } else { // modified orga.dbId = it.value().dbId; // the origin party id must not be overwritten // (but is unknown in some usecases on the client - therefore overwrite here) orga.originPartyId(originPartyId); m_mainHandler->db()->update(toDb(orga)); *it = orga; } dts()->publish(orga); } } checkLongActionBreak(); QJsonArray jsonArray; // finally ... publish participant data via dds if no error occured for (Participant *currentParticipant : qAsConst(participantsWithChanges)) { currentParticipant->publishParticipant(); NOTICE_LOG(m_logger) << "publishParticipant id=" << currentParticipant->id(); jsonArray.append(currentParticipant->id()); // QTimer::singleShot(500, this, [=](){ customedSetGearCharges(currentParticipant);}); } QJsonObject jsonObj; jsonObj[QStringLiteral("participants")] = jsonArray; QJsonDocument doc(jsonObj); QByteArray bytes = doc.toJson(QJsonDocument::Compact); // 4. In const char* wandeln const char *errStr = ""; const std::vector errArgs = {}; const char *additionalInfo = bytes.constData(); return Core::FunctionReplyObject(request.requestId(), errStr, errArgs, additionalInfo); } void ParticipantHandlerForExercise::onModifyStateRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::ModifyStateRequest &request) { DEBUG_LOG(m_logger) << "onModifyStateRequest: " << request; auto ret = modifyStateRequest(cookie, request); if (ret.error()) { WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->modifyStateReply(cookie, ret); } // delayed answer in non-error case } Core::FunctionReplyObject ParticipantHandlerForExercise::modifyStateRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::ModifyStateRequest &request) { TrainingAreaBase::DemandVulnerabilityStateChangeRequest req; int failedParticipantId = 0; if (!participantIds2EquipmentNumbers(request.participantIds(), req.equipmentNumbers(), &failedParticipantId)) return Core::FunctionReplyObject(0, tr_lts("dds", "Got invalid participant id %1 in modify state request")).arg(failedParticipantId); req.requestId(s_tabRequestId++); req.workstationId(request.workstationId()); req.vulnerabilityState(request.vulnerabilityState()); req.vulnerabilityKind(request.vulnerabilityKind()); // Pv San req.vitality(request.vitality()); // Pv San return m_proxyListener->demandVulnerabilityStateChange(cookie, req); } void ParticipantHandlerForExercise::onInitParticipantRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::InitParticipantRequest &request) { DEBUG_LOG(m_logger) << "onInitParticipantRequest: " << request; auto ret = initParticipantRequest(cookie, request); if (ret.error()) { WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->initParticipantReply(cookie, ret); } // delayed answer in non-error case } Core::FunctionReplyObject ParticipantHandlerForExercise::initParticipantRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::InitParticipantRequest &request) { TrainingAreaBase::DemandInitRequest req; int failedParticipantId = 0; if (!participantIds2EquipmentNumbers(request.participantIds(), req.equipmentNumbers(), &failedParticipantId)) return Core::FunctionReplyObject(0, tr_lts("dds", "Got invalid participant id %1 in init request")).arg(failedParticipantId); req.requestId(s_tabRequestId++); req.workstationId(request.workstationId()); req.restockCharges(request.restockCharges()); req.resetManipulation(request.resetManipulation()); req.resetVulnerabilityState(request.resetVulnerabilityState()); req.restockSanitationMaterial(request.restockSanitationMaterial()); return m_proxyListener->demandInitRequest(cookie, req); } void ParticipantHandlerForExercise::onSetParticipantModeRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::SetParticipantModeRequest &request) { DEBUG_LOG(m_logger) << "onSetParticipantModeRequest: " << request; auto ret = setParticipantModeRequest(cookie, request); if (ret.error()) { WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setParticipantModeReply(cookie, ret); } // delayed answer in non-error case } Core::FunctionReplyObject ParticipantHandlerForExercise::setParticipantModeRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::SetParticipantModeRequest &request) { const Participant *participant = request.participantId() ? participantById(request.participantId()) : nullptr; if (participant == nullptr && request.participantId() != 0) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Got SetParticipantModeRequest for unknown participant id %1")).arg(request.participantId()); TrainingAreaBase::SendParticipantModeRequest tabRequest; tabRequest.requestId(s_tabRequestId++); tabRequest.equipmentNumber(participant ? participant->equipmentNumber() : 0); tabRequest.participantId(request.participantId()); tabRequest.language(request.language()); tabRequest.operationMode(request.opMode()); tabRequest.reactivationDetoxication(request.reactivationDecontamination()); tabRequest.reactivationMaintenance(request.reactivationMaintenance()); tabRequest.reactivationMedic(request.reactivationMedic()); tabRequest.protectionModeVest(request.modeVest()); tabRequest.protectionModeHelmet(request.modeHelmet()); tabRequest.personellProtection(request.modePersonalProtection()); tabRequest.protectionLevel(request.protectionLevel()); tabRequest.handgrenadeSearchMode(request.handgrenadeSearchMode()); tabRequest.internationalMode(request.internationalMode()); tabRequest.abcProtectionMode(request.abcProtectionMode()); return m_proxyListener->sendParticipantModeRequest(cookie, tabRequest); } void ParticipantHandlerForExercise::onSendMessageRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::SendMessageRequest &request) { DEBUG_LOG(m_logger) << "onSendMessageRequest: " << request; auto ret = sendMessageRequest(cookie, request); if (ret.error()) { WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->sendMessageReply(cookie, ret); } // delayed answer in non-error case } Core::FunctionReplyObject ParticipantHandlerForExercise::sendMessageRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::SendMessageRequest &request) { TrainingAreaBase::SendEquipmentMessageRequest req; int failedParticipantId = 0; if (!participantIds2EquipmentNumbers(request.participantIds(), req.equipmentNumbers(), &failedParticipantId)) return Core::FunctionReplyObject(0, tr_lts("dds", "Got invalid participant id %1 in send message request")).arg(failedParticipantId); req.requestId(s_tabRequestId++); req.message().set(request.message()); return m_proxyListener->sendEquipmentMessage(cookie, req); } void ParticipantHandlerForExercise::onRegisterParticipantRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::RegisterParticipantRequest &request) { DEBUG_LOG(m_logger) << "onRegisterParticipantRequest: " << request; RegisterReturnValue retVal; const auto ret = registerParticipantRequest(request, retVal); if (ret.error()) WARNING_LOG(m_logger) << ret(); RegisterReply rRep(request.participantId(), ret.str(), ret.args(), ret.additionalInfo(), retVal, m_mainHandler->exerciseId()); m_mainHandler->fts()->registerParticipantReply(cookie, rRep); } Core::FunctionReplyObject ParticipantHandlerForExercise::registerParticipantRequest(const RegisterParticipantRequest &request_, RegisterReturnValue &retVal) { RegisterParticipantRequest request(request_); Participant *participantToRegister = m_participants.value(request.participantId(), nullptr); if (participantToRegister == nullptr) // check if participant exists { retVal = RegisterReturnValue::PlayerNotFound; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - participant id %1 is invalid")) .arg(request.participantId()); } const EquipmentType *equipmentType = m_mainHandler->typeHandler()->equipmentTypeByTypeId(participantToRegister->typeId()); if (equipmentType == nullptr) // check if the participant has a valid equipment type { retVal = RegisterReturnValue::PlayerNotFound; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - participant with id %1 has invalid equipmentType")) .arg(request.participantId()); } // special case: warehouse doesn't like the equipment number set (ICD23, State C2) // but we want to use this equipment number -> set ExconOverrule if (participantToRegister->equipmentNumber() != 0 && participantToRegister->assignState() == ParticipantAssignState::HandoutExcon && request.assignState() == ParticipantAssignState::AssignedWms && request.equipmentNumber() == -1) { NOTICE_LOG(m_logger) << "WMS doesn't know equipment number " << participantToRegister->equipmentNumber() << " - we have to override the assignment"; request.assignState(ParticipantAssignState::ExconWmsOverruled); request.equipmentNumber(participantToRegister->equipmentNumber()); } if (!participantToRegister->isNewAssignStateValid(request.assignState(), request.equipmentNumber())) // check if assign state is valid { retVal = RegisterReturnValue::AssignmentIncorrect; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - combination of equipment number and assign state is invalid")); } if (participantToRegister->isDeleted()) // check if participant is marked as deleted { // the warehouse is allowed to set to NotAssigned (== free the equipment number) even when the participant is already deleted if (request.assignState() != ParticipantAssignState::NotAssigned) { retVal = RegisterReturnValue::PlayerDeleted; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - participant is deleted")); } } // only identification and info has changed if (participantToRegister->equipmentNumber() == request.equipmentNumber() && participantToRegister->assignState() == request.assignState()) { RegisterParticipantRequest oldData = participantToRegister->toRegisterParticipantRequest(); auto *pfe = static_cast(participantToRegister); pfe->registerInfoFromFt(request); if (!m_mainHandler->db()->update(participantToRegister->toDbParticipant())) { // rollback pfe->registerFromFt(oldData); retVal = RegisterReturnValue::RegistrationFailed; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - failed to update info/identification in database")); } participantToRegister->publishParticipant(); retVal = RegisterReturnValue::RegistrationOk; return Core::FunctionReplyObject(request.participantId()); } if (participantByEquipmentNumber(request.equipmentNumber()) != nullptr && participantByEquipmentNumber(request.equipmentNumber()) != participantToRegister && request.equipmentNumber() > 0) // check if equipment number is already in use { retVal = RegisterReturnValue::EquipNoAlreadyAssigned; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - equipment number %1 is already assigned")) .arg(request.equipmentNumber()); } const bool hasToCheckEquipmentNumber = request.assignState() != ParticipantAssignState::HandoutWms && request.assignState() != ParticipantAssignState::ExconWmsOverruled && request.assignState() != ParticipantAssignState::NotAssigned; if (participantToRegister->equipmentNumber() > 0 && hasToCheckEquipmentNumber) // check if the participant already has an equipment number { retVal = RegisterReturnValue::AssignmentIncorrect; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - participant with id %1 has already an assigned equipment number %2")) .arg(request.participantId()) .arg(request.equipmentNumber()); } if ((request.equipmentNumber() > equipmentType->maxEquipNo() || request.equipmentNumber() < equipmentType->minEquipNo()) && request.equipmentNumber() > 0) { // check if the equipment number is in range for the associated equipment type retVal = RegisterReturnValue::AssignmentIncorrect; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - Equipment number %1 is not in range (%2 - %3)")) .arg(request.equipmentNumber()) .arg(equipmentType->minEquipNo()) .arg(equipmentType->maxEquipNo()); } const Type *type = typeById(participantToRegister->typeId()); if (type == nullptr) { retVal = RegisterReturnValue::RegistrationFailed; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - participant has invalid type id")); } RegisterParticipantRequest oldData = participantToRegister->toRegisterParticipantRequest(); auto *pfe = static_cast(participantToRegister); if (!pfe->registerFromFt(request)) { retVal = RegisterReturnValue::EquipNoAlreadyAssigned; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - equipment number %1 is already assigned")) .arg(request.equipmentNumber()); } if (!m_mainHandler->db()->update(participantToRegister->toDbParticipant())) { // rollback pfe->registerFromFt(oldData); retVal = RegisterReturnValue::RegistrationFailed; return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not register participant - failed to update in database")); } participantToRegister->publishParticipant(); retVal = RegisterReturnValue::RegistrationOk; return Core::FunctionReplyObject(request.participantId()); } void ParticipantHandlerForExercise::onSetSimulatedPositionRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedPosition &request) { ERROR_LOG(m_logger) << "onSetSimulatedPositionRequest: " << request; const auto ret = setSimulatedPositionRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setSimulatedPositionReply(cookie, ret); } void ParticipantHandlerForExercise::setSimulatedPosition(Participant *participant, const SetSimulatedPosition &request) { if (!participant) return; ERROR_LOG(m_logger) << "setSimulatedPosition start.. "; static const qint64 outDated = 10 * 60 * 1000; // 10 min const qint64 oldTime = request.onlyOutdatedParticipants() ? (participant->position().sampleTime() + outDated) : 0; const qint64 curTime = QDateTime::currentMSecsSinceEpoch(); if (oldTime < curTime) { TrainingAreaBase::EquipmentPosition pos; pos.equipmentNumber(-participant->id()); pos.sampleTime(request.onlyOutdatedParticipants() ? participant->position().sampleTime() + 1 : curTime); pos.position().position(request.position()); pos.position().reportingSource(Core::PositionReportingSource::SetByOperator); if (!m_dtsTab) { m_dtsTab = TrainingAreaBase::Interface::Dts::create(m_mainHandler->dds()); m_dtsTab->enableEquipmentPosition(); } m_dtsTab->publish(pos); } for (auto childId : participant->inferiors()) setSimulatedPosition(m_participants.value(childId), request); } void ParticipantHandlerForExercise::onSetSimulatedPositionRelativeRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedPositionRelative &request) { DEBUG_LOG(m_logger) << "onSetSimulatedPositionRelativeRequest: " << request; const auto ret = setSimulatedPositionRelativeRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setSimulatedPositionRelativeReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::setSimulatedPositionRelativeRequest(const SetSimulatedPositionRelative &request) { if (request.participantId() <= 0) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not move participant - participant id %1 is invalid")).arg(request.participantId()); Participant *participant = m_participants.value(request.participantId(), nullptr); if (participant == nullptr) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not move participant - participant id %1 is invalid")).arg(request.participantId()); setSimulatedPositionRelative(participant, request); return Core::FunctionReplyObject(request.participantId()); } void ParticipantHandlerForExercise::updateParticipantFormation(Participant *participant, const Core::GeoPosition &position, const QPointF &offset) { if (!participant) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " id = " << participant->id(); Core::GeoPosition tempPos; if (qFuzzyIsNull(position.lon()) || qFuzzyIsNull(position.lat())) { // typical for units: DEBUG_LOG(m_logger) << LOGFUNCTION << " participantId=" << participant->id() << ", skip due to position isFuzzyNull =" << position; } else { // add offset in meter: const LtsCoordTrans::UtmCoordinate partCoord = LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(position.lon(), position.lat())); const QPointF coordinate = partCoord.geographic(QPointF(offset.x(), offset.y())); // offet in meter DEBUG_LOG(m_logger) << "coordinate=" << coordinate.x() << " - " << coordinate.y() << " offset(" << offset.x() << ";" << offset.y() << ")"; tempPos.lon(coordinate.x()); tempPos.lat(coordinate.y()); TrainingAreaBase::EquipmentPosition pos; pos.equipmentNumber(-participant->id()); const qint64 curTime = QDateTime::currentMSecsSinceEpoch(); pos.sampleTime(curTime); pos.position().position(tempPos); pos.position().reportingSource(Core::PositionReportingSource::SetByOperator); if (!m_dtsTab) { m_dtsTab = TrainingAreaBase::Interface::Dts::create(m_mainHandler->dds()); m_dtsTab->enableEquipmentPosition(); } m_dtsTab->publish(pos); } } void ParticipantHandlerForExercise::setFormationAsLine(Participant *participant, const SetSimulatedParticipantFormation &request, const double distance, const double angleDeg) { DEBUG_LOG(m_logger) << LOGFUNCTION << " set formation As line..."; if (!participant) return; DEBUG_LOG(m_logger) << " participant Name: " << participant->participantData().name() << " inferiorCount: " << participant->inferiors().size(); const std::vector &inferiors = participant->inferiors(); if (inferiors.empty()) return; else if (inferiors.size() == 1) // in case only one pp { const SetSimulatedPosition simPosReq(participant->id(), request.position(), false); setSimulatedPosition(participant, simPosReq); return; } // adjust participant positions distance to one another QPolygonF ppPoints; ppPoints.reserve(inferiors.size()); for (int i = 0; i < (int)inferiors.size(); ++i) ppPoints.push_back(QPointF(i * distance, 0)); double rotationAngle = 0; // set rotation to 0° North(12h) counter clock wise { rotationAngle = angleDeg < 360. ? angleDeg : std::fmod(angleDeg, 360.); rotationAngle = rotationAngle < 90. ? std::fabs(90 - rotationAngle) : std::fabs(360 - rotationAngle + 90); } const double length = (inferiors.size() - 1) * distance; QTransform transform = QTransform().translate(-length / 2.0, 0); ppPoints = transform.map(ppPoints); transform.reset(); // rotate ppPoints = transform.rotate(rotationAngle).map(ppPoints); for (int index = 0; index < (int)inferiors.size(); ++index) updateParticipantFormation(m_participants.value(inferiors[index]), request.position(), ppPoints.at(index)); } void ParticipantHandlerForExercise::setSimulatedPositionRelative(Participant *participant, const SetSimulatedPositionRelative &request) { if (!participant) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " id = " << participant->id(); Core::GeoPosition tempPos = participant->position().pos(); if (qFuzzyIsNull(tempPos.lon()) || qFuzzyIsNull(tempPos.lat())) { // typical for units: DEBUG_LOG(m_logger) << LOGFUNCTION << " participantId=" << participant->id() << ", skip due to position isFuzzyNull =" << tempPos; } else { // add offset in meter: LtsCoordTrans::UtmCoordinate partCoord = LtsCoordTrans::UtmCoordinate::fromGeographic(QPointF(tempPos.lon(), tempPos.lat())); QPointF posWithOffset = partCoord.geographic(QPointF(request.positionOffsetX(), request.positionOffsetY())); // offet in meter DEBUG_LOG(m_logger) << LOGFUNCTION << " posWithOffset=" << posWithOffset.x() << " - " << posWithOffset.y(); tempPos.lon(posWithOffset.x()); tempPos.lat(posWithOffset.y()); TrainingAreaBase::EquipmentPosition pos; pos.equipmentNumber(-participant->id()); const qint64 curTime = QDateTime::currentMSecsSinceEpoch(); pos.sampleTime(curTime); pos.position().position(tempPos); pos.position().reportingSource(Core::PositionReportingSource::SetByOperator); if (!m_dtsTab) { m_dtsTab = TrainingAreaBase::Interface::Dts::create(m_mainHandler->dds()); m_dtsTab->enableEquipmentPosition(); } m_dtsTab->publish(pos); } for (auto childId : participant->inferiors()) setSimulatedPositionRelative(m_participants.value(childId), request); // recursive } void ParticipantHandlerForExercise::onSetSimulatedParticipantFormationRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedParticipantFormation &request) { DEBUG_LOG(m_logger) << "onSetSimulatedParticipantFormationRequest: " << request; const auto ret = setSimulatedParticipantFormationRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setSimulatedParticipantFormationReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::setSimulatedParticipantFormationRequest(const SetSimulatedParticipantFormation &request) { if (request.participantId() <= 0) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not move participant - participant id %1 is invalid")).arg(request.participantId()); Participant *participant = m_participants.value(request.participantId(), nullptr); if (participant == nullptr) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not move participant - participant id %1 is invalid")).arg(request.participantId()); const ParticipantFormationStruct formation(QString::fromStdString(request.formation())); if (formation.hasFormationData()) { DEBUG_LOG(m_logger) << "setSimulatedParticipantFormation type: " << formation.geometry << " | distance: " << formation.distance << " | angle: " << formation.angle; setFormationAsLine(participant, request, formation.distance, formation.angle); } else { DEBUG_LOG(m_logger) << "setSimulatedPositionRelative WITHOUT FORMATION"; SetSimulatedPositionRelative relSimRequest(request.participantId(), request.positionOffsetX(), request.positionOffsetY()); setSimulatedPositionRelative(participant, relSimRequest); } return Core::FunctionReplyObject(request.participantId()); } void ParticipantHandlerForExercise::onSetSanitationRequest(const rti::core::SampleIdentity &cookie, const SetSanitationRequest &request) { DEBUG_LOG(m_logger) << "onSetSanitationRequest: " << request; const auto ret = setSanitationRequest(cookie, request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setSanitationReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::setSanitationRequest(const rti::core::SampleIdentity &cookie, const SetSanitationRequest &request) { Participant *participant = m_participants.value(request.id(), nullptr); if (participant == nullptr) return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Could not set participant - participant id %1 is invalid")).arg(request.id()); TrainingAreaBase::SendSetSanitationRequest tabRequest; tabRequest.equipmentNumber(participant->equipmentNumber()); tabRequest.originatorEquipmentNumber(request.data().originatorEquipmentNumber()); tabRequest.participantId(request.id()); tabRequest.qualification(static_cast(request.data().qualification().underlying())); tabRequest.sanitationProceduresTaken(request.data().sanitationProceduresTaken()); return m_proxyListener->sendSetSanitationRequest(cookie, tabRequest); } void ParticipantHandlerForExercise::onSelectiveReammunitionRequest(const rti::core::SampleIdentity &cookie, const SelectiveReammunitionRequest &request) { DEBUG_LOG(m_logger) << "onSelectiveReammunitionRequest: " << request; const auto ret = selectiveReammunitionRequest(cookie, request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->selectiveReammunitionReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::selectiveReammunitionRequest(const rti::core::SampleIdentity &cookie, const SelectiveReammunitionRequest &request) { Participant *participant = m_participants.value(request.id(), nullptr); INFO_LOG(m_logger) << "ParticipantHandlerForExercise::selectiveReammunitionRequest: "; if (participant == nullptr) return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Could not set participant - participant id %1 is invalid")).arg(request.id()); INFO_LOG(m_logger) << "Participant id = " << request.id(); TrainingAreaBase::SendSelectiveReammunitionRequest tabRequest; tabRequest.equipmentNumber(participant->equipmentNumber()); tabRequest.participantId(request.id()); tabRequest.gearId(request.gearId()); INFO_LOG(m_logger) << "Gear id = " << request.gearId(); for (const Reammunition ra : qAsConst(request.reammunition())) { TrainingAreaBase::Reammunition tabRa; tabRa.chargeTypeId(ra.chargeTypeId()); tabRa.newDefaultStock(ra.newDefaultStock()); tabRa.newStock(ra.newStock()); tabRa.tracerRatio(ra.tracerRatio()); INFO_LOG(m_logger) << "Charge type id = " << ra.chargeTypeId(); tabRequest.reammunition().push_back(tabRa); } return m_proxyListener->selectiveReammunitionRequest(cookie, tabRequest); } void ParticipantHandlerForExercise::onSetAsSimulatedRequest(const rti::core::SampleIdentity &cookie, const SetAsSimulated &request) { DEBUG_LOG(m_logger) << " onSetAsSimulatedRequest: " << request; const auto ret = setAsSimulatedRequest(request); if (ret.error()) WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->setAsSimulatedReply(cookie, ret); } Core::FunctionReplyObject ParticipantHandlerForExercise::setAsSimulatedRequest(const SetAsSimulated &request) { DEBUG_LOG(m_logger) << "setAsSimulatedRequest: " << request; ParticipantManagement::Participant participant = m_ppmDtc->participant(request.participantId()); participant.isSimulated(request.setSimulated()); const ParticipantManagement::ParticipantData &data = m_ppmDtc->convertParticipantToParticipantData(participant); Database::PpmParticipant dbParticipant = toDbParticipant(data, request.participantId()); dbParticipant.superiorId = participant.superiorId(); DEBUG_LOG(m_logger) << "PpmParticipant setted supId: " << dbParticipant.tacticalIdentifier << " superiorId: " << dbParticipant.superiorId; // db update in ppm_participant if (!m_mainHandler->db()->update(dbParticipant)) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not update participant data in database, participant data may be inconsistent")); // publishing participant data dts()->publish(participant); return Core::FunctionReplyObject(request.participantId()); } void ParticipantHandlerForExercise::onOcCommandRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::OcCommandRequest &request) { DEBUG_LOG(m_logger) << "onOcCommandRequest: " << request; auto ret = ocCommandRequest(cookie, request); if (ret.error()) { WARNING_LOG(m_logger) << ret(); m_mainHandler->fts()->ocCommandReply(cookie, ret); } // delayed answer in non-error case } Core::FunctionReplyObject ParticipantHandlerForExercise::ocCommandRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::OcCommandRequest &request) { TrainingAreaBase::SendOcCommandRequest req; int failedParticipantId = 0; if (!participantIds2EquipmentNumbers(request.participantIds(), req.equipmentNumbers(), &failedParticipantId)) return Core::FunctionReplyObject(0, tr_lts("dds", "Got invalid participant id %1 in init request")).arg(failedParticipantId); req.requestId(s_tabRequestId++); req.action(request.action()); return m_proxyListener->sendOcCommandRequest(cookie, req); } Core::FunctionReplyObject ParticipantHandlerForExercise::setSimulatedPositionRequest(const SetSimulatedPosition &request) { if (request.participantId() <= 0) { for (Participant *participant : qAsConst(m_participants)) { if (participant->isDeleted() || participant->superiorId() != 0) continue; setSimulatedPosition(participant, request); } } else { Participant *participant = m_participants.value(request.participantId(), nullptr); if (participant == nullptr) return Core::FunctionReplyObject(request.participantId(), tr_lts("dds", "Could not set participant - participant id %1 is invalid")).arg(request.participantId()); setSimulatedPosition(participant, request); } return Core::FunctionReplyObject(request.participantId()); } Core::FunctionReplyObject ParticipantHandlerForExercise::createNewParticipants(std::vector &dbParticipantsToBeCreated, const std::vector &dbParticipantsToBeCreatedIndexes, const QVector &participantsToBeCreatedOrModified, QHash &modIdToParticipantId, QSet &participantsWithChanges) { DEBUG_LOG(m_logger) << "Trying to create " << dbParticipantsToBeCreated.size() << " participants"; // creating db entries if (!m_mainHandler->db()->insert(dbParticipantsToBeCreated)) return Core::FunctionReplyObject(tr_lts("dds", "Error inserting new participant into database, participant data may be inconsistent")); for (size_t i = 0; i < dbParticipantsToBeCreated.size(); ++i) { const int index = dbParticipantsToBeCreatedIndexes.at(i); const auto &ddsParticipant = participantsToBeCreatedOrModified.at(index); ParticipantForExercise *newParticipant = new ParticipantForExercise(m_mainHandler, this, m_unitPositionCalculator.get(), dbParticipantsToBeCreated.at(i)); // add to m_participants m_participants.insert(newParticipant->id(), newParticipant); // map mod id modIdToParticipantId.insert(ddsParticipant.modId(), newParticipant->id()); // register to publish changes participantsWithChanges.insert(newParticipant); } return Core::FunctionReplyObject(); } Core::FunctionReplyObject ParticipantHandlerForExercise::validateParticipantData(ValidateParticipantData &data, const ParticipantManagement::ParticipantData &participantToValidate) { DEBUG_LOG(m_logger) << "checking party id"; if (m_mainHandler->partyHandler()->partyById(participantToValidate.partyId()) == nullptr) { return Core::FunctionReplyObject(tr_lts("dds", "Got invalid party id %1")).arg(participantToValidate.partyId()); } if (isSupportType(participantToValidate.typeId())) { if (participantToValidate.partyId() != 1) { return Core::FunctionReplyObject(tr_lts("dds", "Got party id %1 for a support participant. Support participants have to be in party 1")) .arg(participantToValidate.partyId()); } } else if (participantToValidate.partyId() == 1) return Core::FunctionReplyObject(tr_lts("dds", "Not possible to add a non support participant to party with id 1")); DEBUG_LOG(m_logger) << "... ok"; const int partId = participantToValidate.id(); auto *existingParticipant = participantById(partId); // participant id valid? if (participantToValidate.id() != 0 && (existingParticipant != nullptr)) // got participantData with Id and participant already exits { // assign state valid? DEBUG_LOG(m_logger) << "checking assign state"; int32_t equipmentNumber = 0; if (participantToValidate.equipmentNumber().is_set()) equipmentNumber = participantToValidate.equipmentNumber().get(); bool isAssignStateValid = existingParticipant->isNewAssignStateValid(participantToValidate.assignState(), equipmentNumber); if (!isAssignStateValid) { return Core::FunctionReplyObject(partId, tr_lts("dds", "The combination of equipment number and assign state for particpant '%1' is invalid")) .arg(participantInfosFromId(partId)); } DEBUG_LOG(m_logger) << "... ok"; // typeId must not be changed when an equipmentNumber is already set if (existingParticipant->equipmentNumber() != 0) { // type id same when participantId is set? DEBUG_LOG(m_logger) << "checking if typeId is not modified"; if (existingParticipant->typeId() != participantToValidate.typeId()) { return Core::FunctionReplyObject(partId, tr_lts("dds", "It is not allowed to change the type of a participant (%1)")).arg(participantInfosFromId(partId)); } DEBUG_LOG(m_logger) << "... ok"; } data.modIdToParticipantId.insert(participantToValidate.modId(), existingParticipant->id()); /* register participant to indicate that this participant should just be modified and not newly created */ } else { DEBUG_LOG(m_logger) << "New participant to be added: " << participantToValidate; DEBUG_LOG(m_logger) << "checking participant id " << partId; if (participantToValidate.id() != 0 && !data.forceAddWithId) { return Core::FunctionReplyObject(partId, tr_lts("dds", "An invalid participant '%1' was given")).arg(participantInfosFromId(partId)); } DEBUG_LOG(m_logger) << "... ok"; // new participant if (participantToValidate.equipmentNumber().is_set()) { if (participantToValidate.assignState() != ParticipantAssignState::HandoutExcon) return Core::FunctionReplyObject(participantToValidate.modId(), tr_lts("dds", "Got a new participant with an equipment number (%1) but assign state is not HandoutExcon")) .arg(participantToValidate.equipmentNumber().get()); } else { if (participantToValidate.assignState() != ParticipantAssignState::NotAssigned) return Core::FunctionReplyObject(participantToValidate.modId(), tr_lts("dds", "Got a new participant with no equipment number set but assign state != NotAssigned")); } } // superior ids valid? if (participantToValidate.superiorModId() > 0) { DEBUG_LOG(m_logger) << "checking superiorIds"; // superior exists? bool isExistingSuperiorId = false; for (const ParticipantData &dat : qAsConst(data.participantsToBeCreatedOrModified)) { if (participantToValidate.superiorModId() == dat.modId()) { isExistingSuperiorId = true; break; // for (const ParticipantData &dat : data.dataVec) } } if (!isExistingSuperiorId) { return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - the data set with modId %1 has an invalid superiorModId %2")) .arg(participantToValidate.modId()) .arg(participantToValidate.superiorModId()); } DEBUG_LOG(m_logger) << "... ok"; if (participantToValidate.hierarchyNumber() <= 0) return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - the data has a superior but no valid hierarchyNumber")); // all hierarchy numbers with same superior unique? DEBUG_LOG(m_logger) << "checking hierarchyNumbers"; for (const ParticipantData &dat : qAsConst(data.participantsToBeCreatedOrModified)) { // operator==() on optionals returns false if not set ... if (dat.modId() != participantToValidate.modId() && dat.superiorModId() == participantToValidate.superiorModId() && dat.hierarchyNumber() == participantToValidate.hierarchyNumber()) { return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - superiorModId %1 hierarchyNumber %2 is not unique")) .arg(dat.superiorModId()) .arg(dat.hierarchyNumber()); } } DEBUG_LOG(m_logger) << "... ok"; } else { const Participant *curParticipant = m_participants.value(participantToValidate.id()); bool bOk = curParticipant != nullptr; // superiorModId == 0 -> root knot (can be new because of participant organisation) if (curParticipant && participantToValidate.superiorModId() != 0) { // an update of an existing participant, the hierarchy number must not change if (participantToValidate.hierarchyNumber() != curParticipant->toDbParticipant().hierarchyNumber) { WARNING_LOG(m_logger) << "participant: " << curParticipant->toDdsParticipant(curParticipant->toDbParticipant()); bOk = false; } // anything else to check here? } else bOk = (participantToValidate.hierarchyNumber() == 1); // root know of a participant tree - the hierarchy number must be 1 if (!bOk) { WARNING_LOG(m_logger) << "participantToValidate: " << participantToValidate; return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - the root participant (%1) must have a hierarchy number of '1'")).arg(participantToValidate.id()); } } // typeId valid? DEBUG_LOG(m_logger) << "checking typeId"; const Type *type = typeById(participantToValidate.typeId()); if (type == nullptr) // type invalid return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - data with modId %1 has invalid typeId %2")) .arg(participantToValidate.modId()) .arg(participantToValidate.typeId()); DEBUG_LOG(m_logger) << "... ok"; DEBUG_LOG(m_logger) << "Check if type is allowed in ex_" << m_mainHandler->exerciseId(); if (m_mainHandler->exerciseId() < 1 && !isSupportTypeClass(type->typeClass())) // only support types allowed return Core::FunctionReplyObject(tr_lts("dds", "Internal Error - got non support participant data in ex_0 --> not allowed")); DEBUG_LOG(m_logger) << "... ok"; if (participantToValidate.equipmentNumber().is_set()) { const int equipNo = participantToValidate.equipmentNumber().get(); const int participantId = participantToValidate.id(); DEBUG_LOG(m_logger) << "Check if equipment number is globally unique " << equipNo; int partIdLookup = m_equipNumber2ParticipantId.value(equipNo); if (partIdLookup != 0 && partIdLookup != participantId) { if (participantToValidate.partyId() == 0) // in pool, participant was moved from template to pool (otherwise the equipment number could not be twice) { // mod id since it can be new (in fact it must be new here) data.participantsToResetEquipmentNumber += participantToValidate.modId(); INFO_LOG(m_logger) << "Moved a participant from template to pool with an equipment number which already exists - remove equipment number: " << equipNo; } else return Core::FunctionReplyObject(tr_lts("dds", "Another participant already has the same equipment number %1 (%2)")) .arg(equipNo) .arg(participantInfosFromId(participantId)); } else if (isCurrentExerciseActive()) { auto it = m_equipmentExerciseMappings.find(equipNo); if (it != m_equipmentExerciseMappings.end()) { if (it.value().exerciseId != m_mainHandler->exerciseId() || it.value().participantId != participantToValidate.id()) return Core::FunctionReplyObject(tr_lts("dds", "The equipment number %1 is already used by another participant in another exercise (%2)")) .arg(equipNo) .arg(participantInfosFromId(participantId)); } } DEBUG_LOG(m_logger) << "... ok"; } return Core::FunctionReplyObject(); } std::vector ParticipantHandlerForExercise::inferiorIdsForParticipant(int32_t superiorParticipantId) const { std::vector result; for (Participant *currentParticipant : m_participants) { if (currentParticipant->superiorId() == superiorParticipantId) result.push_back(currentParticipant->id()); } return result; } Core::FunctionReplyObject ParticipantHandlerForExercise::checkDeleteParticipant(int id) { Participant *participant = m_participants.value(id); if (!participant) return Core::FunctionReplyObject(id, "Could not delete participant with id %1, because this ID does not exist").arg(id); int eqIdToReset = participant->equipmentNumber(); if (eqIdToReset != 0) { // when we are not 'owner' of the equipment number, we are not allowed to remove this participant switch (participant->assignState().underlying()) { case ParticipantAssignState::NotAssigned: case ParticipantAssignState::HandoutExcon: case ParticipantAssignState::ExconWmsOverruled: // really ExconWmsOverruled? break; case ParticipantAssignState::AssignedWms: case ParticipantAssignState::HandoutWms: return Core::FunctionReplyObject(id, tr_lts("dds", "Can not remove participant with equipment number %1 - handled by warehouse")).arg(eqIdToReset); } } for (int32_t inferiorId : participant->inferiors()) { auto ret = checkDeleteParticipant(inferiorId); if (ret.error()) return ret; } return Core::FunctionReplyObject(); } Core::FunctionReplyObject ParticipantHandlerForExercise::deleteParticipant(int32_t id, bool permanentRemove, bool isTriggeredFromFtRequest) { Participant *participant = m_participants.value(id); if (!participant) return Core::FunctionReplyObject(id, "Could not delete participant with id %1, because this ID does not exist").arg(id); for (int32_t inferiorId : participant->inferiors()) { auto rep = deleteParticipant(inferiorId, permanentRemove); if (rep.error()) return rep; } int eqIdToReset = participant->equipmentNumber(); DEBUG_LOG(m_logger) << "RemoveParticipant, eqId: " << eqIdToReset << ", assignState: " << participant->assignState(); // only remove equipment number when assign state is excon otherwise we will go out of sync with warehouse // not assigned and eqid != 0 ? should never happen but who knows if (participant->assignState() != ParticipantAssignState::NotAssigned && participant->assignState() != ParticipantAssignState::HandoutExcon && participant->assignState() != ParticipantAssignState::ExconWmsOverruled) // really ExconWmsOverruled? return Core::FunctionReplyObject(id, tr_lts("dds", "Can not remove participant with equipment number %1 - handled by warehouse")).arg(eqIdToReset); if (m_mainHandler->ddsHandler()->dtc()->isTypeUas(participant->typeId()) && m_mainHandler->ddsHandler()->dtc()->getUasByParticipantId(participant->id(), m_mainHandler->exerciseId()).id() > 0) { // is in use return Core::FunctionReplyObject(id, tr_lts("dds", "Can not remove participant assigned to UAS: %1")) .arg(m_mainHandler->ddsHandler()->dtc()->getUasByParticipantId(participant->id(), m_mainHandler->exerciseId()).deviceIdentifier()); } if (permanentRemove) { if (!dts()->removeParticipantByKey(id)) return Core::FunctionReplyObject(id, tr_lts("dds", "Error removing participant with id %1 from DDS")).arg(id); if (!m_mainHandler->db()->removePpmParticipant(id)) return Core::FunctionReplyObject(id, tr_lts("dds", "Error removing participant with id %1 from Database")).arg(id); auto it = m_participants.find(id); if (it != m_participants.end()) { delete it.value(); m_participants.erase(it); } } else { // if (eqIdToReset != 0) { auto ftData = participant->toRegisterParticipantRequest(); ftData.equipmentNumber(0); ftData.assignState(ParticipantAssignState::NotAssigned); auto *pfe = static_cast(participant); pfe->registerFromFt(ftData); } participant->markAsDeleted(); // change in database and publish if (!m_mainHandler->db()->update(participant->toDbParticipant())) return Core::FunctionReplyObject(id, "Error while updating participant with id %1 in database").arg(id); if (!participant->publishParticipant()) return Core::FunctionReplyObject(id, "Error while publishing participant with id %1 to DDS").arg(id); } if (eqIdToReset) m_equipNumber2ParticipantId.remove(eqIdToReset); // publish superior changes if (isTriggeredFromFtRequest && participant->superiorId()) { Participant *superior = m_participants.value(participant->superiorId()); if (superior && superior->hasNewInferiorStructure(inferiorIdsForParticipant(superior->id()))) superior->publishParticipant(); } return Core::FunctionReplyObject(); } const Type *ParticipantHandlerForExercise::typeById(int typeId) const { return m_mainHandler->typeHandler()->typeById(typeId); } bool ParticipantHandlerForExercise::isSupportType(int typeId) const { return m_mainHandler->typeHandler()->isSupportType(typeId); } bool ParticipantHandlerForExercise::isParticipantType(int typeId) const { return m_mainHandler->typeHandler()->isParticipantType(typeId); } void ParticipantHandlerForExercise::startLongAction() { m_longActionTimer.start(); } void ParticipantHandlerForExercise::checkLongActionBreak() { if (m_longActionTimer.elapsed() > 300) { m_mainHandler->ddsHandler()->longActionBreak(); m_longActionTimer.start(); } } bool ParticipantHandlerForExercise::participantIds2EquipmentNumbers(const std::vector &participantIds, std::vector &equipmentNumbers, int *failedParticipantId) const { *failedParticipantId = 0; for (int participantId : participantIds) { if (participantId == 0) { if (participantIds.size() != 1) { WARNING_LOG(m_logger) << "ParticipantId 0 found, but more than on entry - not allowed"; return false; } equipmentNumbers.push_back(0); return true; } else { const Participant *participant = participantById(participantId); if (!participant) { *failedParticipantId = participantId; WARNING_LOG(m_logger) << "Could not find participant for participantId " << participantId; return false; } if (participant->equipmentNumber() == 0) { *failedParticipantId = participantId; WARNING_LOG(m_logger) << "Participant with id " << participantId << " has no equipment number"; return false; } equipmentNumbers.push_back(participant->equipmentNumber()); } } return true; } void ParticipantHandlerForExercise::onEquipmentExerciseMappingChanged(const EquipmentExerciseMapping &data, DataModification mod) { switch (mod) { case DataModification::eAdded: case DataModification::eModified: { QSet oldEqIds; for (const auto &db : qAsConst(m_equipmentExerciseMappings)) { if (db.exerciseId != data.exerciseId()) continue; oldEqIds.insert(db.equipmentNumber); } Database::EquipmentExerciseMapping eem; eem.exerciseId = data.exerciseId(); for (const Equipment2ParticipantId &e2p : data.mapping()) { eem.equipmentNumber = e2p.equipmentNumber(); eem.participantId = e2p.participantId(); oldEqIds.remove(eem.equipmentNumber); m_equipmentExerciseMappings[e2p.equipmentNumber()] = eem; } for (int oldEqId : oldEqIds) { auto it = m_equipmentExerciseMappings.find(oldEqId); if (it == m_equipmentExerciseMappings.end()) continue; Database::EquipmentExerciseMapping &eemOld = it.value(); if (eemOld.exerciseId != data.exerciseId()) continue; m_equipmentExerciseMappings.erase(it); } break; } case DataModification::eRemoved: // exercise stopped or shut down if (data.exerciseId() == m_mainHandler->exerciseId()) return; for (const Equipment2ParticipantId &e2p : data.mapping()) m_equipmentExerciseMappings.remove(e2p.equipmentNumber()); break; } } void ParticipantHandlerForExercise::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification mod) { if (data.id() != m_mainHandler->exerciseId()) return; bool bPublishNow = false; if (mod == DataModification::eRemoved || data.state() == ExerciseManagement::ExerciseState::Created || data.state() == ExerciseManagement::ExerciseState::Stopped || data.state() == ExerciseManagement::ExerciseState::Terminated) bPublishNow = false; else bPublishNow = true; if (m_publishEquipmentExerciseMappings == bPublishNow) return; m_publishEquipmentExerciseMappings = bPublishNow; publishEquipmentExerciseMapping(); } void ParticipantHandlerForExercise::publishEquipmentExerciseMapping() { EquipmentExerciseMapping eem; eem.exerciseId(m_mainHandler->exerciseId()); if (!m_publishEquipmentExerciseMappings) { m_mainHandler->exAnyDts()->publish(eem); // empty return; } eem.mapping().reserve(m_equipNumber2ParticipantId.size()); for (auto it = m_equipNumber2ParticipantId.cbegin(); it != m_equipNumber2ParticipantId.cend(); ++it) eem.mapping().emplace_back(it.key(), it.value()); m_mainHandler->exAnyDts()->publish(eem); } bool ParticipantHandlerForExercise::isCurrentExerciseActive() const { const auto exState = m_exmDtc->exercise(m_mainHandler->exerciseId()).state(); return (exState == ExerciseManagement::ExerciseState::Started || exState == ExerciseManagement::ExerciseState::Paused); } // more or less copy from ppm_dtc_impl_custom.cpp QString ParticipantHandlerForExercise::lookUpHierachy(int participantId) const { if (participantId <= 0) return QString(); const auto participant = m_participants.value(participantId); if (!participant) return QString(); QString ret = QString::fromStdString(participant->participantData().designator()); // First separator is "./" if (participant->superiorId() <= 0) return (ret + QLatin1String("./")); QString supStr = lookUpHierachy(participant->superiorId()); if (supStr.endsWith(QLatin1String("./"))) return (supStr + ret); return (supStr.isEmpty() ? ret : supStr + QLatin1Char(',') + ret); } QString ParticipantHandlerForExercise::participantInfosFromId(int participantId) const { const auto participant = m_participants.value(participantId); if (!participant) return QStringLiteral("Unknown participant: %1").arg(participantId); const QString hierachy = lookUpHierachy(participantId); if (participant->equipmentNumber() <= 0) return hierachy; return QStringLiteral("%1 (%2)").arg(hierachy).arg(participant->equipmentNumber()); } void ParticipantHandlerForExercise::customedSetGearCharges(Participant *participant) { const auto ppType = m_mainHandler->typeHandler()->typeById(participant->typeId()); if (!ppType || ppType->typeClass() == TypeClass_def::Unknown || ppType->properties().empty()) return; const QJsonObject &typObj = QJsonDocument::fromJson(QByteArray::fromStdString(ppType->properties())).object(); if (typObj.isEmpty()) return; const bool isArtillery = typObj[QStringLiteral("isArtillery")].toBool(false); const int gearClassId = typObj[QStringLiteral("gearClassId")].toInt(0); const ProjectDataManagement::GearClass_def::type gearClass = static_cast(gearClassId); static QVector validGearTypes{ProjectDataManagement::GearClass_def::Mortar, ProjectDataManagement::GearClass_def::Rocket, ProjectDataManagement::GearClass_def::Artillery}; if (!isArtillery || !validGearTypes.contains(gearClass)) return; if (!m_pdmDtc) { m_pdmDtc = m_mainHandler->ddsHandler()->pdmDtc(); m_pdmDtc->waitForHistoricalDataGearType(); m_pdmDtc->waitForHistoricalDataGearTypeChargeType(); } const auto &gearTypes = m_pdmDtc->gearTypes(); const auto >Cts = m_pdmDtc->gearTypeChargeTypes(); for (const auto > : gearTypes) { if (gt.gearClassId() != gearClass) continue; for (const auto >c : gtCts) { if (gtc.gearTypeId() != gt.id()) continue; DEBUG_LOG(m_logger) << " >>> dataid: " << participant->id() << " gTypeId: " << gtc.gearTypeId() << " chargeType: " << gtc.chargeTypeId(); GearInformation gearInfo(gtc.gearTypeId(), gtc.gearTypeId(), gtc.chargeTypeId(), 0, QDateTime::currentMSecsSinceEpoch(), false); participant->setGearCharge(gearInfo); } // for participant->publishDynamicData(); break; } // for } void ParticipantHandlerForExercise::onModifyUasStateRequest(const rti::core::SampleIdentity &cookie, const ModifyUasStateRequest &request) { Core::FunctionReplyObject reply = modifyUasStateRequest(cookie, request); if (reply.error()) { WARNING_LOG(m_logger) << reply(); } m_mainHandler->fts()->modifyUasStateReply(cookie, reply); } Core::FunctionReplyObject ParticipantHandlerForExercise::modifyUasStateRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::ModifyUasStateRequest &request) { DEBUG_LOG(m_logger) << "-- onModifyUasStateRequest: uasId:" << request.uasId() << " // EquipmentNumber: " << request.equipmentNumber() << " // State: " << request.uasState(); if (request.uasId() <= 0 || request.equipmentNumber() <= 0) return Core::FunctionReplyObject(request.uasId(), tr_lts("dds", "uas id ( %1 ) or equipmentNumber ( %2 ) not found!")) .arg(request.uasId()) .arg(request.equipmentNumber()); TrainingAreaBase::SendModifyUasStateRequest req; req.requestId(s_tabRequestId++); req.uasId(request.uasId()); req.uasState(request.uasState()); req.equipmentNumber(request.equipmentNumber()); return m_proxyListener->sendModifyUasStateRequest(cookie, req); } void ParticipantHandlerForExercise::onGearChargeChanged(int participantId, int partyId) { // Count charges for ADLER participants if one of them has changed data if (!m_ppmDtc->isAdlerType(m_participants.value(participantId)->typeId())) return; // chargeTypeId, remain QMap chargeTypeGearData; for (const Participant *part : m_participants) { if (!m_ppmDtc->isAdlerType(part->typeId()) || part->partyId() != partyId) continue; for (const GearInformation &gi : part->dynamicParticipantData().gear()) chargeTypeGearData[gi.chargeTypeId()] += gi.remain(); } DEBUG_LOG(m_logger) << "chargeTypeGearData size: " << chargeTypeGearData.size(); QMapIterator it(chargeTypeGearData); while (it.hasNext()) { it.next(); DEBUG_LOG(m_logger) << "partyId: " << partyId << ", chargeTypeId: " << it.key() << ", amount: " << it.value(); if (!m_pdmDtc->isAdlerChargeType(it.key())) continue; AreaEffectSimulation::ChargeAdministrationModifyRequest request; request.chargeTypeId(it.key()); request.amount(it.value()); request.partyId(partyId); request.effectSourceId(ProjectDataManagement::EffectSource::ES_ADLER); request.amountChangedByOperator(false); m_mainHandler->aesFtc()->chargeAdministrationModifyRequest(request); } } } // namespace ParticipantHandling } // namespace ParticipantManagement