Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,144 @@
/**
* The BiteHandler class manages everything todo with bite requests.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-06-06
*/
#include "bite_handler.h"
#include "ParticipantHandling/participant.h"
#include "ParticipantHandling/participant_donator.h"
#include "main_handler.h"
#include <QTimer>
namespace ParticipantManagement
{
namespace BiteHandling
{
BiteHandler::BiteHandler(MainHandler *mainHandler)
: QObject()
, Interface::Fts::CallBiteRequestHandler(mainHandler->fts())
, m_mainHandler(mainHandler)
, m_logger(getLogger("BiteHandler"))
{
}
bool BiteHandler::init()
{
return true;
}
void BiteHandler::onDemandBiteReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
WARNING_LOG(m_logger) << "Got an demandBiteError: " << replyObject();
auto it = m_demandedBiteParticipantRequests.find(replyObject.id());
if (it != m_demandedBiteParticipantRequests.end())
{
const DemandedParticipants &dp = it.value();
m_mainHandler->fts()->callBiteReply(dp.cookie, replyObject);
dp.timer->deleteLater();
m_demandedBiteParticipants -= dp.demandedBiteParticipants;
m_demandedBiteParticipantRequests.erase(it);
}
else
WARNING_LOG(m_logger) << "Bite request with request id " << replyObject.id() << " was not replied to by the proxy";
}
void BiteHandler::onCallBiteRequest(const rti::core::SampleIdentity &cookie,
const CallBiteRequest &request)
{
DEBUG_LOG(m_logger) << "onCallBiteRequest: " << request;
auto ret = callBiteRequest(cookie, request);
if (ret.error() || ret.id())
{
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->callBiteReply(cookie, ret);
}
// delayed answer in non-error case
}
Core::FunctionReplyObject BiteHandler::callBiteRequest(const rti::core::SampleIdentity &cookie,
const CallBiteRequest &request)
{
TrainingAreaBase::DemandBiteRequest req;
req.equipmentNumbers().reserve(request.participantIds().size());
req.withSelfTest(request.withSelfTest());
bool bOneFound = false;
bool bAlreadyDemandedFound = false;
QSet<int> currentDemandedParticipants;
for (const int participantId : request.participantIds())
{
if (m_demandedBiteParticipants.contains(participantId))
{
DEBUG_LOG(m_logger) << "demandBiteRequest for participant Id "
<< participantId
<< " already triggered - reply is still to be done";
bAlreadyDemandedFound = true;
continue;
}
const ParticipantHandling::Participant *participant = m_mainHandler->participantHandler()->participantById(participantId);
if (participant == nullptr)
{
WARNING_LOG(m_logger) << "Got BITE request for non existing participant id " << participantId;
continue;
}
if (participant->equipmentNumber() == 0)
{
WARNING_LOG(m_logger) << "Got BITE request participant id " << participantId << " but has no equipment number set";
continue;
}
bOneFound = true;
m_demandedBiteParticipants.insert(participant->id());
currentDemandedParticipants.insert(participant->id());
req.equipmentNumbers().push_back(participant->equipmentNumber());
}
if (req.equipmentNumbers().size() == 0 && bAlreadyDemandedFound)
return Core::FunctionReplyObject(req.requestId());
if (req.equipmentNumbers().size() != 0)
{
static int s_requestId = 1;
req.requestId(s_requestId++);
if (!m_mainHandler->tabFtc()->demandBiteRequest(req, this))
return Core::FunctionReplyObject(req.requestId(),
tr_lts("dds", "Error passing the BITE request to the proxy"));
QTimer *timeoutTimer = new QTimer(this);
timeoutTimer->setSingleShot(true);
timeoutTimer->setInterval(5000);
const int requestId = req.requestId();
m_demandedBiteParticipantRequests.insert(requestId, {currentDemandedParticipants, cookie, timeoutTimer});
auto cb = [this, requestId]()
{
onBiteRequestTimeout(requestId);
};
connect(timeoutTimer, &QTimer::timeout, this, cb);
return Core::FunctionReplyObject();
}
// at least one found but this was already requested and we're waiting for answer -> don't answer with error
if (bOneFound)
return Core::FunctionReplyObject();
return Core::FunctionReplyObject(req.requestId(),
tr_lts("dds", "Got no valid participants for BITE request"));
}
void BiteHandler::onBiteRequestTimeout(int requestId)
{
INFO_LOG(m_logger) << "onBiteRequestTimeout for " << requestId;
onDemandBiteReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
}
} // namespace BiteHandling
} // namespace ParticipantManagement
@@ -0,0 +1,66 @@
/**
* The BiteHandler class manages everything todo with bite requests.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-06-06
*/
#pragma once
#include <QSet>
#include <QMap>
#include "dds_handler.h"
#include "logger.h"
#include <TrainingAreaBase/tab_ftc.h>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QVector>
class QTimer;
namespace ParticipantManagement
{
class MainHandler;
namespace BiteHandling
{
class BiteReplier;
class BiteHandler final : public QObject
, protected Interface::Fts::CallBiteRequestHandler
, protected TrainingAreaBase::Interface::Ftc::DemandBiteReplyHandler
{
Q_OBJECT
public:
explicit BiteHandler(MainHandler *mainHandler);
bool init();
void onDemandBiteReply(const Core::FunctionReplyObject &replyObject) override;
void onCallBiteRequest(const rti::core::SampleIdentity &cookie,
const CallBiteRequest &request) override;
private:
Core::FunctionReplyObject callBiteRequest(const rti::core::SampleIdentity &cookie,
const CallBiteRequest &request);
void onBiteRequestTimeout(int requestId);
private:
MainHandler *m_mainHandler;
struct DemandedParticipants
{
QSet<int> demandedBiteParticipants;
rti::core::SampleIdentity cookie;
QPointer<QTimer> timer;
};
QMap<int, DemandedParticipants> m_demandedBiteParticipantRequests;
QSet<int32_t> m_demandedBiteParticipants; // participant IDs
LOGGER_T m_logger;
}; // class BiteHandler
} // namespace BiteHandling
} // namespace ParticipantManagement
+302
View File
@@ -0,0 +1,302 @@
#
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE ppm_service.xml)
#
# --- Library svcPpmCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
ppm_export.h
ppm_common.cpp
ppm_common.h
ppm_db_impl_custom.cpp
ppm_db_impl_custom.h
logger.cpp
logger.h
)
# Generate ppm_enums.idl / ppm.idl
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate ppm_db_structs.cpp/.h
CODEGEN_DATABASE_STRUCTS(SRCS_common ${xml_FILE})
# Generate ppm_db.cpp, ppm_db_impl.cpp/.h
CODEGEN_DATABASE_SQL(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from idl
RTIDDSGEN(SRCS_common ppm_enums.idl)
RTIDDSGEN(SRCS_common ppm.idl)
# Generate ppm_dt[s,c]_types.h, ppm_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# generate ppm_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS HLA svcPpmCommon SHARED ${SRCS_common})
NGL_TARGET_INCLUDE_DIRECTORIES(svcPpmCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcPpmCommon
PRIVATE
RTI::nddscpp2
LtsUtilsCore
PUBLIC
Qt5::Core
Qt5::Gui
ddscore
NGLLogger
svcPdmCommon
LtsUtilsCore
LtsCoordTrans
ngldb
)
NGL_INSTALL_TARGET(svcPpmCommon)
#
# --- Library svcPpmClient --------------------------------------------------------------------------------
#
SET(SRCS_client
ppm_export.h
ppm_dtc_impl_custom.cpp
ppm_dtc_impl_custom.h
)
# generate ppm_[d|f]tc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
NGL_ADD_LIBRARY(WS AAR SV HLA svcPpmClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcPpmClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcPpmClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcPpmCommon
ddsftc
)
NGL_INSTALL_TARGET(svcPpmClient)
#
# --- Library svcPpmServer --------------------------------------------------------------------------------
#
SET(SRCS_server
ppm_export.h
convert_db_to_dds.h
)
# generate ppm_[d|f]ts.h, ppm_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
# WS for RecordReplayPlugin
NGL_ADD_LIBRARY(AAR SV WS svcPpmServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcPpmServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcPpmServer
PRIVATE
RTI::nddscpp2
ddsconnector
ddsfts
PUBLIC
svcPpmCommon
ngldb
LtsUtilsCore
)
NGL_INSTALL_TARGET(svcPpmServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- RecordReplayPlugin --------------------------------------------------------------
#
CREATE_RECORD_REPLAY_PLUGIN_SHARED(${xml_FILE})
IF (TARGET ppmRecordReplayPlugin)
TARGET_SOURCES(ppmRecordReplayPlugin
PRIVATE
RecordReplay/participant_for_replay.h
RecordReplay/participant_handler_for_replay.cpp
RecordReplay/participant_handler_for_replay.h
RecordReplay/ppm_record_replay_plugin_custom.cpp
RecordReplay/ppm_record_replay_plugin_custom.h
ParticipantHandling/participant_donator.h
ParticipantHandling/participant.cpp
ParticipantHandling/participant.h
ParticipantHandling/participant_handler.cpp
ParticipantHandling/participant_handler.h
ParticipantHandling/unit_position_calculator.cpp
ParticipantHandling/unit_position_calculator.h
settings.cpp
settings.h
web_socket_handler.cpp
web_socket_handler.h
)
TARGET_LINK_LIBRARIES(ppmRecordReplayPlugin
Qt5::Concurrent
Qt5::WebSockets
tabRecordReplayPlugin
svcBldClient
svcPpmServer
svcPdmClient
LtsCoordTrans
LtsUtilsCoreHeaderOnly # unit_position_calculator.cpp => LtsUtilsCore/qpoint3d.h
nglsettings
ngltools
)
ENDIF()
#
# --- Executable svcParticipantManagement (sPPM) -------------------------------------------------
#
LIST(APPEND SRCS_exe
BiteHandling/bite_handler.cpp
BiteHandling/bite_handler.h
dds_handler.cpp
dds_handler.h
EquipmentEventHandling/equipment_event_handler.cpp
EquipmentEventHandling/equipment_event_handler.h
exercise_time.cpp
exercise_time.h
main.cpp
main_handler.cpp
main_handler.h
ParticipantHandling/firing_unit_validator.cpp
ParticipantHandling/firing_unit_validator.h
ParticipantHandling/get_participants.cpp
ParticipantHandling/get_participants.h
ParticipantHandling/participant_donator.h
ParticipantHandling/participant.cpp
ParticipantHandling/participant.h
ParticipantHandling/participant_for_exercise.cpp
ParticipantHandling/participant_for_exercise.h
ParticipantHandling/participant_handler.cpp
ParticipantHandling/participant_handler.h
ParticipantHandling/participant_handler_for_exercise.cpp
ParticipantHandling/participant_handler_for_exercise.h
ParticipantHandling/proxy_listener.cpp
ParticipantHandling/proxy_listener.h
ParticipantHandling/unit_position_calculator.cpp
ParticipantHandling/unit_position_calculator.h
PartyHandling/party.cpp
PartyHandling/party_donator.h
PartyHandling/party.h
PartyHandling/party_handler.cpp
PartyHandling/party_handler.h
settings.cpp
settings.h
TargetHandling/object_target_information.cpp
TargetHandling/object_target_information.h
TargetHandling/handler_target_information.cpp
TargetHandling/handler_target_information.h
TemplateHandling/template.cpp
TemplateHandling/template.h
TemplateHandling/template_handler.cpp
TemplateHandling/template_handler.h
TypeHandling/type_donator.h
TypeHandling/type_handler.cpp
TypeHandling/type_handler_exercise.cpp
TypeHandling/type_handler_exercise.h
TypeHandling/type_handler_global.cpp
TypeHandling/type_handler_global.h
TypeHandling/type_handler.h
UasHandling/uas.cpp
UasHandling/uas.h
UasHandling/uas_handler.cpp
UasHandling/uas_handler.h
UasHandling/mission_plan_handler.h
UasHandling/mission_plan_handler.cpp
UasHandling/object_mission_plan.cpp
UasHandling/object_mission_plan.h
WebSocketInterface/data_manager.cpp
WebSocketInterface/data_manager.h
WebSocketInterface/json_keys.h
WebSocketInterface/websocket_interface_handler.cpp
WebSocketInterface/websocket_interface_handler.h
WebSocketInterface/Messages/client_message.cpp
WebSocketInterface/Messages/client_message.h
WebSocketInterface/Messages/reply_message.cpp
WebSocketInterface/Messages/reply_message.h
web_socket_handler.cpp
web_socket_handler.h
${xml_FILE}
)
# Generate ppm.sql, ppm.xml
CODEGEN_SQL(SRCS_exe ${xml_FILE})
NGL_ADD_SVC_ICON(SRCS_exe)
NGL_ADD_EXECUTABLE(AAR SV svcParticipantManagement ${SRCS_exe})
NGL_TARGET_LINK_LIBRARIES(svcParticipantManagement
ppmRecordReplayPlugin
Qt5::Core
Qt5::Concurrent
Qt5::WebSockets
ddsconnector
LtsCommandLineParser
nglsettings
nglcoreapplication
NGLLogger
ProjectConfig
svcCoreClient
svcPdmClient
svcPpmCommon
svcPpmServer
svcPpmClient
svcTabClient
svcAesClient
svcCesServer
svcCesClient
svcBldClient
svcTabServer
svcExmClient
ngltools
LtsCoordTrans
LtsWebSocketServer
)
NGL_INSTALL_TARGET(svcParticipantManagement)
NGL_INSTALL(AAR SV FILES log4cxx_ppm.props DESTINATION bin)
NGL_ADD_TARGET_LINK(svcParticipantManagement sPPM)
# copy ppm_predefined_data.sql to share
SET(in_file_sql "ppm_predefined_data.sql")
NGL_INSTALL(AAR SV FILES ppm_predefined_data.sql DESTINATION share/Lts/sql)
# Uncomment if necessary
#ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(ModuleTests)
@@ -0,0 +1,144 @@
/**
* The DeviceEventHandler class manages device events from TAB.
* $Id$
* $HeadURL$
* $Author$
* Created by Dassow Philipp, SLEA, 2017-09-04
*/
#include "equipment_event_handler.h"
#include "main_handler.h"
#include "ParticipantHandling/participant.h"
#include <QTimer>
#include <LtsUtilsCore/dds_vector.h>
ParticipantManagement::EquipmentEventHandling::EquipmentEventHandler::EquipmentEventHandler(MainHandler *mainHandler, QObject *parent)
: QObject(parent)
, Interface::Fts::EquipmentSystemEventDataRequestHandler(mainHandler->fts())
, m_mainHandler (mainHandler)
, m_filterId(0)
, m_logger(getLogger("EquipmentEventHandler"))
{
}
bool ParticipantManagement::EquipmentEventHandling::EquipmentEventHandler::init()
{
if (!m_mainHandler->tabDtc()->enableEquipmentEvent())
{
ERROR_LOG (m_logger) << " TAB Dtc enableEquipmentEvent() failed !";
return false;
}
if (!m_mainHandler->dts()->enableEquipmentSystemEventFtcReply())
{
ERROR_LOG (m_logger) << " Dts enableEquipmentSystemEventDataFtcReply() failed !";
return false;
}
connect(m_mainHandler->tabDtc().get(), &TrainingAreaBase::Interface::Dtc::equipmentEventChanged, this, &EquipmentEventHandler::onEquipmentEventChanged);
return true;
}
void ParticipantManagement::EquipmentEventHandling::EquipmentEventHandler::onEquipmentSystemEventDataRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::EquipmentSystemEventDataRequest &request)
{
INFO_LOG(m_logger) << "IN onEquipmentSystemEventDataRequest";
Core::FunctionReplyObject ret;
QString stmt = QStringLiteral(" SELECT rec_id, equipment_number, sample_time, position_lon, position_lat, position_z, equipment_function_id, sub_device_id,"
" unique_device_id, device_specific_event_code, event_data"
" FROM rec_equipment_system_event WHERE sample_time > ") + QString::number(request.startTime()) + QStringLiteral(" AND "
" sample_time <= ") + QString::number(request.endTime());
if (request.equipmentFunctionId() > -1)
stmt += QStringLiteral(" AND equipment_function_id=") + QString::number(request.equipmentFunctionId());
if (request.subDeviceId() > -1)
stmt += QStringLiteral(" AND sub_device_id=") + QString::number(request.subDeviceId());
if (request.deviceSpecificEventCode() > -1)
stmt += QStringLiteral(" AND device_specific_event_code=") + QString::number(request.deviceSpecificEventCode());
INFO_LOG(m_logger) << "stmt: " << stmt;
NSqlQuery q(m_mainHandler->db()->database());
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while selecting event-data ";
ERROR_LOG(m_logger) << "stmt : " << stmt;
ret.setStr("Error while selecting data");
m_mainHandler->fts()->equipmentSystemEventDataReply(cookie, ret);
return;
}
// Unique FilterId for each requestEquipmentSystemEventFtcReply
m_filterId++;
ret.setId(m_filterId);
QVector<qint64> keys;
ParticipantManagement::EquipmentSystemEventFtcReply data;
while (q.next())
{
int i = 0;
// Unique Key with filterId important for removing from dds
qint64 id = m_filterId;
id <<= 32;
id |= q.value(i++).toInt();
keys += id;
data.id(id);
data.equipmentNumber(q.value(i++).toInt());
data.timeStamp(q.value(i++).toLongLong());
double pos_lon = q.value(i++).toDouble();
double pos_lat = q.value(i++).toDouble();
double pos_z = q.value(i++).toDouble();
data.position(Core::GeoPosition(pos_lon, pos_lat, pos_z));
data.equipmentFunctionId(q.value(i++).toInt());
data.subDeviceId(q.value(i++).toInt());
data.uniqueDeviceId(q.value(i++).toInt());
data.deviceSpecificEventCode(q.value(i++).toInt());
QByteArray evtData(q.value(i++).toByteArray());
if (evtData.size() != 0)
data.eventData(LtsUtils::convert<std::vector<uint8_t>>(evtData));
data.filterId(m_filterId);
m_mainHandler->dts()->publish(data);
}
QTimer::singleShot(5 * 60 * 1000, this, [keys, this](){ removeFromDDS(keys); });
m_mainHandler->fts()->equipmentSystemEventDataReply(cookie, ret);
}
void ParticipantManagement::EquipmentEventHandling::EquipmentEventHandler::onEquipmentEventChanged(const TrainingAreaBase::EquipmentEvent &data, DataModification modType)
{
if (modType != DataModification::eAdded && modType != DataModification::eModified)
return;
ParticipantManagement::EquipmentSystemEvent event;
event.id(data.equipmentNumber());
event.equipmentNumber(data.equipmentNumber());
event.sampleTime(data.sampleTime());
if (!std::isnan(event.position().lon()) && !std::isnan(event.position().lat()))
event.position(Core::GeoPosition(data.position().lon(), data.position().lat(), 0.0));
else
event.position(m_mainHandler->participantHandler()->participantByEquipmentNumber(data.equipmentNumber())->position().pos());
event.equipmentFunctionId(data.source().equipmentFunctionId());
event.subDeviceId(data.source().subDeviceId());
event.uniqueDeviceId(data.source().unqiueId());
event.deviceSpecificEventCode(data.code());
event.eventData(data.data());
if (!m_mainHandler->dts()->publish(event))
{
ERROR_LOG(m_logger) << "Error publishing equipment event to dds from eqnr: " << event.equipmentNumber() ;
}
}
void ParticipantManagement::EquipmentEventHandling::EquipmentEventHandler::removeFromDDS(const QVector<qint64> &keys)
{
for (qint64 key : keys)
m_mainHandler->dts()->removeEquipmentSystemEventFtcReplyByKey(key);
}
@@ -0,0 +1,52 @@
/**
* The EquipmentEventHandler class manages EquipmentEvents from TAB.
* $Id$
* $HeadURL$
* $Author$
* Created by Dassow Philipp, SLEA, 2017-09-04
*/
#pragma once
#include "dds_handler.h"
#include "logger.h"
#include <TrainingAreaBase/tab_dtc.h>
#include <QObject>
#include <ngldb/nsqlquery.h>
namespace ParticipantManagement
{
class MainHandler;
namespace EquipmentEventHandling
{
class EquipmentEventHandler
: public QObject
, protected Interface::Fts::EquipmentSystemEventDataRequestHandler
{
Q_OBJECT
public:
explicit EquipmentEventHandler(ParticipantManagement::MainHandler * mainHandler, QObject *parent = nullptr);
bool init();
void onEquipmentSystemEventDataRequest(const rti::core::SampleIdentity &cookie,
const EquipmentSystemEventDataRequest &request);
public slots:
void onEquipmentEventChanged(const TrainingAreaBase::EquipmentEvent &data, DataModification modType);
private:
void removeFromDDS(const QVector<qint64> &keys);
ParticipantManagement::MainHandler *m_mainHandler;
int m_filterId;
LOGGER_T m_logger;
}; // class EquipmentEventHandler
} // namespace EquipmentEventHandling
} // namespace ParticipantManagement
@@ -0,0 +1,31 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(SRCS_PPM_TEST
ppm_tests.cpp
)
SET(HDRS_PPM_TEST
ppm_tests.h
)
IF(BUILD_TESTING AND TARGET svcPpmClient)
# Testing FunctionTopic Functionality (FTC)
ADD_EXECUTABLE(ppmTests ${SRCS_PPM_TEST} ${HDRS_PPM_TEST})
TARGET_LINK_LIBRARIES(ppmTests
Qt5::Core
Qt5::Test
RTI::nddscpp2
svcPpmClient
svcPpmCommon
LtsCommandLineParser
ddsconnector
nglcoreapplication
)
IF(NOT BUILD_RPM)
NGL_INSTALL(AAR SV TARGETS ppmTests DESTINATION bin/test)
NGL_INSTALL(AAR SV FILES ../log4cxx_ppm.props DESTINATION bin/test)
ENDIF()
ENDIF()
@@ -0,0 +1,691 @@
/**
* @RDE_HEADER@
*
* PPM Function Topic test executable
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hannes Baeuerle, SLSE, 2015-07-18
*/
#include "ppm_tests.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QTest>
#include <QTimer>
//--------------------------------------------------------------------------------
ParticipantManagement::PpmTests::PpmTests(QObject *parent)
: QObject(parent)
, m_testId(-1)
, m_partyId(-1)
, m_typeId(-1)
, m_equipmentId(-1)
, m_participantId(1)
, m_bTestsSuccessful(true)
, m_logger(getLogger("Test"))
{
}
//--------------------------------------------------------------------------------
ParticipantManagement::PpmTests::~PpmTests()
{
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::startTest()
{
INFO_LOG(m_logger) << "********* startTest **************";
switch (m_testId)
{
case 1:
tryCreateEquipmentType();
break;
case 2:
tryCreateType();
break;
case 3:
tryModifyType();
break;
case 4:
tryRemoveType();
break;
case 5:
tryCreateParticipant();
break;
case 6:
tryCreateTemplate();
break;
case 7:
tryUpdateInfoParticipant();
break;
case 8:
tryRegisterParticipant();
break;
case 10:
tryCreateParty();
break;
case 11:
tryModifyParty();
break;
case 12:
tryRemoveParty();
break;
case 13:
trySyncParties();
break;
case 14:
tryChangeUsed();
break;
default:
failExit();
break;
}
INFO_LOG(m_logger) << "********* endTest **************";
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------------
bool ParticipantManagement::PpmTests::init(int domainId, int exerciseId, int testId, int partyId, int typeId, int equipmentId, int participantId)
{
INFO_LOG(m_logger) << "init begin";
m_testId = testId;
m_partyId = partyId;
m_typeId = typeId;
m_equipmentId = equipmentId;
m_participantId = participantId;
if (exerciseId > 0)
m_ddsConnector.reset(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Exercise, exerciseId));
else
m_ddsConnector.reset(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Global));
if (!m_ddsConnector->init())
return false;
m_ddsConnector->initPublisherSubscriber();
DEBUG_LOG(m_logger) << "create DTC PPM";
m_dtc = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
DEBUG_LOG(m_logger) << "init DTC PPM";
bool bRet = m_dtc->enableAll();
DEBUG_LOG(m_logger) << "create FTC PPM";
m_ftc = ParticipantManagement::Interface::Ftc::create(m_ddsConnector);
DEBUG_LOG(m_logger) << "init FTC PPM";
bRet &= m_ftc->enableAll();
DEBUG_LOG(m_logger) << "init end PPM - bRet = " << bRet;
DEBUG_LOG(m_logger) << "Domain Id = " << domainId;
DEBUG_LOG(m_logger) << "Test Id = " << testId;
DEBUG_LOG(m_logger) << "Party Id = " << partyId;
DEBUG_LOG(m_logger) << "Type Id = " << typeId;
DEBUG_LOG(m_logger) << "Equipment Id = " << equipmentId;
return bRet;
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryCreateTemplate()
{
INFO_LOG(m_logger) << "Trying to send Create Template";
std::vector<ParticipantManagement::TemplateData> data;
ParticipantManagement::TemplateData td;
td.modId(100);
td.name("Test Template 100");
td.designator("100");
td.hierarchyNumber(100);
td.typeId(19);
td.superiorModId(1);
td.tacticalIdentifier("100");
data.push_back(td);
m_dtc->waitForHistoricalDataTemplateParticipant();
ParticipantManagement::TemplateParticipant tp = m_dtc->templateParticipant(1);
const ParticipantManagement::TemplateData tdSup(m_dtc->convertFromParticipantTemplateToTemplateData(tp));
DEBUG_LOG(m_logger) << "SuperiorModId: " << tdSup.modId();
data.push_back(tdSup);
if (!m_ftc->modifyTemplateDataRequest(ModifyTemplateDataRequest(data),this))
{
ERROR_LOG(m_logger) << "Error creating template data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryCreateParticipant()
{
INFO_LOG(m_logger) << "Trying to send Create ParticipantData per ModifyParticipantData";
ParticipantManagement::ParticipantData ptd;
ptd.modId(1);
ptd.name("Test Participant 1");
ptd.typeId(m_typeId);
ptd.partyId(m_partyId);
ptd.equipmentNumber(m_equipmentId);
ptd.hierarchyNumber(1);
ptd.assignState(ParticipantManagement::ParticipantAssignState_def::HandoutExcon);
std::vector<ParticipantManagement::ParticipantData> data;
data.push_back(ptd);
ModifyParticipantDataRequest req;
req.data(data);
if (!m_ftc->modifyParticipantDataRequest(req,this))
{
ERROR_LOG(m_logger) << "Error creating participant data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryUpdateInfoParticipant()
{
INFO_LOG(m_logger) << "Trying to send update info per registerForParticantRequest";
ParticipantManagement::RegisterParticipantRequest rpr;
rpr.participantId(m_participantId);
rpr.equipmentNumber(m_equipmentId);
rpr.assignState(ParticipantManagement::ParticipantAssignState_def::HandoutExcon);
rpr.identification("Test Indentification");
rpr.info("Test Info2");
if (!m_ftc->registerParticipantRequest(rpr,this))
{
ERROR_LOG(m_logger) << "Error updating info/identification for participant";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryRegisterParticipant()
{
INFO_LOG(m_logger) << "Trying to send registerForParticantRequest";
ParticipantManagement::RegisterParticipantRequest rpr;
rpr.participantId(m_participantId);
rpr.equipmentNumber(m_equipmentId);
rpr.assignState(ParticipantManagement::ParticipantAssignState_def::HandoutExcon);
rpr.identification("Test");
rpr.info("Test Info");
if (!m_ftc->registerParticipantRequest(rpr,this))
{
ERROR_LOG(m_logger) << "Error updating info/identification for participant";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryCreateParty()
{
INFO_LOG(m_logger) << "Trying to Create Party per ModifyPartyData";
ParticipantManagement::Party party;
party.name("Test Party AAA");
party.role(ParticipantManagement::Role::Friend);
party.color(-65536);
party.fadingColor(-65536);
ModifyPartyDataRequest req;
req.id(-1);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(false);
if (!m_ftc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
failExit();
}
party.name("Test Party BBB");
party.role(ParticipantManagement::Role::Friend);
party.color(-16711936);
party.fadingColor(-16711936);
req.id(-1);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(false);
if (!m_ftc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
failExit();
}
party.name("Test Party CCC");
party.role(ParticipantManagement::Role::Enemy);
party.color(-16711936);
party.fadingColor(-16711936);
req.id(-1);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(false);
if (!m_ftc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
failExit();
}
party.name("Test Party DDD");
party.role(ParticipantManagement::Role::Enemy);
party.color(-16711936);
party.fadingColor(-16711936);
req.id(-1);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(false);
if (!m_ftc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryModifyParty()
{
INFO_LOG(m_logger) << "Trying to send Modify PartyData per ModifyPartyData";
ParticipantManagement::Party party;
m_dtc->waitForHistoricalDataParty();
party = m_dtc->party(m_partyId);
if (party.id() < 0)
{
ERROR_LOG(m_logger) << "Party not found: id = " << m_partyId;
failExit();
}
ModifyPartyDataRequest req;
req.id(m_partyId);
req.name(std::string("Test Party ")+QString::number(m_partyId).toStdString()); // updating name
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(true); // updating isUsed
if (!m_ftc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error modifying party data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryRemoveParty()
{
INFO_LOG(m_logger) << "Trying to send RemoveParty";
ParticipantManagement::Party party;
m_dtc->waitForHistoricalDataParty();
party = m_dtc->party(m_partyId);
if (party.id() < 0)
{
ERROR_LOG(m_logger) << "Party not found: id = " << m_partyId;
failExit();
}
RemovePartyRequest req;
req.partyId(m_partyId);
if (!m_ftc->removePartyRequest(req,this))
{
if (party.isUsedBy())
{
INFO_LOG(m_logger) << "Not possible to delete used party!" ;
successExit();
}
else
{
ERROR_LOG(m_logger) << "Failed to delete not used party with id " << m_partyId;
failExit();
}
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::trySyncParties()
{
INFO_LOG(m_logger) << "Trying to sync parties with Ex 0";
SyncPartiesRequest req;
req.exerciseId(42);
if (!m_ftc->syncPartiesRequest(req, this))
{
ERROR_LOG(m_logger) << "Error sync parties";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryChangeUsed()
{
INFO_LOG(m_logger) << "Trying to send change used";
ParticipantManagement::Party party;
m_dtc->waitForHistoricalDataParty();
party = m_dtc->party(m_partyId);
if (party.id() < 0)
{
ERROR_LOG(m_logger) << "Party not found: id = " << m_partyId;
failExit();
}
ChangePartyUsedStateRequest req;
req.partyId(m_partyId);
req.isUsedBy(false); // updating isUsed
if (!m_ftc->changePartyUsedStateRequest(req, this))
{
ERROR_LOG(m_logger) << "Error modifying used party";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryCreateEquipmentType()
{
INFO_LOG(m_logger) << "Trying to send Create EquipmentType per ModifyEquipmentTypeDataRequest";
ParticipantManagement::EquipmentType etd;
etd.id(1);
etd.name("Test Equipment Type 1");
etd.minEquipNo(1000);
etd.maxEquipNo(1999);
etd.traVulnerabilityModelId(1);
etd.traPlayerTypeId(110);
etd.wmsTypeClass(ParticipantManagement::WmsTypeClass::Human);
etd.deletedInWms(false);
std::vector<ParticipantManagement::EquipmentType> data;
data.push_back(etd);
if (!m_ftc->modifyEquipmentTypeDataRequest(ModifyEquipmentTypeDataRequest(data, false),this))
{
ERROR_LOG(m_logger) << "Error creating equipment type data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryCreateType()
{
INFO_LOG(m_logger) << "Trying to send CreateTypeRequest";
if (!m_ftc->modifyTypeRequest(Type(0,
ParticipantManagement::TypeClass::SupportEntity,
20,
std::string("1"),
std::string("TestType"),
1,
0,
false,
{}, std::string()),
this))
{
ERROR_LOG(m_logger) << "Error creating type data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryModifyType()
{
INFO_LOG(m_logger) << "Trying to send ModifyTypeRequest";
if (!m_ftc->modifyTypeRequest(Type(m_typeId,
ParticipantManagement::TypeClass::SupportEntity,
20,
std::string("1"),
std::string("TestType Modified"),
1,
0,
false,
{}, std::string()),
this))
{
ERROR_LOG(m_logger) << "Error modifying type data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::tryRemoveType()
{
INFO_LOG(m_logger) << "Trying to send RemoveTypeRequest";
if (!m_ftc->removeTypeRequest(RemoveTypeRequest(m_typeId), this))
{
ERROR_LOG(m_logger) << "Error modifying type data";
failExit();
}
QTest::qWait(1000);
}
//********************* GENERAL *************************************************
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onError(const QString &reason)
{
ERROR_LOG(m_logger) << "Got Reply error: " << reason;
failExit();
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onCreateReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
onError(replyObject.translated());
return;
}
INFO_LOG(m_logger) << "Got createReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
onError(replyObject.translated());
return;
}
INFO_LOG(m_logger) << "Got modifyRequest with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onRemoveReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
onError(replyObject.translated());
return;
}
INFO_LOG(m_logger) << "Got removeReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onSyncPartiesReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
onError(replyObject.translated());
return;
}
INFO_LOG(m_logger) << "Got syncParties with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::onRemovePartyReply(const Core::FunctionReplyObject &replyObject)
{
ParticipantManagement::Party party;
m_dtc->waitForHistoricalDataParty();
party = m_dtc->party(m_partyId);
if (replyObject.error() && !party.isUsedBy())
{
onError(replyObject.translated());
return;
}
INFO_LOG(m_logger) << "Got removeReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
void ParticipantManagement::PpmTests::onRegisterParticipantReply(const RegisterReply &reply)
{
ERROR_LOG(m_logger) << "onRegisterParticipantReply " << reply.errorString() << "exerciseId: " << reply.exerciseId();
for ( std::string str : reply.errorParams())
ERROR_LOG(m_logger) << "error params: " << str;
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::failExit()
{
ERROR_LOG(m_logger) << "FAIL EXIT !";
m_bTestsSuccessful = false;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ParticipantManagement::PpmTests::successExit()
{
INFO_LOG(m_logger) << "SUCCESS EXIT !";
m_bTestsSuccessful = true;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
addOption( { QStringList() << QLatin1String("t") << QLatin1String("testId")
, QLatin1String("TestId.")
, QLatin1String("Default: -1")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("p") << QLatin1String("partyId")
, QLatin1String("PartyId")
, QLatin1String("Default: -1")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("y") << QLatin1String("typeId")
, QLatin1String("TypeId")
, QLatin1String("Default: -1")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("q") << QLatin1String("equipmentId")
, QLatin1String("EquipmentId.")
, QLatin1String("Default: -1")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("a") << QLatin1String("participantId")
, QLatin1String("ParticipantId.")
, QLatin1String("Default: 1")
, QLatin1String("0")
} );
}
int testId() const { return isSet(QLatin1String("t")) ? value(QLatin1String("t")).toInt() : -1; }
int partyId() const { return isSet(QLatin1String("p")) ? value(QLatin1String("p")).toInt() : -1; }
int typeId() const { return isSet(QLatin1String("y")) ? value(QLatin1String("y")).toInt() : -1; }
int equipmentId() const { return isSet(QLatin1String("q")) ? value(QLatin1String("q")).toInt() : -1; }
int participantId() const { return isSet(QLatin1String("a")) ? value(QLatin1String("a")).toInt() : 1; }
};
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
int iRet = -1;
NGLCoreApplication app(argc, argv);
app.createLogger("PPM", 0 );
CmdLineParser options;
if (!options.init())
return iRet;
std::unique_ptr<ParticipantManagement::PpmTests> PpmTests(new ParticipantManagement::PpmTests);
if(!PpmTests->init(options.domainId(), options.exerciseId(), options.testId(), options.partyId(),
options.typeId(), options.equipmentId(), options.participantId()))
return iRet;
QTimer::singleShot(0, PpmTests.get(), &ParticipantManagement::PpmTests::startTest);
STARTUP_LOG(app.baseLogger());
app.exec();
iRet = PpmTests->testsSuccessful() ? 0 : 1;
fprintf(stderr, "\n%d\n\n", iRet);
INFO_LOG(app.baseLogger()) << "** TEST result = " << iRet;
SHUTDOWN_LOG(app.baseLogger());
return iRet;
}
+106
View File
@@ -0,0 +1,106 @@
/**
* @RDE_HEADER@
*
* PPM Function Topic test executable
*
* ModuleTests for PPM function topics
* ---------------------------------------------------
*
* => (!) Preconditons:
* - Service svcParticipantManagement -e 0 running
*
* -------------------------------------------------
*
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hannes Baeuerle, SLSE, 2015-07-18
*/
#pragma once
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <ParticipantManagement/ppm_ftc_types.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <ParticipantManagement/logger.h>
//----------------------------------------------------------------------------------
namespace ParticipantManagement
{
class PpmTests : public QObject
, protected ParticipantManagement::Interface::Ftc::ModifyTypeReplyHandler
, protected ParticipantManagement::Interface::Ftc::RemoveTypeReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyEquipmentTypeDataReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyParticipantDataReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyPartyDataReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyTemplateDataReplyHandler
, protected ParticipantManagement::Interface::Ftc::RemovePartyReplyHandler
, protected ParticipantManagement::Interface::Ftc::SyncPartiesReplyHandler
, protected ParticipantManagement::Interface::Ftc::ChangePartyUsedStateReplyHandler
, protected ParticipantManagement::Interface::Ftc::RegisterParticipantReplyHandler
{
Q_OBJECT
public:
explicit PpmTests(QObject *parent = nullptr);
~PpmTests();
bool init(int domainId, int exerciseId, int testId, int partyId, int typeId, int equipmentId, int participantId);
bool testsSuccessful() const { return m_bTestsSuccessful; }
public Q_SLOTS:
void startTest();
protected Q_SLOTS:
void tryCreateEquipmentType();
void tryCreateType();
void tryModifyType();
void tryRemoveType();
void tryCreateParticipant();
void tryCreateParty();
void tryModifyParty();
void tryRemoveParty();
void trySyncParties();
void tryChangeUsed();
void tryCreateTemplate();
void tryUpdateInfoParticipant();
void tryRegisterParticipant();
protected:
void onModifyTypeReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onRemoveTypeReply(const Core::FunctionReplyObject &replyObject) override final { onRemoveReply(replyObject); }
void onModifyEquipmentTypeDataReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onModifyParticipantDataReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onModifyPartyDataReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onModifyTemplateDataReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onRemovePartyReply(const Core::FunctionReplyObject &replyObject) override final;
void onSyncPartiesReply(const Core::FunctionReplyObject &replyObject) override final;
void onChangePartyUsedStateReply(const Core::FunctionReplyObject &replyObject) override final { onModifyReply(replyObject); }
void onRegisterParticipantReply(const RegisterReply &reply) override final;
// general
void onError(const QString &reason);
void onCreateReply(const Core::FunctionReplyObject &replyObject);
void onModifyReply(const Core::FunctionReplyObject &replyObject);
void onRemoveReply(const Core::FunctionReplyObject &replyObject);
void failExit();
void successExit();
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ftc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_dtc;
int m_testId;
int m_partyId;
int m_typeId;
int m_equipmentId;
int m_participantId;
bool m_bTestsSuccessful;
LOGGER_T m_logger;
};
} // namespace ParticipantManagement
@@ -0,0 +1,147 @@
/**
* @RDE_HEADER@
* The FiringUnitValidator regulates the rules for firing units
* $Id: participant_dis_helper.h 37747 2017-12-08 08:07:01Z ehrlicher $
* $HeadURL: svn://10.196.0.54/TDS/branches/product/features/24_vulcano/Lts/Plugins/ParticipantManagement/Gui/Common/participant_dis_helper.h $
* $Author: ehrlicher $
* Created by Hannes Fritsche, ESZS, 2024-03-07
*/
#include "firing_unit_validator.h"
#include <LtsUtilsSettings/settings.h>
#include <ParticipantManagement/ppm.hpp>
#include "ParticipantManagement/settings.h"
#include "TypeHandling/type_handler.h"
#include "participant_handler.h"
#include "participant.h"
namespace ParticipantManagement
{
namespace ParticipantHandling
{
FiringUnitValidator::FiringUnitValidator(const std::shared_ptr<ParticipantHandler> &participantHandler,
const std::shared_ptr<TypeHandling::TypeHandler> &typeHandler,
QObject *parent)
: QObject(parent)
, m_participantHandler(participantHandler)
, m_typeHandler(typeHandler)
{
}
Core::FunctionReplyObject FiringUnitValidator::firingUnitError(const QHash<int, int> &modIdToParticipantId, const QVector<ParticipantData> &participantData)
{
QString error;
for (const ParticipantData &currentData : participantData)
{
if (!m_typeHandler->isFiringUnitType(currentData.typeId()))
continue;
int unitModId = currentData.modId();
error = getError(modIdToParticipantId, participantData, unitModId);
if (!error.isEmpty())
break;
}
return Core::FunctionReplyObject(error.toStdString().c_str());
}
QString FiringUnitValidator::getError(const QHash<int, int> &modIdToParticipantId, const QVector<ParticipantData> &participantData, int unitModId) const
{
bool hasCapacityError = false;
bool hasFalseTypeError = false;
bool hasMixedTypeError = false;
uint maxParticipantsPerFiringUnit = Settings::maxParticipantsPerFiringUnit();
uint participantsInFiringUnit = nonModdedInferiorsCount(unitModId, modIdToParticipantId);
int typeId = participantTypeInFiringUnit(unitModId, modIdToParticipantId);
for (const ParticipantData &currentData : participantData)
{
if (currentData.superiorModId() != unitModId)
continue;
// capacity error
hasCapacityError = ++participantsInFiringUnit > maxParticipantsPerFiringUnit;
if (hasCapacityError)
return tr("There are only %1 participants allowed per firing unit.").arg(maxParticipantsPerFiringUnit);
// false type error
hasFalseTypeError = !m_typeHandler->isArtilleryParticipantType(currentData.typeId());
if (hasFalseTypeError)
return tr("There are only artillery participants allowed in firing units.");
// mixed type error
if (typeId == -1)
typeId = currentData.typeId();
hasMixedTypeError = currentData.typeId() != typeId;
if (hasMixedTypeError)
return tr("There are no mixed types allowed in firing units.");
}
return QString();
}
int FiringUnitValidator::nonModdedInferiorsCount(int unitModId, const QHash<int, int> &modIdToParticipantId) const
{
if (!modIdToParticipantId.contains(unitModId))
return 0;
const int unitParticipantId = modIdToParticipantId.value(unitModId);
const Participant *unitParticipant = m_participantHandler->participantById(unitParticipantId);
if (!unitParticipant)
return 0;
const QList<int> moddedParticipantIds = modIdToParticipantId.keys();
int nonModdedInferiorsCountRet = 0;
for (int inferiorId : unitParticipant->inferiors())
{
if (moddedParticipantIds.contains(inferiorId)) // only non modded participants
continue;
++nonModdedInferiorsCountRet;
}
return nonModdedInferiorsCountRet;
}
int FiringUnitValidator::participantTypeInFiringUnit(int unitModId, const QHash<int, int> &modIdToParticipantId) const
{
if (!modIdToParticipantId.contains(unitModId))
return -1;
const int unitParticipantId = modIdToParticipantId.value(unitModId);
const Participant *unitParticipant = m_participantHandler->participantById(unitParticipantId);
if (!unitParticipant)
return -1;
const QList<int> moddedParticipantIds = modIdToParticipantId.keys();
for (int inferiorId : unitParticipant->inferiors())
{
if (moddedParticipantIds.contains(inferiorId)) // only non modded participants
continue;
const Participant *inferior = m_participantHandler->participantById(inferiorId);
if (!inferior)
continue;
return inferior->typeId();
}
return -1;
}
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* The FiringUnitValidator regulates the rules for firing units serverside
* $Id: participant_dis_helper.h 37747 2017-12-08 08:07:01Z ehrlicher $
* $HeadURL: svn://10.196.0.54/TDS/branches/product/features/24_vulcano/Lts/Plugins/ParticipantManagement/Gui/Common/participant_dis_helper.h $
* $Author: ehrlicher $
* Created by Hannes Fritsche, ESZS, 2024-03-07
*/
#pragma once
#include <QObject>
#include <memory>
#include <DDS/Core/function_reply_object.h>
namespace ParticipantManagement
{
class ParticipantData;
namespace TypeHandling
{
class TypeHandler;
} //namespace TypeHandling
namespace ParticipantHandling
{
class ParticipantHandler;
class FiringUnitValidator : public QObject
{
Q_OBJECT
public:
FiringUnitValidator(const std::shared_ptr<ParticipantHandler> &participantHandler,
const std::shared_ptr<TypeHandling::TypeHandler> &typeHandler,
QObject *parent = nullptr);
Core::FunctionReplyObject firingUnitError(const QHash<int, int> &modIdToParticipantId, const QVector<ParticipantData> &participantData);
private:
QString getError(const QHash<int, int> &modIdToParticipantId, const QVector<ParticipantData> &participantData, int unitModId) const;
int nonModdedInferiorsCount(int unitModId, const QHash<int, int> &modIdToParticipantId) const;
int participantTypeInFiringUnit(int unitModId, const QHash<int, int> &modIdToParticipantId) const;
std::shared_ptr<ParticipantHandler> m_participantHandler;
std::shared_ptr<TypeHandling::TypeHandler> m_typeHandler;
}; // namespace FiringUnitValidator
} //namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,76 @@
/**
* @RDE_HEADER@
* Handler for all request journal objects
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-01-13
*/
#include "get_participants.h"
#include "main_handler.h"
#include <ParticipantManagement/ppm_record_replay_plugin.h>
#include <ngldb/nsqlquery.h>
namespace ParticipantManagement
{
GetParticipants::GetParticipants(MainHandler *mainHandler)
: Interface::Fts::GetParticipantsRequestHandler(mainHandler->fts())
, m_mainHandler(mainHandler)
, m_logger(getLogger("GetParticipants"))
{}
GetParticipants::~GetParticipants()
{
}
void GetParticipants::onGetParticipantsRequest(const rti::core::SampleIdentity &cookie,
const GetParticipantsRequest &request)
{
DEBUG_LOG(m_logger) << "onGetParticipantsRequest" << request;
// Initialize RecordReplay to get initial participants
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.readParticipantAt(q, request.timeStamp());
// key is sampleTime -> multiMap
auto participantsMmap = recordReplay.getParticipantMap();
QMap<int, Participant> participants;
// only take the newest entries (participantsMmap may contain more than one entry per participantId
for (auto it = participantsMmap.begin(); it != participantsMmap.end(); ++it)
{
Participant &p = it.value();
if (p.isDeleted())
participants.remove(p.id());
else
participants[p.id()] = std::move(p);
}
DEBUG_LOG(m_logger) << "Starting to read participant from record replay. Size: "
<< participants.size();
GetParticipantsReply reply;
reply.requestId(request.requestId());
auto &vecPart = reply.participants();
vecPart.reserve(participants.size());
for (auto it = participants.begin(); it != participants.end(); ++it)
{
if (request.partyId() >= 0 && it.value().partyId() != request.partyId())
continue;
vecPart.push_back(it.value());
}
m_mainHandler->fts()->getParticipantsReply(cookie, reply);
}
} // Namespace ParticipantManagement
@@ -0,0 +1,32 @@
/**
* @RDE_HEADER@
* Handler for all situation replay objects
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-01-13
*/
#pragma once
#include "logger.h"
#include <ParticipantManagement/ppm_fts.h>
namespace ParticipantManagement
{
class MainHandler;
class GetParticipants final : protected Interface::Fts::GetParticipantsRequestHandler
{
public:
explicit GetParticipants(MainHandler *mainHandler);
~GetParticipants();
private:
void onGetParticipantsRequest(const rti::core::SampleIdentity &cookie,
const GetParticipantsRequest &request) override;
private:
MainHandler *m_mainHandler;
NGL::INGLLogger &m_logger;
};
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,147 @@
/**
* The Participant class is the base class for either Buildings or ExerciseParticipants.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-23
*/
#pragma once
#include "logger.h"
#include <ParticipantManagement/ppm_db_structs.h>
#include <ParticipantManagement/ppm.hpp>
#include <TrainingAreaBase/tab.hpp>
#include <QMutex>
namespace ParticipantManagement
{
class MainHandler;
namespace Interface
{
class Dts;
}
namespace ParticipantHandling
{
class ParticipantHandler;
class UnitPositionCalculator;
class Participant
{
public:
Participant(ParticipantHandler *participantHandler,
UnitPositionCalculator *unitPosCalculator,
const Database::PpmParticipant &data,
const char *loggerName);
virtual ~Participant();
int32_t id() const { return m_participantData.id(); }
int32_t typeId() const { return m_participantData.typeId(); }
int32_t superiorId() const { return m_participantData.superiorId(); }
// return optional?
int32_t equipmentNumber() const { return m_participantData.equipmentNumber().is_set() ? m_participantData.equipmentNumber().get() : 0; }
int32_t partyId() const { return m_participantData.partyId(); }
bool isDeleted() const { return m_participantData.isDeleted(); }
ParticipantAssignState assignState() const { return m_participantData.assignState(); }
const std::vector<int32_t> &inferiors() const { return m_participantData.inferiorIds(); }
ParticipantPosition position() const { return m_positionPublished; }
const DynamicParticipantData &dynamicParticipantData() const { return m_dynamicData; }
const ParticipantManagement::Participant &participantData() const { return m_participantData; }
void markAsDeleted();
bool isValidPosition(qint64 sampleTime, const Core::GeoPosition &pos, Core::PositionReportingSource repSource) const;
bool setPosition(const TrainingAreaBase::EquipmentPosition &newPosition);
bool setPosition(const ParticipantManagement::ParticipantPosition &newPosition, bool bForce = false);
// returns true when the positionTime is newer than the old positionTime
bool setCalculatedPosition(const Core::GeoPosition &pos, int64_t positionTime);
bool setState(const TrainingAreaBase::EquipmentState &newState, int originatorId);
bool setSpecialSensorData(const TrainingAreaBase::SpecialSensorData &data);
bool setGear(const TrainingAreaBase::EquipmentGearAssignment &gearAssignment);
bool setSanitationMaterial(int materialCount,
const std::vector<int> &materialStock,
int abgSn,
qint64 sampleTime,
Qualification qualif,
qint64 eqNo);
bool setSanitation(qint64 sanitationProceduresTaken,
TrainingAreaBase::SanitationResult sanitationResult,
TrainingAreaBase::SanitationEvaluation sanitationEvaluation,
int originatorId,
TrainingAreaBase::Qualification qualification,
qint64 sampleTime);
bool setOperatorAction(int action, int originatorId, qint64 sampleTime);
bool setEquipmentEvent(int event, qint64 sampleTime);
bool setGearCharge(const TrainingAreaBase::GearCharge &charge, qint64 sampleTime);
bool setGearCharge(const GearInformation &gearInfo);
void setDynamicData(const DynamicParticipantData &data);
void setMode(const ParticipantMode &data);
bool isNewAssignStateValid(const ParticipantManagement::ParticipantAssignState &newAssignState, int32_t equipmentNumber) const;
/**
* @brief Method to set this participants data by a modify request and to
* detect if the request really changes the data
* @param data - participant data from a modify request
* @return true if data differs from this participants data, otherwise false
*/
bool hasToBeChanged(const ParticipantManagement::Participant &data);
bool hasToBeChanged(const ParticipantManagement::ParticipantBase &data);
bool hasNewSuperiorId(int32_t superiorId);
bool hasNewInferiorStructure(std::vector<int32_t> inferiorIds);
bool setOperationalState(const TrainingAreaBase::EquipmentOperationalState &operationalState);
RegisterParticipantRequest toRegisterParticipantRequest() const;
virtual void removeFromDds();
virtual bool publishPosition();
virtual bool publishDynamicData();
virtual bool publishMode();
virtual bool publishParticipant();
Database::PpmParticipant toDbParticipant() const;
static ParticipantManagement::Participant toDdsParticipant(const Database::PpmParticipant &data);
protected:
virtual bool updateEquipmentExerciseMapping(int oldEqId, int newEqId) = 0;
bool setPosition(const TrainingAreaBase::EquipmentPosition &newPosition, int mountedToId);
void unmountParticipantById(const TrainingAreaBase::EquipmentPosition &newPosition, int32_t id);
void removeManipulation(int32_t equipmentFunctionId);
void cleanManipulations();
bool gearHasCharge(int32_t gearId) const;
int32_t indexByGearAndChargeId(int32_t gearId, int32_t chargeTypeId = -1) const;
ParticipantHandler *particpantHandler() { return m_participantHandler; }
const ParticipantHandler *particpantHandler() const { return m_participantHandler; }
qint64 currentTimeMSec() const;
std::shared_ptr<Interface::Dts> dts() const;
protected: // attributes
ParticipantHandler *m_participantHandler;
UnitPositionCalculator *m_unitPosCalculator;
qint64 m_lastGearUsage;
qint64 m_lastSanMatUsage{0};
qint64 m_lastManipulationReset;
qint64 m_lastNbcState;
qint64 m_lastSpecialSensorData;
ParticipantPosition m_positionPublished;
ParticipantPosition m_positionToPublish;
DynamicParticipantData m_dynamicData;
DynamicParticipantData m_dynamicDataPublished;
ParticipantManagement::Participant m_participantData;
ParticipantManagement::Participant m_participantPublished;
ParticipantManagement::ParticipantMode m_participantMode;
ParticipantManagement::ParticipantMode m_participantModePublished;
bool m_bPublishPosition; // false for units
QMutex m_mutex;
LOGGER_T m_logger;
}; // class ParticipantBase
} // namespace ParticipantManagement
} // namespace ParticipantHandling
@@ -0,0 +1,22 @@
/**
* The ParticipantDonator class is an interface for returning a Participant to a given id.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-06-06
*/
#pragma once
namespace ParticipantManagement
{
namespace ParticipantHandling
{
class Participant;
class ParticipantDonator
{
public:
virtual Participant *participantById(int participantId) const = 0;
virtual bool isUnitType(int typeId) const = 0;
}; // class ParticipantDonator
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,141 @@
/**
* The Participant class is the base class for either Buildings or ExerciseParticipants.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-23
*/
#include "participant_for_exercise.h"
#include "main_handler.h"
#include "convert_db_to_dds.h"
namespace ParticipantManagement
{
namespace ParticipantHandling
{
//
// ParticipantForExercise
//
ParticipantForExercise::ParticipantForExercise(MainHandler *mainHandler,
ParticipantHandler *particpantHandler,
UnitPositionCalculator *unitPosCalculator,
const Database::PpmParticipant &data)
: Participant(particpantHandler, unitPosCalculator, data, "Participant")
, m_mainHandler(mainHandler)
{}
ParticipantForExercise::~ParticipantForExercise()
{
removeFromDds();
}
bool ParticipantForExercise::applyEquipmentExerciseMapping(int newEqId)
{
if (!newEqId)
return true;
if (m_equipmentExerciseMappingPublished.equipmentNumber == newEqId &&
m_equipmentExerciseMappingPublished.participantId == id() &&
m_equipmentExerciseMappingPublished.exerciseId == m_mainHandler->exerciseId())
return true;
Database::EquipmentExerciseMapping dbEem;
dbEem.equipmentNumber = newEqId;
dbEem.participantId = id();
dbEem.exerciseId = m_mainHandler->exerciseId();
DEBUG_LOG(m_logger) << "Inserting EquipmentExerciseMapping into db: " << dbEem.equipmentNumber
<< ", " << dbEem.participantId
<< ", " << dbEem.exerciseId;
if (!m_mainHandler->dbEx0()->insert(dbEem))
{
m_participantData.equipmentNumber().reset();
m_participantData.assignState(ParticipantAssignState::NotAssigned);
publishParticipant();
return false;
}
m_participantHandler->setEquipmentNumberForParticipant(m_equipmentExerciseMappingPublished.equipmentNumber,
dbEem.equipmentNumber,
id());
m_equipmentExerciseMappingPublished = dbEem;
return true;
}
void ParticipantForExercise::removeEquipmentExerciseMapping(int oldEqId, bool bIsShutdown)
{
if (!oldEqId)
return;
m_equipmentExerciseMappingPublished = Database::EquipmentExerciseMapping();
if (!bIsShutdown)
{
Database::EquipmentExerciseMapping dbEem;
dbEem.equipmentNumber = oldEqId;
dbEem.participantId = id();
dbEem.exerciseId = m_mainHandler->exerciseId();
m_mainHandler->dbEx0()->removeEquipmentExerciseMapping(dbEem);
m_participantHandler->setEquipmentNumberForParticipant(oldEqId, 0, id());
}
}
bool ParticipantForExercise::updateEquipmentExerciseMapping(int oldEqId, int newEqId)
{
DEBUG_LOG(m_logger) << "updateEquipmentExerciseMapping from " << oldEqId << " to " << newEqId;
if (oldEqId == newEqId)
return true;
removeEquipmentExerciseMapping(oldEqId, false);
return applyEquipmentExerciseMapping(newEqId);
}
bool ParticipantForExercise::publishParticipant()
{
return Participant::publishParticipant();
}
void ParticipantForExercise::removeFromDds() // only called on shutdown
{
removeEquipmentExerciseMapping(equipmentNumber(), true);
Participant::removeFromDds();
}
void ParticipantForExercise::registerInfoFromFt(const RegisterParticipantRequest &request)
{
m_participantData.info(request.info());
m_participantData.identification(request.identification());
}
bool ParticipantForExercise::registerFromFt(const RegisterParticipantRequest &request)
{
int newEquipmentNumber = request.equipmentNumber();
// just to be sure ...
if (request.assignState() == ParticipantAssignState::NotAssigned)
newEquipmentNumber = 0;
if (!updateEquipmentExerciseMapping(equipmentNumber(), newEquipmentNumber))
return false;
particpantHandler()->setEquipmentNumberForParticipant(equipmentNumber(),
newEquipmentNumber,
id());
INFO_LOG(m_logger) << "Set assign state for participant " << id() << " from "
<< assignState() << " to " << request.assignState()
<< ", old eqid: " << equipmentNumber() << ", new eqid: "
<< newEquipmentNumber;
if (newEquipmentNumber)
m_participantData.equipmentNumber(newEquipmentNumber);
else
m_participantData.equipmentNumber().reset();
m_participantData.assignState(request.assignState());
m_participantData.info(request.info());
m_participantData.identification(request.identification());
return true;
}
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,45 @@
/**
* The Participant class is the base class for either Buildings or ExerciseParticipants.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-23
*/
#pragma once
#include "participant.h"
namespace ParticipantManagement
{
class MainHandler;
namespace ParticipantHandling
{
class ParticipantHandler;
class UnitPositionCalculator;
class ParticipantForExercise final : public Participant
{
public:
ParticipantForExercise(MainHandler *mainHandler,
ParticipantHandler *particpantHandler,
UnitPositionCalculator *unitPosCalculator,
const Database::PpmParticipant &data);
~ParticipantForExercise();
bool publishParticipant() override;
void removeFromDds() override;
bool registerFromFt(const RegisterParticipantRequest &request);
void registerInfoFromFt(const RegisterParticipantRequest &request);
private:
bool applyEquipmentExerciseMapping(int newEqId);
void removeEquipmentExerciseMapping(int oldEqId, bool bIsShutdown);
bool updateEquipmentExerciseMapping(int oldEqId, int newEqId) override;
private:
Database::EquipmentExerciseMapping m_equipmentExerciseMappingPublished;
ParticipantManagement::MainHandler *m_mainHandler;
}; // class Participant
} // namespace ParticipantManagement
} // namespace ParticipantHandling
@@ -0,0 +1,778 @@
/**
* 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.h"
#include "participant.h"
#include "ppm_common.h"
#include "settings.h"
#include "unit_position_calculator.h"
#include <Buildings/bld.hpp>
#include <ProjectDataManagement/pdm_dtc.h>
#include <LtsUtilsCore/outdated_position.h>
namespace ParticipantManagement
{
namespace ParticipantHandling
{
//
// ParticipantHandler
//
ParticipantHandler::ParticipantHandler(BaseDataProvider *bdp, const char *loggerName) : QObject(), m_hitLineId(0), m_bdp(bdp), m_logger(getLogger(loggerName)) {}
ParticipantHandler::~ParticipantHandler()
{
if (m_unitPositionCalculator)
m_unitPositionCalculator->stopCalculation();
clearParticipants();
}
void ParticipantHandler::clearParticipants()
{
// clear maps
for (auto it = m_hitLineInfoMap.cbegin(); it != m_hitLineInfoMap.cend(); ++it)
dts()->removeHitLineInfoByKey(it.value().id());
m_hitLineInfoMap.clear();
for (auto p : qAsConst(m_participants))
{
p->removeFromDds();
delete p;
}
m_participants.clear();
}
bool ParticipantHandler::init()
{
clearParticipants();
m_unitPositionCalculator.reset(new UnitPositionCalculator(this));
m_webSocketHandler = std::make_shared<WebSocketHandler>(this);
if (!m_webSocketHandler->init(Settings::getWebSocketServerPortRange(2)))
{
ERROR_LOG(m_logger) << "could not init websockethandler";
return false;
}
return true;
}
void ParticipantHandler::setEquipmentNumberForParticipant(int oldEqNo, int newEqNo, int participantId)
{
DEBUG_LOG(m_logger) << "setEquipmentNumberForParticipant, old: " << oldEqNo << ", new: " << newEqNo << ", participant: " << participantId;
if (oldEqNo > 0)
m_equipNumber2ParticipantId.remove(oldEqNo);
if (newEqNo > 0)
{
int &val = m_equipNumber2ParticipantId[newEqNo];
if (val != 0 && val != participantId)
WARNING_LOG(m_logger) << "Another participant (" << val << ") already has the equipment number " << newEqNo << " - overriding it with participant "
<< participantId;
val = participantId;
}
}
Participant *ParticipantHandler::participantById(int32_t participantId) const { return m_participants.value(participantId); }
Participant *ParticipantHandler::participantByEquipmentNumber(int32_t equipmentNumber)
{
const int participantId = m_equipNumber2ParticipantId.value(equipmentNumber);
DEBUG_LOG(m_logger) << " >>>>>> [ParticipantHandler] participantByEquipmentNumber participantId: " << participantId << " | equipNumb: " << equipmentNumber;
return participantById(participantId);
}
void ParticipantHandler::onNewGearEvent(const TrainingAreaBase::EquipmentGearEvent &gearEvent)
{
if (isTelegramInFuture(gearEvent))
{
WARNING_LOG(m_logger) << "Got an EquipmentGearEvent which has a timestamp in the future: " << gearEvent.sampleTime();
return;
}
// NOTE: gearEvent.chargeAmount() is not used because there is no usecase for this value ... atm
Participant *participantToBeChanged = participantByEquipmentNumber(gearEvent.equipmentNumber());
if (!participantToBeChanged)
{
WARNING_LOG(m_logger) << "Got gear charge state for unknown equipment number " << gearEvent.equipmentNumber();
return;
}
participantToBeChanged->setGearCharge(gearEvent.charge(), gearEvent.sampleTime());
participantToBeChanged->publishDynamicData();
// publish gear event
dts()->publish(
ParticipantGearEvent(participantToBeChanged->id(),
gearEvent.sampleTime(),
gearEvent.chargeAmount(),
GearCharge(gearEvent.charge().gearId(), gearEvent.charge().gearTypeId(), gearEvent.charge().chargeTypeId(), gearEvent.charge().remain())));
}
void ParticipantHandler::onNewGearChargeStateFromProxy(const TrainingAreaBase::EquipmentGearChargeState &chargeState)
{
if (isTelegramInFuture(chargeState))
{
WARNING_LOG(m_logger) << "Got an EquipmentGearChargeState which has a timestamp in the future: " << chargeState.sampleTime();
return;
}
const int equipNo = chargeState.equipmentNumber();
Participant *participantToBeChanged = equipNo < 0 ? participantById(-equipNo) : participantByEquipmentNumber(equipNo);
if (!participantToBeChanged)
{
WARNING_LOG(m_logger) << "Got gear charge state for unknown equipment number " << chargeState.equipmentNumber();
return;
}
for (const TrainingAreaBase::GearCharge &charge : chargeState.charge())
participantToBeChanged->setGearCharge(charge, chargeState.sampleTime());
participantToBeChanged->publishDynamicData();
emit gearChargeChanged(participantToBeChanged->id(), participantToBeChanged->partyId());
}
void ParticipantHandler::onNewGearAssignmentFromProxy(const TrainingAreaBase::EquipmentGearAssignment &gearAssignment)
{
if (isTelegramInFuture(gearAssignment))
{
WARNING_LOG(m_logger) << "Got an EquipmentGearAssignment which has a timestamp in the future: " << gearAssignment.sampleTime();
return;
}
Participant *participantToBeChanged = participantByEquipmentNumber(gearAssignment.equipmentNumber());
if (!participantToBeChanged)
{
WARNING_LOG(m_logger) << "Got gear assignment for unknown equipment number " << gearAssignment.equipmentNumber();
return;
}
participantToBeChanged->setGear(gearAssignment);
participantToBeChanged->publishDynamicData();
}
void ParticipantHandler::onNewStateFromProxy(const TrainingAreaBase::EquipmentState &data)
{
if (isTelegramInFuture(data))
{
WARNING_LOG(m_logger) << "Got an EquipmentState which has a timestamp in the future: " << data.sampleTime();
return;
}
// 1. handle included EquipmentPosition:
TrainingAreaBase::EquipmentPosition pos;
pos.equipmentNumber(data.equipmentNumber());
pos.sampleTime(data.sampleTime());
if (data.position().is_set())
{
pos.position(data.position().get());
onNewPositionFromProxy(pos);
}
// 2. handle EquipmentState
Participant *participantToBeChanged = participantByEquipmentNumber(data.equipmentNumber());
if (!participantToBeChanged)
{
WARNING_LOG(m_logger) << "Got equipment state for unknown equipment number " << data.equipmentNumber();
return;
}
// convert eqNo to participantId if originator was a participant and data.originatorId() is an equipment number
int originatorId = data.originatorId();
Participant *originParticipant = nullptr;
if (data.originatorType() == ProjectDataManagement::StateChangeOriginator::Participant)
{
originParticipant = participantByEquipmentNumber(data.originatorId());
if (originParticipant)
originatorId = originParticipant->id();
else
originatorId = -originatorId;
}
// Make Laser Designations to Laser Designations
auto dtcPdm = m_bdp->pdmDtc();
if (dtcPdm && dtcPdm->getChargeTypeSpecialProperty(data.originatorChargeTypeId(), 0, 0) == ProjectDataManagement::ChargeTypeProperty_def::LTD && originParticipant)
{
QSet<int> ldGearTypes{1043}; // Need Property for LTD...
DynamicParticipantData dpd = originParticipant->dynamicParticipantData();
auto it = std::find_if(dpd.gear().cbegin(), dpd.gear().cend(), [ldGearTypes](const GearInformation &gi) { return ldGearTypes.contains(gi.gearTypeId()); });
if (it != dpd.gear().cend())
{
for (auto &ld : dpd.designations())
{
if (ld.gearId() != it->gearId())
continue;
ld.spotLocation() = pos.position().position();
ld.outputPower() = 1.0;
originParticipant->setDynamicData(dpd);
originParticipant->publishDynamicData();
break;
}
}
}
if (!participantToBeChanged->setState(data, originatorId))
return; // outdated - ignore
participantToBeChanged->publishDynamicData();
createHitlineInfo(originatorId, originParticipant, participantToBeChanged, pos, data.originatorChargeTypeId(), data.sampleTime());
}
void ParticipantHandler::createHitlineInfo(int, Participant *, Participant *, const TrainingAreaBase::EquipmentPosition &, int, qint64)
{
// replay participant is filled with setHitlineInfoFromReplay
return;
}
void ParticipantHandler::onNewPositionFromProxy(const TrainingAreaBase::EquipmentPosition &position)
{
if (isTelegramInFuture(position))
{
WARNING_LOG(m_logger) << "Got an EquipmentPosition which has a timestamp in the future: " << position.sampleTime();
return;
}
if (isTelegramOutdated(position) && position.position().reportingSource() != Core::PositionReportingSource::SetByOperator)
{
INFO_LOG(m_logger) << "position is outdated: " << position;
return;
}
DEBUG_LOG(m_logger) << "Got new position from proxy: " << position;
const int equipNo = position.equipmentNumber();
// negative - treat as participantId
Participant *participantWithNewPosition = equipNo < 0 ? participantById(-equipNo) : participantByEquipmentNumber(equipNo);
if (!participantWithNewPosition)
{
WARNING_LOG(m_logger) << "Got EquipmentPosition " << position << " for unknown equipment number: " << equipNo;
return;
}
if (participantWithNewPosition->setPosition(position))
participantWithNewPosition->publishPosition();
}
void ParticipantHandler::onNewEquipmentOperationalStateFromProxy(const TrainingAreaBase::EquipmentOperationalState &operationalState)
{
if (isTelegramInFuture(operationalState))
{
WARNING_LOG(m_logger) << "Got an EquipmentOperationalState which has a timestamp in the future: " << operationalState.sampleTime();
return;
}
Participant *participantToUpdate = participantByEquipmentNumber(operationalState.equipmentNumber());
if (!participantToUpdate)
{
WARNING_LOG(m_logger) << "Got EquipmentOperationalState for unknown equipment number: " << operationalState;
return;
}
participantToUpdate->setOperationalState(operationalState);
participantToUpdate->publishDynamicData();
}
void ParticipantHandler::onNewBiteFromProxy(const TrainingAreaBase::EquipmentBite &bite)
{
if (isTelegramInFuture(bite))
{
WARNING_LOG(m_logger) << "Got an EquipmentBite which has a timestamp in the future: " << bite.sampleTime();
return;
}
if (isTelegramOutdated(bite))
{
WARNING_LOG(m_logger) << "Got an EquipmentBite which has a timestamp in the past: " << bite.sampleTime();
return;
}
Bite biteData;
Participant *p = participantByEquipmentNumber(bite.equipmentNumber());
biteData.participantId(p ? p->id() : -1);
biteData.equipmentNumber(bite.equipmentNumber());
biteData.sampleTime(bite.sampleTime());
biteData.registerType(bite.registerType());
biteData.biteState(bite.biteState());
biteData.configState(bite.configState());
biteData.batteryState(bite.batteryState());
biteData.configError(bite.configError());
biteData.biteErrors().reserve(bite.biteErrors().size());
for (const TrainingAreaBase::EquipmentBiteError &error : bite.biteErrors())
biteData.biteErrors().emplace_back(error.errorNumber(), error.canDeviceNumber(), error.subdeviceNumber(), error.uniqueId());
dts()->publish(biteData);
}
void ParticipantHandler::onNewCtaDataErrorFromProxy(const TrainingAreaBase::CtaDataError &error)
{
CtaError ctasErrorData;
ctasErrorData.equipmentNumber(error.equipmentNumber());
ctasErrorData.timeReceipt(currentTimeMSec());
ctasErrorData.timePlayerUnit(error.sampleTime());
ctasErrorData.errorReason(error.reason());
ctasErrorData.telegramId(error.originTelegramId());
dts()->publish(ctasErrorData);
}
void ParticipantHandler::onNewParticipantModeReport(const TrainingAreaBase::ParticipantModeReport &report)
{
if (isTelegramInFuture(report))
{
WARNING_LOG(m_logger) << "Got a ParticipantModeReport which has a timestamp in the future: " << report.sampleTime();
return;
}
if (isTelegramOutdated(report))
{
WARNING_LOG(m_logger) << "Got a ParticipantModeReport which has a timestamp in the past: " << report.sampleTime();
return;
}
Participant *participant = participantByEquipmentNumber(report.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << report.equipmentNumber();
return;
}
ParticipantMode ddsData;
ddsData.participantId(participant->id());
ddsData.language(report.language());
ddsData.opMode(report.operationMode());
ddsData.reactivationDecontamination(report.reactivationDetoxication());
ddsData.reactivationMaintenance(report.reactivationMaintenance());
ddsData.reactivationMedic(report.reactivationMedic());
ddsData.modeVest(report.protectionModeVest());
ddsData.modeHelmet(report.protectionModeHelmet());
ddsData.modePersonalProtection(report.personellProtectionMode());
ddsData.protectionLevel(report.protectionLevel());
ddsData.handgrenadeSearchMode(report.handgrenadeSearchMode());
ddsData.internationalMode(report.internationalMode());
ddsData.abcProtectionMode(report.abcProtectionMode());
ddsData.sampleTime(report.sampleTime());
participant->setMode(ddsData);
participant->publishMode();
}
void ParticipantHandler::onNewSpecialSensorData(const TrainingAreaBase::SpecialSensorData &data)
{
if (isTelegramInFuture(data))
{
WARNING_LOG(m_logger) << "Got a SpecialSensorData which has a timestamp in the future: " << data.sampleTime();
return;
}
if (isTelegramOutdated(data))
{
WARNING_LOG(m_logger) << "Got a SpecialSensorData which has a timestamp in the past: " << data.sampleTime();
return;
}
Participant *participant = data.equipmentNumber() < 0 ? participantById(-data.equipmentNumber()) : participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
TrainingAreaBase::EquipmentPosition pos;
pos.equipmentNumber(data.equipmentNumber());
pos.sampleTime(data.sampleTime());
if (data.position().is_set())
{
Core::GeoPosition geoPos(data.position().get().lon(), data.position().get().lat(), 0);
pos.position().position(geoPos);
if (participant->setPosition(pos))
participant->publishPosition();
}
participant->setSpecialSensorData(data);
participant->publishDynamicData();
}
void ParticipantHandler::onNewLaserDesignation(const TrainingAreaBase::LaserDesignation &data)
{
DEBUG_LOG(m_logger) << "New Laser Designation: "
<< "Code: " << data.code() << ", GearID: " << data.gearId() << ", EqNo: " << data.equipmentNumber() << ", Lon: " << data.spotLocation().lon()
<< ", Lat: " << data.spotLocation().lat() << ", Power: " << data.outputPower();
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
DynamicParticipantData dpd = participant->dynamicParticipantData();
DEBUG_LOG(m_logger) << "Begin Update Laser Designation: ";
updateLaserDesignation(dpd, data);
DEBUG_LOG(m_logger) << "End Update Laser Designation: ";
DEBUG_LOG(m_logger) << "Set DPD and publish ";
participant->setDynamicData(dpd);
participant->publishDynamicData();
}
void ParticipantHandler::onLaserDesignationRemoved(int equipmentNumber, int gearId)
{
DEBUG_LOG(m_logger) << "On Remove Laser Designation: ";
Participant *participant = participantByEquipmentNumber(equipmentNumber);
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << equipmentNumber;
return;
}
DynamicParticipantData dpd = participant->dynamicParticipantData();
DEBUG_LOG(m_logger) << "Begin Remove Laser Designation: ";
removeLaserDesignation(dpd, gearId);
DEBUG_LOG(m_logger) << "End Remove Laser Designation: ";
DEBUG_LOG(m_logger) << "Set DPD and publish ";
participant->setDynamicData(dpd);
participant->publishDynamicData();
}
void ParticipantHandler::onBuildingHitLineInfo(const Buildings::BuildingHitLineInfo &data)
{
DEBUG_LOG(m_logger) << "onBuildingHitLineInfo "
<< "BuildingId=" << data.buildingId() << " originatorParticipantId=" << data.originatorParticipantId();
auto dtcPdm = m_bdp->pdmDtc();
// NOTE: igniterTypeId/IgniterFunction = 0 as long as BuildingHitLineInfo does not contain values
if (dtcPdm->getChargeTypeSpecialProperty(data.chargeTypeId(), 0, 0) != ProjectDataManagement::ChargeTypeProperty_def::LTD)
return;
Participant *participant = participantById(data.originatorParticipantId());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with participantId: " << data.originatorParticipantId();
return;
}
DynamicParticipantData dpd = participant->dynamicParticipantData();
DEBUG_LOG(m_logger) << "Begin Update Laser Designation: ";
updateLaserDesignation(dpd, data);
DEBUG_LOG(m_logger) << "End Update Laser Designation: ";
DEBUG_LOG(m_logger) << "setDynamicData";
participant->setDynamicData(dpd);
DEBUG_LOG(m_logger) << "publishDynamicData"
<< " dpd.designations().size()=" << dpd.designations().size();
participant->publishDynamicData();
}
void ParticipantHandler::onSanitaryMaterialStockReport(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data)
{
DEBUG_LOG(m_logger) << "onSanitaryMaterialStockReport";
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
if (data.qualification() == TrainingAreaBase::Qualification::NotDefined)
{
DEBUG_LOG(m_logger) << "!!! Clear Materials : equipment number " << data.equipmentNumber();
DEBUG_LOG(m_logger) << "!!! Clear Materials : sample time " << data.sampleTime();
participant->setSanitationMaterial(0,
std::vector<int>(),
data.uniqueId(),
data.sampleTime(),
Qualification::NotAssigned,
data.originatorEquipmentNumber()); // this is a deassociation so clear material
}
else
{
DEBUG_LOG(m_logger) << "!!! Set Materials : equipment number " << data.equipmentNumber();
DEBUG_LOG(m_logger) << "!!! Set Materials : sample time " << data.sampleTime();
participant->setSanitationMaterial(data.sanitaryMaterialCount(),
data.sanitaryMaterialStock(),
data.uniqueId(),
data.sampleTime(),
static_cast<ParticipantManagement::Qualification_def::type>(data.qualification().underlying()),
data.originatorEquipmentNumber());
}
participant->publishDynamicData();
}
void ParticipantHandler::onParticipantSanitation(const TrainingAreaBase::ParticipantSanitation &data)
{
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
Participant *participantOrig = participantByEquipmentNumber(data.originatorEquipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
int participantOrigId = data.originatorEquipmentNumber();
if (participantOrig)
participantOrigId = participantOrig->id();
if (data.qualification() == TrainingAreaBase::Qualification::NotDefined && data.proceduresTaken() == 0)
{
DEBUG_LOG(m_logger) << "!!! Clear Sanitation = " << data.sampleTime();
DEBUG_LOG(m_logger) << "!!! Clear Sanitation: equipNo = " << data.equipmentNumber();
participant->setSanitationMaterial(0, std::vector<int>(), 0, data.sampleTime(), Qualification::NotAssigned, 0); // this is a deassociation so clear material
}
else
{
DEBUG_LOG(m_logger) << "!!! SetSanitation: sample time = " << data.sampleTime();
DEBUG_LOG(m_logger) << "!!! SetSanitation: equipNo = " << data.equipmentNumber();
participant->setSanitation(data.proceduresTaken(), data.sanitationResult(), data.sanitationEvaluation(), participantOrigId, data.qualification(), data.sampleTime());
}
participant->publishDynamicData();
}
void ParticipantHandler::onOperatorAction(const TrainingAreaBase::OperatorAction &data)
{
// maybe use other commands later?!
if (data.action() != 8 && data.action() != 35)
return;
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
if (participant->setOperatorAction(data.action(), data.operatorId(), data.sampleTime()))
participant->publishDynamicData();
}
void ParticipantHandler::onEquipmentEvent(const TrainingAreaBase::EquipmentEvent &data)
{
// maybe use other commands later?!
if (data.source().subDeviceId() != 14 || data.source().equipmentFunctionId() != 5)
return;
if (data.code() == 1 || data.code() == 2) // 1: 9Liner 2: Transport
{
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
return;
if (participant->setEquipmentEvent(data.code(), data.sampleTime()))
participant->publishDynamicData();
}
}
void ParticipantHandler::onNewFunctionalAreaEntryChanged(const TrainingAreaBase::FunctionalAreaEntry &data)
{
Participant *participant = participantByEquipmentNumber(data.equipmentNumber());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with equipment number " << data.equipmentNumber();
return;
}
TrainingAreaBase::EquipmentPosition pos;
pos.equipmentNumber(data.equipmentNumber());
pos.sampleTime(data.sampleTime());
Core::GeoPosition geoPos(data.position().lon(), data.position().lat(), 0);
pos.position().position(geoPos);
if (participant->setPosition(pos))
participant->publishPosition();
StateInformation oldState = youngestStateInformation(participant->dynamicParticipantData().stateHistory(), false);
TrainingAreaBase::EquipmentState state;
state.equipmentNumber(data.equipmentNumber());
state.originatorId(data.aesItemId());
state.originatorType(ProjectDataManagement::StateChangeOriginator::FunctionalArea);
state.sampleTime(data.sampleTime());
state.vulnerabilityState(ProjectDataManagement::VulnerabilityState::HitWithNoEffect); // no effect - it's an entry into a functional area
state.vulnerabilityBits(oldState.vulnerabilityBits());
state.vulnerabilityKindId(oldState.vulnerabilityKindId());
state.vitality(oldState.vitality());
participant->setState(state, data.aesItemId());
participant->publishDynamicData();
}
inline bool isOutdated(qint64 curTime, qint64 posTime)
{
// maxDisplayDuration = 2min -> use 3min
return curTime > (posTime + 3 * 60 * 1000);
}
void ParticipantHandler::onHitLineInfoTimeout()
{
const qint64 tNow = currentTimeMSec();
auto it = m_hitLineInfoMap.begin();
while (it != m_hitLineInfoMap.end())
{
if (!isOutdated(tNow, it.value().sampleTime()))
break;
dts()->removeHitLineInfoByKey(it.value().id());
it = m_hitLineInfoMap.erase(it);
}
}
void ParticipantHandler::setPosition(const ParticipantManagement::ParticipantPosition &data)
{
Participant *participant = participantById(data.participantId());
if (!participant)
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Could not find participant with id " << data.participantId();
return;
}
participant->setPosition(data, true);
}
void ParticipantHandler::setHitlineInfoFromReplay(const HitLineInfo &data)
{
if (!dts()->publish(data))
return;
m_hitLineInfoMap.insert(data.id(), data);
}
void ParticipantHandler::setUASFromReplay(const UAS &data)
{
dts()->publish(data);
}
bool ParticipantHandler::isSupportTypeClass(const ParticipantManagement::TypeClass &typeClass)
{
return (typeClass == TypeClass::SupportUnit || typeClass == TypeClass::SupportEntity);
}
bool ParticipantHandler::isSampleTimeOutdated(qint64 sampleTime) const
{
const qint64 curTime = currentTimeMSec();
return sampleTime < curTime - 11 * 60 * 1000;
}
bool ParticipantHandler::isSampleTimeInFuture(qint64 sampleTime) const
{
const qint64 curTime = currentTimeMSec();
return sampleTime > curTime + 1 * 60 * 1000;
}
std::shared_ptr<Interface::Dts> ParticipantHandler::dts() const { return m_bdp->dts(); }
qint64 ParticipantHandler::currentTimeMSec() const { return m_bdp->currentTimeMSec(); }
quint16 ParticipantHandler::getWebSocketPort() const { return m_webSocketHandler->listenPort(); }
void ParticipantHandler::updateLaserDesignation(DynamicParticipantData &data, const TrainingAreaBase::LaserDesignation &newLaserDesignation)
{
LOGGER_T m_logger = getLogger("ParticipantHandler.Laser");
DEBUG_LOG(m_logger) << "-Update Laser Designation - Start ";
for (LaserDesignation &laserDesignation : data.designations())
{
if (laserDesignation.gearId() != newLaserDesignation.gearId())
continue;
DEBUG_LOG(m_logger) << "-Update Laser Designation - Gear with id " << laserDesignation.gearId() << " found in designations";
laserDesignation.code(newLaserDesignation.code());
if (!qFuzzyIsNull(laserDesignation.outputPower()) && !qFuzzyIsNull(newLaserDesignation.spotLocation().lat())
&& !qFuzzyIsNull(newLaserDesignation.spotLocation().lon()))
laserDesignation.spotLocation(Core::GeoPosition(newLaserDesignation.spotLocation().lon(), newLaserDesignation.spotLocation().lat(), 0.0));
// If Laser was on before and now is turned off, we have to reset spot Location
if (!qFuzzyIsNull(laserDesignation.outputPower()) && qFuzzyIsNull(newLaserDesignation.outputPower()))
laserDesignation.spotLocation(Core::GeoPosition(0.0, 0.0, 0.0));
laserDesignation.outputPower(newLaserDesignation.outputPower());
DEBUG_LOG(m_logger) << "-Update Laser Designation - Data updated, returning";
return;
}
DEBUG_LOG(m_logger) << "-Update Laser Designation - Gear with id " << newLaserDesignation.gearId() << " NOT found in designations";
int gearId = newLaserDesignation.gearId();
auto it = std::find_if(data.gear().cbegin(), data.gear().cend(), [gearId](const GearInformation &gi) { return gearId == gi.gearId(); });
if (it == data.gear().cend()) // No gear, no designation...
{
DEBUG_LOG(m_logger) << "-Update Laser Designation - Gear with id " << newLaserDesignation.gearId() << " NOT found in Gears";
return;
}
DEBUG_LOG(m_logger) << "-Update Laser Designation - Gear with id " << newLaserDesignation.gearId() << " found in Gears";
DEBUG_LOG(m_logger) << "-Update Laser Designation - Adding new designation";
data.designations().emplace_back(newLaserDesignation.gearId(),
Core::GeoPosition(newLaserDesignation.spotLocation().lon(), newLaserDesignation.spotLocation().lat(), 0.0),
newLaserDesignation.code(),
newLaserDesignation.outputPower());
}
void ParticipantHandler::updateLaserDesignation(DynamicParticipantData &dataInOut, const Buildings::BuildingHitLineInfo &newLaserDesignation)
{
LOGGER_T m_logger = getLogger("ParticipantHandler.Laser");
// Todo: Die erste vorhandene LaserDesignation wird angepasst. Ein Laser On muss vorher gekommen sein, damit das Element da ist.
// Mehrere Designatoren pro Teilnehmer werden nicht unterstützt.
DEBUG_LOG(m_logger) << "-Update Laser Designation by Building Hitline Info";
for (LaserDesignation &laserDesignation : dataInOut.designations())
{
DEBUG_LOG(m_logger) << "-Update Laser Designation by Building Hitline Info - Updating (first) Designation with GearId " << laserDesignation.gearId();
laserDesignation.outputPower(1.0);
laserDesignation.spotLocation(newLaserDesignation.buildingPos());
return;
}
QSet<int> ldGearTypes{1043}; // Need Property for LTD...
auto it = std::find_if(dataInOut.gear().cbegin(), dataInOut.gear().cend(), [ldGearTypes](const GearInformation &gi) { return ldGearTypes.contains(gi.gearTypeId()); });
if (it != dataInOut.gear().cend())
{
LaserDesignation ld;
ld.gearId() = it->gearId();
ld.code() = 0;
ld.spotLocation() = newLaserDesignation.buildingPos();
ld.outputPower(1.0);
dataInOut.designations().emplace_back(ld);
ERROR_LOG(m_logger) << "-Update Laser Designation by Building Hitline Info - Added Designation";
}
ERROR_LOG(m_logger) << "-Update Laser Designation by Building Hitline Info - No laserDesignation available participantId="
<< newLaserDesignation.originatorParticipantId();
}
void ParticipantHandler::removeLaserDesignation(DynamicParticipantData &data, qint32 gearId)
{
LOGGER_T m_logger = getLogger("ParticipantHandler.Laser");
DEBUG_LOG(m_logger) << "-Remove Laser Designation: Start";
auto validationFunction = [gearId](ParticipantManagement::LaserDesignation laserDesignation) { return laserDesignation.gearId() == gearId; };
auto eraseIterator = std::find_if(data.designations().begin(), data.designations().end(), validationFunction);
if (eraseIterator == data.designations().end())
{
DEBUG_LOG(m_logger) << "-Remove Laser Designation: GearId NOT found";
return;
}
DEBUG_LOG(m_logger) << "-Remove Laser Designation: Removing Designation";
data.designations().erase(eraseIterator);
}
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,176 @@
/**
* 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
*/
#pragma once
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <Proxies/TrainingAreaBase/tab_dtc.h>
#include <ParticipantManagement/ppm_db_structs.h>
#include <ParticipantManagement/ppm_dts.h>
#include <ParticipantManagement/web_socket_handler.h>
#include "logger.h"
#include "participant_donator.h"
namespace Buildings
{
class BuildingHitLineInfo;
}
namespace ProjectDataManagement
{
namespace Interface
{
class Dtc;
}
} // namespace ProjectDataManagement
namespace ParticipantManagement
{
class MainHandler;
namespace ParticipantHandling
{
class Participant;
class ExerciseParticipant;
class SupportParticipant;
class UnitPositionCalculator;
class BaseDataProvider
{
public:
BaseDataProvider() = default;
virtual ~BaseDataProvider() = default;
virtual std::shared_ptr<Interface::Dts> dts() const = 0;
virtual std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const = 0;
virtual qint64 currentTimeMSec() const = 0;
};
struct ParticipantOrganisationEx : public ParticipantOrganisation
{
int dbId = 0;
void copy(const ParticipantOrganisation &o)
{
ParticipantOrganisation *that = this;
*that = o;
}
};
class ParticipantHandler
: public QObject
, public ParticipantDonator
{
Q_OBJECT
public:
explicit ParticipantHandler(BaseDataProvider *bdp, const char *loggerName);
~ParticipantHandler();
void clearParticipants();
virtual bool init();
virtual void setEquipmentNumberForParticipant(int oldEqNo, int newEqNo, int participantId);
Participant *participantById(int32_t participantId) const override;
Participant *participantByEquipmentNumber(int32_t equipmentNumber);
// only needed in Exercise, correctly reimplemented in ParticipantHandlerForExercise
bool isUnitType(int typeId) const override
{
Q_UNUSED(typeId);
return false;
}
Q_SIGNALS:
void gearChargeChanged(int participantId, int partyId);
public Q_SLOTS:
void onNewGearEvent(const TrainingAreaBase::EquipmentGearEvent &gearEvent);
void onNewGearChargeStateFromProxy(const TrainingAreaBase::EquipmentGearChargeState &chargeState);
void onNewGearAssignmentFromProxy(const TrainingAreaBase::EquipmentGearAssignment &gearAssignment);
void onNewStateFromProxy(const TrainingAreaBase::EquipmentState &data);
void onNewPositionFromProxy(const TrainingAreaBase::EquipmentPosition &position);
void onNewEquipmentOperationalStateFromProxy(const TrainingAreaBase::EquipmentOperationalState &operationalState);
void onNewBiteFromProxy(const TrainingAreaBase::EquipmentBite &bite);
void onNewCtaDataErrorFromProxy(const TrainingAreaBase::CtaDataError &error);
void onNewParticipantModeReport(const TrainingAreaBase::ParticipantModeReport &report);
void onNewSpecialSensorData(const TrainingAreaBase::SpecialSensorData &data);
void onNewFunctionalAreaEntryChanged(const TrainingAreaBase::FunctionalAreaEntry &data);
void onNewLaserDesignation(const TrainingAreaBase::LaserDesignation &data);
void onLaserDesignationRemoved(int equipmentNumber, int gearId);
void onSanitaryMaterialStockReport(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data);
void onParticipantSanitation(const TrainingAreaBase::ParticipantSanitation &data);
void onOperatorAction(const TrainingAreaBase::OperatorAction &data);
void onEquipmentEvent(const TrainingAreaBase::EquipmentEvent &data);
void onBuildingHitLineInfo(const Buildings::BuildingHitLineInfo &data);
void onHitLineInfoTimeout();
public:
void newData(const TrainingAreaBase::EquipmentGearEvent &data) { onNewGearEvent(data); }
void newData(const TrainingAreaBase::EquipmentGearChargeState &data) { onNewGearChargeStateFromProxy(data); }
void newData(const TrainingAreaBase::EquipmentGearAssignment &data) { onNewGearAssignmentFromProxy(data); }
void newData(const TrainingAreaBase::EquipmentState &data) { onNewStateFromProxy(data); }
void newData(const TrainingAreaBase::EquipmentPosition &data) { onNewPositionFromProxy(data); }
void newData(const TrainingAreaBase::EquipmentOperationalState &data) { onNewEquipmentOperationalStateFromProxy(data); }
void newData(const TrainingAreaBase::EquipmentBite &data) { onNewBiteFromProxy(data); }
void newData(const TrainingAreaBase::CtaDataError &data) { onNewCtaDataErrorFromProxy(data); }
void newData(const TrainingAreaBase::ParticipantModeReport &data) { onNewParticipantModeReport(data); }
void newData(const TrainingAreaBase::SpecialSensorData &data) { onNewSpecialSensorData(data); }
void newData(const TrainingAreaBase::ParticipantSanitation &data) { onParticipantSanitation(data); }
void newData(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data) { onSanitaryMaterialStockReport(data); }
void newData(const TrainingAreaBase::EquipmentEvent &data) { onEquipmentEvent(data); }
void newData(const ParticipantPosition &data) { setPosition(data); }
void newData(const HitLineInfo &data) { setHitlineInfoFromReplay(data); }
void newData(const UAS &data) { setUASFromReplay(data); }
std::shared_ptr<Interface::Dts> dts() const;
qint64 currentTimeMSec() const;
quint16 getWebSocketPort() const;
protected:
static void updateLaserDesignation(DynamicParticipantData &data, const TrainingAreaBase::LaserDesignation &newLaserDesignation);
void updateLaserDesignation(DynamicParticipantData &dataInOut, const Buildings::BuildingHitLineInfo &newLaserDesignation);
static void removeLaserDesignation(DynamicParticipantData &data, qint32 gearId);
void setPosition(const ParticipantPosition &data);
void setHitlineInfoFromReplay(const HitLineInfo &data);
void setUASFromReplay(const UAS &data);
virtual void createHitlineInfo(int originatorId,
Participant *originParticipant,
Participant *participantToBeChanged,
const TrainingAreaBase::EquipmentPosition &pos,
int originatorChargeTypeId,
qint64 sampleTime);
static bool isSupportTypeClass(const TypeClass &typeClass);
// t - 11min <= sampleTime
template <typename T> inline bool isTelegramOutdated(const T &tele) const { return isSampleTimeOutdated(tele.sampleTime()); }
virtual bool isSampleTimeOutdated(qint64 sampleTime) const;
// sampleTime > t + 1min
template <typename T> inline bool isTelegramInFuture(const T &tele) const { return isSampleTimeInFuture(tele.sampleTime()); }
virtual bool isSampleTimeInFuture(qint64 sampleTime) const;
protected:
std::unique_ptr<UnitPositionCalculator> m_unitPositionCalculator;
QHash<int, int> m_equipNumber2ParticipantId;
QHash<int, Participant *> m_participants;
QHash<int, ParticipantOrganisationEx> m_participantOrganisations;
int m_hitLineId;
QMap<int, HitLineInfo> m_hitLineInfoMap;
BaseDataProvider *m_bdp;
LOGGER_T m_logger;
std::shared_ptr<WebSocketHandler> m_webSocketHandler;
friend class Participant;
};
} // namespace ParticipantHandling
} // namespace ParticipantManagement
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,199 @@
/**
* 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
*/
#pragma once
#include "participant_handler.h"
#include <QtCore/QElapsedTimer>
#include <QtCore/QHash>
#include <QtCore/QMutex>
#include <QtCore/QString>
#include <QJsonDocument>
#include <ExerciseManagement/exm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_fts.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <TrainingAreaBase/tab_dts.h>
class QTimer;
class QTransform;
namespace ParticipantManagement
{
class MainHandler;
namespace ParticipantHandling
{
class FiringUnitValidator;
class ProxyListener;
class ParticipantHandlerForExercise final
: public ParticipantHandler
, protected Interface::Fts::ModifyStateRequestHandler
, protected Interface::Fts::ModifyParticipantDataRequestHandler
, protected Interface::Fts::InitParticipantRequestHandler
, protected Interface::Fts::SetParticipantModeRequestHandler
, protected Interface::Fts::SendMessageRequestHandler
, protected Interface::Fts::RegisterParticipantRequestHandler
, protected Interface::Fts::SetSimulatedPositionRequestHandler
, protected Interface::Fts::SetSimulatedPositionRelativeRequestHandler
, protected Interface::Fts::SetSimulatedParticipantFormationRequestHandler
, protected Interface::Fts::SetSanitationRequestHandler
, protected Interface::Fts::SelectiveReammunitionRequestHandler
, protected Interface::Fts::SetAsSimulatedRequestHandler
, protected Interface::Fts::OcCommandRequestHandler
, protected Interface::Fts::ModifyUasStateRequestHandler
{
Q_OBJECT
public:
explicit ParticipantHandlerForExercise(MainHandler *mainHandler);
~ParticipantHandlerForExercise();
bool init() override;
void setEquipmentNumberForParticipant(int oldEqNo, int newEqNo, int participantId) override;
bool isUnitType(int typeId) const override;
public Q_SLOTS:
void onGearChargeChanged(int participantId, int partyId);
protected:
struct ParticipantFormationStruct
{
QString geometry = QStringLiteral("line");
double distance = -1;
double angle = -1;
QString jsonData;
QJsonObject distanceSetsObj;
QVariantMap distanceSets;
ParticipantFormationStruct() {}
explicit ParticipantFormationStruct(const QString &formationProperty) : jsonData(formationProperty)
{
const QJsonObject formationObj = QJsonDocument::fromJson(QByteArray::fromStdString(jsonData.toStdString())).object();
if (formationObj.isEmpty())
return;
geometry = formationObj[QStringLiteral("geometry")].toString(QStringLiteral("line"));
angle = formationObj[QStringLiteral("angle")].toDouble(-1);
distance = formationObj[QStringLiteral("distance")].toDouble(-1);
distanceSetsObj = formationObj[QStringLiteral("distanceSets")].toObject();
distanceSets = distanceSetsObj.toVariantMap();
}
public:
bool hasFormationData() const { return ((angle != -1) && (distance != -1)); }
};
void getFormationParameters(const QString &jsdata, QString &geometry, double &distance, double &angle);
void setFormationAsLine(Participant *participant, const SetSimulatedParticipantFormation &request, const double distance, const double angleDeg);
void createHitlineInfo(int originatorId,
Participant *originParticipant,
Participant *participantToBeChanged,
const TrainingAreaBase::EquipmentPosition &pos,
int originatorChargeTypeId,
qint64 sampleTime) override;
void onModifyParticipantDataRequest(const rti::core::SampleIdentity &cookie, const ModifyParticipantDataRequest &request) override;
Core::FunctionReplyObject modifyParticipantDataRequest(const ModifyParticipantDataRequest &request);
// ModifyState
void onModifyStateRequest(const rti::core::SampleIdentity &cookie, const ModifyStateRequest &request) override;
Core::FunctionReplyObject modifyStateRequest(const rti::core::SampleIdentity &cookie, const ModifyStateRequest &request);
// Init
void onInitParticipantRequest(const rti::core::SampleIdentity &cookie, const InitParticipantRequest &request) override;
Core::FunctionReplyObject initParticipantRequest(const rti::core::SampleIdentity &cookie, const InitParticipantRequest &request);
// Mode
void onSetParticipantModeRequest(const rti::core::SampleIdentity &cookie, const SetParticipantModeRequest &request) override;
Core::FunctionReplyObject setParticipantModeRequest(const rti::core::SampleIdentity &cookie, const SetParticipantModeRequest &request);
void onSendMessageRequest(const rti::core::SampleIdentity &cookie, const SendMessageRequest &request) override;
Core::FunctionReplyObject sendMessageRequest(const rti::core::SampleIdentity &cookie, const SendMessageRequest &request);
void onRegisterParticipantRequest(const rti::core::SampleIdentity &cookie, const RegisterParticipantRequest &request) override;
Core::FunctionReplyObject registerParticipantRequest(const RegisterParticipantRequest &request_, RegisterReturnValue &retVal);
void onSetSimulatedPositionRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedPosition &request) override;
Core::FunctionReplyObject setSimulatedPositionRequest(const SetSimulatedPosition &request);
void setSimulatedPosition(Participant *participant, const SetSimulatedPosition &request);
void onSetSimulatedPositionRelativeRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedPositionRelative &request) override;
Core::FunctionReplyObject setSimulatedPositionRelativeRequest(const SetSimulatedPositionRelative &request);
void setSimulatedPositionRelative(Participant *participant, const SetSimulatedPositionRelative &request);
void onSetSimulatedParticipantFormationRequest(const rti::core::SampleIdentity &cookie, const SetSimulatedParticipantFormation &request) override;
Core::FunctionReplyObject setSimulatedParticipantFormationRequest(const SetSimulatedParticipantFormation &request);
void onSetSanitationRequest(const rti::core::SampleIdentity &cookie, const SetSanitationRequest &request) override;
Core::FunctionReplyObject setSanitationRequest(const rti::core::SampleIdentity &cookie, const SetSanitationRequest &request);
void onSelectiveReammunitionRequest(const rti::core::SampleIdentity &cookie, const SelectiveReammunitionRequest &request) override;
Core::FunctionReplyObject selectiveReammunitionRequest(const rti::core::SampleIdentity &cookie, const SelectiveReammunitionRequest &request);
void onSetAsSimulatedRequest(const rti::core::SampleIdentity &cookie, const SetAsSimulated &request) override;
Core::FunctionReplyObject setAsSimulatedRequest(const SetAsSimulated &request);
// OcCommand
void onOcCommandRequest(const rti::core::SampleIdentity &cookie, const OcCommandRequest &request) override;
Core::FunctionReplyObject ocCommandRequest(const rti::core::SampleIdentity &cookie, const OcCommandRequest &request);
void updateParticipantFormation(Participant *participant, const Core::GeoPosition &position, const QPointF &offset);
void setAsSimulated(const SetAsSimulated &request);
void onModifyUasStateRequest(const rti::core::SampleIdentity &cookie, const ModifyUasStateRequest &request) override final;
private: // methods
struct ValidateParticipantData // is needed as QtConcurrent can only handle functions with up to 5 parameter ...
{
QVector<ParticipantData> participantsToBeCreatedOrModified;
QHash<int, int> modIdToParticipantId; // <modId, participantId>
QVector<int> participantsToResetEquipmentNumber; // when move from template to pool, we have to reset the equipment number if it already exists
bool forceAddWithId;
};
Core::FunctionReplyObject createNewParticipants(std::vector<Database::PpmParticipant> &dbParticipantsToBeCreated,
const std::vector<int> &dbParticipantsToBeCreatedIndexes,
const QVector<ParticipantData> &participantsToBeCreatedOrModified,
QHash<int, int> &modIdToParticipantId,
QSet<Participant *> &participantsWithChanges);
Core::FunctionReplyObject validateParticipantData(ValidateParticipantData &data, const ParticipantManagement::ParticipantData &participantToValidate);
std::vector<int32_t> inferiorIdsForParticipant(int32_t superiorParticipantId) const;
Core::FunctionReplyObject checkDeleteParticipant(int id);
Core::FunctionReplyObject deleteParticipant(int32_t id, bool permanentRemove, bool isTriggeredFromFtRequest = false);
const Type *typeById(int typeId) const;
bool isSupportType(int typeId) const;
bool isParticipantType(int typeId) const;
void startLongAction();
void checkLongActionBreak();
bool participantIds2EquipmentNumbers(const std::vector<int32_t> &participantIds, std::vector<int32_t> &equipmentNumbers, int *failedParticipantId) const;
void onEquipmentExerciseMappingChanged(const EquipmentExerciseMapping &data, DataModification mod);
void onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification mod);
void publishEquipmentExerciseMapping();
bool isCurrentExerciseActive() const;
QString lookUpHierachy(int participantId) const;
QString participantInfosFromId(int participantId) const;
void customedSetGearCharges(Participant *participant);
Core::FunctionReplyObject modifyUasStateRequest(const rti::core::SampleIdentity &cookie, const ParticipantManagement::ModifyUasStateRequest &request);
private: // attributes
MainHandler *m_mainHandler;
FiringUnitValidator *m_firingUnitValidator{nullptr};
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
std::shared_ptr<TrainingAreaBase::Interface::Dts> m_dtsTab;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::unique_ptr<ProxyListener> m_proxyListener;
QElapsedTimer m_longActionTimer;
QTimer *m_hitLineInfoTimer;
QTimer *m_publishEquipmentExerciseMappingsTimer;
QHash<int, Database::EquipmentExerciseMapping> m_equipmentExerciseMappings; // QHash<eqId, Database::EquipmentExerciseMapping>
bool m_publishEquipmentExerciseMappings;
}; // class ParticipantHandler
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,589 @@
/**
* The ProxyListener class subscribes to participant related proxy data topics and distributes
* the collected data via Q_SIGNALS.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-13
*/
#include "proxy_listener.h"
#include "main_handler.h"
#include <UasHandling/mission_plan_handler.h>
namespace ParticipantManagement
{
namespace ParticipantHandling
{
ProxyListener::ProxyListener(MainHandler *mainHandler)
: QObject()
, m_mainHandler(mainHandler)
, m_logger(getLogger("ProxyListener"))
{}
bool ProxyListener::init()
{
m_tabDtc = TrainingAreaBase::Interface::Dtc::create(m_mainHandler->dds());
m_bldDtc = Buildings::Interface::Dtc::create(m_mainHandler->dds());
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentBiteChanged,
this, &ProxyListener::onProxyEquipmentBiteChanged);
if (!m_tabDtc->enableEquipmentBite())
{
ERROR_LOG(m_logger) << "Could not initialize TraininAreaBase data topic client";
return false;
}
if (m_mainHandler->exerciseId() > 0)
{
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentPositionChanged,
this, &ProxyListener::onProxyEquipmentPositionChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentGearAssignmentChanged,
this, &ProxyListener::onProxyEquipmentGearAssignmentChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentGearEventChanged,
this, &ProxyListener::onProxyEquipmentGearEventChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::participantSanitaryMaterialStockReportChanged,
this, &ProxyListener::onProxyParticipantSanitaryMaterialStockReportChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentGearChargeStateChanged,
this, &ProxyListener::onProxyEquipmentGearChargeStateChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentStateChanged,
this, &ProxyListener::onProxyEquipmentStateChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentOperationalStateChanged,
this, &ProxyListener::onProxyEquipmentOperationalStateChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::ctaDataErrorChanged,
this, &ProxyListener::onProxyCtaDataErrorChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::participantModeReportChanged,
this, &ProxyListener::onProxyParticipantModeReportChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::specialSensorDataChanged,
this, &ProxyListener::onSpecialSensorDataChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::functionalAreaEntryChanged,
this, &ProxyListener::onFunctionalAreaEntryChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::laserDesignationChanged,
this, &ProxyListener::onLaserDesignationChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::participantSanitationChanged,
this, &ProxyListener::onProxyParticipantSanitationChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::operatorActionChanged,
this, &ProxyListener::onOperatorActionChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::equipmentEventChanged,
this, &ProxyListener::onEquipmentEventChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::uASStatusChanged,
this, &ProxyListener::onUASStatusChanged);
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::uASMissionStatusChanged,
this, &ProxyListener::onUASMissionStatusChanged);
connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingHitLineInfoChanged,
this, &ProxyListener::onBuildingHitLineInfoChanged);
if (!m_tabDtc->enableEquipmentPosition() ||
!m_tabDtc->enableEquipmentGearAssignment() ||
!m_tabDtc->enableEquipmentEvent() ||
!m_tabDtc->enableEquipmentGearEvent() ||
!m_tabDtc->enableEquipmentGearChargeState() ||
!m_tabDtc->enableEquipmentState() ||
!m_tabDtc->enableEquipmentOperationalState() ||
!m_tabDtc->enableCtaDataError() ||
!m_tabDtc->enableParticipantModeReport() ||
!m_tabDtc->enableFunctionalAreaEntry() ||
!m_tabDtc->enableLaserDesignation() ||
!m_tabDtc->enableSpecialSensorData() ||
!m_tabDtc->enableParticipantSanitaryMaterialStockReport() ||
!m_tabDtc->enableOperatorAction() ||
!m_tabDtc->enableParticipantSanitation() ||
!m_tabDtc->enableUASStatus() ||
!m_tabDtc->enableUASMissionStatus() ||
!m_bldDtc->enableBuildingHitLineInfo())
{
ERROR_LOG(m_logger) << "Could not initialize TraininAreaBase data topic client";
return false;
}
}
return true;
}
void ProxyListener::onProxyEquipmentPositionChanged(const TrainingAreaBase::EquipmentPosition &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new position data from proxy " << data;
emit newPosition(data);
}
void ProxyListener::onProxyEquipmentGearAssignmentChanged(const TrainingAreaBase::EquipmentGearAssignment &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new gear assignment from proxy " << data;
emit newGearAssignment(data);
}
void ProxyListener::onProxyEquipmentGearEventChanged(const TrainingAreaBase::EquipmentGearEvent &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new gear event " << data;
emit newGearEvent(data);
}
void ProxyListener::onProxyEquipmentGearChargeStateChanged(const TrainingAreaBase::EquipmentGearChargeState &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new gear charge state " << data;
emit newGearChargeState(data);
}
void ProxyListener::onProxyParticipantSanitaryMaterialStockReportChanged(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "FUCKING FUCK FUCK" << data;
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new material stock report" << data;
emit newSanitaryMaterialStockReport(data);
}
void ProxyListener::onProxyEquipmentStateChanged(const TrainingAreaBase::EquipmentState &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new equipment state " << data;
emit newState(data);
}
void ProxyListener::onProxyEquipmentOperationalStateChanged(const TrainingAreaBase::EquipmentOperationalState &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new equipment operational state " << data;
emit newEquipmentOperationalState(data);
}
void ProxyListener::onProxyEquipmentBiteChanged(const TrainingAreaBase::EquipmentBite &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new equipment bite " << data;
emit newEquipmentBite(data);
}
void ProxyListener::onProxyCtaDataErrorChanged(const TrainingAreaBase::CtaDataError &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Combat Training Area Data error: " << data;
emit newCtaDataError(data);
}
void ProxyListener::onSpecialSensorDataChanged(const TrainingAreaBase::SpecialSensorData &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Special Sensor data: " << data;
emit newSpecialSensorData(data);
}
void ProxyListener::onFunctionalAreaEntryChanged(const TrainingAreaBase::FunctionalAreaEntry &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got functional area entry: " << data;
emit newFunctionalAreaEntryChanged(data);
}
void ProxyListener::onLaserDesignationChanged(const TrainingAreaBase::LaserDesignation &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
{
DEBUG_LOG(m_logger) << "Got no laser designation data: " << data;
emit laserDesignationRemoved(data.equipmentNumber(), data.gearId());
return;
}
DEBUG_LOG(m_logger) << "Got laser designation data: " << data;
emit newLaserDesignationData(data);
}
void ProxyListener::onProxyParticipantSanitationChanged(const TrainingAreaBase::ParticipantSanitation &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Participant Sanitation" << data;
emit newParticipantSanitation(data);
}
void ProxyListener::onOperatorActionChanged(const TrainingAreaBase::OperatorAction &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Operator Action" << data;
emit newOperatorAction(data);
}
void ProxyListener::onEquipmentEventChanged(const TrainingAreaBase::EquipmentEvent &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Operator Action" << data;
emit newEquipmentEvent(data);
}
void ProxyListener::onUASStatusChanged(const TrainingAreaBase::UASStatus &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
m_mainHandler->ddsHandler()->dtc()->waitForHistoricalDataParticipant(3000);
int participantId = m_mainHandler->ddsHandler()->dtc()->participantIdByEquipmentNumber(data.equipmentNumber());
if (participantId <= 0)
return;
const auto uas = m_mainHandler->ddsHandler()->dtc()->getUasByParticipantId(participantId, m_mainHandler->exerciseId());
if (uas.id() <= 0)
return;
//new PPM Telegram
UASRadioLinkConnectionStatus newState;
newState.id(uas.id());
newState.uasState(data.uasState());
DEBUG_LOG(m_logger) << "Publish new UAS State " << newState;
m_mainHandler->ddsHandler()->exAnyDts()->publish(newState);
}
void ProxyListener::onUASMissionStatusChanged(const TrainingAreaBase::UASMissionStatus &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "currentTask = " << (int)data.currentTask().underlying() << ", currentMissionName = " << data.currentMissionName() << ", name of ended mission: " << data.missionEnded();
/*
was wenn random mehrfach hintereinander loiter gesendet wird??
dann überspringt er eine action! der tab darf also nur senden wenn wirklich ein state change kam
- aber wie stellt man dann sicher dass der handler den letzen state kennt?
z.B. würde ein IA restart ein neues loiter senden und somit würde eine action übersprungen werden
*/
if (modType == DataModification::eRemoved)
{
DEBUG_LOG(m_logger) << "removed - stop";
return;
}
if (!data.currentMissionName().empty() || data.currentTask().underlying() != ProjectDataManagement::UasActionTask::Loiter || data.missionEnded().empty())
{
//DEBUG As this will happen a lot
DEBUG_LOG(m_logger) << "not fulfilling current mission ("<<data.currentMissionName()<<") action because either a mission is still active or current task ("<<(int)data.currentTask().underlying()<<") is not Loiter or mission not ended ("<< data.missionEnded() << (data.missionEnded().empty() ? " [NOT ENDED]" : " [ ENDED ]")<<")";
return;
}
m_mainHandler->ddsHandler()->dtc()->waitForHistoricalDataParticipant(3000);
const int participantId = m_mainHandler->ddsHandler()->dtc()->participantIdByEquipmentNumber(data.equipmentNumber());
if (participantId <= 0)
{
ERROR_LOG(m_logger) << "participant for eqid " << data.equipmentNumber() << " not found - unable to fulfill current mission action";
return;
}
QTimer::singleShot(1000, [this, participantId, data]() // give tab plugin time to clean stuff up before sending new missions (by marking an action as fulfilled)
{
// mission empty & task = loiter? mission done, run next mission (by setting current mission's current action as fulfilled, which will trigger the next action automatically (see ces))
if (!m_mainHandler->missionPlanHandler()->setCurrentMissionActionFulfilled(participantId, QString::fromStdString(data.missionEnded())))
{
ERROR_LOG(m_logger) << "setCurrentMissionActionFulfilled didn't work out";
return;
}
DEBUG_LOG(m_logger) << "setCurrentMissionActionFulfilled done !";
});
}
void ProxyListener::onBuildingHitLineInfoChanged(const Buildings::BuildingHitLineInfo &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Building Hitline Info" << data;
emit newBuildingHitLineInfo(data);
}
void ProxyListener::onProxyParticipantModeReportChanged(const TrainingAreaBase::ParticipantModeReport &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
DEBUG_LOG(m_logger) << "Got new Participant Mode Report Training Area Data error " << data;
emit newParticipantModeReport(data);
}
// VulnerabilityStateChange
Core::FunctionReplyObject ProxyListener::demandVulnerabilityStateChange(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::DemandVulnerabilityStateChangeRequest &req)
{
if (!m_mainHandler->tabFtc()->demandVulnerabilityStateChangeRequest(req, this))
return Core::FunctionReplyObject(tr_lts("dds", "Error passing the Vulnerability state change request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "VulnerabilityStateChange timeout for " << requestId;
onDemandVulnerabilityStateChangeReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject();
}
void ProxyListener::onDemandVulnerabilityStateChangeReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->modifyStateReply(cookie, replyObject);
};
handleReply(cb, replyObject, "VulnerabilityStateChange");
}
Core::FunctionReplyObject ProxyListener::sendModifyUasStateRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendModifyUasStateRequest &req)
{
DEBUG_LOG(m_logger) << " -- [ProxyListener] sendModifyUasStateRequest uasId: " << req.uasId();
if (!m_mainHandler->tabFtc()->sendModifyUasStateRequest(req, this))
return Core::FunctionReplyObject(tr_lts("dds", "Error passing the UAS state change request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "ModifyUasState timeout for " << requestId;
onSendModifyUasStateReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject();
}
void ProxyListener::onSendModifyUasStateReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->modifyUasStateReply(cookie, replyObject);
};
handleReply(cb, replyObject, "ModifyUasState");
}
// Init
Core::FunctionReplyObject ProxyListener::demandInitRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::DemandInitRequest &req)
{
if (!m_mainHandler->tabFtc()->demandInitRequest(req, this))
return Core::FunctionReplyObject(tr_lts("dds", "Error passing the INIT request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "DemandInit timeout for " << requestId;
onDemandInitReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject();
}
void ProxyListener::onDemandInitReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->initParticipantReply(cookie, replyObject);
};
handleReply(cb, replyObject, "DemandInit");
}
// SendEquipmentMessage
Core::FunctionReplyObject ProxyListener::sendEquipmentMessage(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendEquipmentMessageRequest &req)
{
if (!m_mainHandler->tabFtc()->sendEquipmentMessageRequest(req, this))
return Core::FunctionReplyObject(tr_lts("dds", "Error passing SendMessage request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "SendMessage timeout for " << requestId;
onSendEquipmentMessageReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject();
}
void ProxyListener::onSendEquipmentMessageReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->sendMessageReply(cookie, replyObject);
};
handleReply(cb, replyObject, "SendParticipantMessage");
}
// SendParticipantMode
Core::FunctionReplyObject ProxyListener::sendParticipantModeRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendParticipantModeRequest &req)
{
if (!m_mainHandler->tabFtc()->sendParticipantModeRequest(req, this))
return Core::FunctionReplyObject(req.participantId(), tr_lts("dds", "Error passing the INIT request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "SendParticipantMode timeout for " << requestId;
onSendParticipantModeReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject(req.participantId());
}
void ProxyListener::onSendParticipantModeReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->setParticipantModeReply(cookie, replyObject);
};
handleReply(cb, replyObject, "SendParticipantMode");
}
Core::FunctionReplyObject ProxyListener::sendSetSanitationRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendSetSanitationRequest &req)
{
if (!m_mainHandler->tabFtc()->sendSetSanitationRequest(req, this))
return Core::FunctionReplyObject(req.participantId(), tr_lts("dds", "Error passing the SetSanitation request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "sendSetSanitationRequest timeout for " << requestId;
onSendSetSanitationReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject(req.participantId());
}
void ProxyListener::onSendSetSanitationReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->setSanitationReply(cookie, replyObject);
};
handleReply(cb, replyObject, "SetSanitationReply");
}
Core::FunctionReplyObject ProxyListener::selectiveReammunitionRequest(const rti::core::SampleIdentity &cookie, const TrainingAreaBase::SendSelectiveReammunitionRequest &req)
{
if (!m_mainHandler->tabFtc()->sendSelectiveReammunitionRequest(req, this))
return Core::FunctionReplyObject(req.participantId(), tr_lts("dds", "Error passing the SendSelectiveReammunition request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "selectiveReammunitionRequest timeout for " << requestId;
onSendSelectiveReammunitionReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject(req.participantId());
}
void ProxyListener::onSendSelectiveReammunitionReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->selectiveReammunitionReply(cookie, replyObject);
};
handleReply(cb, replyObject, "SelectiveReammunitionReply");
}
// OcCommand
Core::FunctionReplyObject ProxyListener::sendOcCommandRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendOcCommandRequest &req)
{
if (!m_mainHandler->tabFtc()->sendOcCommandRequest(req, this))
return Core::FunctionReplyObject(tr_lts("dds", "Error passing the OcCommand request to the proxy"));
const int requestId = req.requestId();
auto cb = [this, requestId]()
{
INFO_LOG(m_logger) << "SendOcCommand timeout for " << requestId;
onSendOcCommandReply(Core::FunctionReplyObject(requestId, tr_lts("dds", "Timeout while waiting for the proxy")));
};
addToTrainingAreaRequests(cb, req.requestId(), cookie);
return Core::FunctionReplyObject();
}
void ProxyListener::onSendOcCommandReply(const Core::FunctionReplyObject &replyObject)
{
auto cb = [this](const rti::core::SampleIdentity & cookie, const Core::FunctionReplyObject & replyObject)
{
m_mainHandler->fts()->ocCommandReply(cookie, replyObject);
};
handleReply(cb, replyObject, "SendOcCommand");
}
// Helper
void ProxyListener::addToTrainingAreaRequests(const std::function<void()> &cb,
int requestId,
const rti::core::SampleIdentity &cookie)
{
QTimer *timeoutTimer = new QTimer(this);
timeoutTimer->setSingleShot(true);
timeoutTimer->setInterval(5000);
m_trainingAreaRequests.insert(requestId, {cookie, timeoutTimer});
connect(timeoutTimer, &QTimer::timeout, this, cb);
}
void ProxyListener::handleReply(const std::function<void(const rti::core::SampleIdentity &, const Core::FunctionReplyObject &)> &cb,
const Core::FunctionReplyObject &replyObject,
const char *requestType)
{
if (replyObject.error())
WARNING_LOG(m_logger) << requestType << ": " << replyObject();
auto it = m_trainingAreaRequests.find(replyObject.id());
if (it != m_trainingAreaRequests.end())
{
const auto &dp = it.value();
if (dp.timer)
dp.timer->deleteLater();
cb(dp.cookie, replyObject);
m_trainingAreaRequests.erase(it);
}
else
WARNING_LOG(m_logger) << requestType << " with request id " << replyObject.id() << " was not replied to by the proxy";
}
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,136 @@
/**
* The ProxyListener class subscribes to participant related proxy data topics and distributes
* the collected data via Q_SIGNALS.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-13
*/
#pragma once
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QTimer>
#include <memory>
#include "logger.h"
#include <TrainingAreaBase/tab_dtc.h>
#include <TrainingAreaBase/tab_ftc.h>
#include <Buildings/bld_dtc.h>
namespace ParticipantManagement
{
class MainHandler;
namespace ParticipantHandling
{
class ProxyListener final
: public QObject
, public TrainingAreaBase::Interface::Ftc::DemandVulnerabilityStateChangeReplyHandler
, public TrainingAreaBase::Interface::Ftc::DemandInitReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendEquipmentMessageReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendParticipantModeReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendSetSanitationReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendSelectiveReammunitionReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendOcCommandReplyHandler
, public TrainingAreaBase::Interface::Ftc::SendModifyUasStateReplyHandler
{
Q_OBJECT
public:
explicit ProxyListener(MainHandler *mainHandler);
bool init();
Core::FunctionReplyObject demandVulnerabilityStateChange(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::DemandVulnerabilityStateChangeRequest &req);
Core::FunctionReplyObject demandInitRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::DemandInitRequest &req);
Core::FunctionReplyObject sendEquipmentMessage(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendEquipmentMessageRequest &req);
Core::FunctionReplyObject sendParticipantModeRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendParticipantModeRequest &req);
Core::FunctionReplyObject sendSetSanitationRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendSetSanitationRequest &req);
Core::FunctionReplyObject selectiveReammunitionRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendSelectiveReammunitionRequest &req);
Core::FunctionReplyObject sendOcCommandRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendOcCommandRequest &req);
Core::FunctionReplyObject sendModifyUasStateRequest(const rti::core::SampleIdentity &cookie,
const TrainingAreaBase::SendModifyUasStateRequest &req);
private:
void onDemandVulnerabilityStateChangeReply(const Core::FunctionReplyObject &replyObject) override;
void onDemandInitReply(const Core::FunctionReplyObject &replyObject) override;
void onSendEquipmentMessageReply(const Core::FunctionReplyObject &replyObject) override;
void onSendParticipantModeReply(const Core::FunctionReplyObject &replyObject) override;
void onSendSetSanitationReply(const Core::FunctionReplyObject &replyObject) override;
void onSendSelectiveReammunitionReply(const Core::FunctionReplyObject &replyObject) override;
void onSendOcCommandReply(const Core::FunctionReplyObject &replyObject) override;
void onSendModifyUasStateReply(const Core::FunctionReplyObject &replyObject) override;
// Helper
void addToTrainingAreaRequests(const std::function<void()> &cb,
int requestId,
const rti::core::SampleIdentity &cookie);
void handleReply(const std::function<void(const rti::core::SampleIdentity &, const Core::FunctionReplyObject &)> &cb,
const Core::FunctionReplyObject &replyObject,
const char *requestType);
Q_SIGNALS:
void newPosition(const TrainingAreaBase::EquipmentPosition &position);
void newState(const TrainingAreaBase::EquipmentState &state);
void newGearAssignment(const TrainingAreaBase::EquipmentGearAssignment &gearAssignment);
void newGearChargeState(const TrainingAreaBase::EquipmentGearChargeState &chargeState);
void newGearEvent(const TrainingAreaBase::EquipmentGearEvent &gearEvent);
void newEquipmentOperationalState(const TrainingAreaBase::EquipmentOperationalState &operationalState);
void newEquipmentBite(const TrainingAreaBase::EquipmentBite &bite);
void newCtaDataError(const TrainingAreaBase::CtaDataError &error);
void newParticipantModeReport(const TrainingAreaBase::ParticipantModeReport &modeReport);
void newSpecialSensorData(const TrainingAreaBase::SpecialSensorData &data);
void newFunctionalAreaEntryChanged(const TrainingAreaBase::FunctionalAreaEntry &data);
void newLaserDesignationData(const TrainingAreaBase::LaserDesignation &data);
void newSanitaryMaterialStockReport(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data);
void laserDesignationRemoved(int equipmentNumber, int gearId);
void newParticipantSanitation(const TrainingAreaBase::ParticipantSanitation &data);
void newOperatorAction(const TrainingAreaBase::OperatorAction &data);
void newEquipmentEvent(const TrainingAreaBase::EquipmentEvent &data);
void newBuildingHitLineInfo(const Buildings::BuildingHitLineInfo &data);
private Q_SLOTS:
void onProxyEquipmentPositionChanged(const TrainingAreaBase::EquipmentPosition &data, DataModification modType);
void onProxyEquipmentGearAssignmentChanged(const TrainingAreaBase::EquipmentGearAssignment &data, DataModification modType);
void onProxyEquipmentGearEventChanged(const TrainingAreaBase::EquipmentGearEvent &data, DataModification modType);
void onProxyEquipmentGearChargeStateChanged(const TrainingAreaBase::EquipmentGearChargeState &data, DataModification modType);
void onProxyParticipantSanitaryMaterialStockReportChanged(const TrainingAreaBase::ParticipantSanitaryMaterialStockReport &data, DataModification modType);
void onProxyEquipmentStateChanged(const TrainingAreaBase::EquipmentState &data, DataModification modType);
void onProxyEquipmentOperationalStateChanged(const TrainingAreaBase::EquipmentOperationalState &data, DataModification modType);
void onProxyEquipmentBiteChanged(const TrainingAreaBase::EquipmentBite &data, DataModification modType);
void onProxyCtaDataErrorChanged(const TrainingAreaBase::CtaDataError &data, DataModification modType);
void onProxyParticipantModeReportChanged(const TrainingAreaBase::ParticipantModeReport &data, DataModification modType);
void onSpecialSensorDataChanged(const TrainingAreaBase::SpecialSensorData &data, DataModification modType);
void onFunctionalAreaEntryChanged(const TrainingAreaBase::FunctionalAreaEntry &data, DataModification modType);
void onLaserDesignationChanged(const TrainingAreaBase::LaserDesignation &data, DataModification modType);
void onProxyParticipantSanitationChanged(const TrainingAreaBase::ParticipantSanitation &data, DataModification modType);
void onOperatorActionChanged(const TrainingAreaBase::OperatorAction &data, DataModification modType);
void onEquipmentEventChanged(const TrainingAreaBase::EquipmentEvent &data, DataModification modType);
void onUASStatusChanged(const TrainingAreaBase::UASStatus &data, DataModification modType);
void onUASMissionStatusChanged(const TrainingAreaBase::UASMissionStatus &data, DataModification modType);
void onBuildingHitLineInfoChanged(const Buildings::BuildingHitLineInfo &data, DataModification modType);
private: // attributes
struct TrainingAreaRequests
{
rti::core::SampleIdentity cookie;
QPointer<QTimer> timer;
};
QMap<int, TrainingAreaRequests> m_trainingAreaRequests;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_tabDtc;
std::shared_ptr<Buildings::Interface::Dtc> m_bldDtc;
MainHandler *m_mainHandler;
LOGGER_T m_logger;
}; // class ProxyListener
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,334 @@
/**
* Calculate the focal point for units based on it's superiors
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-09-05
*/
#include "unit_position_calculator.h"
#include "participant_handler.h"
#include <QTimer>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <LtsUtilsCore/outdated_position.h>
#include <QPointF>
#include <QDateTime>
#include <QtConcurrent>
namespace ParticipantManagement
{
namespace ParticipantHandling
{
UnitPositionCalculator::UnitPositionCalculator(ParticipantHandler *participantHandler, QObject *parent)
: QObject(parent)
, m_participantHandler(participantHandler)
, m_bSomethingChangedSinceLastCalculation(false)
, m_timer(new QTimer(this))
, m_bRunning(true)
, m_logger(getLogger("UnitPositionCalculator"))
{
connect(m_timer, &QTimer::timeout, this, &UnitPositionCalculator::calculate);
m_timer->setSingleShot(true);
connect(&m_calculationWatcher, &QFutureWatcher<Result>::finished,
this, &UnitPositionCalculator::onCalculationDone);
}
UnitPositionCalculator::~UnitPositionCalculator()
{
stopCalculation();
m_calculationWatcher.disconnect(this);
m_calculationWatcher.cancel();
m_calculationWatcher.waitForFinished();
}
void UnitPositionCalculator::startCalculation()
{
DEBUG_LOG(m_logger) << "Starting calculation timer";
m_timer->start();
m_bRunning = true;
}
void UnitPositionCalculator::stopCalculation()
{
m_timer->stop();
m_participantData.clear();
m_publishedPositionData.clear();
m_bRunning = false;
}
void UnitPositionCalculator::waitForCalculationFinished()
{
if (m_calculationWatcher.isRunning())
m_calculationWatcher.waitForFinished();
}
void UnitPositionCalculator::onParticipantDeleted(int participantId)
{
DEBUG_LOG(m_logger) << "Participant " << participantId << " deleted";
auto it = m_participantData.find(participantId);
if (it == m_participantData.end())
return;
m_participantData.erase(it);
}
void UnitPositionCalculator::onPositionChanged(int participantId, int64_t positionTime, const Core::GeoPosition &pos)
{
DEBUG_LOG(m_logger) << "Position for participant " << participantId << " changed to "
<< pos << " (" << positionTime / 1000 << "s)";
CalculatedUnitPosition &data = getParticipant(participantId);
if (data.sampleTimeLeader() >= positionTime)
return;
data.sampleTimeCenter(positionTime);
data.sampleTimeLeader(positionTime);
data.positionCenter(pos);
data.positionLeader(pos);
m_bSomethingChangedSinceLastCalculation = true;
}
void UnitPositionCalculator::onSuperiorChanged(int participantId, int superiorId)
{
DEBUG_LOG(m_logger) << "Superior for participant " << participantId << " changed to " << superiorId;
CalculatedUnitPosition &data = getParticipant(participantId);
data.superiorId = superiorId;
m_bSomethingChangedSinceLastCalculation = true;
}
void UnitPositionCalculator::onInferiorChanged(int participantId, const std::vector<int> &inferiorIds)
{
if (m_logger.isDebugEnabled())
{
QStringList slIds;
slIds.reserve(inferiorIds.size());
for (int id : inferiorIds)
slIds += QString::number(id);
DEBUG_LOG(m_logger) << "Inferior for participant " << participantId << " changed, size: " << slIds.join(QLatin1Char(' '));
}
CalculatedUnitPosition &data = getParticipant(participantId);
data.inferiorIds = inferiorIds;
m_bSomethingChangedSinceLastCalculation = true;
}
UnitPositionCalculator::CalculatedUnitPosition &UnitPositionCalculator::getParticipant(int participantId)
{
auto it = m_participantData.find(participantId);
if (it == m_participantData.end())
{
DEBUG_LOG(m_logger) << "No information for participant " << participantId << " - creating dummy data";
CalculatedUnitPosition data;
data.participantId(participantId);
static const Core::GeoPosition s_invalidPos(std::nan(""), std::nan(""), std::nan(""));
data.positionCenter(s_invalidPos);
data.positionLeader(s_invalidPos);
it = m_participantData.insert({participantId, std::move(data)}).first;
}
return it->second;
}
void UnitPositionCalculator::calculate()
{
static bool s_bDebugOnce = false;
// theoretisches Problem: wenn sich keiner bewegt werden die Unit-Symbole nicht aktualisiert wenn
// dann Teilnehmer out-of-date gehen
if (!m_bSomethingChangedSinceLastCalculation)
{
if (!s_bDebugOnce)
DEBUG_LOG(m_logger) << "Nothing changed - no calculation needed -> waiting for new data";
s_bDebugOnce = true;
m_timer->start(1000);
return;
}
s_bDebugOnce = false;
m_bSomethingChangedSinceLastCalculation = false;
// copy data (before thread starts), can be done better with c++14 (lambda captures)
std::unordered_map<int, CalculatedUnitPosition> *participantData(new std::unordered_map<int, CalculatedUnitPosition>(m_participantData));
const qint64 currentTime = m_participantHandler->currentTimeMSec();
auto cb = [this, participantData, currentTime]() -> Result
{
const auto ret = doCalculation(*participantData, currentTime, m_logger);
delete participantData;
return ret;
};
m_calculationWatcher.setFuture(QtConcurrent::run(cb));
}
UnitPositionCalculator::Result UnitPositionCalculator::doCalculation(std::unordered_map<int, CalculatedUnitPosition> &participantData,
qint64 currentTime,
NGL::INGLLogger &logger)
{
DEBUG_LOG(logger) << "Starting calculation loop";
Result result;
result.reserve(participantData.size());
// search all top-level units
std::vector<int> topLevelIds;
topLevelIds.reserve(10);
for (const auto &data : participantData)
{
const int superiorId = data.second.superiorId;
const int participantId = data.second.participantId();
DEBUG_LOG(logger) << "Participant " << participantId << " has superior " << superiorId;
if (superiorId == 0 || participantData.find(superiorId) == participantData.end())
{
DEBUG_LOG(logger) << "Participant " << participantId << " is a toplevel participant";
topLevelIds.push_back(data.second.participantId());
}
}
// TODO: check for correct (non-cyclic) hierarchy?
for (const int topLevelId : topLevelIds)
{
DEBUG_LOG(logger) << "Calculating positions for toplevel participant " << topLevelId;
calculatePosition(topLevelId, participantData, currentTime, 1, logger, result);
}
return result;
}
void UnitPositionCalculator::calculatePosition(int participantId,
std::unordered_map<int, CalculatedUnitPosition> &participantData,
qint64 currentTime,
int level,
NGL::INGLLogger &logger,
Result &result)
{
static const Core::GeoPosition s_invalidPos(std::nan(""), std::nan(""), std::nan(""));
const QByteArray dbgLvlSpaces(level, ' ');
auto it = participantData.find(participantId);
if (it == participantData.end())
{
WARNING_LOG(logger) << "Participant " << participantId << " not found for unit position calculation!";
return;
}
CalculatedUnitPosition &data = it->second;
// found leaf -> return
if (data.inferiorIds.size() == 0)
{
DEBUG_LOG(logger) << dbgLvlSpaces << "Participant " << participantId
<< " is a leaf: " << data.positionCenter()
<< " (" << data.sampleTimeCenter() / 1000 << "s)";
return;
}
data.positionCenter(s_invalidPos);
data.positionLeader(s_invalidPos);
// calculate position for inferiors
DEBUG_LOG(logger) << dbgLvlSpaces << "Participant " << participantId << ": Start calculating child positions";
for (const int inferiorId : data.inferiorIds)
{
DEBUG_LOG(logger) << dbgLvlSpaces << "Participant " << participantId << ": calculating child position for " << inferiorId;
calculatePosition(inferiorId, participantData, currentTime, level + 1, logger, result);
}
DEBUG_LOG(logger) << dbgLvlSpaces << "Participant " << participantId << ": Finished calculating child positions";
// now calc own position
int utmZone = -1;
int childCount = 0;
LtsUtils::QPoint3D pos;
qint64 childPositionTime = 0;
bool bFirst = true;
for (const int inferiorId : data.inferiorIds)
{
auto itChild = participantData.find(inferiorId);
// Warning was already given in loop before
if (itChild == participantData.end())
{
WARNING_LOG(logger) << dbgLvlSpaces << "Child " << inferiorId << ": could not find data";
continue;
}
const CalculatedUnitPosition &child = itChild->second;
// older than 10 minutes -> ignore
if (LtsUtils::isPositionTimeOutdated(currentTime, child.sampleTimeCenter()))
{
DEBUG_LOG(logger) << dbgLvlSpaces << "Child " << inferiorId << ": position out-of-date - ignoring";
continue;
}
// invalid position -> ignore
if (std::isnan(child.positionCenter().lon()) ||
std::isnan(child.positionCenter().lat()))
{
DEBUG_LOG(logger) << dbgLvlSpaces << "Child " << inferiorId << ": has invalid position - ignoring";
continue;
}
if (bFirst)
{
bFirst = false;
data.positionLeader(child.positionLeader());
data.sampleTimeLeader(child.sampleTimeLeader());
}
const auto utm = LtsCoordTrans::UtmCoordinate::fromGeographic(child.positionCenter().lon(),
child.positionCenter().lat(),
utmZone);
if (utmZone == -1)
utmZone = utm.zone;
pos += LtsUtils::QPoint3D(utm.x, utm.y, child.positionCenter().z());
if (child.sampleTimeCenter() > childPositionTime)
childPositionTime = child.sampleTimeCenter();
++childCount;
}
data.sampleTimeCenter(childPositionTime);
if (childCount > 0)
{
pos /= childCount;
const LtsCoordTrans::UtmCoordinate utm(utmZone, pos.x(), pos.y(),true);
const QPointF lonLat = utm.geographic();
data.positionCenter(Core::GeoPosition(lonLat.x(), lonLat.y(), pos.z()));
}
DEBUG_LOG(logger) << dbgLvlSpaces
<< "Participant " << participantId
<< ": Mean position: " << data.positionCenter()
<< " #relavant children: " << childCount
<< " (" << data.sampleTimeCenter() / 1000 << "s)";
result.push_back(std::move(data));
}
void UnitPositionCalculator::onCalculationDone()
{
const Result &calculatedPositions = m_calculationWatcher.future().result();
auto dts = m_participantHandler->dts();
for (const auto &pos : calculatedPositions)
{
auto it = m_publishedPositionData.find(pos.participantId());
if (it != m_publishedPositionData.end())
{
ParticipantManagement::UnitPosition &up = it->second;
DEBUG_LOG(m_logger) << "old: " << up << ", new: " << pos << ", equal: " << (up == pos);
if (up == pos)
continue;
// nan == nan is always false, have to check it on our own
if (std::isnan(up.positionCenter().lat()) && std::isnan(pos.positionCenter().lat()))
continue;
up = pos;
}
else
{
m_publishedPositionData.insert(std::make_pair(pos.participantId(), pos));
}
dts->publish(pos);
}
m_timer->start(1000);
}
} // namespace ParticipantHandling
} // namespace ParticipantManagement
@@ -0,0 +1,77 @@
/**
* Calculate the focal point for units based on it's superiors
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-09-05
*/
#pragma once
#include <QObject>
#include <QFutureWatcher>
#include <Core/core.hpp>
#include <unordered_map>
#include "participant_handler.h"
class QTimer;
namespace ParticipantManagement
{
namespace ParticipantHandling
{
class UnitPositionCalculator : public QObject
{
Q_OBJECT
public:
explicit UnitPositionCalculator(ParticipantHandler *participantHandler, QObject *parent = nullptr);
~UnitPositionCalculator();
void startCalculation();
void stopCalculation(); // also clear all participants
void waitForCalculationFinished();
void onParticipantDeleted(int participantId);
void onPositionChanged(int participantId, int64_t positionTime, const Core::GeoPosition &pos);
void onSuperiorChanged(int participantId, int superiorId);
void onInferiorChanged(int participantId, const std::vector<int> &inferiorIds);
private:
struct CalculatedUnitPosition : public ParticipantManagement::UnitPosition
{
int superiorId = 0;
std::vector<int> inferiorIds;
};
private:
CalculatedUnitPosition &getParticipant(int participantId);
void calculate();
// no const ref - must be a copy since this is running in a different thread
using Result = std::vector<CalculatedUnitPosition>;
static Result doCalculation(std::unordered_map<int, CalculatedUnitPosition> &participantData,
qint64 currentTime,
NGL::INGLLogger &logger);
static void calculatePosition(int participantId,
std::unordered_map<int, CalculatedUnitPosition> &participantData,
qint64 currentTime,
int level,
NGL::INGLLogger &logger,
Result &result);
private Q_SLOTS:
void onCalculationDone();
private:
std::unordered_map<int, CalculatedUnitPosition> m_participantData;
std::unordered_map<int, CalculatedUnitPosition> m_publishedPositionData;
QFutureWatcher<Result> m_calculationWatcher;
ParticipantHandler *m_participantHandler;
bool m_bSomethingChangedSinceLastCalculation;
QTimer *m_timer;
bool m_bRunning;
NGL::INGLLogger &m_logger;
};
} // namespace ParticipantHandling
} // namespace ParticipantManagement
+59
View File
@@ -0,0 +1,59 @@
/**
* The Party class represents a party. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-12
*/
#include "party.h"
bool ParticipantManagement::PartyHandling::Party::hasChanges(const ParticipantManagement::Party &request,
const bool bIgnoreIsUsedBy)
{
const QString requestProperties = QString::fromStdString(request.properties());
bool hasChanges = request.color() != m_data.color
|| request.fadingColor() != m_data.fadingColor
|| request.name() != m_data.name.toStdString()
|| request.hlaForceIdentifier().underlying() != m_data.hlaForceIdentifier
|| request.role() != m_data.roleId
|| requestProperties != m_data.properties;
if (!bIgnoreIsUsedBy && !hasChanges)
hasChanges = request.isUsedBy() != m_data.isUsedBy;
if (hasChanges)
{
m_data.color = request.color();
m_data.fadingColor = request.fadingColor();
m_data.name = QString::fromStdString(request.name());
m_data.roleId = request.role();
m_data.hlaForceIdentifier = request.hlaForceIdentifier().underlying();
m_data.properties = requestProperties;
if (!bIgnoreIsUsedBy)
m_data.isUsedBy = request.isUsedBy();
}
return hasChanges;
}
ParticipantManagement::Database::PpmParty ParticipantManagement::PartyHandling::Party::toDbParty() const
{
return m_data;
}
ParticipantManagement::Party ParticipantManagement::PartyHandling::Party::toDdsParty() const
{
ParticipantManagement::Party result;
result.id(id());
result.color(m_data.color);
result.fadingColor(m_data.fadingColor);
result.name(m_data.name.toStdString());
result.role(m_data.roleId);
result.hlaForceIdentifier(static_cast<HlaForceIdentifierEnum::inner_enum>(m_data.hlaForceIdentifier));
result.isUsedBy(m_data.isUsedBy);
result.sampleTime(QDateTime::currentMSecsSinceEpoch());
result.properties(m_data.properties.toStdString());
return result;
}
+36
View File
@@ -0,0 +1,36 @@
/**
* The Party class represents a party. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-12
*/
#pragma once
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm_dtc.h>
namespace ParticipantManagement
{
namespace PartyHandling
{
class Party
{
public:
explicit Party(const Database::PpmParty &data)
: m_data(data)
{}
int32_t id() const { return m_data.id; }
Database::PpmParty toDbParty() const;
ParticipantManagement::Party toDdsParty() const;
bool hasChanges(const ParticipantManagement::Party &request, const bool bIgnoreIsUsedBy);
bool isUsedBy() const { return m_data.isUsedBy; }
private:
Database::PpmParty m_data;
};
} // namespace PartyHandling
} // namespace ParticipantManagement
@@ -0,0 +1,21 @@
/**
* The PartyDonator class is an interface for returning a Patry to a given id.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-06-06
*/
#pragma once
namespace ParticipantManagement
{
namespace PartyHandling
{
class Party;
class PartyDonator
{
public:
virtual Party *partyById(int partyId) const = 0;
}; // class PartyDonator
} // namespace PartyHandling
} // namespace ParticipantManagement
@@ -0,0 +1,384 @@
/**
* The PartyHandler class manages all party related function topics and holds classes for
* further party data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-12
*/
#include "PartyHandling/party_handler.h"
#include "PartyHandling/party.h"
#include <ngldb/db_service_handler.h>
#include <ppm_db.h>
#include "main_handler.h"
namespace ParticipantManagement
{
//------------------------------------------------------------------------------------------
PartyHandling::PartyHandler::PartyHandler(MainHandler *mainHandler)
: ModifyPartyDataRequestHandler(mainHandler->fts())
, RemovePartyRequestHandler(mainHandler->fts())
, SyncPartiesRequestHandler(mainHandler->exerciseId() <= 0 ? nullptr : mainHandler->fts())
, ChangePartyUsedStateRequestHandler(mainHandler->fts())
, m_mainHandler(mainHandler)
, m_logger(getLogger("PartyHandler"))
{
}
//------------------------------------------------------------------------------------------
PartyHandling::PartyHandler::~PartyHandler()
{
qDeleteAll(m_parties);
}
//------------------------------------------------------------------------------------------
PartyHandling::Party *PartyHandling::PartyHandler::partyById(int32_t partyId) const
{
return m_parties.value(partyId, nullptr);
}
//------------------------------------------------------------------------------------------
bool PartyHandling::PartyHandler::init()
{
qDeleteAll(m_parties);
m_parties.clear();
QVector<Database::PpmParty> partyVec;
// read parties from db
if (!m_mainHandler->db()->select(partyVec))
{
ERROR_LOG(m_logger) << "Error while reading party data from database";
return false;
}
// if there are no parties in db and this instance of ParticipantManagement runs not in ex_0
// parties are fetched from ex_0 PPM
if (m_mainHandler->exerciseId() > 0 && partyVec.isEmpty())
partyVec = loadDefaultParties(true);
if (partyVec.isEmpty())
{
ERROR_LOG(m_logger) << "Error while reading party data from database";
return false;
}
for ( Database::PpmParty currentParty : qAsConst(partyVec))
{
resetAdlerProperties(currentParty.properties, currentParty.id);
PartyHandling::Party *newParty = new PartyHandling::Party(currentParty);
m_parties.insert(currentParty.id, newParty);
m_mainHandler->dts()->publish(newParty->toDdsParty());
}
return true;
}
//------------------------------------------------------------------------------------------
void PartyHandling::PartyHandler::resetAdlerProperties( QString &properties, const int partyId)
{
DEBUG_LOG(m_logger) << "properties: " << properties;
QJsonObject obj = QJsonDocument::fromJson(properties.toUtf8()).object();
if (!obj.contains(QStringLiteral("Adler")))
return;
const ParticipantManagement::ConnectState connectState = m_mainHandler->ddsHandler()->dtc()->saaConnectedState(partyId);
if (connectState == ParticipantManagement::ConnectState::Undefined)
return;
DEBUG_LOG(m_logger) << "connectState: " << connectState;
if (connectState != ParticipantManagement::ConnectState::Disconnected)
{
QJsonObject propAdler = obj[QStringLiteral("Adler")].toObject();
propAdler[QStringLiteral("saaConnectedState")] = ParticipantManagement::ConnectState::Disconnected;
obj[QStringLiteral("Adler")] = propAdler;
const QByteArray propertiesStr = QJsonDocument(obj).toJson(QJsonDocument::Compact);
properties = QString::fromStdString(propertiesStr.data());
INFO_LOG(m_logger) << "isSaaConnected set to false";
}
}
//------------------------------------------------------------------------------------------
QVector<Database::PpmParty> PartyHandling::PartyHandler::loadDefaultParties(bool withInsert) const
{
QVector<Database::PpmParty> partyVec;
if (!m_mainHandler->dbEx0()->select(partyVec))
ERROR_LOG(m_logger) << "Error while reading party data from database (ex_0)";
auto cb = [](const Database::PpmParty &p1,const Database::PpmParty &p2) -> bool
{
return p1.id < p2.id;
};
std::sort(partyVec.begin(), partyVec.end(), cb);
if (withInsert)
{
for (auto it = partyVec.begin(); it != partyVec.end(); ++it)
{
// just inserting a new party, means is not in use
(*it).isUsedBy = false;
m_mainHandler->db()->insert(*it);
}
m_mainHandler->db()->fixUpSequencePpmParty();
}
return partyVec;
}
//------------------------------------------------------------------------------------------
void PartyHandling::PartyHandler::onModifyPartyDataRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::Party &request)
{
auto ret = modifyPartyDataRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->modifyPartyDataReply(cookie, ret);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject PartyHandling::PartyHandler::modifyPartyDataRequest(const ParticipantManagement::Party &request)
{
DEBUG_LOG(m_logger) << "onModifyPartyDataRequest: " << request;
Party *partyToPublish = m_parties.value(request.id());
if (!partyToPublish && request.id() >= 0)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Could not modify party with id %1, because no party with that id exists"))
.arg(request.id());
if (!partyToPublish) // create new party
{
DEBUG_LOG(m_logger) << "Creating new party";
// insert into database
Database::PpmParty newDbParty;
newDbParty.color = request.color();
newDbParty.fadingColor = request.fadingColor();
newDbParty.name = QString::fromStdString(request.name());
newDbParty.roleId = request.role();
newDbParty.isUsedBy = request.isUsedBy();
newDbParty.hlaForceIdentifier = request.hlaForceIdentifier().underlying();
newDbParty.properties = QString::fromStdString(request.properties());
if (!m_mainHandler->db()->insert(newDbParty))
{
QString errText = m_mainHandler->db()->getLastError().databaseText();
ERROR_LOG(m_logger) << "Error inserting Party into database: " << errText;
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Error inserting new party to database: %1")).arg(errText);
}
partyToPublish = new Party(newDbParty);
m_parties.insert(partyToPublish->id(), partyToPublish);
}
else // modify existing party
{
DEBUG_LOG(m_logger) << "Modifying party with id " << partyToPublish->id();
if (!partyToPublish->hasChanges(request, true))
partyToPublish = nullptr; // so it will not be published at the end of this method ...
else // update in database
{
if (!m_mainHandler->db()->update(partyToPublish->toDbParty()))
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Error updating party in database"));
}
}
if (partyToPublish)
{
auto party = partyToPublish->toDdsParty();
m_mainHandler->dts()->publish(party);
}
return Core::FunctionReplyObject(request.id());
}
//------------------------------------------------------------------------------------------
void PartyHandling::PartyHandler::onRemovePartyRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::RemovePartyRequest &request)
{
DEBUG_LOG(m_logger) << "onRemovePartyRequest: " << request;
auto ret = removePartyRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->removePartyReply(cookie, ret);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject PartyHandling::PartyHandler::removePartyRequest(const ParticipantManagement::RemovePartyRequest &request)
{
DEBUG_LOG(m_logger) << "onRemovePartyRequest " << request;
if (request.partyId() < 0)
return Core::FunctionReplyObject(tr_lts("dds", "Got request to delete a party, but no valid party id to delete"));
Party *partyToDelete = m_parties.value(request.partyId());
if (!partyToDelete)
return Core::FunctionReplyObject(tr_lts("dds", "Could not delete party with id %1, because no party with that id exists"))
.arg(request.partyId());
if (partyToDelete->isUsedBy() == true)
return Core::FunctionReplyObject(tr_lts("dds", "Not possible to delete a party in use"));
DEBUG_LOG(m_logger) << "Removing party with ID " << request.partyId();
return removeParty(partyToDelete);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject PartyHandling::PartyHandler::removeParty(Party *partyToDelete)
{
const int partyId = partyToDelete->id();
// delete from db
if (!m_mainHandler->db()->removePpmParty(partyId))
return Core::FunctionReplyObject(tr_lts("dds", "Error removing party with id %1 from database")).arg(partyId);
// delete from dds
m_mainHandler->dts()->removePartyByKey(partyId);
// delete from map
m_parties.remove(partyId);
delete partyToDelete;
return Core::FunctionReplyObject(partyId);
}
//------------------------------------------------------------------------------------------
void PartyHandling::PartyHandler::onSyncPartiesRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::SyncPartiesRequest &request)
{
DEBUG_LOG(m_logger) << "onSyncPartiesRequest: " << request;
Core::FunctionReplyObject rep;
QVector<Database::PpmParty> partyVec = loadDefaultParties(false);
for (auto it = partyVec.begin(); it != partyVec.end(); ++it)
{
int partyId = (*it).id;
DEBUG_LOG(m_logger) << "Checking Party Id: " << partyId;
Database::PpmParty partyEx0 = (*it);
if (!m_parties.contains(partyId))
{
// just inserting a new party, means is not yet in use
partyEx0.isUsedBy = false;
if (!m_mainHandler->db()->insert(partyEx0))
{
QString errText = m_mainHandler->db()->getLastError().databaseText();
WARNING_LOG(m_logger) << "Error inserting new party with id " << partyId << " to database: " << errText;
continue;
}
else
{
DEBUG_LOG(m_logger) << "Inserting Party Id: " << partyId;
PartyHandling::Party *newParty = new PartyHandling::Party(partyEx0);
m_parties.insert(partyEx0.id, newParty);
m_mainHandler->dts()->publish(newParty->toDdsParty());
}
}
else
{
Party *partyEx = m_parties.value(partyId);
bool hasChanges = partyEx0.color != partyEx->toDbParty().color
|| partyEx0.fadingColor != partyEx->toDbParty().fadingColor
|| partyEx0.name != partyEx->toDbParty().name
|| partyEx0.hlaForceIdentifier != partyEx->toDbParty().hlaForceIdentifier
|| partyEx0.roleId != partyEx->toDbParty().roleId;
DEBUG_LOG(m_logger) << "Has changes = " << hasChanges;
if (hasChanges)
{
ParticipantManagement::Party req;
req.id(partyId);
// taking new properties from ex_0
req.color(partyEx0.color);
req.fadingColor(partyEx0.fadingColor);
req.name(partyEx0.name.toStdString());
req.role(partyEx0.roleId);
req.hlaForceIdentifier(static_cast<HlaForceIdentifierEnum::inner_enum>(partyEx0.hlaForceIdentifier));
// this remains as it is
req.isUsedBy(partyEx->isUsedBy());
auto ret = modifyPartyDataRequest(req);
if (ret.error())
WARNING_LOG(m_logger) << ret();
}
}
}
// now checking for non-existent parties in ex_0
const auto partiesCopy(m_parties);
for (Party *curParty : partiesCopy)
{
bool found = false;
for (auto it = partyVec.begin(); it != partyVec.end(); ++it)
{
int partyId = (*it).id;
if (partyId == curParty->id())
{
found = true;
break;
}
}
if (!found)
{
DEBUG_LOG(m_logger) << "Party id to be deleted = " << curParty->id();
if (curParty->isUsedBy())
INFO_LOG(m_logger) << "Party with id " << curParty->id() << " cannot be deleted, is in use";
else
{
auto ret = removeParty(curParty); // removed from map etc
if (ret.error())
WARNING_LOG(m_logger) << ret();
}
}
}
m_mainHandler->db()->fixUpSequencePpmParty();
m_mainHandler->fts()->syncPartiesReply(cookie, rep);
}
//------------------------------------------------------------------------------------------
void PartyHandling::PartyHandler::onChangePartyUsedStateRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::ChangePartyUsedStateRequest &request)
{
DEBUG_LOG(m_logger) << "onChangePartyUsedStateRequest: " << request;
auto ret = changePartyUsedStateRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->changePartyUsedStateReply(cookie, ret);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject PartyHandling::PartyHandler::changePartyUsedStateRequest(const ParticipantManagement::ChangePartyUsedStateRequest &request)
{
Party *party = m_parties.value(request.partyId());
if (!party)
return Core::FunctionReplyObject(request.partyId(), tr_lts("dds", "Could not modify party with id %1, because no party with that id exists"))
.arg(request.partyId());
ParticipantManagement::Party ddsParty = party->toDdsParty();
ddsParty.isUsedBy(request.isUsedBy());
if (party->hasChanges(ddsParty, false))
{
if (!m_mainHandler->db()->update(party->toDbParty()))
return Core::FunctionReplyObject(request.partyId(), tr_lts("dds", "Error updating party used state in database"));
DEBUG_LOG(m_logger) << "Change used state for party " << party->id() << " to " << party->isUsedBy();
m_mainHandler->dts()->publish(party->toDdsParty());
}
return Core::FunctionReplyObject(request.partyId());
}
} // namespace ParticipantManagement
@@ -0,0 +1,62 @@
/**
* The PartyHandler class manages all party related function topics and holds classes for
* further party data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-12
*/
#pragma once
#include <QtCore/QHash>
#include "PartyHandling/party_donator.h"
#include <ParticipantManagement/ppm_fts.h>
#include "logger.h"
#include "ppm_db.h"
namespace ParticipantManagement
{
class MainHandler;
namespace PartyHandling
{
class Party;
class PartyHandler final
: public PartyDonator
, protected Interface::Fts::ModifyPartyDataRequestHandler
, protected Interface::Fts::RemovePartyRequestHandler
, protected Interface::Fts::SyncPartiesRequestHandler
, protected Interface::Fts::ChangePartyUsedStateRequestHandler
{
public:
explicit PartyHandler(MainHandler *mainHandler);
~PartyHandler();
Party *partyById(int32_t partyId) const override;
bool init();
void resetAdlerProperties(QString &properties, const int partyId);
Core::FunctionReplyObject changePartyUsedStateRequest(const ChangePartyUsedStateRequest &request);
protected:
void onChangePartyUsedStateRequest(const rti::core::SampleIdentity &cookie,
const ChangePartyUsedStateRequest &request) override;
void onModifyPartyDataRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::Party &request) override;
Core::FunctionReplyObject modifyPartyDataRequest(const ParticipantManagement::Party &request);
void onRemovePartyRequest(const rti::core::SampleIdentity &cookie,
const RemovePartyRequest &request) override;
Core::FunctionReplyObject removePartyRequest(const RemovePartyRequest &request);
Core::FunctionReplyObject removeParty(Party *partyToDelete);
void onSyncPartiesRequest(const rti::core::SampleIdentity &cookie,
const SyncPartiesRequest &request) override;
private:
QVector<Database::PpmParty> loadDefaultParties(bool withInsert) const;
private:
MainHandler *m_mainHandler;
QHash<int32_t, Party *> m_parties; // <partyId, Party>
LOGGER_T m_logger;
};
} // namespace PartyHandling
} // namespace ParticipantManagement
@@ -0,0 +1,28 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#include <ParticipantHandling/participant.h>
namespace ParticipantManagement
{
class ParticipantForReplay final : public ParticipantHandling::Participant
{
public:
ParticipantForReplay(ParticipantHandling::ParticipantHandler *participantHandler,
ParticipantHandling::UnitPositionCalculator *unitPosCalculator,
const Database::PpmParticipant &data)
: ParticipantHandling::Participant(participantHandler,
unitPosCalculator,
data,
"ParticipantForReplay")
{}
protected:
bool updateEquipmentExerciseMapping(int, int) { return true; }
};
} // namespace ParticipantManagement
@@ -0,0 +1,115 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#include "participant_handler_for_replay.h"
#include "participant_for_replay.h"
#include "ppm_record_replay_plugin_custom.h"
#include <ParticipantHandling/unit_position_calculator.h>
namespace ParticipantManagement
{
//
// ParticipantHandlerForReplay
//
ParticipantHandlerForReplay::ParticipantHandlerForReplay(RecordReplayPluginCustom *replayPlugin)
: ParticipantHandling::ParticipantHandler(replayPlugin, "ParticipantHandlerForReplay")
{}
static inline Database::PpmParticipant toDb(const ParticipantManagement::Participant &ddsPart)
{
Database::PpmParticipant db;
db.id = ddsPart.id();
db.assignState = ddsPart.assignState();
db.partyId = ddsPart.partyId();
db.typeId = ddsPart.typeId();
db.externalId = ddsPart.externalId();
db.designator = QString::fromStdString(ddsPart.designator());
db.equipmentNumber = ddsPart.equipmentNumber().is_set() ? ddsPart.equipmentNumber().get() : 0;
db.hierarchyNumber = ddsPart.hierarchyNumber();
db.identification = QString::fromStdString(ddsPart.identification());
db.info = QString::fromStdString(ddsPart.info());
db.isDeleted = ddsPart.isDeleted();
db.isSimulated = ddsPart.isSimulated();
db.name = QString::fromStdString(ddsPart.name());
db.superiorId = ddsPart.superiorId();
db.symbol = QString::fromStdString(ddsPart.symbol());
db.tacticalIdentifier = QString::fromStdString(ddsPart.tacticalIdentifier());
db.properties = QString::fromStdString(ddsPart.properties());
return db;
}
void ParticipantHandlerForReplay::createUpdateParticipant(const ParticipantManagement::Participant &ddsPart)
{
auto *p = m_participants.value(ddsPart.id());
if (!p)
{
p = new ParticipantForReplay(this,
m_unitPositionCalculator.get(),
toDb(ddsPart));
m_participants.insert(ddsPart.id(), p);
}
else
{
p->hasToBeChanged(ddsPart);
}
}
void ParticipantHandlerForReplay::removeParticipant(const ParticipantManagement::Participant &ddsPart)
{
// maybe clear m_hitLineInfoMap for this participant also but is this a real-world problem?
auto *p = m_participants.take(ddsPart.id());
if (p)
{
p->removeFromDds();
delete p;
}
}
void ParticipantHandlerForReplay::setDynamicParticipantData(const ParticipantManagement::DynamicParticipantData &dynPart)
{
auto *p = m_participants.value(dynPart.participantId());
if (!p) {
WARNING_LOG(m_logger) << "Got dynamic participant data for unknown participant with id " << dynPart.participantId();
return;
}
p->setDynamicData(dynPart);
}
void ParticipantHandlerForReplay::publishData()
{
for (auto p : qAsConst(m_participants))
{
p->publishParticipant();
p->publishDynamicData();
p->publishPosition();
}
}
void ParticipantHandlerForReplay::startUnitPositionCalculator()
{
m_unitPositionCalculator->startCalculation();
}
void ParticipantHandlerForReplay::stopUnitPositionCalculator()
{
m_unitPositionCalculator->stopCalculation();
}
void ParticipantHandlerForReplay::waitForUnitPositionCalculator()
{
m_unitPositionCalculator->waitForCalculationFinished();
}
} // namespace ParticipantManagement
@@ -0,0 +1,36 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm_dts.h>
#include <ParticipantHandling/participant_handler.h>
namespace ParticipantManagement
{
class RecordReplayPluginCustom;
class ParticipantHandlerForReplay final : public ParticipantHandling::ParticipantHandler
{
Q_OBJECT
public:
explicit ParticipantHandlerForReplay(RecordReplayPluginCustom *replayPlugin);
void createUpdateParticipant(const ParticipantManagement::Participant &ddsPart);
void removeParticipant(const ParticipantManagement::Participant &ddsPart);
void setDynamicParticipantData(const ParticipantManagement::DynamicParticipantData &dynPart);
void publishData();
void startUnitPositionCalculator();
void stopUnitPositionCalculator();
void waitForUnitPositionCalculator();
protected:
bool isSampleTimeInFuture(qint64 sampleTime) const { Q_UNUSED(sampleTime); return false; }
bool isSampleTimeOutdated(qint64 sampleTime) const { Q_UNUSED(sampleTime); return false; }
};
} // namespace ParticipantManagement
@@ -0,0 +1,410 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#include "ppm_record_replay_plugin_custom.h"
#include "participant_handler_for_replay.h"
#include <DDS/DTS/generic_dts_impl.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <QtConcurrent>
static bool OPTIMZE = true; // GUEZE-3521 "Überarbeitung des Replays zur Performance Steigerung"
namespace ParticipantManagement
{
//
// RecordReplayPluginCustom
//
RecordReplayPluginCustom::RecordReplayPluginCustom()
: m_participantHandler(nullptr)
, m_tabRecordReplay(nullptr)
, m_currentReplayTime(0)
{
INFO_LOG(m_logger) << "RecordReplayPluginCustom ctor";
}
RecordReplayPluginCustom::~RecordReplayPluginCustom()
{
delete m_tabRecordReplay;
delete m_participantHandler;
}
bool RecordReplayPluginCustom::initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId,
bool bCanHandleSchemas,
NSqlQuery &query)
{
m_tabRecordReplay = new TrainingAreaBase::RecordReplayPluginCustom;
// they will both return false due to missing ddsConnector...
m_tabRecordReplay->initDts(nullptr, exerciseId, bCanHandleSchemas, query);
RecordReplayPlugin::initDts(nullptr, exerciseId, bCanHandleSchemas, query);
// *after* initDts
m_ddsConnector = ddsConnector;
// own published data
m_ddsDts = ParticipantManagement::Interface::Dts::create(m_ddsConnector);
m_ddsDts->enableBite();
m_ddsDts->enableDynamicParticipantData();
m_ddsDts->enableHitLineInfo();
m_ddsDts->enableUAS();
m_ddsDts->enableParticipant();
m_ddsDts->enableParticipantGearEvent();
m_ddsDts->enableParticipantMode();
m_ddsDts->enableParticipantPosition();
m_ddsDts->enableUnitPosition();
m_ddsDts->enableParticipant();
// data from recording tables (published via RecordReplayPlugin::publishFoo() functions)
m_dtsParty.reset(m_ddsConnector->createDTS<DTS::PartyImpl>());
// publish initial party information to be able to fill the comboboxes (e.g. for filtering)
const qint64 startTime = time(nullptr) * 1000LL;
readPartyAt(query, startTime);
publishParty(startTime);
readParticipantAt(query, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readParticipantAt before" << m_participantMap.count();
removeOld(m_participantMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readParticipantAt AFTER " << m_participantMap.count();
publishParticipant(startTime);
DEBUG_LOG(m_logger) << "RecordReplayPluginCustom::initDts successful";
return true;
}
template<typename T>
void RecordReplayPluginCustom::removeOld(QMultiMap<long long, T> &map, qint64 desiredTime)
{
if (!OPTIMZE)
return;
Q_UNUSED(desiredTime);
auto copy = map;
for (const auto &item : copy.keys())
{
for (const auto &inner : map.values(item))
{
// Remove delted ones
if (inner.isDeleted())
{
map.remove(item, inner);
}
}
}
}
bool RecordReplayPluginCustom::setReplayStartTime(NSqlQuery &q, qint64 startTime)
{
INFO_LOG(m_logger) << "setReplayStartTime " << startTime;
m_currentReplayTime = 0;
if (!readParticipantAt(q, startTime))
return false;
INFO_LOG(m_logger) << "setReplayStartTime-readParticipantAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "setReplayStartTime-readParticipantAt before" << m_participantMap.count();
removeOld(m_participantMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readParticipantAt after" << m_participantMap.count();
//create eqMap
// 2026-03-16: N.Thoden, E01562-183 "Prüfung Replay"
// If container 'm_equipmentNumbers' is filled and used at makeConcurrent()
// (at least for type EquipmentPosition) some participant are accidentally skipped:
// Since manually 'move players' / 'Tln nachführen' uses the participant id (and not eqno),
// they are not honored by preparing 'm_equipmentNumbers'
// Later on, while preparing the publishing of the EquipmentPosition, the corresponding
// Participant does not exist and so the EquipmentPosition could not be published.
// So keep m_equipmentNumbers empty or remove the completly.
m_equipmentNumbers.clear();
// if (OPTIMZE)
// {
// for (const auto &p : m_participantMap)
// {
// if (p.equipmentNumber().is_set())
// m_equipmentNumbers.insert(p.equipmentNumber().get());
// }
// DEBUG_LOG(m_logger) << "setReplayStartTime-m_equipmentNumbers after" << m_equipmentNumbers.count();
// }
if (!readDynamicParticipantDataAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
//Manual
DEBUG_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt before" << m_dynamicParticipantDataMap.count();
if (OPTIMZE)
{
const auto &backup = m_dynamicParticipantDataMap;
for (const auto &key : backup.keys())
{
const auto &inner = backup.values(key);
for (const auto &value : inner)
{
if (m_participantKeys.find(value.participantId()) == m_participantKeys.end())
m_dynamicParticipantDataMap.remove(key, value);
}
}
}
DEBUG_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt after" << m_dynamicParticipantDataMap.count();
if (!readPartyAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readPartyAt " << startTime << " DONE";
if (!readHitLineInfoAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readHitLineInfoAt " << startTime << " DONE";
//removeOld(m_hitLineInfoMap, startTime);
if (!readUASAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readUASAt" << startTime << " DONE";
if (!m_tabRecordReplay->ppmSetReplayStartTime(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-ppmSetReplayStartTime " << startTime << " DONE";
publishParty(startTime);
INFO_LOG(m_logger) << "setReplayStartTime-publishParty " << startTime << " DONE";
// create data for ParticipantManagement
if (!m_participantHandler)
{
m_participantHandler = new ParticipantHandlerForReplay(this);
if (!m_participantHandler->init())
return false;
}
m_participantHandler->stopUnitPositionCalculator();
m_participantHandler->clearParticipants();
INFO_LOG(m_logger) << "setReplayStartTime-clearParticipants " << startTime << " DONE";
for (const auto &ddsParticipant : qAsConst(m_participantMap))
m_participantHandler->createUpdateParticipant(ddsParticipant);
INFO_LOG(m_logger) << "setReplayStartTime-createUpdateParticipant " << startTime << " DONE";
for (const auto &dynParticipant : qAsConst(m_dynamicParticipantDataMap))
m_participantHandler->setDynamicParticipantData(dynParticipant);
INFO_LOG(m_logger) << "setReplayStartTime-setDynamicParticipantData " << startTime << " DONE";
setDataForParticipants(startTime);
INFO_LOG(m_logger) << "setReplayStartTime-setDataForParticipants " << startTime << " DONE";
m_currentReplayTime = startTime;
m_participantHandler->startUnitPositionCalculator();
INFO_LOG(m_logger) << "setReplayStartTime-startUnitPositionCalculator " << startTime << " DONE";
INFO_LOG(m_logger) << "setReplayStartTime " << startTime << " ... DONE";
return true;
}
bool RecordReplayPluginCustom::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
INFO_LOG(m_logger) << "ddsTopicsFromDatabase " << fromTime << " to " << toTime;
if (!readParticipant(q, fromTime, toTime)) return false;
if (!readParty(q, fromTime, toTime)) return false;
if (!readHitLineInfo(q, fromTime, toTime)) return false;
if (!readUAS(q, fromTime, toTime)) return false;
return m_tabRecordReplay->ppmDdsTopicsFromDatabase(q, fromTime, toTime);
}
bool RecordReplayPluginCustom::publishData(qint64 desiredTime)
{
if (!m_participantHandler)
return false;
INFO_LOG(m_logger) << "publishData " << desiredTime;
m_currentReplayTime = desiredTime;
if (!publishParty(desiredTime))
{
WARNING_LOG(m_logger) << "Can not publish party information for " << desiredTime;
return false;
}
for (const Participant &p : qAsConst(m_participantMap))
m_participantHandler->createUpdateParticipant(p);
for (const Participant &p : qAsConst(m_participantDisposedMap))
m_participantHandler->removeParticipant(p);
setDataForParticipants(desiredTime);
m_participantHandler->onHitLineInfoTimeout();
m_participantHandler->publishData();
m_equipmentNumbers.clear();
return true;
}
bool RecordReplayPluginCustom::waitForAsyncOperations(qint64 desiredTime)
{
if (!m_participantHandler)
return false;
Q_UNUSED(desiredTime);
m_participantHandler->waitForUnitPositionCalculator();
m_participantHandler->publishData();
return true;
}
void RecordReplayPluginCustom::clearData()
{
RecordReplayPlugin::clearData();
if (m_tabRecordReplay)
m_tabRecordReplay->clearData();
if (m_participantHandler)
m_participantHandler->clearParticipants();
// we do not remove the party information here so we're able to modify the filters during stop
// clearDataParty();
}
template<typename T>
void RecordReplayPluginCustom::newDataForParticipants(T &cont, qint64 desiredTime)
{
auto it = cont.begin();
while (it != cont.end() && it.key() <= desiredTime)
{
m_participantHandler->newData(*it);
it = cont.erase(it);
}
}
template<typename T>
QVector<QFuture<void>> RecordReplayPluginCustom::makeConcurrentWithoutEquipNo(T &content, qint64 startTime)
{
QVector<T> contentVec;
contentVec.append(T());
QVector<QFuture<void>> result;
// split content into several (contentVec)
auto it = content.begin();
while (it != content.end() && it.key() <= startTime)
{
contentVec[contentVec.size() - 1].insert(it.key(), it.value());
if (contentVec[contentVec.size() - 1].size() == 200)
contentVec.append(T());
it = content.erase(it);
} // while
for (auto contentIndex : qAsConst(contentVec))
{
// execute in workerThread
result.append(QtConcurrent::run([this, contentIndex]()
{
for (auto it = contentIndex.begin(); it != contentIndex.end(); ++it)
{
m_participantHandler->newData(*it);
}
}));
} // for
return result;
}
template<typename T>
QVector<QFuture<void>> RecordReplayPluginCustom::makeConcurrent(T &content, qint64 startTime)
{
QVector<T> contentVec;
contentVec.append(T());
QVector<QFuture<void>> result;
// split content into several (contentVec)
auto it = content.begin();
while (it != content.end() && it.key() <= startTime)
{
contentVec[contentVec.size() - 1].insert(it.key(), it.value());
if (contentVec[contentVec.size() - 1].size() == 200)
contentVec.append(T());
it = content.erase(it);
} // while
for (auto contentIndex : qAsConst(contentVec))
{
// execute in workerThread
result.append(QtConcurrent::run([this, contentIndex]()
{
for (auto it = contentIndex.begin(); it != contentIndex.end(); ++it)
{
// following is relavant to a) OPTIMZE = true; // GUEZE-3521 and b) E01562-183 "Prüfung Replay"
if (m_equipmentNumbers.isEmpty() || m_equipmentNumbers.contains(abs(it->equipmentNumber())))
m_participantHandler->newData(*it);
}
}));
} // for
return result;
}
bool RecordReplayPluginCustom::setDataForParticipants(qint64 startTime)
{
if (!m_participantHandler)
return false;
// now read data from Tab
// not (yet) used functions in ParticipantHandler:
// void onNewCtaDataErrorFromProxy(const TrainingAreaBase::CtaDataError &error);
// void onNewParticipantModeReport(const TrainingAreaBase::ParticipantModeReport &report);
// not (yet) used TAB topics
// getCtaDataErrorMap()
// getDefinitionIedMap()
// getEquipmentConfigurationReportMap()
// getEquipmentEventMap()
// getEquipmentMessageMap()
// getFireSmokeMarkerDataMap()
// getFunctionalAreaEntryMap()
// getGeopairingDataMap()
// getMineTriggeredMap()
// getMinelayerThrowerDataMap()
// getMinelayerTrailerDataMap()
// getOperatorActionMap()
// getParticipantConfigurationReportMap()
// getParticipantModeReportMap()
// getParticipantSwVersionsMap()
// getStateIedMap()
// getTargetDataMap()
//#define USE_THIS_THREAD
#ifdef USE_THIS_THREAD
newDataForParticipants(m_tabRecordReplay->getEquipmentBiteMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentGearAssignmentMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentGearChargeStateMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentGearEventMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentOperationalStateMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentPositionMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentStateMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getSpecialSensorDataMap(), startTime);
newDataForParticipants(getHitLineInfoMap(), startTime);
#else
// This approach is from Hannes to overcome blocking dds writer:
QVector<QFuture<void>> futVec = makeConcurrent(m_tabRecordReplay->getEquipmentPositionMap(), startTime);
futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentBiteMap(), startTime));
//futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentGearAssignmentMap(), startTime));
//futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentGearChargeStateMap(), startTime));
//futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentGearEventMap(), startTime));
newDataForParticipants(m_tabRecordReplay->getEquipmentGearAssignmentMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentGearChargeStateMap(), startTime);
newDataForParticipants(m_tabRecordReplay->getEquipmentGearEventMap(), startTime);
futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentOperationalStateMap(), startTime));
futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentStateMap(), startTime));
futVec.append(makeConcurrent(m_tabRecordReplay->getSpecialSensorDataMap(), startTime));
futVec.append(makeConcurrent(m_tabRecordReplay->getParticipantSanitationMap(), startTime));
futVec.append(makeConcurrent(m_tabRecordReplay->getParticipantSanitaryMaterialStockReportMap(), startTime));
futVec.append(makeConcurrent(m_tabRecordReplay->getEquipmentEventMap(), startTime));
futVec.append(makeConcurrentWithoutEquipNo(getHitLineInfoMap(), startTime));
futVec.append(makeConcurrentWithoutEquipNo(getUASMap(), startTime));
for (QFuture<void> &futRef : futVec)
futRef.waitForFinished();
#endif // USE_THIS_THREAD
return true;
}
} // namespace ParticipantManagement
@@ -0,0 +1,65 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm_record_replay_plugin.h>
#include <TrainingAreaBase/tab_record_replay_plugin_custom.h>
#include <ParticipantManagement/ppm_dts.h>
#include <ParticipantManagement/ParticipantHandling/participant_handler.h>
#include <QFuture>
namespace ParticipantManagement
{
class ParticipantHandlerForReplay;
class PPMRECORDREPLAYPLUGIN_EXPORT RecordReplayPluginCustom final : public RecordReplayPlugin
, public ParticipantHandling::BaseDataProvider
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "special:3:recordreplay.ppm")
Q_INTERFACES(RecordReplay::RecordReplayPlugin)
public:
RecordReplayPluginCustom();
~RecordReplayPluginCustom();
bool initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
int exerciseId,
bool bCanHandleSchemas,
NSqlQuery &query) override;
bool setReplayStartTime(NSqlQuery &q, qint64 startTime) override;
bool ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime) override;
bool publishData(qint64 desiredTime) override;
bool waitForAsyncOperations(qint64 desiredTime) override;
void clearData() override;
qint64 currentTimeMSec() const override { return m_currentReplayTime; }
std::shared_ptr<ParticipantManagement::Interface::Dts> dts() const override { return m_ddsDts; }
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const override { return nullptr; }
private:
template<typename T>
void newDataForParticipants(T &cont, qint64 desiredTime);
private:
template<typename T>
void removeOld(QMultiMap<long long int , T> &map, qint64 desiredTime);
template<typename T>
QVector<QFuture<void>> makeConcurrent(T &content, qint64 startTime);
template<typename T>
QVector<QFuture<void>> makeConcurrentWithoutEquipNo(T &content, qint64 startTime);
bool setDataForParticipants(qint64 startTime);
std::shared_ptr<ParticipantManagement::Interface::Dts> m_ddsDts;
ParticipantHandlerForReplay *m_participantHandler;
TrainingAreaBase::RecordReplayPluginCustom *m_tabRecordReplay;
QSet<int64_t> m_equipmentNumbers;
qint64 m_currentReplayTime;
};
} // namespace ParticipantManagement
@@ -0,0 +1,136 @@
/**
* Handler ShootbackTargets (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, NLE, 06.04.2020
*/
#include "main_handler.h"
#include "handler_target_information.h"
#include "object_target_information.h"
using namespace ParticipantManagement;
//--------------------------------------------------------------------------
HandlerTargetInformation::HandlerTargetInformation(MainHandler *mainHandler)
: ModifyTargetInformationRequestHandler(mainHandler->fts())
, RemoveTargetInformationRequestHandler(mainHandler->fts())
, m_mainHandler(mainHandler)
, m_logger(getLogger("HandlerTargetInformation"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//--------------------------------------------------------------------------
HandlerTargetInformation::~HandlerTargetInformation()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//--------------------------------------------------------------------------
bool HandlerTargetInformation::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
bool bRet = true;
// create objects
auto cb = [this, &bRet](const Database::PpmTargetInformation & data)
{
DEBUG_LOG(m_logger) << "Reading from DB PpmTargetInformation with id = " << data.id;
ObjectTargetInformation *ifs(new ObjectTargetInformation(m_mainHandler));
Core::FunctionReplyObject ret = ifs->createObject(ObjectTargetInformation::toDdsData(data));
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger) << ret();
bRet = false;
return;
}
m_objectMap.insert(ifs->getId(), ifs);
};
if (!m_mainHandler->db()->select(cb) || !bRet)
{
ERROR_LOG(m_logger) << "Error reading data from database!";
return false;
}
DEBUG_LOG(m_logger) << "m_objectMap.size() = " << m_objectMap.size();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
//--------------------------------------------------------------------------
void HandlerTargetInformation::onModifyTargetInformationRequest(const rti::core::SampleIdentity &cookie,
const ModifyTargetInformationRequest &request)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
const auto ret = targetInformationModifyRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->modifyTargetInformationReply(cookie, ret);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//--------------------------------------------------------------------------
Core::FunctionReplyObject HandlerTargetInformation::targetInformationModifyRequest(const ModifyTargetInformationRequest &request)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
const int requestId = request.targetInfo().id();
// modify
if (requestId > 0)
{
ObjectTargetInformation *ifs = m_objectMap.value(requestId);
if (!ifs)
return Core::FunctionReplyObject(requestId, tr_lts("dds", "TargetInformation with id %1 not found")).arg(requestId);
auto ret = ifs->updateObject(request.targetInfo());
return ret;
}
// create
ObjectTargetInformation *ifs(new ObjectTargetInformation(m_mainHandler));
auto ret = ifs->createObject(request.targetInfo());
if (ret.error())
{
delete ifs;
ifs = nullptr;
}
else
m_objectMap.insert(ifs->getId(), ifs);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return ret;
}
//--------------------------------------------------------------------------
void HandlerTargetInformation::onRemoveTargetInformationRequest(const rti::core::SampleIdentity &cookie,
const RemoveTargetInformationRequest &request)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " request=" << request;
Core::FunctionReplyObject ret;
ObjectTargetInformation *ifs = m_objectMap.value(request.id());
if (ifs == nullptr)
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "ShootbackTarget with id %1 not found")).arg(request.id());
m_mainHandler->fts()->removeTargetInformationReply(cookie, ret);
return;
}
ret = ifs->removeObject();
if (ret.error())
ERROR_LOG(m_logger) << ret();
else
{
// delete Object
delete ifs;
m_objectMap.remove(request.id());
}
m_mainHandler->fts()->removeTargetInformationReply(cookie, ret);
return;
}
@@ -0,0 +1,61 @@
/**
* Handler ShootbackTargets (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, NLE, 06.04.2020
*/
#pragma once
#include <QObject>
#include "dds_handler.h"
#include <ParticipantManagement/logger.h>
#include <ParticipantManagement/ppm_dts.h>
#include <ParticipantManagement/ppm_fts.h>
namespace ParticipantManagement
{
class MainHandler;
class ObjectTargetInformation;
class HandlerTargetInformation final
: protected Interface::Fts::ModifyTargetInformationRequestHandler
, protected Interface::Fts::RemoveTargetInformationRequestHandler
{
public:
explicit HandlerTargetInformation(MainHandler *mainHandler);
~HandlerTargetInformation();
/**
* Initialize the handler to manage information
*/
bool init();
const QHash<int, ObjectTargetInformation *> &getObjectMap() const { return m_objectMap; }
ObjectTargetInformation *data(int id)
{
if (!m_objectMap.contains(id))
return nullptr;
return m_objectMap[id];
}
private: // methods
void onModifyTargetInformationRequest(const rti::core::SampleIdentity &cookie,
const ModifyTargetInformationRequest &request) override;
Core::FunctionReplyObject targetInformationModifyRequest(const ModifyTargetInformationRequest &request);
void onRemoveTargetInformationRequest(const rti::core::SampleIdentity &cookie,
const RemoveTargetInformationRequest &request) override;
private: // attributes
QHash<int, ObjectTargetInformation *> m_objectMap;
MainHandler *m_mainHandler;
NGL::INGLLogger &m_logger; ///< Interface to the Logging-Facility
};
} // namespace ParticipantManagement
@@ -0,0 +1,262 @@
/**
* Handler ShootbackTargets (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, SLTE, 31.05.2022
*/
#include "object_target_information.h"
#include "../main_handler.h"
#include <QJsonDocument>
#include <QDebug>
//------------------------------------------------------------------------------
namespace ParticipantManagement
{
//
// Core::GeoPosition2D
Core::GeoPosition2D posFromJson(const QJsonValue &val)
{
const QJsonObject objElement(val.toObject());
Core::GeoPosition2D element;
{
const QJsonValue valLon(objElement.value(QStringLiteral("lon")));
element.lon(valLon.toDouble());
}
{
const QJsonValue valLat(objElement.value(QStringLiteral("lat")));
element.lat(valLat.toDouble());
}
return element;
}
QJsonObject toJson(const Core::GeoPosition2D &element)
{
QJsonObject obj;
obj[QStringLiteral("lon")] = element.lon();
obj[QStringLiteral("lat")] = element.lat();
return obj;
}
//
// ShootbackArea
ShootbackArea areaFromJson(const QJsonValue &val)
{
ShootbackArea area;
// use val.toObject().value instead val[key] introduced with qt5.10
area.identifier(val.toObject().value(QStringLiteral("identifier")).toString().toStdString());
if (val.toObject().value(QStringLiteral("coords")).isArray())
{
const QJsonArray &arr = val.toObject().value(QStringLiteral("coords")).toArray();
if (arr.size() > 0) area.coord1( posFromJson(arr.at(0)) );
if (arr.size() > 1) area.coord2( posFromJson(arr.at(1)) );
if (arr.size() > 2) area.coord3( posFromJson(arr.at(2)) );
if (arr.size() > 3) area.coord4( posFromJson(arr.at(3)) );
}
return area;
}
QJsonObject toJson(const ShootbackArea &area)
{
QJsonObject obj;
obj.insert(QStringLiteral("identifier"), QString::fromStdString(area.identifier()));
QJsonArray arr;
arr.push_back(toJson(area.coord1()));
arr.push_back(toJson(area.coord2()));
arr.push_back(toJson(area.coord3()));
arr.push_back(toJson(area.coord4()));
obj.insert(QStringLiteral("coords"), arr);
return obj;
}
//
// vector<ShootbackArea>
QJsonObject toJson(const std::vector<ShootbackArea>& shootback_areas)
{
if (shootback_areas.size() == 0)
return {};
QJsonArray arr;
for (const ShootbackArea &area : shootback_areas)
arr.push_back(toJson(area));
QJsonObject obj;
obj.insert(QStringLiteral("shootback_areas"), arr);
return obj;
}
std::vector<ShootbackArea> vecfromJson(const QJsonValue &val)
{
std::vector<ShootbackArea> ret;
if (val.isNull())
return ret;
// use val.toObject().value instead val[key] introduced with qt5.10
if (val.toObject().value(QStringLiteral("shootback_areas")).isArray())
{
const QJsonArray &arr = val.toObject().value(QStringLiteral("shootback_areas")).toArray();
ret.reserve(arr.size());
for (const auto& item : arr)
ret.push_back(areaFromJson(item));
}
return ret;
}
ObjectTargetInformation::ObjectTargetInformation(MainHandler *mainHandler)
: m_mainHandler(mainHandler)
, m_logger(getLogger("ObjectTargetInformation"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//------------------------------------------------------------------------------
ObjectTargetInformation::~ObjectTargetInformation()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (isValid())
m_mainHandler->dts()->removeTargetInformationByKey(m_data.id());
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//------------------------------------------------------------------------------
bool ObjectTargetInformation::isValid() const
{
return m_data.id() > 0;
}
//------------------------------------------------------------------------------
Core::FunctionReplyObject ObjectTargetInformation::createObject(const TargetInformation &data)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_data = data;
if (m_data.id() <= 0)
{
Database::PpmTargetInformation dbData(toDbData(data));
if (!m_mainHandler->db()->insert(dbData))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting TargetInformation-data into database"));
m_data.id(dbData.id);
DEBUG_LOG(m_logger) << "New PpmTargetInformation: " /*<< m_data.id()*/;
}
// Publish Data via DTS
onPublishData();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return Core::FunctionReplyObject(m_data.id());
}
//------------------------------------------------------------------------------
Core::FunctionReplyObject ObjectTargetInformation::updateObject(const TargetInformation &data)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, participantId=" << data.participantId()
<< " size =" << data.shootbackAreas().size();
Database::PpmTargetInformation dbData;
dbData = toDbData(data);
if (!m_mainHandler->db()->update(dbData))
return Core::FunctionReplyObject(data.id(), tr_lts("dds", "Error updating TargetInformation-data into database"));
m_data = data;
DEBUG_LOG(m_logger) << "Data changed for TargetInformation: " << m_data.participantId();
// Publish Data via DTS
onPublishData();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, id=" << data.id()
<< " size =" << data.shootbackAreas().size();
return Core::FunctionReplyObject(m_data.id());
}
//------------------------------------------------------------------------------
Core::FunctionReplyObject ObjectTargetInformation::removeObject()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_mainHandler->db()->removePpmTargetInformation(m_data.id()))
return Core::FunctionReplyObject(m_data.id(), tr_lts("dds", "TargetInformation with id %1 can not be removed from DB")).arg(m_data.id());
if (!m_mainHandler->dts()->removeTargetInformationByKey(m_data.id()))
return Core::FunctionReplyObject(m_data.id(), tr_lts("dds", "TargetInformation with id %1 can not be removed from DDS")).arg(m_data.id());
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return Core::FunctionReplyObject(m_data.id());
}
//------------------------------------------------------------------------------
void ObjectTargetInformation::onPublishData()
{
TRACE_LOG(m_logger) << "Publish TargetInformation-data to dds (" << m_data.id() << ") ...";
if (!m_mainHandler->dts()->publish(m_data))
ERROR_LOG(m_logger) << "Error publishing TargetInformation-data to dds";
}
// -------------------------------------------------------------------------------------------------------
// convert methods
// -------------------------------------------------------------------------------------------------------
// ParticipantManagement::TargetInformation => Database::PpmTargetInformation
Database::PpmTargetInformation ObjectTargetInformation::toDbData(const TargetInformation &ddsData)
{
Database::PpmTargetInformation dbData;
dbData.id = ddsData.id();
dbData.participantId = ddsData.participantId();
dbData.preWarningInterval = ddsData.prewarningInterval();
dbData.preWarningEffect = static_cast<int>(ddsData.prewarningEffect().underlying());
dbData.takeDownInterval = ddsData.takedownInterval();
dbData.takeDownEffect = static_cast<int>(ddsData.takedownEffect().underlying());
const std::vector<ParticipantManagement::ShootbackArea>& shootback_areas = ddsData.shootbackAreas();
const QJsonObject &obj = toJson(shootback_areas);
dbData.properties = QString::fromStdString( QJsonDocument(obj).toJson(QJsonDocument::Compact).toStdString() );
return dbData;
}
// -------------------------------------------------------------------------------------------------------
// Database::PpmTargetInformation => ParticipantManagement::TargetInformation
TargetInformation ObjectTargetInformation::toDdsData(const Database::PpmTargetInformation &dbData)
{
TargetInformation ddsData;
ddsData.id(dbData.id);
ddsData.participantId(dbData.participantId);
ddsData.prewarningInterval(dbData.preWarningInterval);
ddsData.prewarningEffect(static_cast<ProjectDataManagement::VulnerabilityState::inner_enum>(dbData.preWarningEffect));
ddsData.takedownInterval(dbData.takeDownInterval);
ddsData.takedownEffect(static_cast<ProjectDataManagement::VulnerabilityState::inner_enum>(dbData.takeDownEffect));
const QByteArray &propertiesBa = dbData.properties.toUtf8();
QJsonParseError parserError;
const QJsonDocument doc(QJsonDocument::fromJson(propertiesBa, &parserError));
if (parserError.error != QJsonParseError::NoError)
qCritical() << "Error parsing json document: " << parserError.errorString() << " '" << propertiesBa << "'";
qDebug() << "json to parse: " << doc.toJson();
QJsonObject obj(doc.object());
const std::vector<ShootbackArea> &sbAreas = vecfromJson(obj);
ddsData.shootbackAreas(sbAreas);
return ddsData;
}
// -------------------------------------------------------------------------------------------------------
} // Namespace ParticipantManagement
@@ -0,0 +1,57 @@
/**
* Handler ObjectTargetInformation (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, SLTE, 31.05.2022
*/
#pragma once
#include "dds_handler.h"
#include <ParticipantManagement/logger.h>
#include <DDS/Core/function_reply_object.h>
// Generated
#include <ParticipantManagement/ppm.hpp>
#include <ParticipantManagement/ppm_db.h>
namespace NGL
{
class INGLLogger;
}
namespace ParticipantManagement
{
class MainHandler;
class ObjectTargetInformation
{
public:
explicit ObjectTargetInformation(MainHandler *mainHandler);
~ObjectTargetInformation();
Core::FunctionReplyObject createObject(const TargetInformation &data);
Core::FunctionReplyObject updateObject(const TargetInformation &data);
Core::FunctionReplyObject removeObject();
qint32 getId() const { return m_data.id(); }
qint32 getParticipantId() const { return m_data.participantId(); }
const TargetInformation &getData() const { return m_data; }
bool isValid() const;
// convert methods
static Database::PpmTargetInformation toDbData(const TargetInformation &ddsData); // dds -> db
static TargetInformation toDdsData(const Database::PpmTargetInformation &dbData); // db -> dds
protected:
void onPublishData();
private:
MainHandler *m_mainHandler;
TargetInformation m_data;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,81 @@
/**
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-23
*/
#include "template.h"
ParticipantManagement::TemplateParticipant ParticipantManagement::TemplateHandling::Template::toDdsTemplateParticipant() const
{
TemplateParticipant result;
result.templateId(id());
result.typeId(typeId());
result.name(m_data.name.toStdString());
result.designator(m_data.designator.toStdString());
result.info(m_data.info.toStdString());
result.symbol(m_data.symbol.toStdString());
result.tacticalIdentifier(m_data.tacticalIdentifier.toStdString());
result.hierarchyNumber(m_data.hierarchyNumber);
result.superiorTemplateId(superiorId());
result.equipmentNumber(m_data.equipmentNumber);
if (!m_inferiorIds.empty())
result.inferiorIds().set(m_inferiorIds);
return result;
}
bool ParticipantManagement::TemplateHandling::Template::hasToBeChanged(const ParticipantManagement::TemplateData &newData)
{
bool hasNoChanges =
m_data.typeId == newData.typeId() &&
m_data.hierarchyNumber == newData.hierarchyNumber() &&
m_data.equipmentNumber == newData.equipmentNumber() &&
m_data.name == QString::fromStdString(newData.name()) &&
m_data.symbol == QString::fromStdString(newData.symbol()) &&
m_data.info == QString::fromStdString(newData.info()) &&
m_data.designator == QString::fromStdString(newData.designator()) &&
m_data.tacticalIdentifier == QString::fromStdString(newData.tacticalIdentifier());
if (!hasNoChanges)
{
m_data.typeId = newData.typeId();
m_data.hierarchyNumber = newData.hierarchyNumber();
m_data.name = QString::fromStdString(newData.name());
m_data.symbol = QString::fromStdString(newData.symbol());
m_data.info = QString::fromStdString(newData.info());
m_data.designator = QString::fromStdString(newData.designator());
m_data.tacticalIdentifier = QString::fromStdString(newData.tacticalIdentifier());
m_data.equipmentNumber = newData.equipmentNumber();
}
return !hasNoChanges;
}
bool ParticipantManagement::TemplateHandling::Template::hasNewSuperiorId(int32_t superiorId)
{
bool result = m_data.superiorId != superiorId;
m_data.superiorId = superiorId;
return result;
}
bool ParticipantManagement::TemplateHandling::Template::hasNewInferiorStructure(std::vector<int32_t> inferiorIds)
{
std::sort(inferiorIds.begin(), inferiorIds.end());
if (m_inferiorIds != inferiorIds)
{
std::swap(m_inferiorIds, inferiorIds);
return true;
}
return false;
}
ParticipantManagement::Database::PpmTemplate ParticipantManagement::TemplateHandling::Template::dbEntry() const
{
return m_data;
}
@@ -0,0 +1,40 @@
/**
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-23
*/
#pragma once
#include <QtCore/QString>
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm.hpp>
namespace ParticipantManagement
{
namespace TemplateHandling
{
class Template
{
public:
explicit Template(const Database::PpmTemplate &tmpl)
: m_data(tmpl)
{}
int32_t id() const { return m_data.id; }
int32_t typeId() const { return m_data.typeId; }
int32_t superiorId() const { return m_data.superiorId; }
TemplateParticipant toDdsTemplateParticipant() const;
bool hasToBeChanged(const TemplateData &newData);
bool hasNewSuperiorId(int32_t superiorId);
bool hasNewInferiorStructure(std::vector<int32_t> inferiorIds);
Database::PpmTemplate dbEntry() const;
private:
Database::PpmTemplate m_data;
std::vector<int32_t> m_inferiorIds;
}; // class Template
} //namespace TemplateHandling
} //namespace ParticipantManagement
@@ -0,0 +1,381 @@
/**
* The TemplateHandler class manages all template related function topics and holds classes for
* further tempalte data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-07-27
*/
#include "template_handler.h"
#include "template.h"
#include "main_handler.h"
#include <TypeHandling/type_donator.h>
#include <ngldb/dbtransaction.h>
#include <QtCore/QFuture>
#include <QtCore/QThread>
#include <QtConcurrent/QtConcurrent>
namespace ParticipantManagement
{
namespace TemplateHandling
{
TemplateHandler::TemplateHandler(MainHandler *mainHandler)
: Interface::Fts::ModifyTemplateDataRequestHandler(mainHandler->exerciseId() <= 0 ? mainHandler->fts() : nullptr)
, m_mainHandler(mainHandler)
, m_logger(getLogger("TemplateHandler"))
{}
TemplateHandler::~TemplateHandler()
{
qDeleteAll(m_templates);
}
bool TemplateHandler::init()
{
auto cb = [this](const Database::PpmTemplate &currentTemplate)
{
Template *newTemplate = new Template(currentTemplate);
m_templates.insert(newTemplate->id(), newTemplate);
};
if (!m_mainHandler->db()->select(cb))
{
ERROR_LOG(m_logger) << "Error while reading template data from database";
return false;
}
// build hierarchy an publish
m_mainHandler->exAnyDts()->enableTemplateParticipant();
for (auto it = m_templates.begin(); it != m_templates.end(); ++it)
{
it.value()->hasNewInferiorStructure(inferiorIdsForTemplate(it.key()));
m_mainHandler->exAnyDts()->publish(it.value()->toDdsTemplateParticipant());
}
return true;
}
void TemplateHandler::onModifyTemplateDataRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::ModifyTemplateDataRequest &request)
{
DEBUG_LOG(m_logger) << "onModifyTemplateDataRequest: " << request;
const auto ret = modifyTemplateDataRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->fts()->modifyTemplateDataReply(cookie, ret);
}
Core::FunctionReplyObject TemplateHandler::modifyTemplateDataRequest(const ParticipantManagement::ModifyTemplateDataRequest &request)
{
QVector<Template *> templatesWithChanges;
QHash<int32_t, int32_t> modIdToTemplateId; // <modId, templateId>
QVector<ParticipantManagement::TemplateData> templatesToBeCreatedOrModified;
templatesToBeCreatedOrModified.reserve(request.data().size());
DBTransaction removeTransaction(m_mainHandler->db()->database());
// first remove templates which are marked to be removed
for (const TemplateData &currentTemplate : request.data())
{
if (currentTemplate.isDeleted())
{
if (currentTemplate.templateId() <= 0)
return Core::FunctionReplyObject(tr_lts("dds",
"Internal error - Not possible to remove templateData with modId %1, because it does not have templateId set"))
.arg(currentTemplate.modId());
const auto ret = deleteTemplateById(currentTemplate.templateId(), true);
if (ret.error())
return ret;
}
else
templatesToBeCreatedOrModified.append(currentTemplate);
}
removeTransaction.commit();
DEBUG_LOG(m_logger) << "Starting validating ModifyTemplateDataRequest";
if (templatesToBeCreatedOrModified.size() >= 1000) // concurrent validation for big requests
{
QVector<QFuture<Core::FunctionReplyObject>> futureVec;
int vecSize = templatesToBeCreatedOrModified.size();
if (templatesToBeCreatedOrModified.size() % QThread::idealThreadCount())
vecSize += (QThread::idealThreadCount() - (vecSize % QThread::idealThreadCount()));
const int range = vecSize / QThread::idealThreadCount();
futureVec.reserve(vecSize);
for (int startIndex = 0; startIndex < templatesToBeCreatedOrModified.size(); startIndex += range)
{
QFuture<Core::FunctionReplyObject> fut = QtConcurrent::run(this,
&TemplateHandler::validateTemplateData,
templatesToBeCreatedOrModified,
modIdToTemplateId,
startIndex,
range);
futureVec += fut;
}
for (const QFuture<Core::FunctionReplyObject> &currentFut : qAsConst(futureVec))
{
Core::FunctionReplyObject ret = currentFut.result();
if (ret.error())
return ret;;
}
}
else
{
for (const ParticipantManagement::TemplateData &currentTemplate : qAsConst(templatesToBeCreatedOrModified))
{
DEBUG_LOG(m_logger) << "Validating template: " << currentTemplate;
auto ret = validateTemplateData(templatesToBeCreatedOrModified, modIdToTemplateId, currentTemplate);
if (ret.error())
return ret;
}
}
DBTransaction trans(m_mainHandler->db()->database());
// only continue if there was no error detected
DEBUG_LOG(m_logger) << "ModifyTemplateDataRequest has valid data";
// create new and update existing templates
DEBUG_LOG(m_logger) << "Creating new and updating existing templates ...";
for (const TemplateData &currentData : qAsConst(templatesToBeCreatedOrModified))
{
Template *templateToUpdate = m_templates.value(modIdToTemplateId.value(currentData.modId()));
if (!templateToUpdate)
{
DEBUG_LOG(m_logger) << "Creating new template: " << currentData;
const auto ret = createNewTemplate(currentData, modIdToTemplateId, templatesWithChanges);
if (ret.error())
return ret;
}
else
{
// update existing template
if (templateToUpdate->hasToBeChanged(currentData) &&
!templatesWithChanges.contains(templateToUpdate))
{
DEBUG_LOG(m_logger) << "Template changed: " << templateToUpdate->toDdsTemplateParticipant();
templatesWithChanges.append(templateToUpdate);
}
}
}
DEBUG_LOG(m_logger) << "New templates created and existing templates updated";
// build hierarchy
for (const TemplateData &currentData : qAsConst(templatesToBeCreatedOrModified)) // first set superiorIds
{
Template *templateToUpdate = m_templates.value(modIdToTemplateId.value(currentData.modId()), nullptr);
const int superiorId = modIdToTemplateId.value(currentData.superiorModId());
if (!templateToUpdate->hasNewSuperiorId(superiorId) ||
templatesWithChanges.contains(templateToUpdate))
continue;
templatesWithChanges.append(templateToUpdate);
}
for (Template *currentTemplate : qAsConst(m_templates)) // set inferiorIds
{
if (!currentTemplate->hasNewInferiorStructure(inferiorIdsForTemplate(currentTemplate->id())) ||
templatesWithChanges.contains(currentTemplate))
continue;
DEBUG_LOG(m_logger) << "Template got new inferiors: " << currentTemplate->toDdsTemplateParticipant();
templatesWithChanges.append(currentTemplate);
}
// update in Database
for (Template *currentTemplate : qAsConst(templatesWithChanges))
{
if (!m_mainHandler->db()->update(currentTemplate->dbEntry()))
return Core::FunctionReplyObject(tr_lts("dds", "Could not update template data in database, template data may be inconsistent"));
}
trans.commit();
// finally ... publish template data via dds if no error occured
for (Template *currentTemplate : qAsConst(templatesWithChanges))
m_mainHandler->exAnyDts()->publish(currentTemplate->toDdsTemplateParticipant());
return Core::FunctionReplyObject();
}
std::vector<int32_t> TemplateHandler::inferiorIdsForTemplate(int32_t superiorTemplateId) const
{
std::vector<int32_t> result;
result.reserve(10);
for (Template *currentTemplate : qAsConst(m_templates))
{
if (currentTemplate->superiorId() == superiorTemplateId)
{
DEBUG_LOG(m_logger) << "Building hierarchy, parentId: " << superiorTemplateId << " has child " << currentTemplate->id();
result.push_back(currentTemplate->id());
}
}
return result;
}
Core::FunctionReplyObject TemplateHandler::createNewTemplate(const ParticipantManagement::TemplateData &currentData,
QHash<int32_t, int32_t> &modIdToTemplateId,
QVector<Template *> &templatesWithChanges)
{
// create new template
Database::PpmTemplate newDbTemplate;
newDbTemplate.typeId = currentData.typeId();
newDbTemplate.name = QString::fromStdString(currentData.name());
newDbTemplate.symbol = QString::fromStdString(currentData.symbol());
newDbTemplate.info = QString::fromStdString(currentData.info());
newDbTemplate.designator = QString::fromStdString(currentData.designator());
newDbTemplate.tacticalIdentifier = QString::fromStdString(currentData.tacticalIdentifier());
newDbTemplate.superiorId = modIdToTemplateId.value(currentData.superiorModId(), 0);
newDbTemplate.hierarchyNumber = currentData.hierarchyNumber();
newDbTemplate.equipmentNumber = currentData.equipmentNumber();
// creating db entry
if (!m_mainHandler->db()->insert(newDbTemplate))
return Core::FunctionReplyObject(tr_lts("dds",
"Error inserting new template into database, template data may be inconsistent"));
// adding the new template
Template *newTemplate = new Template(newDbTemplate);
m_templates.insert(newTemplate->id(), newTemplate);
modIdToTemplateId.insert(currentData.modId(), newTemplate->id());
if (!templatesWithChanges.contains(newTemplate))
templatesWithChanges.append(newTemplate);
return Core::FunctionReplyObject();
}
Core::FunctionReplyObject TemplateHandler::validateTemplateData(const QVector<TemplateData> &dataVec,
QHash<int32_t, int32_t> &modIdToTemplateId,
const ParticipantManagement::TemplateData &templateToValidate)
{
if (templateToValidate.templateId() != 0)
{
DEBUG_LOG(m_logger) << "Checking template id";
Template *existingTemplate = m_templates.value(templateToValidate.templateId(), nullptr);
if (existingTemplate == nullptr)
return Core::FunctionReplyObject(tr_lts("dds",
"Could not execute ModifyTemplateDataRequest, because an invalid template ID (%1) was given"))
.arg(templateToValidate.templateId());
DEBUG_LOG(m_logger) << "... ok";
{
QMutexLocker l(&m_mutex);
modIdToTemplateId.insert(templateToValidate.modId(), existingTemplate->id()); /* register template to indicate that this template
should just be modified and not newly created */
}
}
// superior ids valid?
if (templateToValidate.superiorModId() > 0)
{
DEBUG_LOG(m_logger) << "checking superiorIds";
// superior exists?
bool isExistingSuperiorId = false;
for (const TemplateData &dat : dataVec)
{
if (templateToValidate.superiorModId() == dat.modId())
{
isExistingSuperiorId = true;
break; // for (const TemplateData &dat : dataVec)
}
}
if (!isExistingSuperiorId)
return Core::FunctionReplyObject(tr_lts("dds",
"Could not execute ModifyTemplateDataRequest, the data set with modId %1 has an invalid superiorModId %2"))
.arg(templateToValidate.modId())
.arg(templateToValidate.superiorModId());
DEBUG_LOG(m_logger) << "... ok";
if (templateToValidate.hierarchyNumber() <= 0)
return Core::FunctionReplyObject(tr_lts("dds",
"Could not execute ModifyTemplateDataRequest, because a data set has a superior but no hierarchyNumber set"));
// all hierarchy numbers with same superior unique?
DEBUG_LOG(m_logger) << "Checking hierarchyNumbers";
for (const TemplateData &dat : dataVec)
{
// operator==() on optionals returns false if not set ...
if (dat.modId() != templateToValidate.modId() &&
dat.superiorModId() == templateToValidate.superiorModId() &&
dat.hierarchyNumber() == templateToValidate.hierarchyNumber())
return Core::FunctionReplyObject(tr_lts("dds",
"Could not execute ModifyTemplateDataRequest, because for superiorModId %1 hierarchyNumber %2 is not unique"))
.arg(dat.superiorModId())
.arg(dat.hierarchyNumber());
}
DEBUG_LOG(m_logger) << "... ok";
}
// typeId valid?
DEBUG_LOG(m_logger) << "Checking typeId";
if (m_mainHandler->typeHandler()->typeById(templateToValidate.typeId()) == nullptr)
return Core::FunctionReplyObject(tr_lts("dds",
"Could not execute ModifyTemplateDataRequest, because data set with modId %1 has invalid typeId %2"))
.arg(templateToValidate.modId())
.arg(templateToValidate.typeId());
DEBUG_LOG(m_logger) << "... ok";
return Core::FunctionReplyObject();
}
Core::FunctionReplyObject TemplateHandler::validateTemplateData(const QVector<TemplateData> &dataVec,
QHash<int32_t, int32_t> &modIdToTemplateId,
int startIndex,
int range)
{
int endIndex;
if (startIndex + range > dataVec.size())
endIndex = dataVec.size() - startIndex - 1;
else
endIndex = startIndex + range - 1;
for (int currentIndex = startIndex; currentIndex <= endIndex; ++currentIndex)
{
const ParticipantManagement::TemplateData &templateToValidate = dataVec.at(currentIndex);
auto ret = validateTemplateData(dataVec, modIdToTemplateId, templateToValidate);
if (ret.error())
return ret;
}
return Core::FunctionReplyObject();
}
Core::FunctionReplyObject TemplateHandler::deleteTemplateById(int32_t templateId, bool isTriggeredFromFtRequest)
{
Template *templateToBeRemoved = m_templates.value(templateId);
if (!templateToBeRemoved)
return Core::FunctionReplyObject(tr_lts("dds", "Can not find template with id %1 which should be removed")).arg(templateId);
const auto inferiors(inferiorIdsForTemplate(templateId));
for(int32_t currentInferiorTemplateId : inferiors)
{
const Core::FunctionReplyObject ret = deleteTemplateById(currentInferiorTemplateId);
if (ret.error())
return ret;
}
if (!m_mainHandler->db()->removePpmTemplate(templateId))
return Core::FunctionReplyObject(tr_lts("dds", "Could not remove template with id %1 from database")).arg(templateId);
m_mainHandler->exAnyDts()->removeTemplateParticipantByKey(templateId);
m_templates.remove(templateId);
if (isTriggeredFromFtRequest && templateToBeRemoved->superiorId()) // refresh inferiors from parent and publish in dds
{
Template *parentTemplate = m_templates.value(templateToBeRemoved->superiorId());
if (parentTemplate->hasNewInferiorStructure(inferiorIdsForTemplate(parentTemplate->id())))
m_mainHandler->exAnyDts()->publish(parentTemplate->toDdsTemplateParticipant());
}
delete templateToBeRemoved;
return Core::FunctionReplyObject();
}
} // namespace ParticipantManagement
} // namespace TemplateHandling
@@ -0,0 +1,61 @@
/**
* The TemplateHandler class manages all template related function topics and holds classes for
* further tempalte data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-07-27
*/
#pragma once
#include <QtCore/QMutex>
#include <QtCore/QHash>
#include "dds_handler.h"
#include "logger.h"
namespace ParticipantManagement
{
class MainHandler;
namespace TemplateHandling
{
class Template;
class TemplateHandler final
: protected Interface::Fts::ModifyTemplateDataRequestHandler
{
public:
explicit TemplateHandler(MainHandler *mainHandler);
~TemplateHandler();
bool init();
void onModifyTemplateDataRequest(const rti::core::SampleIdentity &cookie,
const ModifyTemplateDataRequest &request) override;
private: // methods
Core::FunctionReplyObject modifyTemplateDataRequest(const ParticipantManagement::ModifyTemplateDataRequest &request);
std::vector<int32_t> inferiorIdsForTemplate(int32_t superiorTemplateId) const;
Core::FunctionReplyObject createNewTemplate(const TemplateData &currentData,
QHash<int32_t, int32_t> &modIdToTemplateId,
QVector<Template *> &templatesWithChanges);
Core::FunctionReplyObject validateTemplateData(const QVector<ParticipantManagement::TemplateData> &dataVec,
QHash<int32_t, int32_t> &modIdToTemplateId,
const ParticipantManagement::TemplateData &templateToValidate);
Core::FunctionReplyObject validateTemplateData(const QVector<ParticipantManagement::TemplateData> &dataVec,
QHash<int32_t, int32_t> &modIdToTemplateId,
int startIndex,
int range);
Core::FunctionReplyObject deleteTemplateById(int32_t templateId, bool isTriggeredFromFtRequest = false);
private: // attributes
MainHandler *m_mainHandler;
QHash<int32_t, Template *> m_templates; // <templateId, Template>
QMutex m_mutex;
LOGGER_T m_logger;
}; // class TemplateHandler
} // namespace TemplateHandling
} // namespace ParticipantManagement
@@ -0,0 +1,33 @@
/**
* The TypeDonator class is an interface for returning a Type to a given id.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-24
*/
#pragma once
#include <QtCore/QVector>
namespace ParticipantManagement
{
class EquipmentType;
class Type;
namespace TypeHandling
{
class TypeDonator
{
public:
virtual ~TypeDonator() = default;
virtual const Type *typeById(int typeId) const = 0;
virtual bool isSupportType(int typeId) const = 0;
virtual bool isParticipantType(int typeId) const = 0;
virtual bool isUnitType(int typeId) const = 0;
virtual bool isFiringUnitType(int typeId) const = 0;
virtual bool isArtilleryParticipantType(int typeId) const = 0;
virtual const EquipmentType *equipmentTypeByTypeId(int typeId) = 0;
}; // class TypeDonator
} // namespace TypeHandling
} // namespace ParticipantManagement
@@ -0,0 +1,153 @@
/**
* The ParticipantHandler class manages all type related function topics and holds classes for
* further type data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-19
*/
#include "type_handler.h"
#include "type_handler_exercise.h"
#include "type_handler_global.h"
#include "main_handler.h"
#include <QJsonDocument>
#include <QJsonObject>
namespace ParticipantManagement
{
namespace TypeHandling
{
TypeHandler::TypeHandler(MainHandler *mainHandler, LOGGER_T logger)
: m_mainHandler(mainHandler)
, m_logger(logger)
{
}
TypeHandler::~TypeHandler()
{
qDeleteAll(m_types);
qDeleteAll(m_equipmentTypes);
}
const ParticipantManagement::Type *TypeHandler::typeById(int32_t typeId) const
{
return m_types.value(typeId);
}
bool TypeHandler::isSupportType(int typeId) const
{
const Type *type = typeById(typeId);
if (!type)
return false;
switch (type->typeClass().underlying())
{
case TypeClass_def::SupportEntity:
case TypeClass_def::SupportUnit:
return true;
case TypeClass_def::Unit:
case TypeClass_def::Human:
case TypeClass_def::GroundVehicle:
case TypeClass_def::WeaponSystem:
case TypeClass_def::Aircraft:
case TypeClass_def::Vessel:
case TypeClass_def::VehicleWithGear:
case TypeClass_def::Unknown:
return false;
}
return false;
}
bool TypeHandler::isParticipantType(int typeId) const
{
const Type *type = typeById(typeId);
if (!type)
return false;
switch (type->typeClass().underlying())
{
case TypeClass_def::SupportEntity:
case TypeClass_def::SupportUnit:
case TypeClass_def::Unknown:
return false;
case TypeClass_def::Unit:
case TypeClass_def::Human:
case TypeClass_def::GroundVehicle:
case TypeClass_def::WeaponSystem:
case TypeClass_def::Aircraft:
case TypeClass_def::Vessel:
case TypeClass_def::VehicleWithGear:
return true;
}
return false;
}
bool TypeHandler::isUnitType(int typeId) const
{
const Type *type = typeById(typeId);
if (!type)
return false;
switch (type->typeClass().underlying())
{
case TypeClass_def::Unit:
case TypeClass_def::SupportUnit:
return true;
default:
break;
}
return false;
}
bool TypeHandler::isFiringUnitType(int typeId) const
{
if (!isUnitType(typeId))
return false;
return isArtilleryType(typeId);
}
bool TypeHandler::isArtilleryParticipantType(int typeId) const
{
if (isUnitType(typeId))
return false;
return isArtilleryType(typeId);
}
const ParticipantManagement::EquipmentType *TypeHandler::equipmentTypeByTypeId(int32_t typeId)
{
const Type *type = typeById(typeId);
return type ? m_equipmentTypes.value(type->equipmentTypeId()) : nullptr;
}
std::shared_ptr<TypeHandler> TypeHandler::createInstance(MainHandler *mainHandler, NGL::INGLLogger &logger)
{
int exerciseId = mainHandler->exerciseId();
if (exerciseId <= 0)
{
DEBUG_LOG(logger) << "Creating TypeHandlerGlobal";
return std::make_shared<TypeHandlerGlobal>(mainHandler);
}
DEBUG_LOG(logger) << "Creating TypeHandlerExercise for ex_"
<< exerciseId;
return std::make_shared<TypeHandlerExercise>(mainHandler);
}
bool TypeHandler::isArtilleryType(int typeId) const
{
const Type *type = typeById(typeId);
if (!type)
return false;
QJsonDocument typeProperties = QJsonDocument::fromJson(QByteArray::fromStdString(type->properties()));
QJsonObject typePropertiesObject = typeProperties.object();
return typePropertiesObject.value(QStringLiteral("isArtillery")).toBool();
}
} // namespace TypeHandling
} // namespace ParticipantManagement
@@ -0,0 +1,53 @@
/**
* The abstract TypeHandler class is on the one hand the base class for all concrete TypeHandler... classes, on the
* other hand it offers a static method to instantiate a concrete TypeHandler... child class.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-19
*/
#pragma once
#include <QtCore/QHash>
#include <memory>
#include <ParticipantManagement/ppm_db.h>
#include "logger.h"
#include "type_donator.h"
#include <ParticipantManagement/ppm.hpp>
namespace ParticipantManagement
{
class MainHandler;
namespace TypeHandling
{
class TypeHandler : public TypeDonator
{
public:
~TypeHandler();
const Type *typeById(int32_t typeId) const override final;
bool isSupportType(int typeId) const override final;
bool isParticipantType(int typeId) const override final;
bool isUnitType(int typeId) const override final;
bool isFiringUnitType(int typeId) const override final;
bool isArtilleryParticipantType(int typeId) const override final;
const EquipmentType *equipmentTypeByTypeId(int32_t typeId) override final;
virtual bool init() = 0;
static std::shared_ptr<TypeHandler> createInstance(MainHandler *mainHandler, NGL::INGLLogger &logger);
protected:
bool isArtilleryType(int typeId) const;
explicit TypeHandler(MainHandler *mainHandler, LOGGER_T logger);
protected:
MainHandler *m_mainHandler;
QHash<int32_t, Type *> m_types;
QHash<int32_t, EquipmentType *> m_equipmentTypes;
LOGGER_T m_logger;
}; // class TypeHandler
} // namespace TypeHandling
} // namespace ParticipantManagement
@@ -0,0 +1,89 @@
/**
* The TypeHandlerExercise class is an abstract class for either an exercise dependent or an exercise independent
* concrete class.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-19
*/
#include "type_handler_exercise.h"
#include "main_handler.h"
#include "convert_db_to_dds.h"
#include <ParticipantManagement/ppm_dtc.h>
namespace ParticipantManagement
{
TypeHandling::TypeHandlerExercise::TypeHandlerExercise(MainHandler *mainHandler)
: QObject()
, TypeHandler(mainHandler, getLogger("TypeHandlerExercise"))
{}
bool TypeHandling::TypeHandlerExercise::init()
{
m_dtc = ParticipantManagement::Interface::Dtc::create(m_mainHandler->dds());
if (m_dtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC";
return false;
}
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::typeChanged,
this, &TypeHandlerExercise::onTypeChanged);
connect(m_dtc.get(), &ParticipantManagement::Interface::Dtc::equipmentTypeChanged,
this, &TypeHandlerExercise::onEquipmentTypeChanged);
if (!m_dtc->enableType() ||
!m_dtc->enableEquipmentType())
{
ERROR_LOG(m_logger) << "Could not initialize DTC";
return false;
}
DEBUG_LOG(m_logger) << "Initialized DTC for receiving Type data";
std::shared_ptr<DatabaseInterface> dbEx0 = m_mainHandler->dbEx0();
// pre-init from database
auto cbType = [this](const Database::PpmType &t)
{
onTypeChanged(convert(t));
};
dbEx0->select(cbType);
auto cbEquipmentType = [this](const Database::PpmEquipmentType &et)
{
onEquipmentTypeChanged(convert(et));
};
dbEx0->select(cbEquipmentType);
return true;
}
void TypeHandling::TypeHandlerExercise::onTypeChanged(const ParticipantManagement::Type &data)
{
Type *changedType = m_types.value(data.id());
if (changedType)
{
*changedType = data;
}
else
{
changedType = new Type(data);
m_types.insert(changedType->id(), changedType);
}
}
void TypeHandling::TypeHandlerExercise::onEquipmentTypeChanged(const ParticipantManagement::EquipmentType &data)
{
EquipmentType *changedEquipmentType = m_equipmentTypes.value(data.id());
if (changedEquipmentType)
{
*changedEquipmentType = data;
}
else
{
changedEquipmentType = new EquipmentType(data);
m_equipmentTypes.insert(changedEquipmentType->id(), changedEquipmentType);
}
}
} // namespace ParticipantManagement
@@ -0,0 +1,40 @@
/**
* The TypeHandlerExercise class is an abstract class for either an exercise dependent or an exercise independent
* concrete class.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-19
*/
#pragma once
#include <QObject>
#include "type_handler.h"
#include <ParticipantManagement/ppm.hpp>
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
namespace TypeHandling
{
class TypeHandlerExercise final
: public QObject
, public TypeHandler
{
Q_OBJECT
public:
explicit TypeHandlerExercise(MainHandler *mainHandler);
bool init() override;
private Q_SLOTS:
void onTypeChanged(const ParticipantManagement::Type &data);
void onEquipmentTypeChanged(const ParticipantManagement::EquipmentType &data);
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_dtc;
}; // class TypeHandlerExercise
} // namespace TypeHandling
} // namespace ParticipantManagement
@@ -0,0 +1,332 @@
/**
* The ParticipantHandlerNonExercise class manages all type related function topics and holds classes for
* further type data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-30
*/
#include "type_handler_global.h"
#include "main_handler.h"
#include "convert_db_to_dds.h"
#include <QtCore/QVector>
#include <ngldb/nsqlquery.h>
namespace ParticipantManagement
{
static Database::PpmEquipmentType toDb(const EquipmentType &et)
{
Database::PpmEquipmentType db;
db.id = et.id();
db.name = QString::fromStdString(et.name());
db.minEquipNo = et.minEquipNo();
db.maxEquipNo = et.maxEquipNo();
db.traVulnerabilityModelId = et.traVulnerabilityModelId();
db.traPlayerTypeId = et.traPlayerTypeId();
db.wmsTypeClassId = et.wmsTypeClass();
db.deletedInWms = et.deletedInWms();
return db;
}
Database::PpmType toDb(const ParticipantManagement::Type &data)
{
Database::PpmType ret;
ret.id = data.id();
ret.typeClassId = data.typeClass();
if (data.equipmentTypeId() != 0)
ret.equipmentTypeId = data.equipmentTypeId(); // QVariant
ret.name = QString::fromStdString(data.name());
ret.symbol = QString::fromStdString(data.symbol());
ret.radioCount = data.radioCount();
ret.hierarchyLevel = data.hierarchyLevel();
ret.isDeleted = data.isDeleted();
#ifdef Q_OS_WIN
ret.disEntities = LtsUtils::BigintArray(data.disEntities());
#else
ret.disEntities = LtsUtils::BigintArray(LtsUtils::convert<std::vector<int64_t>>(data.disEntities()));
#endif
ret.properties = QString::fromStdString(data.properties());
return ret;
}
TypeHandling::TypeHandlerGlobal::TypeHandlerGlobal(MainHandler *mainHandler)
: TypeHandler(mainHandler, getLogger("TypeHandlerGlobal"))
, Interface::Fts::ModifyTypeRequestHandler(mainHandler->exAnyFts())
, Interface::Fts::RemoveTypeRequestHandler(mainHandler->exAnyFts())
, Interface::Fts::ModifyEquipmentTypeDataRequestHandler(mainHandler->exAnyFts())
{
}
bool TypeHandling::TypeHandlerGlobal::init()
{
qDeleteAll(m_types);
m_types.clear();
qDeleteAll(m_equipmentTypes);
m_equipmentTypes.clear();
if (m_mainHandler->db() == nullptr)
{
ERROR_LOG(m_logger) << "DatabaseInterface is nullptr";
return false;
}
m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_mainHandler->dds());
if (m_exmDtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC";
return false;
}
m_exmDtc->enableExercise();
m_exmDtc->waitForHistoricalDataExercise();
m_mainHandler->exAnyFts()->enableModifyType();
m_mainHandler->exAnyFts()->enableRemoveType();
m_mainHandler->exAnyFts()->enableModifyEquipmentTypeData();
// get equipment types
m_mainHandler->exAnyDts()->enableEquipmentType();
auto cbEt = [this] (const Database::PpmEquipmentType &dbEquipmentType)
{
EquipmentType *newEquipmentType = new EquipmentType(convert(dbEquipmentType));
m_equipmentTypes.insert(newEquipmentType->id(), newEquipmentType);
m_mainHandler->exAnyDts()->publish(*newEquipmentType);
};
m_mainHandler->db()->select(cbEt);
// get types
m_mainHandler->exAnyDts()->enableType();
auto cbT = [this] (const Database::PpmType &dbType)
{
Type *newType = new Type(convert(dbType));
m_types.insert(newType->id(), newType);
m_mainHandler->exAnyDts()->publish(*newType);
};
m_mainHandler->db()->select(cbT);
return true;
}
void TypeHandling::TypeHandlerGlobal::onModifyTypeRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::Type &request)
{
DEBUG_LOG(m_logger) << "onModifyTypeRequest" << request;
auto ret = (request.id() > 0 ? modifyTypeRequest(request) : createTypeRequest(request));
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->exAnyFts()->modifyTypeReply(cookie, ret);
}
Core::FunctionReplyObject TypeHandling::TypeHandlerGlobal::createTypeRequest(const ParticipantManagement::Type &request)
{
if (request.equipmentTypeId() != 0 && !m_equipmentTypes.contains(request.equipmentTypeId()))
return Core::FunctionReplyObject(-1, tr_lts("dds", "Could not add new Type - equipmentTypeId %1 invalid")).arg(request.equipmentTypeId());
Database::PpmType newPpmType(toDb(request));
if (!m_mainHandler->db()->insert(newPpmType))
return Core::FunctionReplyObject(-1, tr_lts("dds", "Could not add new Type - insert statement failed"));
Type *newType = new Type(request);
newType->id(newPpmType.id);
// insert into map
m_types.insert(newType->id(), newType);
// publish to dds
m_mainHandler->exAnyDts()->publish(*newType);
DEBUG_LOG(m_logger) << "New type added: " << *newType;
return Core::FunctionReplyObject();
}
Core::FunctionReplyObject TypeHandling::TypeHandlerGlobal::modifyTypeRequest(const ParticipantManagement::Type &request)
{
Type *typeToBeModified = m_types.value(request.id());
if (!typeToBeModified)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Could not add new Type - Type with id %1 not available")).arg(request.id());
if (typeToBeModified->equipmentTypeId() != request.equipmentTypeId())
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "It is not allowed to modify the Equipment Type of a Type"));
if (!m_mainHandler->db()->update(toDb(request)))
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Could not modify Type in database"));
DEBUG_LOG(m_logger) << "Old Type data:\n"
<< *typeToBeModified
<< "\nNew Type data:\n"
<< request;
*typeToBeModified = request;
m_mainHandler->exAnyDts()->publish(*typeToBeModified);
return Core::FunctionReplyObject(request.id());
}
void TypeHandling::TypeHandlerGlobal::onRemoveTypeRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::RemoveTypeRequest &request)
{
DEBUG_LOG(m_logger) << "onRemoveTypeRequest " << request;
Core::FunctionReplyObject rep(request.typeId());
Type *typeToBeRemoved = m_types.value(request.typeId());
if (typeToBeRemoved)
{
if (isTypeUsed(typeToBeRemoved->id()))
rep = Core::FunctionReplyObject(request.typeId(), tr_lts("dds", "Type '%1' with type id %2 cannot be removed, type is used")).arg(typeToBeRemoved->name()).arg(request.typeId());
else
{
typeToBeRemoved->isDeleted(true);
if (!m_mainHandler->db()->update(toDb(*typeToBeRemoved)))
rep = Core::FunctionReplyObject(request.typeId(), tr_lts("dds", "Could not mark type with id %1 as deleted in database")).arg(request.typeId());
m_mainHandler->exAnyDts()->publish(*typeToBeRemoved);
}
}
else
rep = Core::FunctionReplyObject(request.typeId(), tr_lts("dds", "RemoveTypeRequest for non existing type with id %1")).arg(request.typeId());
if (rep.error())
WARNING_LOG(m_logger) << rep();
m_mainHandler->exAnyFts()->removeTypeReply(cookie, rep);
}
void TypeHandling::TypeHandlerGlobal::onModifyEquipmentTypeDataRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::ModifyEquipmentTypeDataRequest &request)
{
Core::FunctionReplyObject rep;
INFO_LOG(m_logger) << "Got " << request.data().size() << " equipment types to modify";
QSet<int> activeIds;
activeIds.reserve(request.data().size());
for (const EquipmentType &currentData : request.data())
{
bool bPublishData = false;
EquipmentType *existingEquipmentType = m_equipmentTypes.value(currentData.id(), nullptr);
if (existingEquipmentType) // update existing equipment type
{
activeIds += currentData.id();
if (*existingEquipmentType != currentData)
{
bPublishData = true;
*existingEquipmentType = currentData;
if (!m_mainHandler->db()->update(toDb(currentData)))
{
rep = Core::FunctionReplyObject(currentData.id(), tr_lts("dds", "Error updating equipment type with %1 data in database")).arg(currentData.id());
break;
}
}
}
else
{
Database::PpmEquipmentType dbEquipmentType(toDb(currentData));
if (!m_mainHandler->db()->insert(dbEquipmentType))
{
rep = Core::FunctionReplyObject(currentData.id(), tr_lts("dds", "Error inserting equipment type with %1 data in database")).arg(currentData.id());
break;
}
existingEquipmentType = new EquipmentType(currentData);
existingEquipmentType->id(dbEquipmentType.id);
m_equipmentTypes.insert(existingEquipmentType->id(), existingEquipmentType);
activeIds += existingEquipmentType->id();
bPublishData = true;
}
INFO_LOG(m_logger) << "Modified EquipmentType with id " << existingEquipmentType->id() << " ?: " << bPublishData;
if (bPublishData)
{
if (!m_mainHandler->exAnyDts()->publish(*existingEquipmentType))
{
rep = Core::FunctionReplyObject(currentData.id(), tr_lts("dds", "Error publishing equipment type %1 to DDS")).arg(currentData.id());
break;
}
}
}
if (request.deleteAll())
{
QHashIterator<int32_t,EquipmentType*> it(m_equipmentTypes);
while (it.hasNext())
{
EquipmentType* et = it.next().value();
if (activeIds.contains(et->id()))
continue;
INFO_LOG(m_logger) << "Deleted in WHG " << et->id() << ", already marked as deleted?: " << et->deletedInWms();
// alle, die nicht behandelt wurden -> deletedInWms
// über alle iterieren
if (et->deletedInWms())
continue;
et->deletedInWms(true);
if (!m_mainHandler->db()->update(toDb(*et)))
{
rep = Core::FunctionReplyObject(et->id(), tr_lts("dds", "Error updating equipment type with %1 data in database")).arg(et->id());
break;
}
if (!m_mainHandler->exAnyDts()->publish(*et))
{
rep = Core::FunctionReplyObject(et->id(), tr_lts("dds", "Error publishing equipment type %1 to DDS")).arg(et->id());
break;
}
}
}
if (rep.error())
WARNING_LOG(m_logger) << rep();
m_mainHandler->exAnyFts()->modifyEquipmentTypeDataReply(cookie, rep);
}
bool TypeHandling::TypeHandlerGlobal::isTypeUsed(int typeId) const
{
DEBUG_LOG(m_logger) << "typeId: " << typeId;
NSqlQuery q(m_mainHandler->db()->database());
const QString stmt = QStringLiteral("SELECT count(*) FROM %1 WHERE type_id = %2");
const auto exercises = m_exmDtc->exercises();
for (const ExerciseManagement::Exercise &ex : exercises)
{
const QString tblName = DBCommon::getExerciseTableName(QStringLiteral("ppm_participant"), ex.id());
//const QString stmt(QStringLiteral("SELECT count(*) FROM %1 WHERE type_id = %2").arg(tblName, QString::number(typeId)));
// Error occured => same as if type exists
if (!q.execAndFetch(stmt.arg(tblName, QString::number(typeId))))
return true;
int count = q.value(0).toInt();
DEBUG_LOG(m_logger) << "exercise: " << ex.id() << ", count: " << count;
if (count > 0)
return true;
}
const QString tblName(DBCommon::getTableName(QStringLiteral("ppm_participant"), QStringLiteral("ppm")));
//const QString stmt(QStringLiteral("SELECT count(*) FROM %1 WHERE type_id = %2").arg(tblName, QString::number(typeId)));
// Error occured => same as if type exists
if (!q.execAndFetch(stmt.arg(tblName, QString::number(typeId))))
return true;
int count = q.value(0).toInt();
DEBUG_LOG(m_logger) << "schema: ppm , count: " << count;
return (count > 0);
}
} // ParticipantManagement
@@ -0,0 +1,44 @@
/**
* The TypeHandlerGlobal class manages all type related function topics and holds classes for
* further type data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-05-30
*/
#pragma once
#include "type_handler.h"
#include <ParticipantManagement/ppm_fts.h>
#include <ExerciseManagement/exm_dtc.h>
namespace ParticipantManagement
{
namespace TypeHandling
{
class TypeHandlerGlobal final
: public TypeHandler
, protected Interface::Fts::ModifyTypeRequestHandler
, protected Interface::Fts::RemoveTypeRequestHandler
, protected Interface::Fts::ModifyEquipmentTypeDataRequestHandler
{
public:
explicit TypeHandlerGlobal(MainHandler *mainHandler);
bool init() override;
void onModifyTypeRequest(const rti::core::SampleIdentity &cookie,
const ParticipantManagement::Type &request) override;
Core::FunctionReplyObject createTypeRequest(const ParticipantManagement::Type &request);
Core::FunctionReplyObject modifyTypeRequest(const ParticipantManagement::Type &request);
void onRemoveTypeRequest(const rti::core::SampleIdentity &cookie,
const RemoveTypeRequest &request) override;
void onModifyEquipmentTypeDataRequest(const rti::core::SampleIdentity &cookie,
const ModifyEquipmentTypeDataRequest &request) override;
private:
bool isTypeUsed(int typeId) const;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
}; // class TypeHandlerGlobal
} // namespace TypeHandling
} // namespace ParticipantManagement
@@ -0,0 +1,444 @@
/**
* 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
}
@@ -0,0 +1,64 @@
/**
* 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
*/
#pragma once
#include <QtCore/QHash>
#include <ParticipantManagement/ppm_fts.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <CentralEffectSimulation/dds_handler.h>
#include "object_mission_plan.h"
#include "logger.h"
namespace ParticipantManagement
{
class ObjectMissionPlan;
class MainHandler;
namespace UasHandling
{
class MissionPlanHandler final
: protected Interface::Fts::ModifyMissionPlanRequestHandler
, protected Interface::Fts::RemoveMissionPlanRequestHandler
, protected Interface::Fts::SetMissionActionFulfilledRequestHandler
{
public:
explicit MissionPlanHandler(MainHandler *mainHandler);
~MissionPlanHandler();
bool init();
void onModifyMissionPlanRequest(const rti::core::SampleIdentity &cookie, const ModifyMissionPlanRequest &request) override final;
void onRemoveMissionPlanRequest(const rti::core::SampleIdentity &cookie, const RemoveMissionPlanRequest &request) override final;
void onSetMissionActionFulfilledRequest(const rti::core::SampleIdentity &cookie, const SetMissionActionFulfilledRequest &request) override final;
bool loadMissionPlansFromDb();
bool setCurrentMissionActionFulfilled(int participantId, const QString &nameOfTheMissionThatEnded);
private:
bool isUasCombatReady(int participantId);
bool isUasInAutoMode(int participantId);
Core::FunctionReplyObject createMissionPlanRequest(const ModifyMissionPlanRequest &request);
Core::FunctionReplyObject modifyMissionPlanRequest(const ModifyMissionPlanRequest &request);
Core::FunctionReplyObject removeMissionPlanRequest(int id);
Core::FunctionReplyObject setMissionActionFulfilledRequest(const SetMissionActionFulfilledRequest &request);
void publishMissionPlan(const MissionPlan &missionplan);
bool isUasParticipant(int participantId) const;
int getNumberOfActionFulfilled(const MissionItem &msi) const;
void setLatestFulfilledItem(MissionPlan &missionPlan); /// update mission plan fulfillment count & fulfillemnt latest time
int64_t getLastestFulfilledAction(const MissionItem &msi) const;
MainHandler *m_mainHandler;
QHash<int32_t, ObjectMissionPlan *> m_objMissionPlans;
LOGGER_T m_logger;
};
}
}// namespace ParticipantManagement
@@ -0,0 +1,398 @@
/**
* The ObjectMissionPlan class represents a ObjectMissionPlan. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#include "object_mission_plan.h"
#include <ngldb/dbtransaction.h>
namespace ParticipantManagement
{
namespace UasHandling
{
ObjectMissionPlan::ObjectMissionPlan(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const ParticipantManagement::MissionPlan & missionPlan,
QObject *parent)
: QObject(parent)
, m_ddsHandler(ddsHandler)
, m_db(db)
, m_missionplan(missionPlan)
, m_logger(getLogger("ObjectMissionPlan"))
{
DEBUG_LOG(m_logger)<<" - ctor ObjectMissionPlan: planId: "<<m_missionplan.id();
}
int32_t ObjectMissionPlan::id() const
{
return m_missionplan.id();
}
int32_t ObjectMissionPlan::participantId() const
{
return m_missionplan.participantId();
}
Database::PpmMissionPlan ObjectMissionPlan::toDbMissionPlan(const ModifyMissionPlanRequest &mp)
{
Database::PpmMissionPlan result;
result.id = mp.id();
result.name = QString::fromStdString(mp.name());
result.missionStart = QDateTime::fromMSecsSinceEpoch(mp.missionStart());
result.repeat = mp.repeat();
result.jumpToStart = mp.jumpToStart();
result.participantId = mp.participantId();
return result;
}
MissionPlan ObjectMissionPlan::fillMissionPlanData(const ModifyMissionPlanRequest &in)
{
MissionPlan out;
out.id(in.id());
out.name(in.name());
out.missionStart(in.missionStart());
out.properties(in.properties());
out.items(in.items());
out.participantId(in.participantId());
out.jumpToStart(in.jumpToStart());
out.repeat(in.repeat());
return out;
}
MissionPlan ObjectMissionPlan::getMissionPlanData() const
{
return m_missionplan;
}
void ObjectMissionPlan::setDbMissionItems(const QMap<int, ParticipantManagement::Database::PpmMissionItem> &dbItems,
const QMap<int, Database::PpmMissionAction> &dbActions)
{
m_mapMissionItems.clear();
m_dbMissionActions.clear();
for (const ParticipantManagement::Database::PpmMissionItem &item : qAsConst(dbItems))
{
ParticipantManagement::MissionItem msi;
msi.actions().reserve(dbActions.size());
msi.name(item.name.toStdString());
msi.location(Core::GeoPosition(item.location.x(), item.location.y(), item.location.z()));
msi.itemNumber(item.elementNumber);
std::vector<Database::PpmMissionAction> actionsVec;
for (const auto &action : dbActions)
{
if (action.missionItemId == item.id)
{
ParticipantManagement::MissionAction msa;
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.planId(item.missionPlanId);
msi.actions().push_back(msa);
actionsVec.push_back(action);
}
}
m_mapMissionItems.insert(item.id, msi);
m_dbMissionActions.push_back(actionsVec);
}
}
const MissionPlan &ObjectMissionPlan::missionPlan() const
{
return m_missionplan;
}
Core::FunctionReplyObject ObjectMissionPlan::createMissionPlan(Database::PpmMissionPlan newMissionPlan, const std::vector<MissionItem> &missionItems)
{
/// 1- insert in DB Mission plan
/// 2- insert in DB Mission Items
/// 3- insert in DB Mission Actions
DBTransaction trans(m_db->database());
newMissionPlan.aborted = false;
if (!m_db->insert(newMissionPlan))
{
QString error = QStringLiteral("Could not insert mission plan into database");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
std::vector<MissionItem> itemVec = missionItems;
m_mapMissionItems.clear();
m_mapMissionItems.reserve(itemVec.size());
std::vector<std::vector<Database::PpmMissionAction>>itemsVec;
m_dbMissionActions.clear();
for ( auto &item : itemVec)
{
Database::PpmMissionItem dbItem;
dbItem.missionPlanId = newMissionPlan.id;
dbItem.name = QString::fromStdString(item.name());
dbItem.elementNumber = item.itemNumber();
dbItem.location = LtsUtils::QPoint3D(item.location().lon(),item.location().lat(), item.location().z());
if (!m_db->insert(dbItem))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error adding 'Mission Item' to database"));
std::vector<Database::PpmMissionAction> actionsVec;
actionsVec.reserve(item.actions().size());
for (auto &action : item.actions())
{
action.planId(newMissionPlan.id);
Database::PpmMissionAction dbAction;
dbAction.missionActionTypeId = action.actionType();
dbAction.missionItemId = dbItem.id;
dbAction.duration = action.duration();
dbAction.name = QString::fromStdString(action.name());
dbAction.properties = QString::fromStdString(action.properties());
dbAction.fulfilled = QDateTime::fromMSecsSinceEpoch(action.fulfilled());
dbAction.elementNumber = action.actionNumber();
if (!m_db->insert(dbAction))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error adding 'Mission Action' to database"));
actionsVec.push_back(dbAction);
}//loop mission actions
m_mapMissionItems.insert(dbItem.id, item);
m_dbMissionActions.push_back(actionsVec); // [elNumber][actionNumber]
}//loop mission items
m_missionplan.id(newMissionPlan.id);
m_missionplan.name(newMissionPlan.name.toStdString());
m_missionplan.missionStart(newMissionPlan.missionStart.toMSecsSinceEpoch());
m_missionplan.participantId(newMissionPlan.participantId);
m_missionplan.jumpToStart(newMissionPlan.jumpToStart);
m_missionplan.repeat(newMissionPlan.repeat);
m_missionplan.items(itemVec);
m_missionplan.fulfilled(0);
m_missionplan.itemsFulfilled(0);
for (auto &item : m_missionplan.items())
{
for (auto &action : item.actions())
DEBUG_LOG(m_logger)<<"[CREATE] itemIdx: "<<item.itemNumber()
<<" act.Num: "<<action.actionNumber()<<" act.ItemNum: "<<action.itemNumber()<<" itemFul:"<<action.fulfilled();
}
trans.commit();
DEBUG_LOG(m_logger)<< ">> [NEW] mission plan with id( "<<newMissionPlan.id<<" ) inserted !!";
return Core::FunctionReplyObject(newMissionPlan.id);
}
Core::FunctionReplyObject ObjectMissionPlan::updateMissionPlan(Database::PpmMissionPlan newMissionPlan, const std::vector<MissionItem> &missionItems)
{
if (missionItems.size() == 0)
return Core::FunctionReplyObject(newMissionPlan.id, tr_lts("dds", "Could not modify MissionPlan without segments."));
/// 1- update missipn plan table
/// 2- remove items and their actions
/// 3- insert new items and their actions
DBTransaction trans(m_db->database());
const auto tmpDbMissionItems = m_mapMissionItems;
newMissionPlan.aborted = false;
/// update mission plan table
if (!m_db->update(newMissionPlan))
{
QString error = QStringLiteral("Could not update mission plan! ");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
/// remove all items and their actions from the database
for (const auto &itemId : tmpDbMissionItems.keys())
{
if (!m_db->removePpmMissionItem(itemId))
{
QString error = QStringLiteral("Could not remove mission plan from database");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(0, tr_lts("dds", "Could not update mission plan into!"));
}
}
/// insert new items with their actions in the data base;
m_mapMissionItems.clear();
m_mapMissionItems.reserve(missionItems.size());
m_dbMissionActions.clear();
std::vector<MissionItem> itemVec = missionItems;
for (auto &item : itemVec)
{
Database::PpmMissionItem dbItem;
dbItem.missionPlanId = newMissionPlan.id;
dbItem.name = QString::fromStdString(item.name());
dbItem.elementNumber = item.itemNumber();
dbItem.location = LtsUtils::QPoint3D(item.location().lon(),item.location().lat(), item.location().z());
if (!m_db->insert(dbItem))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error adding 'Mission Item' to database"));
std::vector<Database::PpmMissionAction> actionsVec;
actionsVec.reserve(item.actions().size());
for (auto &action : item.actions())
{
action.planId(newMissionPlan.id);
Database::PpmMissionAction dbAction;
dbAction.missionActionTypeId = action.actionType();
dbAction.missionItemId = dbItem.id;
dbAction.duration = action.duration();
dbAction.name = QString::fromStdString(action.name());
dbAction.properties = QString::fromStdString(action.properties());
dbAction.fulfilled = QDateTime::fromMSecsSinceEpoch(action.fulfilled());
dbAction.elementNumber = action.actionNumber();
if (!m_db->insert(dbAction))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error adding 'Mission Action' to database"));
actionsVec.push_back(dbAction);
}//loop mission actions
m_mapMissionItems.insert(dbItem.id, item);
m_dbMissionActions.push_back(actionsVec); // [elNumber][actionNumber]
}//loop mission items
trans.commit();
m_missionplan.id(newMissionPlan.id);
m_missionplan.name(newMissionPlan.name.toStdString());
m_missionplan.missionStart(newMissionPlan.missionStart.toMSecsSinceEpoch());
m_missionplan.participantId(newMissionPlan.participantId);
m_missionplan.jumpToStart(newMissionPlan.jumpToStart);
m_missionplan.repeat(newMissionPlan.repeat);
m_missionplan.items(itemVec);
m_missionplan.itemsFulfilled(0); // on update items and action entries will be cleared
m_missionplan.fulfilled(0);
DEBUG_LOG(m_logger)<< ">> [UPDATE] mission plan with id( "<<m_missionplan.id()<<" ) Updated !!";
return Core::FunctionReplyObject(m_missionplan.id());
}
Core::FunctionReplyObject ObjectMissionPlan::removeMissionPlan(int id)
{
DBTransaction trans(m_db->database());
if (!m_db->removePpmMissionPlan(id))
{
QString error = QStringLiteral("Could not remove mission plan ! ");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
trans.commit();
m_dbMissionActions.clear();
m_mapMissionItems.clear();
DEBUG_LOG(m_logger)<< ">> [REMOVE] mission Action with id( "<<id<<" ) deleted !!";
return Core::FunctionReplyObject();
}
Core::FunctionReplyObject ObjectMissionPlan::setMissionActionFulfilled(SetMissionActionFulfilledRequest req, qint64 currentTimeMSec)
{
//0- if actionNumb and itemNumb are -1 -> missionplan is aborted. only update missionPlan table with status aborted = true
//1- get db mission action from actionNumber
//2- set mission action as fulfilled
//3- set mission action save in db
//4- update cache data
if (!m_db)
{
ERROR_LOG(m_logger) << "no init yet....";
return Core::FunctionReplyObject("");
}
if (req.missionId() != m_missionplan.id())
{
QString error = QStringLiteral("Wrong missionPlan Object caught Request: %1 / currPlanId: %2").arg(req.missionId()).arg(m_missionplan.id());
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
// Abort in case itemNumb=-1 && actionNumb=-1
if (req.actionNumber() < 0 && req.itemNumber() < 0)
{
m_missionplan.aborted(true);
return setMissionPlanAborted(true);
}
auto &dbActions = m_dbMissionActions[req.itemNumber()]; //[elemNum][actionNum]
if (dbActions.empty()
|| req.actionNumber() >= static_cast<int>(dbActions.size())
|| req.actionNumber() < 0
|| req.itemNumber() < 0) // actionNumber start by 0
{
QString error = QStringLiteral("Could not set mission action with id: %1 ! ").arg(req.actionNumber());
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
Database::PpmMissionAction &dbAction = dbActions[req.actionNumber()];
dbAction.fulfilled = QDateTime::fromMSecsSinceEpoch(currentTimeMSec);
if (!m_db->update(dbAction))
{
QString error = QStringLiteral("Could not update mission action! ");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
//update item in case actions fulfilled
//1- actions count
//2- number of actions fulfilled
//3- check if action count == item.actions.size()
//4- if yes update missioPlan & missionItem fulfilled
MissionItem &msi = m_mapMissionItems[dbAction.missionItemId];
msi.fulfilled(dbAction.fulfilled.toMSecsSinceEpoch());
msi.actionsFulfilled(msi.actionsFulfilled() + 1);
MissionAction &msa = m_mapMissionItems[dbAction.missionItemId].actions()[dbAction.elementNumber];
msa.fulfilled(dbAction.fulfilled.toMSecsSinceEpoch());
m_missionplan.items()[req.itemNumber()] = m_mapMissionItems[dbAction.missionItemId]; // item in MissionPlan updated through elementNumber
/// update fulfilled time for items and mission plan
/// update fulfilled items count
int numbItemFulfilled = 0;
for(const auto &item : m_mapMissionItems)
{
if (item.actionsFulfilled() == static_cast<int>(item.actions().size()))
numbItemFulfilled++;
}
m_missionplan.itemsFulfilled(numbItemFulfilled);
m_missionplan.fulfilled(dbAction.fulfilled.toMSecsSinceEpoch());
return Core::FunctionReplyObject(m_missionplan.id());
}
Core::FunctionReplyObject ObjectMissionPlan::setMissionPlanAborted(bool state)
{
DEBUG_LOG(m_logger)<<" -- [setMissionPlanAborted] abort mission plan: "<<m_missionplan.id();
Database::PpmMissionPlan dbMissionPlan;
// m_db->selectPpmMissionPlan(m_missionplan.id(),dbMissionPlan);
dbMissionPlan.missionStart = QDateTime::fromMSecsSinceEpoch(m_missionplan.missionStart());
dbMissionPlan.jumpToStart = m_missionplan.jumpToStart();
dbMissionPlan.id = m_missionplan.id();
dbMissionPlan.name = QString::fromStdString(m_missionplan.name());
dbMissionPlan.participantId = m_missionplan.participantId();
dbMissionPlan.repeat = m_missionplan.repeat();
dbMissionPlan.aborted = state;
/// update mission plan table
if (!m_db->update(dbMissionPlan))
{
QString error = QStringLiteral("Could not update mission plan! ");
ERROR_LOG(m_logger)<< error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
return Core::FunctionReplyObject(m_missionplan.id());
}
} // namespace UasHandling
} // namespace ParticipantManagement
@@ -0,0 +1,60 @@
/**
* The ObjectMissionPlan class represents a ObjectMissionPlan. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Steve Kemayou, ESZS, 2025-04-08
*/
#pragma once
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/logger.h>
#include <ParticipantManagement/dds_handler.h>
#include "logger.h"
#include <QObject>
namespace ParticipantManagement
{
namespace UasHandling
{
class ObjectMissionPlan : public QObject
{
Q_OBJECT
public:
explicit ObjectMissionPlan(const std::shared_ptr<ParticipantManagement::DdsHandler> &ddsHandler,
const std::shared_ptr<ParticipantManagement::DatabaseInterface> &db,
const ParticipantManagement::MissionPlan &missionPlan,
QObject *parent = nullptr);
int32_t id() const;
int32_t participantId() const;
static Database::PpmMissionPlan toDbMissionPlan(const ParticipantManagement::ModifyMissionPlanRequest &mp);
static MissionPlan fillMissionPlanData(const ModifyMissionPlanRequest &in);
MissionPlan getMissionPlanData() const;
void setDbMissionItems( const QMap <int, ParticipantManagement::Database::PpmMissionItem> &dbItems,
const QMap <int, ParticipantManagement::Database::PpmMissionAction> &dbActions);
const ParticipantManagement::MissionPlan &missionPlan() const;
Core::FunctionReplyObject createMissionPlan(Database::PpmMissionPlan newMissionPlan, const std::vector<MissionItem> &missionItems);
Core::FunctionReplyObject updateMissionPlan(Database::PpmMissionPlan newMissionPlan, const std::vector<MissionItem> &missionItems);
Core::FunctionReplyObject removeMissionPlan(int id);
Core::FunctionReplyObject setMissionActionFulfilled(SetMissionActionFulfilledRequest req, qint64 currentTimeMSec);
Core::FunctionReplyObject setMissionPlanAborted(bool state);
private:
std::shared_ptr<ParticipantManagement::DdsHandler> m_ddsHandler;
std::shared_ptr<ParticipantManagement::DatabaseInterface> m_db;
ParticipantManagement::MissionPlan m_missionplan;
QHash<int /*dbItemId*/, ParticipantManagement::MissionItem > m_mapMissionItems; /// key:dbItemId | Value MissionItem
std::vector<std::vector<ParticipantManagement::Database::PpmMissionAction> > m_dbMissionActions; /// [ItemNumber][ActionNumber]
LOGGER_T m_logger;
};
} // namespace UasHandling
} // namespace ParticipantManagement
+91
View File
@@ -0,0 +1,91 @@
/**
* The Uas class represents a Uas. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-04-08
*/
#include "uas.h"
namespace ParticipantManagement
{
namespace UasHandling
{
bool Uas::hasChanges(const CreateModifyUasRequest &request)
{
bool hasChanges = request.deviceIdentifier() != m_data.deviceIdentifier.toStdString() ||
request.participantId() != m_data.participantId ||
request.exerciseId() != m_data.exerciseId ||
request.hostname() != m_data.hostname.toStdString() ||
request.hostport() != m_data.hostport ||
request.useTcp() != m_data.useTcp;
if (hasChanges)
{
m_data.deviceIdentifier = QString::fromStdString(request.deviceIdentifier());
m_data.participantId = request.participantId();
m_data.exerciseId = request.exerciseId();
m_data.hostname = QString::fromStdString(request.hostname());
m_data.hostport = request.hostport();
m_data.useTcp = request.useTcp();
}
return hasChanges;
}
bool Uas::compareHostname( const QString &hostname)
{
return (m_data.hostname.compare(hostname, Qt::CaseInsensitive) == 0);
}
bool Uas::compareHostPort(int hostport)
{
return (m_data.hostport == hostport);
}
void Uas::unlinkUas()
{
m_data.participantId = 0;
m_data.exerciseId = 0;
}
void Uas::setState(const ProjectDataManagement::UasState &newState)
{
m_data.uasStateId = newState.underlying();
}
int32_t Uas::id() const
{
return m_data.id;
}
int32_t Uas::participantId() const
{
return m_data.participantId;
}
Database::PpmUas Uas::toDbUas() const
{
return m_data;
}
UAS Uas::toDdsUas() const
{
ParticipantManagement::UAS result;
result.id(m_data.id);
result.deviceIdentifier(m_data.deviceIdentifier.toStdString());
result.uasState(static_cast<ProjectDataManagement::UasState::inner_enum>(m_data.uasStateId));
result.participantId(m_data.participantId);
result.exerciseId(m_data.exerciseId);
result.hostname(m_data.hostname.toStdString());
result.hostport(m_data.hostport);
result.sampleTime(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
result.useTcp(m_data.useTcp);
return result;
}
} // namespace ParticipantManagement
} // namespace UasHandling
+43
View File
@@ -0,0 +1,43 @@
/**
* The Uas class represents a Uas. It holds the data and offers some conversion methods.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-04-08
*/
#pragma once
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm_dtc.h>
namespace ParticipantManagement
{
namespace UasHandling
{
class Uas
{
public:
explicit Uas(const Database::PpmUas &data)
: m_data(data)
{}
int32_t id() const;
int32_t participantId() const;
Database::PpmUas toDbUas() const;
UAS toDdsUas() const;
bool hasChanges(const CreateModifyUasRequest &request);
void unlinkUas();
void setState(const ProjectDataManagement::UasState &newState);
bool compareHostname( const QString &hostname);
bool compareHostPort(int hostport);
private:
Database::PpmUas m_data;
};
} // namespace UasHandling
} // namespace ParticipantManagement
@@ -0,0 +1,297 @@
/**
* The UasHandler class manages all Uas related function topics and holds classes for
* further Uas data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-04-08
*/
#include "uas_handler.h"
#include "uas.h"
#include <ngldb/db_service_handler.h>
#include "main_handler.h"
#include "ParticipantHandling/participant.h"
#include <QJsonDocument>
namespace ParticipantManagement
{
namespace UasHandling
{
//------------------------------------------------------------------------------------------
UasHandler::UasHandler(MainHandler *mainHandler)
: QObject()
, CreateModifyUasRequestHandler(mainHandler->exAnyFts())
, RemoveUasRequestHandler(mainHandler->exAnyFts())
, m_mainHandler(mainHandler)
, m_logger(getLogger("UasHandler"))
{
m_mainHandler->exAnyFts()->enableCreateModifyUas();
m_mainHandler->exAnyFts()->enableRemoveUas();
}
//------------------------------------------------------------------------------------------
UasHandler::~UasHandler()
{
qDeleteAll(m_uass);
}
//------------------------------------------------------------------------------------------
bool UasHandler::init()
{
qDeleteAll(m_uass);
m_uass.clear();
m_mainHandler->exAnyDts()->enableUAS();
QVector<Database::PpmUas> uasVec;
// read parties from db
if (!m_mainHandler->db()->select(uasVec))
{
ERROR_LOG(m_logger) << "Error while reading Uas data from database";
return false;
}
for (const Database::PpmUas &currentUas : qAsConst(uasVec))
{
UasHandling::Uas *newUas = new UasHandling::Uas(currentUas);
m_uass.insert(currentUas.id, newUas);
m_mainHandler->exAnyDts()->publish(newUas->toDdsUas());
}
//now the exercise part
m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_mainHandler->dds());
connect(m_exmDtc.get(), &ExerciseManagement::Interface::Dtc::exerciseChanged,
this, &UasHandler::onExerciseChanged);
if (!m_exmDtc->enableExercise())
{
ERROR_LOG(m_logger) << "Could not enable Topic 'Exercise' of EXM DTC";
return false;
}
//now connect to ppm
connect(m_mainHandler->ddsHandler()->dtc().get(), &Interface::Dtc::uASRadioLinkConnectionStatusChanged, this , &UasHandler::onUASRadioLinkConnectionStatusChanged);
m_mainHandler->ddsHandler()->dtc()->enableUASRadioLinkConnectionStatus();
return true;
}
//------------------------------------------------------------------------------------------
void UasHandler::onCreateModifyUasRequest(const rti::core::SampleIdentity &cookie, const CreateModifyUasRequest &request)
{
Core::FunctionReplyObject reply = request.id() == 0 ? createUas(request) : modifyUas(request);
m_mainHandler->exAnyFts()->createModifyUasReply(cookie, reply);
}
void UasHandler::onRemoveUasRequest(const rti::core::SampleIdentity &cookie, const RemoveUasRequest &request)
{
DEBUG_LOG(m_logger) << "onRemoveUasRequest: " << request;
auto ret = removeUas(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_mainHandler->exAnyFts()->removeUasReply(cookie, ret);
}
Core::FunctionReplyObject UasHandler::removeUas(const RemoveUasRequest &request)
{
DEBUG_LOG(m_logger) << "onRemoveUasRequest " << request;
if (request.id() < 0)
return Core::FunctionReplyObject(tr_lts("dds", "Got request to delete an UAS, but no valid UAS id to delete"));
UasHandling::Uas *uasToDelete = m_uass.value(request.id());
if (!uasToDelete)
return Core::FunctionReplyObject(tr_lts("dds", "Could not delete UAS with id %1, because no UAS with that id exists"))
.arg(request.id());
if (uasToDelete->participantId() > 0)
return Core::FunctionReplyObject(tr_lts("dds", "Not possible to delete a UAS in use"));
DEBUG_LOG(m_logger) << "Removing UAS with ID " << request.id();
return removeUas(uasToDelete);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject UasHandler::removeUas(UasHandling::Uas *uasToDelete)
{
const int uasId = uasToDelete->id();
// delete from db
if (!m_mainHandler->db()->removePpmUas(uasId))
return Core::FunctionReplyObject(tr_lts("dds", "Error removing UAS with id %1 from database")).arg(uasId);
// delete from dds
m_mainHandler->exAnyDts()->removeUASByKey(uasId);
// delete from map
m_uass.remove(uasId);
delete uasToDelete;
return Core::FunctionReplyObject(uasId);
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject UasHandler::createUas(const CreateModifyUasRequest &request)
{
DEBUG_LOG(m_logger) << LOGFUNCTION;
// cant check here as we dont have access to all party participants
/*
if (request.participantId() > 0 && !isUasParticipant(request.participantId()))
{
QString error = QStringLiteral("Participant with id %1 is no Uas participant.").arg(request.participantId());
ERROR_LOG(m_logger) << error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}*/
Database::PpmUas dbUas;
dbUas.deviceIdentifier = QString::fromStdString(request.deviceIdentifier());
dbUas.uasStateId = ProjectDataManagement::UasState::Undefined;
dbUas.participantId = request.participantId();
dbUas.exerciseId = request.exerciseId();
dbUas.hostname = QString::fromStdString(request.hostname());
dbUas.hostport = request.hostport();
dbUas.useTcp = request.useTcp();
if (hostNameHostPortExists(dbUas.hostname, dbUas.hostport, -1))
return Core::FunctionReplyObject(tr_lts("dds", "Hostname with Port %1:%2 already exists"))
.arg(dbUas.hostname).arg(dbUas.hostport);
if (!m_mainHandler->db()->insert(dbUas))
{
QString error = QStringLiteral("Could not insert Uas into database");
ERROR_LOG(m_logger) << error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
UasHandling::Uas *newUas = new UasHandling::Uas(dbUas);
m_uass.insert(newUas->id(), newUas);
m_mainHandler->exAnyDts()->publish(newUas->toDdsUas());
return Core::FunctionReplyObject();
}
//------------------------------------------------------------------------------------------
Core::FunctionReplyObject UasHandler::modifyUas(const CreateModifyUasRequest &request)
{
if (!m_uass.contains(request.id()))
{
QString error = QStringLiteral("Uas topic with id %1 not present. Not possible to modify it.").arg(request.id());
ERROR_LOG(m_logger) << error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
Uas *uas = m_uass.value(request.id());
// Check if Hostname and Hostport already existing
if (hostNameHostPortExists(QString::fromStdString(request.hostname()), request.hostport(), uas->id()))
return Core::FunctionReplyObject(tr_lts("dds", "Hostname with Port %1:%2 already exists"))
.arg(request.hostname()).arg(request.hostport());
if (!uas->hasChanges(request))
{
QString error = QStringLiteral("There are no changes in modify request.");
ERROR_LOG(m_logger) << error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
//can't check here))
/*
if (request.participantId() > 0 && !isUasParticipant(request.participantId()))
{
QString error = QStringLiteral("Participant with id %1 is no Uas participant.").arg(request.participantId());
ERROR_LOG(m_logger) << error;
return Core::FunctionReplyObject(error.toStdString().c_str());
}
*/
m_mainHandler->exAnyDts()->publish(uas->toDdsUas());
m_mainHandler->db()->update(uas->toDbUas());
return Core::FunctionReplyObject();
}
//------------------------------------------------------------------------------------------
bool UasHandler::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);
}
void UasHandler::onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification mod)
{
Q_UNUSED(mod)
if (data.state() != ExerciseManagement::ExerciseState::Stopped &&
data.state() != ExerciseManagement::ExerciseState::Terminated &&
data.state() != ExerciseManagement::ExerciseState::Unknown )
return;
//Unlink
for (auto uas : m_uass)
{
auto uasData = uas->toDdsUas();
if (uasData.exerciseId() != data.id())
continue;
uas->unlinkUas();
m_mainHandler->exAnyDts()->publish(uas->toDdsUas());
m_mainHandler->db()->update(uas->toDbUas());
}
}
void UasHandler::onUASRadioLinkConnectionStatusChanged(const UASRadioLinkConnectionStatus &data, DataModification mod)
{
if (data.id() <= 0)
return;
if (!m_uass.contains(data.id()))
return;
auto uas = m_uass.value(data.id());
if (mod == DataModification::eRemoved)
uas->setState(ProjectDataManagement::UasState::Undefined);
else
uas->setState(data.uasState());
m_mainHandler->exAnyDts()->publish(uas->toDdsUas());
m_mainHandler->db()->update(uas->toDbUas());
}
bool UasHandler::hostNameHostPortExists(const QString &hostname, int hostport, int uasId)
{
for (Uas *uas : qAsConst(m_uass))
{
if (uas->id() == uasId)
continue;
if (uas->compareHostname(hostname) && uas->compareHostPort(hostport))
return true;
}
return false;
}
} // namespace UasHandling
} // namespace ParticipantManagement
@@ -0,0 +1,61 @@
/**
* The UasHandler class manages all Uas related function topics and holds classes for
* further Uas data.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-04-08
*/
#pragma once
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <ParticipantManagement/ppm_fts.h>
#include <ExerciseManagement/exm_dtc.h>
#include "logger.h"
namespace ParticipantManagement
{
class MainHandler;
namespace UasHandling
{
class Uas;
class UasHandler final:
public QObject
, protected Interface::Fts::CreateModifyUasRequestHandler
, protected Interface::Fts::RemoveUasRequestHandler
{
Q_OBJECT
public:
explicit UasHandler(MainHandler *mainHandler);
~UasHandler();
bool init();
void onCreateModifyUasRequest(const rti::core::SampleIdentity &cookie, const CreateModifyUasRequest &request) override final;
void onRemoveUasRequest(const rti::core::SampleIdentity &cookie, const RemoveUasRequest &request) override final;
private slots:
void onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification mod);
void onUASRadioLinkConnectionStatusChanged(const UASRadioLinkConnectionStatus &data, DataModification mod);
private:
Core::FunctionReplyObject removeUas(const RemoveUasRequest &request);
Core::FunctionReplyObject removeUas(UasHandling::Uas *uasToDelete);
Core::FunctionReplyObject createUas(const CreateModifyUasRequest &request);
Core::FunctionReplyObject modifyUas(const CreateModifyUasRequest &request);
bool isUasParticipant(int participantId) const;
bool hostNameHostPortExists(const QString &host, int hostport, int uasId);
MainHandler *m_mainHandler;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
QHash<int32_t, Uas *> m_uass;
LOGGER_T m_logger;
};
} // namespace UasHandling
} // namespace ParticipantManagement
@@ -0,0 +1,129 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "client_message.h"
#include "WebSocketInterface/Messages/reply_message.h"
#include "WebSocketInterface/json_keys.h"
#include "WebSocketInterface/data_manager.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QUuid>
#include <QJsonObject>
namespace ParticipantManagement
{
namespace WebSocketInterface
{
namespace Messages
{
using namespace JsonKeys;
// ####### ClientMessageFactory begin #######
ClientMessageFactory::ClientMessageFactory(DataManager *dataManager)
: LtsWebSocket::AbstractClientMessageFactory(getLogger("ClientMessageFactory"))
, m_dataManager(dataManager)
{}
LtsWebSocket::AbstractClientMessage *ClientMessageFactory::createMessage(LtsWebSocket::ReplyHandler *client,
const QString &message)
{
LtsWebSocket::AbstractClientMessage *result = checkAttributes(client, message);
if (result)
return result;
if (m_type == OcBaseDownload::WebSocketInterface::JsonKeys::BASE_DL_REQUEST())
result = new BaseDlRequest(m_dataManager, client, m_type, m_typeId, m_uuid); // Todo: Check whether typeId is correct
if (m_type == ParticipantManagement::WebSocketInterface::JsonKeys::OC_POSITION_DATA()) // Todo: Check whether typeId is correct
result = new OcPositionRequest(m_dataManager, client, m_type, m_typeId, m_uuid, QJsonDocument::fromJson(message.toUtf8()).object().value(QStringLiteral("data")).toObject() );
/********************************************************
* Warning: 6006 has been negotiated with ESG for ADLER *
********************************************************/
if (m_type == ParticipantManagement::WebSocketInterface::JsonKeys::PARTY_BASE_DATA())
result = new PartyRequest(m_dataManager, client, m_type, 6006, m_uuid); // Todo: Check whether typeId is correct
return checkResult(client, message, result);
}
// ####### ClientMessageFactory end #######
// ####### ClientMessage begin #######
ClientMessage::ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonObject &data)
: LtsWebSocket::AbstractClientMessage(client, type, typeId, uuid, data)
, m_dataManager(dataManager)
{}
// ####### ClientMessage end #######
// ####### PartyRequest begin #######
PartyRequest::PartyRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
: ClientMessage(dataManager, client, type, typeId, uuid)
{}
void PartyRequest::process()
{
m_client->addReply(m_dataManager->partyBaseData(m_uuid));
}
bool PartyRequest::hasValidData() const
{
return true;
}
// ####### BaseDlRequest begin #######
BaseDlRequest::BaseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
: ClientMessage(dataManager, client, type, typeId, uuid)
{}
void BaseDlRequest::process()
{
if (m_dataManager->exerciseId() == 0) // equipment type, type and participant template only in ex_0
{
m_client->addReply(m_dataManager->equipmentTypeBaseData(m_uuid));
m_client->addReply(m_dataManager->typeBaseData(m_uuid));
}
else
{
m_client->addReply(m_dataManager->partyBaseData(m_uuid));
m_client->addReply(m_dataManager->participantBaseData(m_uuid));
}
}
bool BaseDlRequest::hasValidData() const
{
return true;
}
OcPositionRequest::OcPositionRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonObject &data)
: ClientMessage(dataManager, client, type, typeId, uuid, data )
{
const auto val = data.value(EQUIPMENT_NR());
if (val.toInt() > 0)
m_equipmentNr = val.toInt();
}
void OcPositionRequest::process()
{
if (m_dataManager->exerciseId() != 0) // equipment type, type and participant template only in ex_0
{
m_client->addReply(m_dataManager->ocPositionData(m_equipmentNr, m_uuid));
}
}
bool OcPositionRequest::hasValidData() const
{
return m_equipmentNr > 0;
}
// ####### BaseDlRequest end #######
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,102 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QtCore/QJsonObject>
#include <LtsWebSocket/client_message_factory.h>
#include <LtsWebSocket/client_message.h>
#include "logger.h"
namespace ParticipantManagement
{
namespace WebSocketInterface
{
class DataManager;
namespace Messages
{
/**
* @brief The ClientMessageFactory class creates ClientMessages which will then be processed
*/
class ClientMessageFactory final : public LtsWebSocket::AbstractClientMessageFactory
{
public:
explicit ClientMessageFactory(DataManager *dataManager);
/**
* @brief create Only way to create a ClientMessage object
* @param message Text message which will be converted to a ClientMessage object
* @return A ClientMessage object, which represents a concrete ClientMessage
*/
LtsWebSocket::AbstractClientMessage *createMessage(LtsWebSocket::ReplyHandler *client,
const QString &message);
private:
DataManager *m_dataManager;
}; // class ClientMessageFactory
/**
* @brief The ClientMessage class is an abstract base class for concrete ClientMessage objects
*/
class ClientMessage : public LtsWebSocket::AbstractClientMessage
{
protected: // method
explicit ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonObject &data = QJsonObject());
protected: // attributes
DataManager *m_dataManager;
}; // class ClientMessage
/**
* @brief The PartyRequest class handles the party DL request and sends multiple all party data for the exercise
*/
class PartyRequest : public ClientMessage
{
public:
explicit PartyRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
void process();
bool hasValidData() const;
}; // class BaseDlRequest
/**
* @brief The BaseDlRequest class handles the base DL request and sends multiple replys depending on the data requested
*/
class BaseDlRequest : public ClientMessage
{
public:
explicit BaseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
void process();
bool hasValidData() const;
}; // class BaseDlRequest
/**
* @brief The BaseDlRequest class handles the base DL request and sends multiple replys depending on the data requested
*/
class OcPositionRequest : public ClientMessage
{
public:
explicit OcPositionRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonObject &data);
void process();
bool hasValidData() const;
private:
int m_equipmentNr = 0;
}; // class BaseDlRequest
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,67 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "reply_message.h"
#include <LtsWebSocket/json_keys.h>
#include "WebSocketInterface/json_keys.h"
namespace ParticipantManagement
{
namespace WebSocketInterface
{
namespace Messages
{
using namespace JsonKeys;
using namespace LtsWebSocket::JsonKeys;
// ####### EquipmentTypeBaseData begin #######
EquipmentTypeBaseData::EquipmentTypeBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (EQUIPMENT_TYPE_BASE_DATA(), 1, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### EquipmentTypeBaseData end #######
// ####### TypeBaseData begin #######
TypeBaseData::TypeBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (TYPE_BASE_DATA(), 2, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### TypeBaseData end #######
// ####### PartyBaseData begin #######
/********************************************************
* Warning: 6006 has been negotiated with ESG for ADLER *
********************************************************/
PartyBaseData::PartyBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (PARTY_BASE_DATA(), 6006, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### PartyBaseData end #######
// ####### ParticipantBaseData begin #######
ParticipantBaseData::ParticipantBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (PARTICIPANT_BASE_DATA(), 4, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
OcPositionData::OcPositionData(const QJsonObject &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (OC_POSITION_DATA(), 5, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### ParticipantBaseData end #######
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,89 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <LtsWebSocket/reply_message.h>
#include <QJsonArray>
#include <QString>
namespace ParticipantManagement
{
namespace WebSocketInterface
{
namespace Messages
{
/**
* @brief The EquipmentTypeBaseData class equipment type data for base DL
*/
class EquipmentTypeBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit EquipmentTypeBaseData(const QJsonArray &data, const QString &uuid);
}; // class EquipmentTypeBaseData
/**
* @brief The TypeBaseData class type data for base DL
*/
class TypeBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit TypeBaseData(const QJsonArray &data, const QString &uuid);
}; // class TypeBaseData
/**
* @brief The ParticipantTemplateBaseData class participant template data for base DL
*/
class ParticipantTemplateBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ParticipantTemplateBaseData(const QJsonArray &data, const QString &uuid);
}; // class ParticipantTemplateBaseData
/**
* @brief The PartyBaseData class party data for base DL
*/
class PartyBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit PartyBaseData(const QJsonArray &data, const QString &uuid);
}; // class PartyBaseData
/**
* @brief The ParticipantOrganisationBaseData class participant organisation data for base DL
*/
class ParticipantOrganisationBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ParticipantOrganisationBaseData(const QJsonArray &data, const QString &uuid);
}; // class ParticipantOrganisationBaseData
/**
* @brief The ParticipantBaseData class participant data for base DL
*/
class ParticipantBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ParticipantBaseData(const QJsonArray &data, const QString &uuid);
}; // class ParticipantBaseData
/**
* @brief The ParticipantBaseData class participant data for base DL
*/
class OcPositionData : public LtsWebSocket::ReplyMessage
{
public:
explicit OcPositionData(const QJsonObject &data, const QString &uuid);
}; // class ParticipantBaseData
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* The DataManager class takes strucure element data from the HandlerStructureElement,
* offers an interface to get and set structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "data_manager.h"
#include "Messages/reply_message.h"
#include "main_handler.h"
namespace ParticipantManagement
{
namespace WebSocketInterface
{
//---------------------------------------------------------------------------------------------------------
DataManager::DataManager(int exerciseId, const std::shared_ptr<DatabaseInterface> &dbInterface, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtc)
: LtsWebSocket::DataManagerBaseDl<DatabaseInterface>(dbInterface, getLogger(QStringLiteral("DataManager")))
, m_exerciseId(exerciseId)
, m_ppmDtc(dtc)
{}
//---------------------------------------------------------------------------------------------------------
LtsWebSocket::ReplyMessage *DataManager::equipmentTypeBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::PpmEquipmentType, Messages::EquipmentTypeBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::typeBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::PpmType, Messages::TypeBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::partyBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::PpmParty, Messages::PartyBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::participantBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::PpmParticipant, Messages::ParticipantBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::ocPositionData(int equipmentNr, const QString &uuid) const
{
if (!m_ppmDtc->isParticipantPositionEnabled() || !m_ppmDtc->isParticipantEnabled())
{
m_ppmDtc->enableParticipantPosition();
m_ppmDtc->enableParticipant();
}
ParticipantPosition pos = m_ppmDtc->participantPosition(m_ppmDtc->participantIdByEquipmentNumber(equipmentNr));
QJsonObject obj;
obj[QStringLiteral("participantId")] = pos.participantId();
obj[QStringLiteral("lat")] = pos.pos().lat();
obj[QStringLiteral("lon")] = pos.pos().lon();
obj[QStringLiteral("height")] = pos.pos().z();
obj[QStringLiteral("roomId")] = pos.roomId();
obj[QStringLiteral("buildingId")] = pos.buildingId();
return new ParticipantManagement::WebSocketInterface::Messages::OcPositionData(obj, uuid);
}
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* The DataManager class is a convenience class for managing structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <LtsWebSocket/data_manager_base_dl.h>
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm_dtc.h>
#include "logger.h"
namespace ParticipantManagement
{
class MainHandler;
namespace WebSocketInterface
{
/**
* @brief The DataManager class is a convenience class for managing structure data
*/
class DataManager final : public LtsWebSocket::DataManagerBaseDl<DatabaseInterface>
{
public:
/**
* @brief DataManager Constructor
* @param mainHandler Source for needed handlers
*/
explicit DataManager(int exerciseId,
const std::shared_ptr<DatabaseInterface> &dbInterface,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtc);
int exerciseId() const { return m_exerciseId; }
LtsWebSocket::ReplyMessage *equipmentTypeBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *typeBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *partyBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *participantBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *ocPositionData(int equipmentNr, const QString &uuid) const;
private:
int m_exerciseId;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
}; // class DataManager
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Header file which holds all occuring JSON keys
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QString>
#include <OcBaseDownload/WebSocketInterface/json_keys.h>
namespace ParticipantManagement
{
namespace WebSocketInterface
{
/**
* @brief Namespace which holds all occuring JSON keys as const QString objects
*/
namespace JsonKeys
{
// types for client --> server messages
inline QString EQUIPMENT_NR() { return QStringLiteral("equipmentNr"); }
// types for server --> client messages
inline QString EQUIPMENT_TYPE_BASE_DATA() { return QStringLiteral("equipmentTypeBaseData"); }
inline QString TYPE_BASE_DATA() { return QStringLiteral("typeBaseData"); }
inline QString PARTY_BASE_DATA() { return QStringLiteral("partyBaseData"); }
inline QString PARTICIPANT_BASE_DATA() { return QStringLiteral("participantBaseData"); }
inline QString OC_POSITION_DATA() { return QStringLiteral("ocPositionData"); }
} // namespace JsonKeys
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "websocket_interface_handler.h"
#include "main_handler.h"
#include "data_manager.h"
#include "WebSocketInterface/Messages/client_message.h"
#include "settings.h"
namespace ParticipantManagement
{
namespace WebSocketInterface
{
WebSocketInterfaceHandler::WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent)
: QObject(parent)
, m_webSocketServer(nullptr)
, m_messageFactory(nullptr)
, m_dataManager(nullptr)
, m_mainHandler(mainHandler)
, m_logger(getLogger("WebSocketInterfaceHandler"))
{
}
WebSocketInterfaceHandler::~WebSocketInterfaceHandler()
{
cleanup();
}
bool WebSocketInterfaceHandler::init()
{
cleanup();
DEBUG_LOG(m_logger) << "... ok";
m_dataManager = new DataManager(m_mainHandler->exerciseId(), m_mainHandler->db(), m_mainHandler->ddsHandler()->dtc());
m_messageFactory = new Messages::ClientMessageFactory(m_dataManager);
m_webSocketServer = new LtsWebSocket::LtsWebSocketServer(m_messageFactory, this);
DEBUG_LOG(m_logger) << "Initialising LtsWebSocketServer";
if (!m_webSocketServer->init(QStringLiteral("PPM"), Settings::getWebSocketServerPortRange(1)))
{
ERROR_LOG(m_logger) << "Failed to initialise LtsWebSocketServer";
return false;
}
DEBUG_LOG(m_logger) << "... ok";
DEBUG_LOG(m_logger) << "Connection signals and slots";
return true;
}
void WebSocketInterfaceHandler::cleanup()
{
delete m_webSocketServer;
delete m_messageFactory;
delete m_dataManager;
}
} // namespace WebSocketInterface
} // namespace ParticipantManagement
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QtCore/QObject>
#include <LtsWebSocket/lts_web_socket_server.h>
#include "logger.h"
namespace ParticipantManagement
{
class MainHandler;
namespace WebSocketInterface
{
namespace Messages
{
class ClientMessageFactory;
}
class DataManager;
/**
* @brief The WebSocketInterfaceHandler class is the main class for the WebSocketInterface
*/
class WebSocketInterfaceHandler final : public QObject
{
public:
/**
* @brief WebSocketInterfaceHandler Constructor
* @param mainHandler MainHandler to get the HandlerStructureElement from
*/
explicit WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent = nullptr);
/**
* @brief ~WebSocketInterfaceHandler Destructor
*/
~WebSocketInterfaceHandler();
/**
* @brief init Initialises all needed classes for the WebSocketInterface
* @return True on success, otherwise false
*/
bool init();
/**
* @brief webSocketPort returns the listen port
* @return listen port
*/
unsigned short webSocketPort() { return m_webSocketServer->webSocketPort(); }
private:
void cleanup();
private:
LtsWebSocket::LtsWebSocketServer *m_webSocketServer;
Messages::ClientMessageFactory *m_messageFactory;
DataManager *m_dataManager;
MainHandler *m_mainHandler;
LOGGER_T m_logger;
}; // class WebSocketInterfaceHandle
} // namespace WebSocketInterface
} // namespace ParticipantManagement
+57
View File
@@ -0,0 +1,57 @@
/**
* Convert db structures to dds structures
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-07-20
*/
#pragma once
#include <ParticipantManagement/ppm_db.h>
#include <ParticipantManagement/ppm.hpp>
#include <LtsUtilsCore/dds_vector.h>
#include <limits>
namespace ParticipantManagement
{
namespace Database
{
inline Type convert(const PpmType &data)
{
Type ddsData;
ddsData.id(data.id);
ddsData.typeClass(data.typeClassId);
ddsData.equipmentTypeId(data.equipmentTypeId.isNull() ? 0 : data.equipmentTypeId.toInt());
ddsData.name(data.name.toStdString());
ddsData.symbol(data.symbol.toStdString());
ddsData.radioCount(data.radioCount);
ddsData.hierarchyLevel(data.hierarchyLevel);
ddsData.isDeleted(data.isDeleted);
#ifdef Q_OS_WIN
ddsData.disEntities(data.disEntities());
#else
ddsData.disEntities(LtsUtils::convert<std::vector<rti::core::int64>>(data.disEntities()));
#endif
ddsData.properties(data.properties.toStdString());
return ddsData;
}
inline EquipmentType convert(const PpmEquipmentType &db)
{
EquipmentType et;
et.id(db.id);
et.name(db.name.toStdString());
{ // equipment value are save in the db as int, but in dds unsigned short is used.
et.minEquipNo(db.minEquipNo < USHRT_MAX ? db.minEquipNo : USHRT_MAX);
et.maxEquipNo(db.maxEquipNo < USHRT_MAX ? db.maxEquipNo : USHRT_MAX);
}
et.traVulnerabilityModelId(db.traVulnerabilityModelId);
et.traPlayerTypeId(db.traPlayerTypeId);
et.wmsTypeClass(db.wmsTypeClassId);
et.deletedInWms(db.deletedInWms);
return et;
}
}
}
+163
View File
@@ -0,0 +1,163 @@
/**
* DdsHandler for PPM
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#include "dds_handler.h"
#include "exercise_time.h"
#include <DDS/Core/pacemaker.h>
#include <DDS/DTS/generic_dts_impl.h>
#include <QThread>
namespace ParticipantManagement
{
DdsHandler::DdsHandler(int domainId, int exerciseId)
: m_ddsConnector(std::make_shared<DDSConnector::DdsConnector>(domainId, exerciseId <= 0 ? IDL::PartitionType::Global : IDL::PartitionType::Exercise, exerciseId))
, m_logger(getLogger("DdsHandler"))
{
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
}
DdsHandler::~DdsHandler()
{
m_exerciseTime = nullptr;
m_pacemaker = nullptr;
m_ddsAnyExDts = nullptr;
m_ddsAnyExFts = nullptr;
m_ddsFts = nullptr;
m_ddsDts = nullptr;
m_ddsDtc = nullptr;
m_topicLockMaster = nullptr;
m_ddsConnector = nullptr;
}
bool DdsHandler::init(const QStringList &qosFileList)
{
if (!m_ddsConnector->init(qosFileList))
{
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
return false;
}
// default initialize Publisher and Subscriber
m_ddsConnector->initPublisherSubscriber();
m_ddsConnector->enableBatchedWriter(false);
// Pacemaker
m_pacemaker = std::make_shared<Core::Pacemaker>(m_ddsConnector);
m_topicLockMaster = std::make_shared<Core::TopicLockMaster>(m_ddsConnector);
// Core::PartitionTime -> ExerciseTime
std::shared_ptr<DTS::PartitionTime> dtsPartitionTime;
dtsPartitionTime.reset(m_ddsConnector->createDTS<DTS::PartitionTimeImpl>());
m_exerciseTime = std::make_shared<ExerciseTime>(dtsPartitionTime);
ERROR_LOG(m_logger) << "OKKKKK THIS THE NEW MAIN HANDLER DDS " << this->thread();
m_ddsDts = ParticipantManagement::Interface::Dts::create(m_ddsConnector);
m_ddsFts = ParticipantManagement::Interface::Fts::create(m_ddsConnector);
m_ddsDtc = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(m_ddsConnector);
m_ddsDtc->enableEquipmentExerciseMapping(true);
const int exerciseId = m_ddsConnector->getPartitionId();
m_exAnyConnector = std::make_shared<DDSConnector::DdsConnector>(m_ddsConnector->getDomainParticipant(), IDL::PartitionType::Any);
if (!m_exAnyConnector->init(qosFileList))
{
ERROR_LOG(m_logger) << "Could not init Dds Connector with IDL::PartitionType::Any";
return false;
}
m_exAnyConnector->initPublisherSubscriber();
m_ddsAnyExDts = ParticipantManagement::Interface::Dts::create(m_exAnyConnector);
m_ddsAnyExDts->enableEquipmentExerciseMapping(true);
m_ddsAnyExDts->enableUASRadioLinkConnectionStatus(true);
if (exerciseId <= 0)
{
DEBUG_LOG(m_logger) << "Creating * FTS: " << m_exAnyConnector->getDefaultPartition();
m_ddsAnyExFts = ParticipantManagement::Interface::Fts::create(m_exAnyConnector);
// do not enable anything here - enable where it is used (inside type handling) to make sure
// no other service get a subscription matched information without actually having data
}
const bool bExe = exerciseId > 0; // ex_N
const bool bGlob = exerciseId <= 0; // ex_0
if (!m_ddsDts->enableBite(true) ||
!m_ddsDts->enableDynamicParticipantData(bExe) ||
!m_ddsDts->enableEquipmentType(false) || // published in IDL::PartitionType::Any
!m_ddsDts->enableParticipantGearEvent(bExe) ||
!m_ddsDts->enableEquipmentExerciseMapping(false) || // published in IDL::PartitionType::Any
!m_ddsDts->enableParticipant() ||
!m_ddsDts->enableParticipantPosition(bExe) ||
!m_ddsDts->enableParticipantOrganisation(bExe) ||
!m_ddsDts->enableTargetInformation(bExe) ||
!m_ddsDts->enableParty(bGlob || bExe) ||
!m_ddsDts->enableTemplateParticipant(false) || // published in IDL::PartitionType::Any
!m_ddsDts->enableType(false) || // published in IDL::PartitionType::Any
!m_ddsDts->enableCtaError() ||
!m_ddsDts->enableParticipantMode(bExe) ||
!m_ddsDts->enableEquipmentSystemEvent(bExe) ||
!m_ddsDts->enableHitLineInfo(bExe) ||
!m_ddsDts->enableUnitPosition() ||
!m_ddsDts->enableUAS(bGlob || bExe) ||
!m_ddsDts->enableMissionPlan(bExe)
)
{
ERROR_LOG(m_logger) << "Error initializing DTS";
return false;
}
if (!m_ddsFts->enableCallBite() ||
!m_ddsFts->enableChangePartyUsedState(bExe) || // unused?
!m_ddsFts->enableEquipmentSystemEventData() ||
!m_ddsFts->enableGetParticipants() ||
!m_ddsFts->enableInitParticipant() ||
!m_ddsFts->enableModifyEquipmentTypeData(false) || // published in IDL::PartitionType::Any
!m_ddsFts->enableModifyParticipantData() ||
!m_ddsFts->enableModifyPartyData() ||
!m_ddsFts->enableModifyTemplateData(bGlob) ||
!m_ddsFts->enableModifyType(false) || // published in IDL::PartitionType::Any
!m_ddsFts->enableModifyState() ||
!m_ddsFts->enableRegisterParticipant() ||
!m_ddsFts->enableModifyTargetInformation() ||
!m_ddsFts->enableRemoveParty() ||
!m_ddsFts->enableRemoveType(false) || // published in IDL::PartitionType::Any
!m_ddsFts->enableSendMessage() ||
!m_ddsFts->enableSetParticipantMode() ||
!m_ddsFts->enableSyncParties(bExe) ||
!m_ddsFts->enableSetSimulatedPosition(bExe) ||
!m_ddsFts->enableSetSimulatedPositionRelative(bExe) ||
!m_ddsFts->enableSetSimulatedParticipantFormation(bExe) ||
!m_ddsFts->enableSetSanitation(bExe) ||
!m_ddsFts->enableSelectiveReammunition(bExe) ||
!m_ddsFts->enableSetAsSimulated(bExe) ||
!m_ddsFts->enableOcCommand(bExe) ||
!m_ddsFts->enableSetAsSimulated(bExe) ||
!m_ddsFts->enableModifyMissionPlan(bExe)||
!m_ddsFts->enableRemoveMissionPlan(bExe) ||
!m_ddsFts->enableSetMissionActionFulfilled(bExe)||
!m_ddsFts->enableModifyUasState(bExe)
)
{
ERROR_LOG(m_logger) << "Error initializing FTS";
return false;
}
m_pdmDtc->enableChargeType(true);
return true;
}
void DdsHandler::longActionBreak()
{
// m_pacemaker->publishState(); //not threadSafe!
// m_exerciseTime->publishPartitionTime();
}
} // namespace ParticipantManagement
+70
View File
@@ -0,0 +1,70 @@
/**
* DdsHandler for PPM
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/topic_lock_master.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_dts.h>
#include <ParticipantManagement/ppm_fts.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include "logger.h"
#include <QObject>
namespace Core
{
class Pacemaker;
}
namespace ParticipantManagement
{
class ExerciseTime;
class DdsHandler: public QObject
{
Q_OBJECT
public:
DdsHandler(int domainId, int exerciseId);
~DdsHandler();
bool init(const QStringList &qosFileList = QStringList());
std::shared_ptr<Core::Pacemaker> pacemaker() const { return m_pacemaker; }
std::shared_ptr<Core::TopicLockMaster> topicLockMaster() const { return m_topicLockMaster; }
std::shared_ptr<ParticipantManagement::Interface::Dtc> dtc() const { return m_ddsDtc; }
std::shared_ptr<ParticipantManagement::Interface::Dts> dts() const { return m_ddsDts; }
std::shared_ptr<ParticipantManagement::Interface::Fts> fts() const { return m_ddsFts; }
std::shared_ptr<ParticipantManagement::Interface::Dts> exAnyDts() const { return m_ddsAnyExDts; }
std::shared_ptr<ParticipantManagement::Interface::Fts> exAnyFts() const { return m_ddsAnyExFts; }
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const { return m_pdmDtc; }
std::shared_ptr<DDSConnector::DdsConnector> dds() const { return m_ddsConnector; }
// this should be called when a long action is in progress to
// not run into a timeout. TODO: it would be much better to
// move PaceMaker and ExerciseTime into an own thread instead this hack
void longActionBreak();
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<DDSConnector::DdsConnector> m_exAnyConnector;
std::shared_ptr<ParticipantManagement::Interface::Dts> m_ddsDts;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ddsDtc;
std::shared_ptr<ParticipantManagement::Interface::Fts> m_ddsFts;
std::shared_ptr<ParticipantManagement::Interface::Dts> m_ddsAnyExDts;
std::shared_ptr<ParticipantManagement::Interface::Fts> m_ddsAnyExFts;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<Core::Pacemaker> m_pacemaker;
std::shared_ptr<Core::TopicLockMaster> m_topicLockMaster;
std::shared_ptr<ExerciseTime> m_exerciseTime;
NGL::INGLLogger &m_logger;
};
}
+54
View File
@@ -0,0 +1,54 @@
/**
* Current Time for exercise
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-08-01
*/
#include "exercise_time.h"
#include "ppm_dts.h"
#include <QDateTime>
#include <DDS/DTS/generic_dts_impl.h>
namespace ParticipantManagement
{
ExerciseTime::ExerciseTime(const std::shared_ptr<DTS::PartitionTime> &partitionTime)
: QObject(nullptr)
, m_dtsPartitionTime(partitionTime)
, m_exerciseTimer(new QTimer())
, m_thread(new QThread())
{
m_exerciseTimer->moveToThread(m_thread);
this->moveToThread(m_thread);
m_exerciseTimer->setInterval(1000);
connect(m_thread, &QThread::started, this, [this]()
{
m_exerciseTimer->start(1000);
});
connect(m_exerciseTimer, &QTimer::timeout, this, &ExerciseTime::publishPartitionTime);
connect(m_exerciseTimer, &QTimer::destroyed, m_thread, &QThread::deleteLater);
m_thread->start();
//m_exerciseTimer->start(1000);
}
ExerciseTime::~ExerciseTime()
{
// m_exerciseTimer->moveToThread(this->thread());
m_exerciseTimer->deleteLater();
//m_thread->quit();
// m_exerciseTimer->stop();
}
void ExerciseTime::publishPartitionTime()
{
const qint64 exerciseTime = QDateTime::currentMSecsSinceEpoch();
Core::PartitionTime currentTime(exerciseTime);
m_dtsPartitionTime->write(currentTime);
//m_exerciseTimer->start();
//qWarning() << "This is thread " << this->thread() << " send time";
}
}
+34
View File
@@ -0,0 +1,34 @@
/**
* Current Time for exercise
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2016-08-01
*/
#pragma once
#include <QThread>
#include <QTimer>
#include <DDS/Core/core_dts_types.h>
#include "dds_handler.h"
namespace ParticipantManagement
{
class ExerciseTime final : public QObject
{
Q_OBJECT
public:
explicit ExerciseTime(const std::shared_ptr<DTS::PartitionTime> &partitionTime);
~ExerciseTime();
public Q_SLOTS:
void publishPartitionTime();
private:
std::shared_ptr<DTS::PartitionTime> m_dtsPartitionTime;
QTimer *m_exerciseTimer;
QThread *m_thread;
};
}
+98
View File
@@ -0,0 +1,98 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: ParticipantManagement
###########################################################
#log4j.logger.PPM=DEBUG
#log4j.logger.PPM.BiteHandler=DEBUG
#log4j.logger.PPM.ClientMessageFactory=DEBUG
#log4j.logger.PPM.DataManager=DEBUG
#log4j.logger.PPM.DdsHandler=DEBUG
#log4j.logger.PPM.EquipmentEventHandler=DEBUG
#log4j.logger.PPM.GetParticipants=DEBUG
#log4j.logger.PPM.MainHandler=DEBUG
#log4j.logger.PPM.Participant=DEBUG
#log4j.logger.PPM.ParticipantForReplay=DEBUG
#log4j.logger.PPM.ParticipantHandler=DEBUG
#log4j.logger.PPM.ParticipantHandlerForReplay=DEBUG
#log4j.logger.PPM.PartyHandler=DEBUG
#log4j.logger.PPM.ProxyListener=DEBUG
#log4j.logger.PPM.TemplateHandler=DEBUG
#log4j.logger.PPM.Test=DEBUG
#log4j.logger.PPM.TypeHandler=DEBUG
#log4j.logger.PPM.TypeHandlerExercise=DEBUG
#log4j.logger.PPM.TypeHandlerGlobal=DEBUG
#log4j.logger.PPM.UnitPositionCalculator=DEBUG
#log4j.logger.PPM.WebSocketInterfaceHandler=DEBUG
#log4j.logger.PPM.MissionPlanHandler=DEBUG
#log4j.logger.PPM.ObjectMissionPlan=DEBUG
# File
###########################################################
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
+37
View File
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace ParticipantManagement
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "PPM.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace ParticipantManagement
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "ppm_export.h"
namespace ParticipantManagement
{
using LOGGER_T = NGL::INGLLogger &;
PPM_COMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace ParticipantManagement
+42
View File
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* main for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include "main_handler.h"
#include "logger.h"
#include "settings.h"
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
LtsCommandLineParser::LtsCommandLineParser options;
if (!options.init())
return -1;
app.createLogger("PPM", options.exerciseId());
ParticipantManagement::Settings::setIniFile(options.iniFile());
int iRet = -3;
{
std::unique_ptr<ParticipantManagement::MainHandler> mainHandler(new ParticipantManagement::MainHandler);
if (!mainHandler->init(options.domainId(), options.exerciseId()))
return iRet;
STARTUP_LOG(app.baseLogger());
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
+206
View File
@@ -0,0 +1,206 @@
/**
* MainHandler for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 19.05.2016
*/
#include "main_handler.h"
#include "TypeHandling/type_handler.h"
#include "UasHandling/mission_plan_handler.h"
#include "UasHandling/uas_handler.h"
#include "WebSocketInterface/websocket_interface_handler.h"
#include "settings.h"
#include <DDS/Core/pacemaker.h>
#include <ngldb/db_service_handler.h>
namespace ParticipantManagement
{
MainHandler::MainHandler() : m_logger(getLogger("MainHandler")) {}
MainHandler::~MainHandler()
{
m_getParticipants = nullptr;
m_partyHandler = nullptr;
m_templateHandler = nullptr;
m_biteHandler = nullptr;
m_participantHandler = nullptr;
m_tabFtc = nullptr;
m_tabDtc = nullptr;
m_aesFtc = nullptr;
m_equipmentEventHandler = nullptr;
m_typeHandler = nullptr;
m_websocketHandler = nullptr;
m_uasHandler = nullptr;
m_dbHandlerEx0 = nullptr;
m_dbHandler = nullptr;
m_ddsHandler = nullptr;
}
bool MainHandler::init(int domainId, int exerciseId)
{
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId);
if (!m_ddsHandler->init())
{
ERROR_LOG(m_logger) << "Could not init DdsHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init DdsHandler"));
return false;
}
DEBUG_LOG(m_logger) << "DdsHandler initialized";
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(exerciseId, Settings::settings()))
{
ERROR_LOG(m_logger) << "Could not init DbHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init DbHandler"));
return false;
}
if (exerciseId > 0)
{
m_dbHandlerEx0 = std::make_shared<DbHandler>();
if (!m_dbHandlerEx0->init(0, Settings::settings())) // need a second db connection due to username/password
{
ERROR_LOG(m_logger) << "Could not init DbHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init DbHandler"));
return false;
}
}
else
{
m_dbHandlerEx0 = m_dbHandler;
}
DEBUG_LOG(m_logger) << "DbHandler initialized";
m_typeHandler = TypeHandling::TypeHandler::createInstance(this, m_logger);
if (!m_typeHandler->init())
{
ERROR_LOG(m_logger) << "Could not init TypeHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init TypeHandler"));
return false;
}
DEBUG_LOG(m_logger) << "TypeHandler initialized";
m_partyHandler = std::make_shared<PartyHandling::PartyHandler>(this);
if (!m_partyHandler->init())
{
ERROR_LOG(m_logger) << "Could not init PartyHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init PartyHandler"));
return false;
}
DEBUG_LOG(m_logger) << "PartyHandler initialized";
m_tabFtc = TrainingAreaBase::Interface::Ftc::create(m_ddsHandler->dds());
if (!m_tabFtc->enableAll())
{
ERROR_LOG(m_logger) << "Could not init TrainingAreaBase FTC";
return false;
}
DEBUG_LOG(m_logger) << "TrainingAreaBase FTC initialized";
m_aesFtc = AreaEffectSimulation::Interface::Ftc::create(m_ddsHandler->dds());
if (!m_aesFtc->enableChargeAdministrationModify())
{
ERROR_LOG(m_logger) << "Could not init AreaEffectSimulation FTC";
return false;
}
m_participantHandler = std::make_shared<ParticipantHandling::ParticipantHandlerForExercise>(this);
if (!m_participantHandler->init())
{
ERROR_LOG(m_logger) << "Could not init ParticipantHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init ParticipantHandler"));
return false;
}
DEBUG_LOG(m_logger) << "ParticipantHandler initialized";
m_ddsHandler->pacemaker()->addPort(m_participantHandler->getWebSocketPort(), Core::PortType::WebSocketPositionServer);
m_targetInformationHandler = std::make_shared<ParticipantManagement::HandlerTargetInformation>(this);
if (!m_targetInformationHandler->init())
{
ERROR_LOG(m_logger) << "Could not init TargetInformationHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init TargetInformationHandler"));
return false;
}
NOTICE_LOG(m_logger) << "Enabled TargetInformationHandler for ex " << exerciseId << " do= " << domainId;
m_biteHandler = std::make_shared<BiteHandling::BiteHandler>(this);
if (!m_biteHandler->init())
{
ERROR_LOG(m_logger) << "Could not init BiteHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init BiteHandler"));
return false;
}
DEBUG_LOG(m_logger) << "BiteHandler initialized";
// template handler is not needed in exercise dependent PPM
if (m_ddsHandler->dds()->getPartitionId() <= 0)
{
m_templateHandler = std::make_shared<TemplateHandling::TemplateHandler>(this);
if (!m_templateHandler->init())
{
ERROR_LOG(m_logger) << "Could not init TemplateHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init TemplateHandler"));
return false;
}
DEBUG_LOG(m_logger) << "TemplateHandler initialized";
}
m_getParticipants = std::make_shared<GetParticipants>(this);
m_tabDtc = TrainingAreaBase::Interface::Dtc::create(m_ddsHandler->dds());
m_equipmentEventHandler = std::make_shared<EquipmentEventHandling::EquipmentEventHandler>(this);
if (!m_equipmentEventHandler->init())
{
ERROR_LOG(m_logger) << "Could not init EquipmentEventHandler";
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::StartingError, QStringLiteral("Could not init EquipmentEventHandler"));
return false;
}
DEBUG_LOG(m_logger) << "EquipmentEventHandler initialized";
// WebSocketServer
m_websocketHandler = std::make_shared<WebSocketInterface::WebSocketInterfaceHandler>(this);
if (!m_websocketHandler->init())
{
ERROR_LOG(m_logger) << "Could not init WebSocketInterfaceHandler";
return false;
}
DEBUG_LOG(m_logger) << "WebSocketInterfaceHandler initialized";
m_ddsHandler->pacemaker()->addPort(m_websocketHandler->webSocketPort(), Core::PortType::WebSocketServer);
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
// UasHandler
if (exerciseId == 0)
{
m_uasHandler = std::make_shared<UasHandling::UasHandler>(this);
if (!m_uasHandler->init())
{
ERROR_LOG(m_logger) << "Could not init UasHandler";
return false;
}
DEBUG_LOG(m_logger) << "UasHandler initialized";
}
if (exerciseId != 0)
{
// init missionPlan
m_missionPlanHandler = std::make_shared<UasHandling::MissionPlanHandler>(this);
if (!m_missionPlanHandler->init())
{
ERROR_LOG(m_logger) << "Couldn't init MissionPlanHandler";
return false;
}
}
return true;
}
std::shared_ptr<DatabaseInterface> MainHandler::db() const { return m_dbHandler->db(); }
std::shared_ptr<DatabaseInterface> MainHandler::dbEx0() const { return m_dbHandlerEx0->db(); }
qint64 MainHandler::currentTimeMSec() const { return QDateTime::currentMSecsSinceEpoch(); }
} // namespace ParticipantManagement
+107
View File
@@ -0,0 +1,107 @@
/**
* MainHandler for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 19.05.2016
*/
#pragma once
#include <QtCore/QObject>
#include <memory>
#include "BiteHandling/bite_handler.h"
#include "EquipmentEventHandling/equipment_event_handler.h"
#include "ParticipantHandling/get_participants.h"
#include "ParticipantHandling/participant_handler.h"
#include "ParticipantHandling/participant_handler_for_exercise.h"
#include "PartyHandling/party_handler.h"
#include "TargetHandling/handler_target_information.h"
#include "TemplateHandling/template_handler.h"
#include "TypeHandling/type_handler.h"
#include "dds_handler.h"
#include "logger.h"
#include "ppm_db.h"
#include <ParticipantManagement/ppm_db.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <AreaEffectSimulation/aes_ftc.h>
#include <TrainingAreaBase/tab_dtc.h>
#include <TrainingAreaBase/tab_ftc.h>
namespace NGLDB
{
template <typename DB> class DbServiceHandler;
}
namespace ParticipantManagement
{
namespace UasHandling
{
class UasHandler;
class MissionPlanHandler;
} // namespace UasHandling
namespace WebSocketInterface
{
class WebSocketInterfaceHandler;
}
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class MainHandler final : public ParticipantHandling::BaseDataProvider
{
public:
explicit MainHandler();
~MainHandler();
bool init(int domainId, int exerciseId);
std::shared_ptr<ParticipantManagement::Interface::Dts> dts() const override { return m_ddsHandler->dts(); }
std::shared_ptr<ParticipantManagement::Interface::Fts> fts() const { return m_ddsHandler->fts(); }
std::shared_ptr<ParticipantManagement::Interface::Dts> exAnyDts() const { return m_ddsHandler->exAnyDts(); }
std::shared_ptr<ParticipantManagement::Interface::Fts> exAnyFts() const { return m_ddsHandler->exAnyFts(); }
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const override { return m_ddsHandler->pdmDtc(); }
std::shared_ptr<DDSConnector::DdsConnector> dds() const { return m_ddsHandler->dds(); }
std::shared_ptr<DdsHandler> ddsHandler() const { return m_ddsHandler; }
std::shared_ptr<DatabaseInterface> db() const;
std::shared_ptr<DatabaseInterface> dbEx0() const;
std::shared_ptr<TypeHandling::TypeHandler> typeHandler() const { return m_typeHandler; }
std::shared_ptr<TrainingAreaBase::Interface::Ftc> tabFtc() const { return m_tabFtc; }
std::shared_ptr<TrainingAreaBase::Interface::Dtc> tabDtc() const { return m_tabDtc; }
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> aesFtc() const { return m_aesFtc; }
std::shared_ptr<ParticipantHandling::ParticipantHandlerForExercise> participantHandler() const { return m_participantHandler; }
std::shared_ptr<HandlerTargetInformation> targetInformationHandler() const { return m_targetInformationHandler; }
std::shared_ptr<BiteHandling::BiteHandler> biteHandler() const { return m_biteHandler; }
std::shared_ptr<TemplateHandling::TemplateHandler> templateHandler() const { return m_templateHandler; }
std::shared_ptr<PartyHandling::PartyHandler> partyHandler() const { return m_partyHandler; }
std::shared_ptr<EquipmentEventHandling::EquipmentEventHandler> equipmentEventHandler() const { return m_equipmentEventHandler; }
std::shared_ptr<UasHandling::MissionPlanHandler> missionPlanHandler() const { return m_missionPlanHandler; }
int exerciseId() const { return dds()->getPartitionId(); }
qint64 currentTimeMSec() const override;
private: // attributes
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<DbHandler> m_dbHandlerEx0;
std::shared_ptr<TypeHandling::TypeHandler> m_typeHandler;
std::shared_ptr<TrainingAreaBase::Interface::Ftc> m_tabFtc;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_tabDtc;
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
std::shared_ptr<ParticipantHandling::ParticipantHandlerForExercise> m_participantHandler;
std::shared_ptr<ParticipantManagement::HandlerTargetInformation> m_targetInformationHandler;
std::shared_ptr<BiteHandling::BiteHandler> m_biteHandler;
std::shared_ptr<TemplateHandling::TemplateHandler> m_templateHandler;
std::shared_ptr<PartyHandling::PartyHandler> m_partyHandler;
std::shared_ptr<GetParticipants> m_getParticipants;
std::shared_ptr<EquipmentEventHandling::EquipmentEventHandler> m_equipmentEventHandler;
std::shared_ptr<WebSocketInterface::WebSocketInterfaceHandler> m_websocketHandler;
std::shared_ptr<UasHandling::UasHandler> m_uasHandler;
std::shared_ptr<UasHandling::MissionPlanHandler> m_missionPlanHandler;
LOGGER_T m_logger;
};
} // namespace ParticipantManagement
+197
View File
@@ -0,0 +1,197 @@
/**
* @RDE_HEADER@
* Custom ppm functions (extends tab_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#include "ppm_common.h"
#include <QVector>
#include <QDebug>
namespace ParticipantManagement
{
StateInformation youngestStateInformation(const std::vector<ParticipantManagement::StateInformation> &stateList,
bool bSkipVbgGreaterCatastrophicKill, bool bSkipSanitation, bool debug)
{
std::shared_ptr<int> _(nullptr, [debug](int *) { if (debug) qWarning() << "##############################################"; });
if (debug)
qWarning() << "########## youngestStateInformation ##########";
ParticipantManagement::StateInformation ret;
ret.vulnerabilityState(ProjectDataManagement::VulnerabilityState::NotReady);
if (stateList.empty())
{
if (debug)
qWarning() << ">> stateList empty! not ready";
return ret;
}
// bool readyToDebug = false;
// for (const ParticipantManagement::StateInformation &si : qAsConst(stateList))
// {
// if (si.originatorId() == 0
// && si.originatorType() == ProjectDataManagement::StateChangeOriginator_def::ExerciseControl
// && (si.vulnerabilityState() == ProjectDataManagement::VulnerabilityState_def::CatastrophicKill
// || si.vulnerabilityState() == ProjectDataManagement::VulnerabilityState_def::CombatReady)
// )
// readyToDebug = true;
// }
/* NOTE: Participant::setState in PPM doesn't respect the vulnId sent by the TNE, but instead sets it to
* the highest vuln (ignoring TemporarySeverance, NearMiss and HitWithNoEffect!) that was found in
* the vulnerabilityBits. So the latest state in the history is always the state we are looking for.
* NOTE: StateInformation's stateTime max resolution is seconds instead of milliseconds
* NOTE: StateInformations can have the same stateTime, which means a CombatReady might be incorrectly
* chosen because it has the same stateTime as a CatastrophicKill - if not sorted by time *and* state
*/
std::vector<ParticipantManagement::StateInformation> sortedStateList(stateList); // copy list
// sort list by sample time, desc (highest time = youngest)
std::sort(sortedStateList.begin(), sortedStateList.end(),
[/*&debug,readyToDebug*/](const ParticipantManagement::StateInformation &left, const ParticipantManagement::StateInformation &right)
{
if (left.stateTime() == right.stateTime() && left.vulnerabilityState() != right.vulnerabilityState())
{
// if (readyToDebug)
// debug = true;
// qWarning() << "WARNING: StateInformation with state" << (int)left.vulnerabilityState().underlying() << "has the same stateTime as StateInformation with state" << (int)right.vulnerabilityState().underlying();
// sort by state if time is the same (higher state = winner)
return (left.vulnerabilityState() > right.vulnerabilityState());
}
return (left.stateTime() > right.stateTime());
});
if (debug)
{
qWarning() << ">> printing all SIs";
for (const ParticipantManagement::StateInformation &si : qAsConst(sortedStateList))
qWarning() << ">>>> si" << "siVulnId" << (uint8_t)si.vulnerabilityState().underlying() << "bits" << si.vulnerabilityBits() << "time" << si.stateTime();
qWarning() << ">> checking SIs";
}
for (const ParticipantManagement::StateInformation &si : qAsConst(sortedStateList))
{
const uint8_t siVulnId = si.vulnerabilityState().underlying();
if (debug)
{
qWarning() << "MWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMWMW";
qWarning() << ">>>> si" << "siVulnId" << siVulnId;
}
// we're only interested in 'visible' VBGs, don't care about near miss etc. here
if (bSkipVbgGreaterCatastrophicKill && si.vulnerabilityState() > ProjectDataManagement::VulnerabilityState::CatastrophicKill)
{
if (debug)
qWarning() << ">>>> vulnId is higher than CK!";
continue;
}
if (bSkipSanitation && si.originatorType() == ProjectDataManagement::StateChangeOriginator::Sanitation)
{
if (debug)
qWarning() << ">>>> is sanitation!";
continue;
}
if (debug)
qWarning() << ">>>> match! done!";
return si;
}
if (debug)
qWarning() << ">> no state found" << "bSkipVbgGreaterCatastrophicKill" << bSkipVbgGreaterCatastrophicKill << "bSkipSanitation" << bSkipSanitation;
return ret;
}
StateInformation youngestRealStateInformation(const std::vector<ParticipantManagement::StateInformation> &stateList,
bool bSkipVbgGreaterCatastrophicKill)
{
if (stateList.size() == 0)
{
ParticipantManagement::StateInformation ret;
ret.vulnerabilityState(ProjectDataManagement::VulnerabilityState::NotReady);
return ret;
}
StateInformation youngestState = youngestStateInformation(stateList);
size_t youngest = 0;
for (size_t i = 1; i < stateList.size(); ++i)
{
const auto &tCurrent = stateList.at(i).stateTime();
const auto &tYoungest = stateList.at(youngest).stateTime();
// we're only interested in 'visible' VBGs, don't care about near miss etc. here
if (bSkipVbgGreaterCatastrophicKill && stateList.at(i).vulnerabilityState() > ProjectDataManagement::VulnerabilityState::CatastrophicKill)
continue;
if (stateList.at(i).originatorType() == ProjectDataManagement::StateChangeOriginator::Sanitation ||
stateList.at(i).originatorType() == ProjectDataManagement::StateChangeOriginator::MedicalTimeout)
continue;
if (tCurrent > youngestState.stateTime())
continue;
bool higherState = (stateList.at(i).vulnerabilityState() > stateList.at(youngest).vulnerabilityState() ) ||
(stateList.at(i).vulnerabilityState() == stateList.at(youngest).vulnerabilityState() && (stateList.at(i).vitality() < stateList.at(youngest).vitality()));
if ((tCurrent > tYoungest && (stateList.at(i).originatorType() == ProjectDataManagement::StateChangeOriginator::ObserverController || stateList.at(i).originatorType() == ProjectDataManagement::StateChangeOriginator::ExerciseControl)) ||
(tCurrent > tYoungest && higherState) ||
(tCurrent == tYoungest && higherState))
youngest = i;
}
if (youngest == 0)
return youngestState;
return stateList.at(youngest);
}
ParticipantManagement::SanitationResult proceduresDone(const std::vector<StateInformation> &stateList)
{
// Empty List or 1 Entry ("CombatReady"): No Effect
if (stateList.size() <= 1)
return SanitationResult::NoEffect;
// If that guy is killed: NoEffect
StateInformation youngestReal = youngestRealStateInformation(stateList);
if (youngestReal.vulnerabilityState() == ProjectDataManagement::VulnerabilityState::CatastrophicKill)
return SanitationResult::NoEffect;
SanitationResult ret = SanitationResult::Unhandled;
for (size_t i = 1; i < stateList.size(); ++i)
{
// Better than "Unhandled" can only occur if this is done by Sanitation or O/C
if (stateList.at(i).originatorType() != ProjectDataManagement::StateChangeOriginator::Sanitation
&& stateList.at(i).originatorType() != ProjectDataManagement::StateChangeOriginator::ObserverController )
continue;
// Let's find the newest
if (stateList.at(i).stateTime() < youngestReal.stateTime())
continue;
//
if (ret == SanitationResult::Unhandled ) //we have a sanitation so at least 1
ret = stateList.at(i).sanitationResult();
if (stateList.at(i).sanitationResult() == SanitationResult::Sufficient && ret != SanitationResult::Optimal )
ret = SanitationResult::Sufficient;
if (stateList.at(i).sanitationResult() == SanitationResult::Optimal)
ret = SanitationResult::Optimal;
}
return ret;
}
} // namespace ParticipantManagement
+21
View File
@@ -0,0 +1,21 @@
/**
* @RDE_HEADER@
* Custom ppm functions (extends tab_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm.hpp>
#include "ppm_export.h"
namespace ParticipantManagement
{
PPM_COMMON_EXPORT StateInformation youngestStateInformation(const std::vector<ParticipantManagement::StateInformation> &stateList,
bool bSkipVbgGreaterCatastrophicKill = true, bool bSkipSanitation = true,
bool debug = false);
StateInformation youngestRealStateInformation(const std::vector<ParticipantManagement::StateInformation> &stateList,
bool bSkipVbgGreaterCatastrophicKill = true);
PPM_COMMON_EXPORT SanitationResult proceduresDone(const std::vector<ParticipantManagement::StateInformation> &stateList);
} // namespace ParticipantManagement
+38
View File
@@ -0,0 +1,38 @@
/**
* @RDE_HEADER@
* Custom db functions (extends ppm_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#include "ppm_db_impl_custom.h"
#include <ngldb/nsqlquery.h>
#include <ngldb/sql_query_janitor.h>
namespace ParticipantManagement
{
// custom procs, body must be implemented by user
bool DatabaseInterfaceImplCustom::removeEquipmentExerciseMapping(const Database::EquipmentExerciseMapping &equipmentExerciseMapping)
{
SqlQueryJanitor janitor(m_query);
const QString tableName(getTableName(QStringLiteral("ppm_equipment_exercise_mapping")));
const QString stmt(QStringLiteral("DELETE FROM ") + tableName +
QStringLiteral(" WHERE equipment_number = ") +
QString::number(equipmentExerciseMapping.equipmentNumber) +
QStringLiteral(" AND participant_id = ") +
QString::number(equipmentExerciseMapping.participantId) +
QStringLiteral(" AND exercise_id = ") +
QString::number(equipmentExerciseMapping.exerciseId));
if (!m_query->exec(stmt, m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
return true;
}
}
+22
View File
@@ -0,0 +1,22 @@
/**
* @RDE_HEADER@
* Custom db functions (extends ppm_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm_db_impl.h>
namespace ParticipantManagement
{
class DatabaseInterfaceImplCustom final : public DatabaseInterfaceImpl
{
Q_OBJECT
public:
using DatabaseInterfaceImpl::DatabaseInterfaceImpl;
bool removeEquipmentExerciseMapping(const Database::EquipmentExerciseMapping &equipmentExerciseMapping) override final;
};
} // namespace ParticipantManagement
+861
View File
@@ -0,0 +1,861 @@
/**
* @RDE_HEADER@
* Custom ppm functions (extends tab_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#include "ppm_dtc_impl_custom.h"
#include "ppm_common.h"
#include <DDS/DTC/generic_dtc.h>
#include <DDS/Generic/generic_data_reader.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/json_object.h>
#include <QJsonObject>
#include <QJsonParseError>
#include <QPolygonF>
#include <QSet>
#include <QVector>
namespace ParticipantManagement
{
namespace Interface
{
DtcImplCustom::DtcImplCustom(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: DtcImpl(ddsConnector)
{
}
bool DtcImplCustom::rootTemplates(QVector<TemplateParticipant> &templateVector) const
{
if (!m_dtcTemplateParticipant)
{
ERROR_LOG(m_logger) << "Error retrieving data topic TemplateParticipant";
return false;
}
for (const auto &sample : m_dtcTemplateParticipant->readAlive())
{
if (sample.data().superiorTemplateId() == 0)
templateVector.append(sample.data());
}
return !templateVector.isEmpty();
}
bool DtcImplCustom::templateParticipantsFromTemplateId(int rootTemplateId, QVector<TemplateParticipant> &templateVector)
{
templateVector.append(inferiorTemplates(rootTemplateId));
return !templateVector.isEmpty();
}
TemplateData DtcImplCustom::convertFromParticipantTemplateToTemplateData(const TemplateParticipant &inputTemplate) const
{
TemplateData outputData;
TemplateBase &out = outputData;
out = inputTemplate;
// in cases where the ids are not updated by the treeModel
outputData.modId(inputTemplate.templateId());
outputData.superiorModId(inputTemplate.superiorTemplateId());
return outputData;
}
ParticipantData Interface::DtcImplCustom::convertParticipantToParticipantData(const Participant &inputParticipant) const
{
ParticipantData outputData;
ParticipantBase &out = outputData;
out = inputParticipant;
// in cases where the ids are not updated by the treeModel
outputData.modId(inputParticipant.id());
outputData.superiorModId(inputParticipant.superiorId());
return outputData;
}
ParticipantManagement::StateInformation DtcImplCustom::youngestStateInformation(
const std::vector<ParticipantManagement::StateInformation> &stateList /*, bool debug*/) const
{
return ParticipantManagement::youngestStateInformation(stateList /*, true, true, debug*/);
}
ParticipantManagement::SanitationResult DtcImplCustom::proceduresDone(const std::vector<StateInformation> &stateList) const
{
return ParticipantManagement::proceduresDone(stateList);
}
QMap<int, Participant> DtcImplCustom::units() const
{
QMap<int, Participant> ret;
if (!m_dtcType)
{
ERROR_LOG(m_logger) << "Error retrieving data topic Type";
return ret;
}
if (!m_dtcParticipant)
{
ERROR_LOG(m_logger) << "Error retrieving data topic Participant";
return ret;
}
QSet<int> unitTypeIds;
for (const auto &sample : m_dtcType->readAlive())
{
if (sample.data().typeClass() == TypeClass::Unit || sample.data().typeClass() == TypeClass::SupportUnit)
unitTypeIds.insert(sample.data().id());
}
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (unitTypeIds.contains(sample.data().typeId()))
ret.insert(sample.data().id(), sample.data());
}
return ret;
}
bool DtcImplCustom::participantIdsByUnit(int unitId, const QMap<int, Participant> &participants, QSet<int> &participantIds)
{
const ParticipantManagement::Participant unit(participants.value(unitId));
if (unit.id() != unitId)
return false;
const ParticipantManagement::Type unitType(type(unit.typeId()));
if (unitType.typeClass() != ParticipantManagement::TypeClass::Unit && unitType.typeClass() != ParticipantManagement::TypeClass::SupportUnit)
return false;
participantIds += recursiveInferiosForParticipant(unitId, participants);
return true;
}
bool DtcImplCustom::visibleParties(QMap<int32_t, Party> &visibleParties, const QVector<int> &filter) const
{
if (!m_dtcParty)
{
ERROR_LOG(m_logger) << "Error retrieving data topic Party";
return false;
}
const bool bAll = (filter.size() == 1 && filter.first() == -1);
for (const auto &sample : m_dtcParty->readAlive())
if (bAll || filter.contains(sample.data().id()))
visibleParties.insert(sample.data().id(), sample);
return true;
}
QString DtcImplCustom::hierarchyFromParticipant(int participantId) { return lookUpHierachy(participantId, participants()); }
QString DtcImplCustom::hierarchyFromParticipant(int participantId, const QMap<int, Participant> &participants) const
{
return lookUpHierachy(participantId, participants);
}
bool DtcImplCustom::isEquipmentNumberInUse(int equipmentNumber, int &exerciseId) const
{
if (!m_dtcEquipmentExerciseMapping)
return true; // we're not sure - return true ...
for (const auto &sample : m_dtcEquipmentExerciseMapping->readAlive())
{
for (const Equipment2ParticipantId &e2p : sample.data().mapping())
{
if (e2p.equipmentNumber() == equipmentNumber)
{
exerciseId = sample.data().exerciseId();
return true;
}
}
}
return false;
}
bool DtcImplCustom::isTypeInUse(int typeId) const
{
if (!m_dtcParticipant)
return true; // we're not sure - return true ...
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().typeId() == typeId)
return true;
}
return false;
}
int DtcImplCustom::participantIdByEquipmentNumber(int equipmentNumber) const
{
if (!m_dtcParticipant)
return -1;
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().equipmentNumber().is_set() && sample.data().equipmentNumber().get() == equipmentNumber)
return sample.data().id();
}
return -1;
}
int DtcImplCustom::equipmentNumberByParticipantId(int participantId) const
{
if (!m_dtcParticipant)
return -1;
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().id() == participantId)
{
if (sample.data().equipmentNumber().is_set())
return sample.data().equipmentNumber().get();
}
}
return -1;
}
int DtcImplCustom::uasMissionIdByEquipmentNumber(int equipmentNumber) const
{
if (m_dtcMissionPlan == nullptr)
{
return -1;
}
const int participantId = participantIdByEquipmentNumber(equipmentNumber);
if (participantId < 0)
{
return -1;
}
for (const auto &sample : m_dtcMissionPlan->readAlive())
{
if (sample.data().participantId() == participantId)
{
return sample.data().id();
}
}
return -1;
}
int DtcImplCustom::partyIdFromForceIdentifier(HlaForceIdentifierEnum inputForceIdentifier) const
{
if (!m_dtcParty)
return -1;
for (const auto &sample : m_dtcParty->readAlive())
{
if (sample.data().hlaForceIdentifier() == inputForceIdentifier)
return sample.data().id();
}
return -1;
}
ConnectState DtcImplCustom::saaConnectedState(const int partyId)
{
ConnectState ret = ConnectState::Undefined;
if (!isPartyEnabled())
{
enableParty();
waitForHistoricalDataParty(10);
}
if (!m_dtcParty)
return ret;
for (const auto &sample : m_dtcParty->readAlive())
{
if (partyId != sample.data().id())
continue;
QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(sample.data().properties()));
QJsonObject prop = doc.object();
QJsonObject propAdler;
if (prop.contains(QStringLiteral("Adler")))
{
propAdler = prop[QStringLiteral("Adler")].toObject();
if (propAdler.contains(QStringLiteral("saaConnectedState")))
return static_cast<ConnectState::inner_enum>(propAdler[QStringLiteral("saaConnectedState")].toInt());
}
}
return ret;
}
QVector<int> DtcImplCustom::getSubordinatesByParticipant(const int participantId)
{
QVector<int> result;
if (!m_dtcParticipant)
return result;
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().superiorId() == participantId)
result.append(sample.data().id());
}
return result;
}
QVector<int> DtcImplCustom::getParticipantsByPolygon(const QPolygonF &polygon, bool includeUnits, bool includeOc)
{
QVector<int> participantsInPolygon;
QSet<int> unitTypeIds;
if (!isParticipantPositionEnabled())
{
enableParticipantPosition();
waitForHistoricalDataParticipantPosition(10);
}
if (!includeUnits)
{
if (!isTypeEnabled())
{
enableType();
waitForHistoricalDataType(10);
}
for (const auto &sample : m_dtcType->readAlive())
{
if (sample.data().typeClass() == TypeClass::Unit || sample.data().typeClass() == TypeClass::SupportUnit)
unitTypeIds.insert(sample.data().id());
}
}
participantsInPolygon.reserve(m_dtcParticipantPosition->readAlive().length());
for (const auto &currentPosition : m_dtcParticipantPosition->readAlive())
{
if (!includeUnits && unitTypeIds.contains(currentPosition.data().participantTypeId()))
continue;
if (!includeOc && currentPosition.data().partyId() == 1) // partyId 1 = OC
continue;
const Core::GeoPosition &currentGeoPosition = currentPosition.data().pos();
const QPointF positionPoint(currentGeoPosition.lon(), currentGeoPosition.lat());
if (polygon.containsPoint(positionPoint, Qt::OddEvenFill))
participantsInPolygon.append(currentPosition.data().participantId());
}
return participantsInPolygon;
}
// All Participant-Ids of Type UAS
QVector<int> DtcImplCustom::getUasIds()
{
QVector<int> result;
if (!m_dtcParticipant)
return result;
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (isTypeUas(sample.data().typeId()))
{
result.append(sample.data().id());
}
}
return result;
}
UAS DtcImplCustom::getUasByParticipantId(int participantId, int exerciseId) const
{
ParticipantManagement::UAS uas;
uas.id(-1); // if not found
for (const auto &sample : m_dtcUAS->readAlive())
{
if (sample.data().participantId() != participantId)
continue;
if (sample.data().exerciseId() != exerciseId)
continue;
uas = sample;
break;
}
return uas;
}
MissionAction DtcImplCustom::currentMissionAction(int missionPlanId) const
{
ParticipantManagement::MissionAction missionAction;
missionAction.planId(0); // default compare value in case nothing found
for (const auto &sample : m_dtcMissionPlan->readAlive())
{
if (sample.data().id() != missionPlanId)
continue;
const std::vector<ParticipantManagement::MissionItem> &items = sample.data().items();
for (const auto &item : items)
{
if (item.fulfilled() > 0)
continue;
const std::vector<ParticipantManagement::MissionAction> &actions = item.actions();
for (const auto &action : actions)
{
if (action.fulfilled() > 0)
continue;
return action;
}
}
}
return missionAction;
}
ParticipantManagement::Participant DtcImplCustom::getParticipantByInternalName(int partyId, const QString &unitName, const QString &internalName) const
{
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().partyId() != partyId)
continue;
if (sample.data().isDeleted())
continue;
if (sample.data().name() != unitName.toStdString())
continue;
// Found Unit
// looking for Unit only?
if (internalName.isEmpty())
{
return sample;
}
for (auto inferiorId : sample.data().inferiorIds())
{
const auto &inferiorParticipant = m_participantCont.value(inferiorId);
if (inferiorParticipant.id() <= 0 || inferiorParticipant.isDeleted())
continue;
if (inferiorParticipant.name() == internalName.toStdString())
return inferiorParticipant;
}
}
return ParticipantManagement::Participant();
}
QSet<int> DtcImplCustom::recursiveInferiosForParticipant(int32_t participantId, const QMap<int, Participant> &participants)
{
QSet<int> result;
auto it = participants.find(participantId);
if (it == participants.end())
return result;
result += participantId;
const Participant &p = it.value();
for (int childId : p.inferiorIds())
result += recursiveInferiosForParticipant(childId, participants);
return result;
}
QString DtcImplCustom::lookUpHierachy(int participantId, const QMap<int, Participant> &participants)
{
if (participantId <= 0)
return QString();
const auto itPart = participants.find(participantId);
if (itPart == participants.end())
return QString();
const auto &part = itPart.value();
QString supStr = lookUpHierachy(part.superiorId(), participants);
QString ret = QString::fromStdString(part.designator());
// First separator is "./"
if (part.superiorId() == 0)
return (ret + QLatin1String("./"));
if (supStr.endsWith(QLatin1String("./")))
return (supStr + ret);
return (supStr.isEmpty() ? ret : supStr + QLatin1Char(',') + ret);
}
// private
QVector<TemplateParticipant> DtcImplCustom::inferiorTemplates(int templateId)
{
QVector<TemplateParticipant> retVector;
const TemplateParticipant participant = templateParticipant(templateId);
retVector.append(participant);
if (participant.inferiorIds().is_set())
{
retVector.reserve(participant.inferiorIds().get().size() + retVector.size());
for (int inferiorId : participant.inferiorIds().get())
retVector.append(inferiorTemplates(inferiorId));
}
return retVector;
}
bool DtcImplCustom::isTypePatientSimulator(int typeId) const
{
if (!m_dtcParticipant)
return false;
for (const auto &sample : m_dtcParticipant->readAlive())
{
if (sample.data().typeId() == typeId)
{
const std::string &str = sample.data().properties();
if (str.size() > 0 && getNeedsIp(str) && getNeedsPort(str))
return true;
else
return false;
}
}
return false;
}
bool DtcImplCustom::isAdlerType(int typeId) const
{
if (!m_dtcType)
return false;
for (const Type &type : m_dtcType->readAlive())
{
if (type.id() != typeId)
continue;
const auto &prob = type.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
return false;
const QJsonObject obj(doc.object());
const auto val = obj.value(QStringLiteral("isMovable"));
if (val.isNull() || !val.isBool())
return false;
return !val.toBool();
}
return false;
}
bool DtcImplCustom::isArtilleryType(int typeId) const
{
if (!m_dtcType)
return false;
for (const Type &type : m_dtcType->readAlive())
{
if (type.id() != typeId)
continue;
const auto &prob = type.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
return false;
const QJsonObject obj(doc.object());
const auto val = obj.value(QStringLiteral("isArtillery"));
if (val.isNull() || !val.isBool())
return false;
return val.toBool();
}
return false;
}
bool DtcImplCustom::isEditable(int typeId) const
{
if (!m_dtcType)
return false;
for (const Type &type : m_dtcType->readAlive())
{
if (type.id() != typeId)
continue;
const auto &prob = type.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
return false;
const QJsonObject obj(doc.object());
const auto val = obj.value(QStringLiteral("isEditable"));
if (val.isNull() || !val.isBool())
return true;
return val.toBool();
}
return false;
}
bool DtcImplCustom::isAdlerVerbund(int typeId) const
{
if (!m_dtcType)
return false;
for (const Type &type : m_dtcType->readAlive())
{
if (type.id() != typeId)
continue;
const auto &prob = type.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
return false;
const QJsonObject obj(doc.object());
const auto val = obj.value(QStringLiteral("isAdlerVerbund"));
if (val.isNull() || !val.isBool())
return false;
return val.toBool();
}
return false;
}
bool DtcImplCustom::isPartyAdlerVerbund(int partyId) const
{
if (!m_dtcParty)
return false;
for (const Party &party : m_dtcParty->readAlive())
{
if (party.id() != partyId)
continue;
const auto &prob = party.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
return false;
const QJsonObject obj(doc.object());
if (!obj.contains(QStringLiteral("Adler")))
return false;
const QJsonObject adlerObj = obj[QStringLiteral("Adler")].toObject();
const auto val = adlerObj.value(QStringLiteral("isVerbund"));
if (val.isNull() || !val.isBool())
return false;
return val.toBool();
}
return false;
}
Type DtcImplCustom::getTypeFromAdlerType(const QString &adlerTypeName, bool isUnit, bool isVerbund)
{
Type ret;
if (!m_dtcType)
return ret;
for (const Type &type : m_dtcType->readAlive())
{
if (isUnit && type.typeClass() != TypeClass::Unit)
continue;
if (!isUnit && type.typeClass() != TypeClass::WeaponSystem)
continue;
const auto &prob = type.properties();
QJsonParseError parserError;
const QByteArray props(QByteArray::fromRawData(prob.c_str(), prob.size()));
const QJsonDocument doc(QJsonDocument::fromJson(props, &parserError));
if (parserError.error != QJsonParseError::NoError)
continue;
const QJsonObject obj(doc.object());
const auto val = obj.value(QStringLiteral("adlerType"));
if (val.isNull() || !val.isString())
continue;
if (val.toString() != adlerTypeName)
continue;
if (isAdlerVerbund(type.id()) != isVerbund)
continue;
// Found exact Type
DEBUG_LOG(m_logger) << "--- "
<< "Found exact type --> typeId= " << type.id() << " ---";
return type;
}
return ret;
}
// Properties in table ppm.ppm_type
bool DtcImplCustom::isTypeUas(int typeId)
{
if (!m_dtcType)
return false;
if (!isTypeEnabled())
{
enableType();
waitForHistoricalDataType(10);
}
for (const auto &sample : m_dtcType->readAlive())
{
// DEBUG_LOG(m_logger) << " sample equipmentTypeId=" << sample.data().equipmentTypeId();
if (sample.data().id() == typeId)
{
const std::string &str = sample.data().properties();
if (str.size() > 0 && getIsUAS(str))
return true;
else
return false;
}
}
return false;
}
UAS DtcImplCustom::getUasByParticipantId(int participantId, int exerciseId)
{
if (!isUASEnabled())
{
enableUAS();
waitForHistoricalDataUAS();
}
if (!m_dtcUAS)
return {};
for (const auto &sample : m_dtcUAS->readAlive())
{
if (sample.data().participantId() != participantId)
continue;
if (sample.data().exerciseId() != exerciseId)
continue;
return sample;
}
return {};
}
//---------------------------------------------------------------------------------------------------------------------------------------------
int DtcImplCustom::getParticipantFromPrf(int prf) const
{
DEBUG_LOG(m_logger) << "getParticipantFromPrf prf=" << prf;
if (!m_dtcParticipant)
return -1;
for (const DynamicParticipantData &sample : m_dtcDynamicParticipantData->readAlive())
{
for (const auto &designation : qAsConst(sample.designations()))
{
DEBUG_LOG(m_logger) << " code=" << designation.code();
if (designation.code() == prf)
{
DEBUG_LOG(m_logger) << " return code=" << designation.code();
return sample.participantId();
}
}
}
return -1;
}
int DtcImplCustom::getPrfFromParticipant(int participantId) const
{
DEBUG_LOG(m_logger) << "getPrfFromParticipant participantId=" << participantId;
if (!m_dtcParticipant)
return -1;
for (const DynamicParticipantData &sample : m_dtcDynamicParticipantData->readAlive())
{
if (participantId == sample.participantId())
{
DEBUG_LOG(m_logger) << "participantId=" << sample.participantId() << " designations.size()=" << sample.designations().size();
for (const auto &designation : qAsConst(sample.designations()))
{
DEBUG_LOG(m_logger) << " code=" << designation.code();
if (designation.code() > 0)
{
DEBUG_LOG(m_logger) << " return code=" << designation.code();
return designation.code();
}
}
return -1;
}
}
return -1;
}
bool DtcImplCustom::getNeedsIp(const std::string &s) const
{
QJsonParseError jError;
QJsonDocument doc(QJsonDocument::fromJson(s.c_str(), &jError));
if (jError.error != QJsonParseError::NoError || !doc.isObject())
{
return false;
}
QJsonObject obj(doc.object());
return obj[QStringLiteral("needsIP")].toBool();
}
bool DtcImplCustom::getNeedsPort(const std::string &s) const
{
QJsonParseError jError;
QJsonDocument doc(QJsonDocument::fromJson(s.c_str(), &jError));
if (jError.error != QJsonParseError::NoError || !doc.isObject())
{
return false;
}
QJsonObject obj(doc.object());
return obj[QStringLiteral("needsPort")].toBool();
}
QMap<double, int> DtcImplCustom::sortByDistance(const double lon, const double lat, const QVector<int> &participantIds)
{
DEBUG_LOG(m_logger) << " DtcImplCustom::sortByDistance";
int zone = -1;
if (!isParticipantPositionEnabled())
{
enableParticipantPosition();
waitForHistoricalDataParticipantPosition(10);
}
QMap<double, int> distanceMapSorted;
for (const auto &currentPosition : m_dtcParticipantPosition->readAlive())
{
if (participantIds.contains(currentPosition.data().participantId()))
{
auto pOrigin = LtsCoordTrans::UtmCoordinate::fromGeographic(lon, lat, zone);
auto pPos = LtsCoordTrans::UtmCoordinate::fromGeographic(currentPosition.data().pos().lon(), currentPosition.data().pos().lat(), zone);
double distance = LtsCoordTrans::distance(pPos, pOrigin);
DEBUG_LOG(m_logger) << distance;
// Save distance and particiapnt Id into temporar map.
// the map is sorted so we can use that later instead of writing a sort method vor a vec
// for now
distanceMapSorted.insert(distance, currentPosition.data().participantId());
DEBUG_LOG(m_logger) << "insert distance=" << distance << ", participantId=" << currentPosition.data().participantId();
}
}
QMapIterator<double, int> it(distanceMapSorted);
while (it.hasNext())
{
it.next();
DEBUG_LOG(m_logger) << "distance=" << it.key() << ", participantId=" << it.value();
}
return distanceMapSorted;
}
bool DtcImplCustom::getIsUAS(const std::string &s) const
{
QJsonParseError jError;
QJsonDocument doc(QJsonDocument::fromJson(s.c_str(), &jError));
if (jError.error != QJsonParseError::NoError || !doc.isObject())
{
return false;
}
QJsonObject obj(doc.object());
return obj[QStringLiteral("isUAS")].toBool();
}
} // namespace Interface
} // namespace ParticipantManagement
+103
View File
@@ -0,0 +1,103 @@
/**
* @RDE_HEADER@
* Custom ppm functions (extends tab_db_impl.cpp/.h)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <ParticipantManagement/ppm_dtc_impl.h>
namespace ParticipantManagement
{
namespace Interface
{
class DtcImplCustom final : public Interface::DtcImpl
{
Q_OBJECT
public:
explicit DtcImplCustom(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
bool rootTemplates(QVector<TemplateParticipant> &templateVector) const override;
bool templateParticipantsFromTemplateId(int rootTemplateId, QVector<TemplateParticipant> &templateVector) override;
TemplateData convertFromParticipantTemplateToTemplateData(const TemplateParticipant &inputTemplate) const override;
ParticipantData convertParticipantToParticipantData(const Participant &inputParticipant) const override;
ParticipantManagement::StateInformation youngestStateInformation(
const std::vector<ParticipantManagement::StateInformation> &stateList /*, bool debug = false*/) const override;
SanitationResult proceduresDone(const std::vector<ParticipantManagement::StateInformation> &stateList) const override;
/* To get this method working, the topics Type and Participant have to be enabled */
QMap<int, Participant> units() const override;
/* To get this method working, the topics Type and Participant have to be enabled */
bool participantIdsByUnit(int unitId, const QMap<int, Participant> &participants, QSet<int> &participantIds) override;
/* To get the visible parties, the topic Party has to be enabled */
bool visibleParties(QMap<int32_t, Party> &visibleParties, const QVector<int> &filter) const override;
/* To get the hole hierarchy String of own and superior designators */
QString hierarchyFromParticipant(int participantId) override;
QString hierarchyFromParticipant(int participantId, const QMap<int, Participant> &participants) const override;
bool isEquipmentNumberInUse(int equipmentNumber, int &exerciseId) const override;
bool isTypeInUse(int typeId) const override;
bool isTypePatientSimulator(int typeId) const override;
bool isAdlerType(int typeId) const override;
bool isArtilleryType(int typeId) const override;
bool isEditable(int typeId) const override;
bool isAdlerVerbund(int typeId) const override;
bool isPartyAdlerVerbund(int typeId) const override;
Type getTypeFromAdlerType(const QString &adlerTypeName, bool isUnit, bool isVerbund) override;
bool isTypeUas(int typeId) override;
UAS getUasByParticipantId(int participantId, int exerciseId) override;
int getParticipantFromPrf(int prf) const override;
int getPrfFromParticipant(int participantId) const override;
/* Searching the participant ID for the given EquipmentNumber */
int participantIdByEquipmentNumber(int equipmentNumber) const override;
/* Searching the EquipmentNumber for the given participant Id */
int equipmentNumberByParticipantId(int participantId) const override;
/* Searching the UAS Middion ID for the given EquipmentNumber */
int uasMissionIdByEquipmentNumber(int equipmentNumber) const override;
/* Searching to party ID from the HLA Force Identifier */
int partyIdFromForceIdentifier(HlaForceIdentifierEnum inputForceIdentifier) const override;
/* returns true is SAA is connected for the party */
ConnectState saaConnectedState(const int partyId) override;
// returns all participant IDs that have a position in the given polygon
QVector<int> getParticipantsByPolygon(const QPolygonF &polygon, bool includeUnits, bool includeOc) override;
// returns all participants with superioId == participantId
QVector<int> getSubordinatesByParticipant(const int participantId) override;
QMap<double, int> sortByDistance(const double lon, const double lat, const QVector<int> &participantIds) override;
// provides all participantIds of the drone type (type UAS)
QVector<int> getUasIds() override;
// return uas entry by pariticpant id
ParticipantManagement::UAS getUasByParticipantId(int participantId, int exerciseId) const override;
ParticipantManagement::MissionAction currentMissionAction(int missionPlanId) const override;
ParticipantManagement::Participant getParticipantByInternalName(int partyId, const QString &unitName, const QString &internalName) const override;
private: // methods
static QSet<int> recursiveInferiosForParticipant(int participantId, const QMap<int, Participant> &participants);
static QString lookUpHierachy(int participantId, const QMap<int, Participant> &participants);
QVector<TemplateParticipant> inferiorTemplates(int templateId);
bool getNeedsIp(const std::string &s) const;
bool getNeedsPort(const std::string &s) const;
bool getIsUAS(const std::string &s) const;
};
} // namespace Interface
} // namespace ParticipantManagement
+52
View File
@@ -0,0 +1,52 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-04
*/
#pragma once
#include <QtCore/qglobal.h>
#ifndef PPM_COMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define PPM_COMMON_EXPORT
# else
# if defined(MAKE_SVCPPMCOMMON_LIB)
# define PPM_COMMON_EXPORT Q_DECL_EXPORT
# ifndef WIN32
# ifdef NDDSUSERDllExport
# undef NDDSUSERDllExport
# endif
# define NDDSUSERDllExport Q_DECL_EXPORT
# endif
# else
# define PPM_COMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef PPM_CLIENT_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define PPM_CLIENT_EXPORT
# else
# if defined(MAKE_SVCPPMCLIENT_LIB)
# define PPM_CLIENT_EXPORT Q_DECL_EXPORT
# else
# define PPM_CLIENT_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef PPM_SERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define PPM_SERVER_EXPORT
# else
# if defined(MAKE_SVCPPMSERVER_LIB)
# define PPM_SERVER_EXPORT Q_DECL_EXPORT
# else
# define PPM_SERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
@@ -0,0 +1,8 @@
-- Generic technical areas for all exercises and all training areas (assuming 1 default technical area for each TA)
SET SEARCH_PATH TO ex_%ex_id%, PUBLIC;
INSERT INTO ppm_party (id, role_id, name, color, fading_color, is_used_by, hla_force_identifier, properties ) SELECT id, role_id, name, color, fading_color, false, hla_force_identifier, '{}' FROM ppm.ppm_party;
-- for each ADLER chargeType and each party one entry with 10000 and effectSource = ADLER_STANDALONE(3)
INSERT INTO aes_charge_administration( charge_type_id, party_id, effect_source_id, amount, planned, used, changed, amount_changed_by_operator) SELECT ct.id, p.id, 3, 10000, 0, 0, 0, false from pdm.pdm_charge_type ct CROSS JOIN ppm_party p where ct.properties->>'isAdlerChargeType' = 'true';
File diff suppressed because it is too large. Load diff
+68
View File
@@ -0,0 +1,68 @@
/**
* Settings for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "settings.h"
#include <QtCore/QSettings>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
#include "LtsUtilsCore/lts_portrange.h"
namespace ParticipantManagement
{
class NGLSettingsClass : public NGL::Settings
{
public:
NGLSettingsClass()
: NGL::Settings(s_iniFile.isEmpty() ? NGL::findProjectConfigFile() : s_iniFile)
{}
static void setIniFile(const QString &fn)
{
s_iniFile = fn;
}
static QString s_iniFile;
};
QString NGLSettingsClass::s_iniFile;
Q_GLOBAL_STATIC(NGLSettingsClass, s)
//-----------------------------------------------------------------------------
NGL::Settings *Settings::settings()
{
return s();
}
//-----------------------------------------------------------------------------
void Settings::setIniFile(const QString &ini)
{
NGLSettingsClass::setIniFile(ini);
}
//-----------------------------------------------------------------------------
bool Settings::markVehiclesAsReservedFromExcon()
{
return s()->settings()->value(QStringLiteral("PPM/markVehiclesAsReservedFromExcon"), true).toBool();
}
//-----------------------------------------------------------------------------
uint Settings::maxParticipantsPerFiringUnit()
{
return s()->settings()->value(QStringLiteral("PPM/maxParticipantsPerFiringUnit"), 8).toUInt();
}
//-----------------------------------------------------------------------------
QPair<quint16, quint16> Settings::getWebSocketServerPortRange(int type)
{
return LtsUtils::fromQPoint(settings()->settings()->value(QString(QStringLiteral("PPM/webSocketServerPortRange/%1")).arg(type), QPoint()).toPoint());
}
} // namespace ParticipantManagement
+32
View File
@@ -0,0 +1,32 @@
/**
* Settings for ParticipantManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QString>
#include <QPair>
namespace NGL
{
class Settings;
}
namespace ParticipantManagement
{
namespace Settings
{
//!< The real NGL::Settings - object
NGL::Settings *settings();
void setIniFile(const QString &ini);
// guez hack - the guez warehouse automatically assigns an equipment number
// for vehicles but this is not wanted. Therefore we have to mark them as
// 'Reserved for Excon'
bool markVehiclesAsReservedFromExcon();
uint maxParticipantsPerFiringUnit();
QPair<quint16, quint16> getWebSocketServerPortRange(int type);
}
} // namespace ParticipantManagement
+115
View File
@@ -0,0 +1,115 @@
/**
* Websocket interface to participant positions
* $Id$
* $HeadURL$
* $Author$
* Created by Justus Hertel, SLSE, 2020-07-08
*/
#include "web_socket_handler.h"
#include <QWebSocket>
#include <QJsonObject>
#include <QJsonDocument>
#include <ParticipantManagement/ppm.hpp>
namespace ParticipantManagement
{
WebSocketHandler::WebSocketHandler(QObject *parent)
: QObject(parent)
, m_webSocketServer(new QWebSocketServer(QStringLiteral("ParticipantPositionsSocket"), QWebSocketServer::NonSecureMode, this))
, m_logger(getLogger(QStringLiteral("WebSocketHandler")))
{}
WebSocketHandler::~WebSocketHandler()
{
for (const auto& ws : qAsConst(m_webSockets))
{
ws->disconnect(this);
}
m_webSockets.clear();
}
bool WebSocketHandler::init(quint16 listenPort)
{
if (!m_webSocketServer->listen(QHostAddress::Any, listenPort))
{
WARNING_LOG(m_logger) << "Can not listen on port " << listenPort << " - aborting";
return false;
}
connect(m_webSocketServer, &QWebSocketServer::newConnection,
this, &WebSocketHandler::onNewConnection);
INFO_LOG(m_logger) << "Listening on port " << m_webSocketServer->serverPort();
return true;
}
bool WebSocketHandler::init(const QPair<quint16, quint16> &portRange)
{
for (quint16 currentPort = portRange.first; currentPort <= portRange.second; ++currentPort)
{
if (init(currentPort))
return true;
}
return init();
}
bool WebSocketHandler::sendTelegramInternal(const ParticipantPosition &pos, int eqNo) const
{
QJsonObject obj;
obj[QStringLiteral("participantId")] = pos.participantId();
obj[QStringLiteral("equipmentNumber")] = eqNo;
obj[QStringLiteral("lat")] = pos.pos().lat();
obj[QStringLiteral("lon")] = pos.pos().lon();
obj[QStringLiteral("height")] = pos.pos().z();
const QString jsonStr = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
bool ret = false;
for (const auto &wsw : qAsConst(m_webSockets))
{
if (wsw->sendTextMessage(jsonStr))
ret = true;
}
return ret;
}
void WebSocketHandler::onNewConnection()
{
QWebSocket *ws = m_webSocketServer->nextPendingConnection();
if (!ws)
return;
connect(ws, &QWebSocket::disconnected,
this, &WebSocketHandler::onWebSocketDisconnected);
m_webSockets += ws;
INFO_LOG(m_logger) << "New websocket connected";
}
void WebSocketHandler::onWebSocketDisconnected()
{
QWebSocket *ws = qobject_cast<QWebSocket *>(sender());
if (!ws)
return;
m_webSockets.removeAll(ws);
ws->deleteLater();
INFO_LOG(m_logger) << "A websocket disconnected";
}
bool WebSocketHandler::sendPosition(const ParticipantPosition &participantPosition, int equipmentNumber) const
{
if (!isConnected() || m_webSockets.isEmpty())
return false;
return sendTelegramInternal(participantPosition, equipmentNumber);
}
}
+47
View File
@@ -0,0 +1,47 @@
/**
* Websocket interface to participant positions
* $Id$
* $HeadURL$
* $Author$
* Created by Justus Hertel, SLSE, 2017-01-11
*/
#pragma once
#include <QObject>
#include <QWebSocketServer>
#include <ParticipantManagement/ppm_common.h>
#include "logger.h"
namespace ParticipantManagement
{
class WebSocketHandler : public QObject
{
Q_OBJECT
public:
explicit WebSocketHandler(QObject *parent = nullptr);
~WebSocketHandler();
bool init(quint16 listenPort = 0);
bool init(const QPair<quint16, quint16> &portRange);
bool isConnected() const { return !m_webSockets.isEmpty(); }
quint16 listenPort() const { return m_webSocketServer->serverPort(); }
bool sendPosition(const ParticipantPosition& participantPosition, int equipmentNumber) const;
private:
bool sendTelegramInternal(const ParticipantPosition& pos, int eqNo) const;
private Q_SLOTS:
void onNewConnection();
void onWebSocketDisconnected();
private:
QWebSocketServer* m_webSocketServer = nullptr;
QVector<QWebSocket*> m_webSockets;
LOGGER_T m_logger;
};
}