445 lines
15 KiB
C++
Executable File
445 lines
15 KiB
C++
Executable File
/**
|
|
* The UasHandler class manages all Uas related function topics and holds classes for
|
|
* further Uas data.
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
* Created by Steve Kemayou, ESZS, 2025-06-02
|
|
*/
|
|
|
|
#include <ngldb/db_service_handler.h>
|
|
#include <ngldb/dbtransaction.h>
|
|
#include "main_handler.h"
|
|
#include "ParticipantHandling/participant.h"
|
|
|
|
#include "mission_plan_handler.h"
|
|
|
|
namespace ParticipantManagement
|
|
{
|
|
namespace UasHandling
|
|
{
|
|
MissionPlanHandler::MissionPlanHandler(MainHandler *mainHandler)
|
|
: Interface::Fts::ModifyMissionPlanRequestHandler(mainHandler->fts())
|
|
, Interface::Fts::RemoveMissionPlanRequestHandler(mainHandler->fts())
|
|
, Interface::Fts::SetMissionActionFulfilledRequestHandler(mainHandler->fts())
|
|
, m_mainHandler(mainHandler)
|
|
, m_logger(getLogger("MissionPlanHandler"))
|
|
{
|
|
|
|
}
|
|
|
|
ParticipantManagement::UasHandling::MissionPlanHandler::~MissionPlanHandler()
|
|
{
|
|
qDeleteAll(m_objMissionPlans);
|
|
}
|
|
|
|
bool MissionPlanHandler::init()
|
|
{
|
|
DEBUG_LOG(m_logger)<<" -- MissionPlanHandler init...";
|
|
qDeleteAll(m_objMissionPlans);
|
|
m_objMissionPlans.clear();
|
|
if (!loadMissionPlansFromDb())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void MissionPlanHandler::onModifyMissionPlanRequest(const rti::core::SampleIdentity &cookie, const ModifyMissionPlanRequest &request)
|
|
{
|
|
Core::FunctionReplyObject reply = request.id() == 0 ? createMissionPlanRequest(request)
|
|
: modifyMissionPlanRequest(request);
|
|
m_mainHandler->fts()->modifyMissionPlanReply(cookie, reply);
|
|
}
|
|
|
|
void MissionPlanHandler::onRemoveMissionPlanRequest(const rti::core::SampleIdentity &cookie, const RemoveMissionPlanRequest &request)
|
|
{
|
|
Core::FunctionReplyObject reply = removeMissionPlanRequest(request.id());
|
|
|
|
m_mainHandler->ddsHandler()->fts()->removeMissionPlanReply(cookie, reply);
|
|
}
|
|
|
|
void MissionPlanHandler::onSetMissionActionFulfilledRequest(const rti::core::SampleIdentity &cookie, const SetMissionActionFulfilledRequest &request)
|
|
{
|
|
//DEBUG_LOG(m_logger)<<">> -- onSetMissionActionFulfilledRequest -- ";
|
|
Core::FunctionReplyObject reply = setMissionActionFulfilledRequest(request);
|
|
m_mainHandler->ddsHandler()->fts()->setMissionActionFulfilledReply(cookie, reply);
|
|
}
|
|
|
|
bool MissionPlanHandler::loadMissionPlansFromDb()
|
|
{
|
|
QVector<ParticipantManagement::Database::PpmMissionPlan> missionPlanVec;
|
|
QVector<ParticipantManagement::Database::PpmMissionItem> missionItemVec;
|
|
QVector<ParticipantManagement::Database::PpmMissionAction> missionActionVec;
|
|
if (!m_mainHandler->db()->select(missionActionVec))
|
|
{
|
|
ERROR_LOG(m_logger)<<"Error reading data from database >> PpmMissionAction: "<<missionActionVec.size();
|
|
return false;
|
|
}
|
|
|
|
if (!m_mainHandler->db()->select(missionItemVec))
|
|
{
|
|
ERROR_LOG(m_logger)<<"Error reading data from database >> missionItemVec"<<missionItemVec.size();
|
|
return false;
|
|
}
|
|
|
|
if (!m_mainHandler->db()->select(missionPlanVec))
|
|
{
|
|
ERROR_LOG(m_logger)<<"Error reading data from database >> missionPlanVec"<<missionPlanVec.size();
|
|
return false;
|
|
}
|
|
DEBUG_LOG(m_logger) << "load MissionPlans From Db Plans size = " << missionPlanVec.size()
|
|
<<" Items size = "<<missionItemVec.size()
|
|
<<" Actions size = "<<missionActionVec.size();
|
|
// add to internal buffer
|
|
for (const ParticipantManagement::Database::PpmMissionPlan &data : qAsConst(missionPlanVec))
|
|
{
|
|
ParticipantManagement::MissionPlan missionPlan;
|
|
missionPlan.id(data.id);
|
|
missionPlan.name(data.name.toStdString());
|
|
missionPlan.participantId(data.participantId);
|
|
missionPlan.missionStart(data.missionStart.toMSecsSinceEpoch());
|
|
missionPlan.repeat(data.repeat);
|
|
missionPlan.jumpToStart(data.jumpToStart);
|
|
|
|
|
|
QMap <int, ParticipantManagement::Database::PpmMissionItem> itemsDbMap; // QMap to get it sorted
|
|
QMultiMap <int, ParticipantManagement::Database::PpmMissionAction> actionsDbMap;
|
|
//get mission items and actions for curr mission plan id
|
|
|
|
for (const ParticipantManagement::Database::PpmMissionItem &itemDb : qAsConst(missionItemVec))
|
|
{
|
|
if (itemDb.missionPlanId == data.id)
|
|
{
|
|
itemsDbMap.insert(itemDb.elementNumber, itemDb);
|
|
for (const ParticipantManagement::Database::PpmMissionAction &actionDb : qAsConst(missionActionVec) )
|
|
{
|
|
if (actionDb.missionItemId == itemDb.id)
|
|
actionsDbMap.insert(actionDb.missionItemId, actionDb);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
missionPlan.items().reserve(itemsDbMap.size());
|
|
for (const ParticipantManagement::Database::PpmMissionItem &item : qAsConst(itemsDbMap))
|
|
{
|
|
ParticipantManagement::MissionItem msi;
|
|
msi.actions().reserve(actionsDbMap.size());
|
|
msi.name(item.name.toStdString());
|
|
msi.location(Core::GeoPosition(item.location.x(), item.location.y(), item.location.z()));
|
|
msi.itemNumber(item.elementNumber);
|
|
int actionsFulfilled = 0;
|
|
int64_t latestFulfilled = 0;
|
|
for (const auto &action : actionsDbMap)
|
|
{
|
|
if (action.missionItemId == item.id)
|
|
{
|
|
ParticipantManagement::MissionAction msa;
|
|
msa.planId(missionPlan.id());
|
|
msa.actionType(action.missionActionTypeId);
|
|
msa.duration(action.duration);
|
|
msa.fulfilled(action.fulfilled.toMSecsSinceEpoch());
|
|
msa.properties(action.properties.toStdString());
|
|
msa.name(action.name.toStdString());
|
|
msa.actionNumber(action.elementNumber);
|
|
msa.itemNumber(msi.itemNumber());
|
|
msi.actions().push_back(msa);
|
|
|
|
if (msa.fulfilled() > 0)
|
|
actionsFulfilled++;
|
|
|
|
if (latestFulfilled < msa.fulfilled())
|
|
latestFulfilled = msa.fulfilled();
|
|
|
|
}
|
|
}
|
|
|
|
msi.actionsFulfilled(actionsFulfilled);
|
|
msi.fulfilled(latestFulfilled);
|
|
|
|
missionPlan.items().push_back(msi);
|
|
}
|
|
|
|
setLatestFulfilledItem(missionPlan); // update mission plan fulfillment count & fulfillemnt latest time
|
|
|
|
ObjectMissionPlan *obj = new ObjectMissionPlan(m_mainHandler->ddsHandler(), m_mainHandler->db(), missionPlan);
|
|
obj->setDbMissionItems(itemsDbMap, actionsDbMap);
|
|
m_objMissionPlans.insert(missionPlan.id(), obj);
|
|
|
|
// publish mission plan data
|
|
m_mainHandler->ddsHandler()->dts()->publish(missionPlan);
|
|
|
|
}
|
|
DEBUG_LOG(m_logger)<<" -- END load mission plan From Db -- ";
|
|
return true;
|
|
}
|
|
|
|
bool MissionPlanHandler::isUasInAutoMode(int participantId)
|
|
{
|
|
m_mainHandler->ddsHandler()->dtc()->waitForHistoricalDataUAS(3000);
|
|
const auto uas = m_mainHandler->ddsHandler()->dtc()->getUasByParticipantId(participantId, m_mainHandler->exerciseId());
|
|
|
|
if (uas.id() < 1)
|
|
{
|
|
ERROR_LOG(m_logger) << "no uas for pid " << participantId;
|
|
return false;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << "uas with pid " << participantId << " is in mode: " << (int)uas.uasState().underlying();
|
|
|
|
return (uas.uasState() == ProjectDataManagement::UasState::Auto);
|
|
}
|
|
|
|
// stolen from Lts/Plugins/ParticipantManagement/Gui/ParticipantInfo/participant_info_dialog.cpp
|
|
bool MissionPlanHandler::isUasCombatReady(int participantId)
|
|
{
|
|
m_mainHandler->ddsHandler()->dtc()->waitForHistoricalDataDynamicParticipantData(3000);
|
|
const auto data = m_mainHandler->ddsHandler()->dtc()->dynamicParticipantData(participantId);
|
|
|
|
if (data.participantId() == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//Current State
|
|
if (data.stateHistory().empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &history = data.stateHistory();
|
|
|
|
//LasState is the last "real" state, that has a VBG <=8 (Castastrophic)
|
|
//It's not the last state received, as there can be other state changes like Sanitation, or NearMiss
|
|
auto lastState = m_mainHandler->ddsHandler()->dtc()->youngestStateInformation(history/*, true*/ /* debug */);
|
|
decltype(lastState) youngestState;
|
|
youngestState.stateTime(0);
|
|
for (const auto &si : history)
|
|
{
|
|
DEBUG_LOG(m_logger) << "StateInformation "
|
|
<< " time=" << si.stateTime()
|
|
<< " vulnerabilityBits=" << si.vulnerabilityBits()
|
|
<< " vulnerabilityKindId=" << si.vulnerabilityKindId();
|
|
if (youngestState.stateTime() < si.stateTime())
|
|
youngestState = si;
|
|
}
|
|
|
|
return (youngestState.vulnerabilityState() == ProjectDataManagement::VulnerabilityState::CombatReady);
|
|
}
|
|
|
|
Core::FunctionReplyObject MissionPlanHandler::createMissionPlanRequest(const ModifyMissionPlanRequest &request)
|
|
{
|
|
DEBUG_LOG(m_logger) <<" >> -- createMissionPlanRequest -- ";
|
|
if (request.participantId() > 0 && !isUasParticipant(request.participantId()))
|
|
{
|
|
return Core::FunctionReplyObject(tr_lts("dds", "Participant with id %1 is not a Uas participant.")).arg(request.participantId());
|
|
}
|
|
|
|
if (!isUasInAutoMode(request.participantId()))
|
|
{
|
|
return Core::FunctionReplyObject(tr_lts("dds", "Uas with participant id %1 is not in auto mode - maybe the safety tablet is currently engaged.")).arg(request.participantId());
|
|
}
|
|
|
|
if (!isUasCombatReady(request.participantId()))
|
|
{
|
|
return Core::FunctionReplyObject(tr_lts("dds", "Uas with participant id %1 ------ not combat ready.")).arg(request.participantId());
|
|
}
|
|
|
|
/// 1- insert in DB Mission plan
|
|
/// 2- insert in DB Mission Items
|
|
/// 3- insert in DB Mission Actions
|
|
|
|
//1-) insert mission plan
|
|
Database::PpmMissionPlan dbMissionPlan = ObjectMissionPlan::toDbMissionPlan(request);
|
|
const MissionPlan missionPlan = ObjectMissionPlan::fillMissionPlanData(request);
|
|
|
|
ObjectMissionPlan *obj = new ObjectMissionPlan(m_mainHandler->ddsHandler(), m_mainHandler->db(), missionPlan);
|
|
Core::FunctionReplyObject ret = obj->createMissionPlan(dbMissionPlan, request.items());
|
|
if (ret.error())
|
|
{
|
|
delete obj;
|
|
return ret;
|
|
}
|
|
m_objMissionPlans.insert(obj->id(), obj);
|
|
|
|
publishMissionPlan(obj->getMissionPlanData()); // publish mission plan
|
|
return Core::FunctionReplyObject(dbMissionPlan.id);
|
|
}
|
|
|
|
Core::FunctionReplyObject MissionPlanHandler::modifyMissionPlanRequest(const ModifyMissionPlanRequest &request)
|
|
{
|
|
DEBUG_LOG(m_logger)<<" >> -- modifyMissionPlanRequest -- ";
|
|
/// 1- update Mission plan Table
|
|
/// 2- remove first all mission items and their actions
|
|
/// 3- add New mission items
|
|
/// 4- add new mission actions
|
|
ObjectMissionPlan *obj = m_objMissionPlans.value(request.id());
|
|
if (!obj || request.id() < 0)
|
|
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Couldn't remove Mission plan with id %1")).arg(request.id());
|
|
|
|
const Database::PpmMissionPlan &dbMissionPlan = ObjectMissionPlan::toDbMissionPlan(request);
|
|
|
|
auto ret = obj->updateMissionPlan(dbMissionPlan, request.items());
|
|
publishMissionPlan(obj->getMissionPlanData()); // publish mission plan
|
|
return ret;
|
|
}
|
|
|
|
Core::FunctionReplyObject MissionPlanHandler::removeMissionPlanRequest(int id)
|
|
{
|
|
DEBUG_LOG(m_logger)<<" >> -- removeMissionPlanRequest -- ";
|
|
Core::FunctionReplyObject ret;
|
|
ObjectMissionPlan *obj = m_objMissionPlans.value(id, nullptr);
|
|
if (!obj)
|
|
return ret;
|
|
ret = obj->removeMissionPlan(id);
|
|
if (ret.error())
|
|
return ret;
|
|
delete obj;
|
|
m_objMissionPlans.remove(id);
|
|
|
|
m_mainHandler->ddsHandler()->dts()->removeMissionPlanByKey(id);
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool MissionPlanHandler::setCurrentMissionActionFulfilled(int participantId, const QString &nameOfTheMissionThatEnded)
|
|
{
|
|
// okay we somehow need to find the currently running mission for this participant..... the plan: filter mission plans by participant und by (start < now)
|
|
bool found = false;
|
|
for (const auto &missionPlan : m_objMissionPlans.values())
|
|
{
|
|
if (missionPlan->missionPlan().participantId() == participantId && missionPlan->missionPlan().missionStart() <= m_mainHandler->currentTimeMSec())
|
|
{
|
|
// get current action (stolen from Services/CentralEffectSimulation/Plugins/MissionPlan/ces_mission_plan_object.cpp)
|
|
const std::vector< ParticipantManagement::MissionItem > items = missionPlan->missionPlan().items(); // copy !!
|
|
for (const auto &item : items)
|
|
{
|
|
for (auto &msa : item.actions())
|
|
{
|
|
const QString thisMissionName(QStringLiteral("%1_%2_%3").arg(missionPlan->missionPlan().id()).arg(QString::fromStdString(missionPlan->missionPlan().name()), QString::fromStdString(item.name())));
|
|
|
|
INFO_LOG(m_logger) << ">> mission item: this mission name: " << thisMissionName << ", fulfilled: " << msa.fulfilled();
|
|
|
|
if (msa.fulfilled() > 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (thisMissionName != nameOfTheMissionThatEnded)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
DEBUG_LOG(m_logger) << ">> ######### setMissionActionFulfilledRequest !!! item = " << item.itemNumber() << ", name = " << item.name() << ", missionPlan = " << missionPlan->missionPlan().id();
|
|
SetMissionActionFulfilledRequest req;
|
|
req.actionNumber(msa.actionNumber());
|
|
req.fulfilled(true);
|
|
req.missionId(missionPlan->missionPlan().id());
|
|
req.itemNumber(item.itemNumber());
|
|
const auto res = setMissionActionFulfilledRequest(req);
|
|
if (res.error())
|
|
{
|
|
ERROR_LOG(m_logger) << "failed to set current mission action as fulfilled: " << res.str();
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// maybe okay? last mission action was already fulfilled? is this even possible at this point?
|
|
DEBUG_LOG(m_logger) << "failed to set current mission action as fulfilled: no action found for pId " << participantId;
|
|
return false;
|
|
}
|
|
|
|
Core::FunctionReplyObject MissionPlanHandler::setMissionActionFulfilledRequest(const SetMissionActionFulfilledRequest &request)
|
|
{
|
|
DEBUG_LOG(m_logger)<<" -- [setMissionActionFulfilledRequest] req.missionId: "<<request.missionId();
|
|
Core::FunctionReplyObject ret;
|
|
ObjectMissionPlan *obj = m_objMissionPlans.value(request.missionId(), nullptr);
|
|
if (!obj)
|
|
return ret;
|
|
ret = obj->setMissionActionFulfilled(request, m_mainHandler->currentTimeMSec());
|
|
m_mainHandler->ddsHandler()->dts()->publish(obj->missionPlan());
|
|
return ret;
|
|
}
|
|
|
|
void MissionPlanHandler::publishMissionPlan(const MissionPlan &missionplan)
|
|
{
|
|
m_mainHandler->ddsHandler()->dts()->publish(missionplan);
|
|
}
|
|
|
|
bool MissionPlanHandler::isUasParticipant(int participantId) const
|
|
{
|
|
const ParticipantManagement::ParticipantHandling::Participant *PARTICIPANT = m_mainHandler->participantHandler()->participantById(participantId);
|
|
|
|
if (PARTICIPANT == nullptr)
|
|
{
|
|
WARNING_LOG(m_logger) << "Got invalid participantId " << participantId;
|
|
return false;
|
|
}
|
|
|
|
const int TYPE_ID = PARTICIPANT->typeId();
|
|
return m_mainHandler->ddsHandler()->dtc()->isTypeUas(TYPE_ID);
|
|
}
|
|
|
|
int MissionPlanHandler::getNumberOfActionFulfilled(const MissionItem &msi) const
|
|
{
|
|
int count = 0;
|
|
for (auto action : msi.actions())
|
|
{
|
|
if (action.fulfilled() > 0)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void MissionPlanHandler::setLatestFulfilledItem(MissionPlan &missionPlan)
|
|
{
|
|
int count = 0;
|
|
int64_t latestTime = 0;
|
|
for (auto item : missionPlan.items())
|
|
{
|
|
if (item.actionsFulfilled() > 0)
|
|
count++;
|
|
|
|
if (latestTime < item.fulfilled())
|
|
latestTime = item.fulfilled();
|
|
|
|
missionPlan.itemsFulfilled(count);
|
|
missionPlan.fulfilled(latestTime);
|
|
}
|
|
}
|
|
|
|
int64_t MissionPlanHandler::getLastestFulfilledAction(const MissionItem &msi) const
|
|
{
|
|
int64_t latestTime = 0;
|
|
for (auto action : msi.actions())
|
|
{
|
|
if (action.fulfilled() > latestTime)
|
|
latestTime = action.fulfilled();
|
|
}
|
|
|
|
return latestTime;
|
|
}
|
|
|
|
|
|
}// namespace UasHandling
|
|
}
|