Files
qt5-legatus-legacy/Lts/Services/ParticipantManagement/ParticipantHandling/participant_handler_for_exercise.cpp
T
2026-08-13 18:33:10 +02:00

2027 lines
86 KiB
C++
Executable File

/**
* 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 <AreaEffectSimulation/handler_charge_administration.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <ParticipantManagement/ppm_record_replay_plugin.h>
#include <ngldb/dbtransaction.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include <QVector>
#include <QtGui/QTransform>
namespace ParticipantManagement
{
namespace ParticipantHandling
{
static int s_tabRequestId = 1;
static inline Database::PpmParticipant toDbParticipant(const ParticipantData &currentData, 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<DatabaseInterface> 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 &currentParticipant)
{
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<ParticipantData> 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<Participant *> participantsWithChanges;
ValidateParticipantData validateData;
QSet<int> partiesUsed;
QSet<int> 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 &currentParticipant : 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 &currentParticipant : 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 &currentParticipant : 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<Database::PpmParticipant> dbParticipantsToBeCreated;
std::vector<int> 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 &currentData = 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 &currentData : 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<int, std::vector<int32_t>> 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<Database::PpmParticipant> 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<std::string> 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<ParticipantForExercise *>(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<ParticipantForExercise *>(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<int> &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<TrainingAreaBase::Qualification::inner_enum>(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<Database::PpmParticipant> &dbParticipantsToBeCreated,
const std::vector<int> &dbParticipantsToBeCreatedIndexes,
const QVector<ParticipantData> &participantsToBeCreatedOrModified,
QHash<int, int> &modIdToParticipantId,
QSet<Participant *> &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<int32_t> ParticipantHandlerForExercise::inferiorIdsForParticipant(int32_t superiorParticipantId) const
{
std::vector<int32_t> 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<ParticipantForExercise *>(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<int32_t> &participantIds,
std::vector<int32_t> &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<int> 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<ProjectDataManagement::GearClass_def::type>(gearClassId);
static QVector<ProjectDataManagement::GearClass_def::type> 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 &gtCts = m_pdmDtc->gearTypeChargeTypes();
for (const auto &gt : gearTypes)
{
if (gt.gearClassId() != gearClass)
continue;
for (const auto &gtc : 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<qint32, int> 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<int, int> 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