new_docker_build.sh gains Group 12 (212 GUI/Data plugins); fixes LtsUtilsGuiDds visibility (LTO-stripped exports), LtsProjectionWorkstation unity name shadow, 6 unity-off dirs (dup file-scope statics, ddsftc INTERFACE sources) and reconfigures BUILD_FILTERMANAGEMENT/BUILD_PLUGINS_SERVICEMONITOR=ON
446 lines
15 KiB
C++
Executable File
446 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 <utility>
|
|
#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 : std::as_const(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 : std::as_const(missionItemVec))
|
|
{
|
|
if (itemDb.missionPlanId == data.id)
|
|
{
|
|
itemsDbMap.insert(itemDb.elementNumber, itemDb);
|
|
for (const ParticipantManagement::Database::PpmMissionAction &actionDb : std::as_const(missionActionVec) )
|
|
{
|
|
if (actionDb.missionItemId == itemDb.id)
|
|
actionsDbMap.insert(actionDb.missionItemId, actionDb);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
missionPlan.items.reserve(itemsDbMap.size());
|
|
for (const ParticipantManagement::Database::PpmMissionItem &item : std::as_const(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;
|
|
|
|
return (uas.uasState == ProjectDataManagement::UasState::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::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
|
|
}
|