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

+151
View File
@@ -0,0 +1,151 @@
#
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE alm_service.xml)
#
# --- Library svcAlmCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
alm_export.h
logger.cpp
logger.h
)
# Generate alm_enums.idl / alm.idl
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate alm_db_impl.cpp/.h, alm_db.h
CODEGEN_DATABASE(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from idl
RTIDDSGEN(SRCS_common alm_enums.idl)
RTIDDSGEN(SRCS_common alm.idl)
# Generate alm_dt[s,c]_types.h, alm_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# Generate alm_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS svcAlmCommon SHARED ${SRCS_common})
NGL_TARGET_INCLUDE_DIRECTORIES(svcAlmCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAlmCommon
PRIVATE
RTI::nddscpp2
PUBLIC
Qt5::Core
ddscore
NGLLogger
svcPdmCommon
ngldb
)
NGL_INSTALL_TARGET(svcAlmCommon)
#
# --- Library svcAlmClient --------------------------------------------------------------------------------
#
SET(SRCS_client
alm_export.h
)
# generate alm_[d|f]tc.h, tlm_[d|f]tc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS svcAlmClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcAlmClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAlmClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcAlmCommon
ddsftc
)
NGL_INSTALL_TARGET(svcAlmClient)
#
# --- Library svcAlmServer --------------------------------------------------------------------------------
#
SET(SRCS_server
alm_export.h
dds_handler.cpp
dds_handler.h
handler_configure_alarms.cpp
handler_configure_alarms.h
handler_participant.cpp
handler_participant.h
handler_emergency_call.cpp
handler_emergency_call.h
initialization.cpp
initialization.h
object_emergency_call.cpp
object_emergency_call.h
# settings.cpp
# settings.h
)
# generate alm_[d|f]ts.h, alm_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
# Generate alm.sql, alm.xml
CODEGEN_SQL(SRCS_server ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV svcAlmServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcAlmServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAlmServer
PRIVATE
RTI::nddscpp2
Qt5::Core
ddsfts
ddsconnector
ddsQoS
svcPdmCommon
svcAlmCommon
svcAlmClient
svcEemClient
svcPpmClient
svcMrkClient
svcCoreClient
svcTabClient
svcExmClient
svcTabServer
nglsettings
NGLLogger
ddscore
ngltools
PUBLIC
svcAlmCommon
ngldb
)
NGL_INSTALL_TARGET(svcAlmServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- RecordReplayPlugin --------------------------------------------------------------
#
CREATE_RECORD_REPLAY_PLUGIN(${xml_FILE} SRCS_server)
# Uncomment if necessary
#ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(ModuleTests)
+48
View File
@@ -0,0 +1,48 @@
#
# $Id$
# $HeadURL$
# $Author$
#
IF(BUILD_TESTING)
IF(TARGET svcAlmClient AND TARGET svcTabServer)
####################################
# Message Test
####################################
SET(SRCS_TEST_ALM
alm_tests.cpp
)
SET(HDRS_TEST_ALM
alm_tests.h
)
NGL_TARGET_INCLUDE_DIRECTORIES(almTests
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
# Testing FunctionTopic Functionality (FTC)
ADD_EXECUTABLE(almTests ${SRCS_TEST_ALM} ${HDRS_TEST_ALM})
TARGET_LINK_LIBRARIES(almTests
Qt5::Core
Qt5::Test
RTI::nddscpp2
svcAlmClient
svcCoreClient
svcEemServer
svcPpmServer
LtsCommandLineParser
ddsconnector
nglcoreapplication
)
IF(NOT BUILD_RPM)
NGL_INSTALL(AAR SV TARGETS almTests DESTINATION bin/test)
NGL_INSTALL(AAR SV FILES log4cxx_alm.props DESTINATION bin/test)
ENDIF()
ENDIF()
ENDIF()
+335
View File
@@ -0,0 +1,335 @@
/**
* Msg Function Topic Module Test
* $Id$
* $HeadURL$
* $Author$
*/
#include "alm_tests.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QTimer>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
/* ----------------------------------------------------------------------------------
* HOW TO RUN THE TESTS
* ----------------------------------------------------------------------------------
*
* Preconditions: Nothing
*
* Start services: (SV)
* ./svcExerciseEventManagement -e 42
* ./almTests -t 1 -e 42 // Configure Alarms with participantId=0, send EEM-Event and receive Alarm
* ./almTests -t 2 -e 42 // Configure Alarms with participantId-List, send EEM-Event and receive Alarm
* ./almTests -t 3 -e 42 // Configure Alarms with vulnerability > 0, send EEM-Event and receive Alarm
*/
// ------------------------------------------------------------------
namespace AlarmManagement
{
AlmTests::AlmTests(QObject *parent)
: QObject(parent)
, m_testId(0)
, m_bTestsSuccessful(true)
, m_logger(getLogger("AlmTests"))
, m_participantIndex(0)
{}
// ------------------------------------------------------------------
AlmTests::~AlmTests()
{}
// ------------------------------------------------------------------
bool AlmTests::init(int domainId, int testId, int operatorId)
{
INFO_LOG(m_logger) << "init begin";
m_testId = testId;
m_operatorId = operatorId;
m_participantIndex = 0;
m_ddsConnector.reset(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Any));
if (!m_ddsConnector->init())
return false;
m_ddsConnector->initPublisherSubscriber();
m_ftcAlm = AlarmManagement::Interface::Ftc::create(m_ddsConnector);
bool bRet = m_ftcAlm->enableAll();
m_dtcAlm = AlarmManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_dtcAlm->enableAll();
m_dtsEem = ExerciseEventManagement::Interface::Dts::create(m_ddsConnector);
bRet &= m_dtsEem->enableAll();
m_dtsPpm = ParticipantManagement::Interface::Dts::create(m_ddsConnector);
bRet &= m_dtsPpm->enableAll();
return bRet;
} // init
// ------------------------------------------------------------------
void AlmTests::startTest()
{
INFO_LOG(m_logger) << "startTest m_testId=" << m_testId;
if (m_testId <= 1) // Send FTC ConfigureAlarms and EEM Event
{
QTimer::singleShot(0, this, &AlmTests::tryConfigureAlarms);
QTimer::singleShot(100, this, &AlmTests::trySendEvent);
QTimer::singleShot(200, this, &AlmTests::checkAlarm);
}
if (m_testId == 2)
{
QTimer::singleShot(0, this, &AlmTests::tryCreateParticipant);
QTimer::singleShot(100, this, &AlmTests::tryCreateParticipant);
QTimer::singleShot(200, this, &AlmTests::tryConfigureAlarmsList);
QTimer::singleShot(300, this, &AlmTests::trySendEvent);
QTimer::singleShot(400, this, &AlmTests::checkAlarm);
}
if (m_testId == 3)
{
QTimer::singleShot(0, this, &AlmTests::tryCreateParticipant);
QTimer::singleShot(100, this, &AlmTests::tryCreateParticipant);
QTimer::singleShot(200, this, &AlmTests::tryConfigureAlarmsList);
QTimer::singleShot(300, this, &AlmTests::trySendEvent);
QTimer::singleShot(400, this, &AlmTests::checkAlarm);
}
QTimer::singleShot(5000, this, &AlmTests::failExit);
} // startTest
// ------------------------------------------------------------------
void AlmTests::tryConfigureAlarms()
{
INFO_LOG(m_logger) << "trying to send createRequest Operator Message";
std::vector<AlarmManagement::AlarmAction> alarms;
alarms.resize(2);
alarms[0].setMarker(true);
alarms[0].messageWindow(true);
alarms[0].vulnerabilityState(-1);
alarms[0].eventTypeId(ProjectDataManagement::EventType_def::Hit);
alarms[1].setMarker(true);
alarms[1].messageWindow(true);
alarms[1].vulnerabilityState(0);
alarms[1].eventTypeId(ProjectDataManagement::EventType_def::EnterNuclear);
std::vector<int32_t> participantIds;
participantIds.push_back(0);
bool globalEvent = true;
bool remove = false;
if (!m_ftcAlm->configureAlarmsRequest(ConfigureAlarmsRequest(alarms, m_operatorId, participantIds, globalEvent, remove), this))
{
ERROR_LOG(m_logger) << "Error requesting create data";
failExit();
}
} // tryConfigureAlarms
// ------------------------------------------------------------------
void AlmTests::tryCreateParticipant()
{
INFO_LOG(m_logger) << "trying to create Participant index = " << m_participantIndex;
ParticipantManagement::Participant participant;
switch (m_participantIndex)
{
case 0:
participant.id(1);
participant.superiorId(0);
participant.inferiorIds().resize(2);
participant.inferiorIds().at(0) = 2;
participant.inferiorIds().at(1) = 3;
participant.partyId(5);
break;
case 1:
participant.id(2);
participant.superiorId(1);
participant.inferiorIds().resize(2);
participant.inferiorIds().at(0) = 4;
participant.inferiorIds().at(1) = 5;
participant.partyId(5);
break;
}
DEBUG_LOG(m_logger) << "Publishing Participant";
if (!m_dtsPpm->publish(participant))
{
ERROR_LOG(m_logger) << "Error publish Participant";
failExit();
}
m_participantIndex++;
} // tryCreateParticipant
// ------------------------------------------------------------------
void AlmTests::tryConfigureAlarmsList()
{
INFO_LOG(m_logger) << "trying to send createRequest Operator Message";
std::vector<AlarmManagement::AlarmAction> alarms;
alarms.resize(2);
alarms[0].setMarker(true);
alarms[0].messageWindow(true);
alarms[0].eventTypeId(ProjectDataManagement::EventType_def::Hit);
alarms[0].vulnerabilityState(-1);
alarms[1].setMarker(true);
alarms[1].messageWindow(true);
alarms[1].vulnerabilityState(-1);
alarms[1].eventTypeId(ProjectDataManagement::EventType_def::WeaponAssignment);
std::vector<int32_t> participantIds;
participantIds.push_back(1);
participantIds.push_back(2);
bool globalEvent = true;
bool remove = false;
if (!m_ftcAlm->configureAlarmsRequest(ConfigureAlarmsRequest(alarms, m_operatorId, participantIds, globalEvent, remove), this))
{
ERROR_LOG(m_logger) << "Error requesting create data";
failExit();
}
} // tryConfigureAlarmsList
// ------------------------------------------------------------------
void AlmTests::trySendEvent()
{
INFO_LOG(m_logger) << "trying to send ParticipantEvent";
ExerciseEventManagement::Event pe;
// publish new Event data here
pe.id(100);
//pe.aesItemId(5);
pe.eventTypeId(ProjectDataManagement::EventType_def::Hit); // same as eventTypeId in ConfigureAlarms
pe.participantId(2);
pe.equipmentNumber(55);
pe.sampleTime(QDateTime::currentMSecsSinceEpoch());
//pe.friendlyFire(1);
//pe.vulnerability(0);
QJsonObject obj;
obj[QStringLiteral("FriendlyFire")] = QJsonValue(true);
QString properties = QString::fromUtf8(QJsonDocument(obj).toJson());
pe.properties(properties.toStdString());
DEBUG_LOG(m_logger) << "Publishing EEM Event";
if (!m_dtsEem->publish(pe))
{
ERROR_LOG(m_logger) << "Error publish EEM Event";
failExit();
}
}
// ------------------------------------------------------------------
void AlmTests::onConfigureAlarmsReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "onConfigureAlarmsReply, error = " << replyObject.error();
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Error ConfigureAlarms - aborting";
failExit();
}
} // onConfigureAlarmsReply
// ------------------------------------------------------------------
void AlmTests::checkAlarm()
{
INFO_LOG(m_logger) << "checkAlarm";
m_dtcAlm->waitForHistoricalDataNotification();
qint32 id = 0;
AlarmManagement::Notification notification = m_dtcAlm->notification(id);
INFO_LOG(m_logger) << "Recived AlarmManagement::Notification: " << notification
<< " testId: " << m_testId;
switch (m_testId)
{
case 1:
case 2:
case 3:
INFO_LOG(m_logger) << "ExitSuccess";
ExitSuccess();
break;
default:
// nothing
INFO_LOG(m_logger) << "failExit";
failExit();
break;
}
}
// ------------------------------------------------------------------
void AlmTests::failExit()
{
m_bTestsSuccessful = false;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
} // failExit
// ------------------------------------------------------------------
void AlmTests::ExitSuccess()
{
DEBUG_LOG(m_logger) << "SUCCESS";
m_bTestsSuccessful = true;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
} // ExitSuccess
} // namespace
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
addOption({ QStringList() << QLatin1String("t") << QLatin1String("testId")
, QLatin1String("TestId.")
, QLatin1String("Default: 0 (all tests)")
, QLatin1String("0")
});
addOption({ QStringList() << QLatin1String("o") << QLatin1String("operatorId")
, QLatin1String("OperatorId.")
, QLatin1String("Default: 10")
, QLatin1String("10")
});
}
int testId() const { return isSet(QLatin1String("t")) ? value(QLatin1String("t")).toInt() : 0; }
int operatorId() const { return isSet(QLatin1String("o")) ? value(QLatin1String("o")).toInt() : 10; }
};
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("ALM", "log4cxx_alm.props");
CmdLineParser options;
if (!options.init())
return -1;
int iRet = -1;
{
std::unique_ptr<AlarmManagement::AlmTests> almTests(new AlarmManagement::AlmTests);
if (!almTests->init(options.domainId(), options.testId(), options.operatorId()))
return iRet;
almTests->startTest();
STARTUP_LOG(app.baseLogger());
app.exec();
iRet = almTests->testsSuccessful() ? 0 : 1;
fprintf(stderr, "%d\n", iRet);
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
} // main
+69
View File
@@ -0,0 +1,69 @@
/**
* ModuleTests for Msg
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AlarmManagement/alm_dtc.h>
#include <AlarmManagement/alm_ftc.h>
#include <AlarmManagement/alm_ftc_types.h>
#include <AlarmManagement/logger.h>
#include <ExerciseEventManagement/eem_dts.h>
#include <ParticipantManagement/ppm_dts.h>
#include <DDS/DDSConnector/dds_connector.h>
namespace AlarmManagement
{
class AlmTests : public QObject
, protected AlarmManagement::Interface::Ftc::ConfigureAlarmsReplyHandler
{
Q_OBJECT
public:
explicit AlmTests(QObject *parent = nullptr);
~AlmTests();
bool init(int domainId, int testId, int operatorId);
bool testsSuccessful() const
{
return m_bTestsSuccessful;
}
public Q_SLOTS:
void startTest();
protected Q_SLOTS:
void tryConfigureAlarms();
void tryConfigureAlarmsList();
void trySendEvent();
void tryCreateParticipant();
void failExit();
void ExitSuccess();
protected:
void onConfigureAlarmsReply(const Core::FunctionReplyObject &replyObject) override final;
private:
void checkAlarm();
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<AlarmManagement::Interface::Ftc> m_ftcAlm;
std::shared_ptr<AlarmManagement::Interface::Dtc> m_dtcAlm;
std::shared_ptr<ExerciseEventManagement::Interface::Dts> m_dtsEem;
std::shared_ptr<ParticipantManagement::Interface::Dts> m_dtsPpm;
int m_testId;
bool m_bTestsSuccessful;
int m_operatorId;
NGL::INGLLogger &m_logger;
int m_participantIndex;
};
} // namespace AlarmManagement
+86
View File
@@ -0,0 +1,86 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: AlarmManagement
###########################################################
#log4j.logger.ALM=WARN
#log4j.logger.ALM.main=WARN
#log4j.logger.ALM.DbAdmin=WARN
#log4j.logger.ALM.DdsHandler=WARN
#log4j.logger.ALM.HandlerConfigureAlarms=DEBUG
#log4j.logger.ALM.HandlerParticipant=DEBUG
#log4j.logger.ALM.MainHandler=DEBUG
#log4j.logger.ALM.Settings=WARN
#log4j.logger.ALM.Publisher=WARN
#log4j.logger.ALM.Fts=WARN
# 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:
+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 ALM_COMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define ALM_COMMON_EXPORT
# else
# if defined(MAKE_SVCALMCOMMON_LIB)
# define ALM_COMMON_EXPORT Q_DECL_EXPORT
# ifndef WIN32
# ifdef NDDSUSERDllExport
# undef NDDSUSERDllExport
# endif
# define NDDSUSERDllExport Q_DECL_EXPORT
# endif
# else
# define ALM_COMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef ALM_CLIENT_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define ALM_CLIENT_EXPORT
# else
# if defined(MAKE_SVCALMCLIENT_LIB)
# define ALM_CLIENT_EXPORT Q_DECL_EXPORT
# else
# define ALM_CLIENT_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef ALM_SERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define ALM_SERVER_EXPORT
# else
# if defined(MAKE_SVCALMSERVER_LIB)
# define ALM_SERVER_EXPORT Q_DECL_EXPORT
# else
# define ALM_SERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
+153
View File
@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<service namespace="AlarmManagement" abbreviation="ALM" procNames="AlarmManagement" exerciseDependency="Exercise">
<rcs>
<Id Id="$Id$"/>
<HeadURL HeadURL="$HeadURL$"/>
<Author Author="$Author$"/>
</rcs>
<operatorRights offset="45">
<guiPlugin name="AlarmManagement" offset="1">
<operatorRightGroup name="Alarm Management (Global)" description="Configure Alarms">
<operatorRight right="ConfigGlobalDialog" value="1" description="Configure Alarms"/>
</operatorRightGroup>
</guiPlugin>
<guiPlugin name="AlmConfigParticipantAlarms" offset="2">
<operatorRightGroup name="Alarm Management (Participants)" description="Configure Participant Alarms">
<operatorRight right="ConfigParticipantDialog" value="1" description="Configure Participant Alarms"/>
</operatorRightGroup>
</guiPlugin>
<guiPlugin name="AlmEmergencyCall" offset="3">
<operatorRightGroup name="Emergency Call" description="Allow User to Handle Emergency Calls">
<operatorRight right="EmergencyCall" value="1" description="Allow User to Handle Emergency Calls"/>
</operatorRightGroup>
</guiPlugin>
</operatorRights>
<tables>
<table name="alm_config">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="operator_id" datatype="integer" isForeignKey="1"/>
<column name="event_type_id" datatype="integer" isForeignKey="1"/>
<column name="set_marker" datatype="boolean"/>
<column name="message_window" datatype="boolean"/>
<column name="participant_id" datatype="integer" isOptional="1" isForeignKey="1"/>
<column name="global" datatype="boolean"/>
<column name="vulnerability_state" datatype="integer" comment="-1 = All Types other Values see PDM-&amp;gt;Service-&amp;gt;Enumerations-&amp;gt;VulnerabilityState"/>
<constraint name="IDX_operator_id" type="index">
<column name="operator_id"/>
</constraint>
<foreignKey name="FK_alm_config_opm_operator">
<source table="alm_config" column="operator_id"/>
<destination service="OperatorManagement" serviceAbbr="opm" schema="opm" table="opm_operator" column="id"/>
</foreignKey>
<foreignKey name="FK_alm_config_pdm_event_type">
<source table="alm_config" column="event_type_id"/>
<destination service="ProjectDataManagement" serviceAbbr="pdm" schema="pdm" table="pdm_event_type" column="id"/>
</foreignKey>
<foreignKey name="FK_alm_config_ppm_participant">
<source table="alm_config" column="participant_id"/>
<destination service="ParticipantManagement" serviceAbbr="ppm" table="ppm_participant" column="id"/>
</foreignKey>
</table>
<table name="alm_emergency_call">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="sample_time" datatype="bigint"/>
<column name="incoming_time" datatype="bigint"/>
<column name="name" datatype="text"/>
<column name="issi" datatype="integer"/>
<column name="position" datatype="point_2d"/>
<column name="expired" datatype="boolean"/>
<column name="operator_id" datatype="integer"/>
<column name="deleted" datatype="bigint"/>
<column name="comment" datatype="text"/>
</table>
</tables>
<views/>
<dbServiceStructs>
<serviceStruct name="AlmConfig" table="alm_config"/>
<table name="alm_config"/>
<serviceStruct name="AlmEmergencyCall" table="alm_emergency_call"/>
<table name="alm_emergency_call"/>
</dbServiceStructs>
<topicTypes>
<topicType name="AlmEmergencyCall" isDataObject="1" routeToClient="1" routeToServer="0" routeInReplay="0" recordTopic="1" >
<column name="id" idlType="long" isKey="1"/>
<column name="sampleTime" idlType="long long"/>
<column name="incomingTime" idlType="long long"/>
<column name="name" idlType="string"/>
<column name="issi" idlType="long"/>
<column name="position" idlType="Core::GeoPosition2D"/>
<column name="expired" idlType="boolean"/>
<column name="operatorId" idlType="long"/>
<column name="deleted" idlType="long long"/>
<column name="comment" idlType="string"/>
</topicType>
<topicType name="EmergencyCallAcknowledgeRequest">
<column name="id" idlType="long"/>
<column name="timestamp" idlType="long long"/>
<column name="incomingTime" idlType="long long"/>
<column name="name" idlType="string"/>
<column name="issi" idlType="long"/>
<column name="operatorId" idlType="long"/>
<column name="comment" idlType="string"/>
<column name="expired" idlType="boolean"/>
</topicType>
<topicType name="AlarmAction">
<column name="setMarker" idlType="boolean"/>
<column name="messageWindow" idlType="boolean"/>
<column name="eventTypeId" idlType="ProjectDataManagement::EventType"/>
<column name="vulnerabilityState" idlType="long" comment="Only trigger, when this state is reached"/>
</topicType>
<topicType name="AlarmConfig" isDataObject="1" routeToClient="1" routeToServer="0" routeInReplay="0">
<column name="operatorId" idlType="long"/>
<column name="participantId" idlType="long"/>
<column name="eventTypeId" idlType="ProjectDataManagement::EventType"/>
<column name="messageWindow" idlType="boolean"/>
<column name="setMarker" idlType="boolean"/>
<column name="id" idlType="long" isKey="1"/>
<column name="global" idlType="boolean"/>
<column name="vulnerabilityState" idlType="long"/>
</topicType>
<topicType name="ConfigureAlarmsRequest">
<column name="alarms" idlType="sequence&lt;AlarmAction&gt;"/>
<column name="operatorId" idlType="long"/>
<column name="participantIds" idlType="sequence&lt;long&gt;" comment="&lt;ul&gt; &lt;li&gt;Any event that comes for a participant in the list is sent as an alarm.&lt;/li&gt; &lt;/ul&gt; &lt;ul&gt; &lt;li&gt;If the participant is 0, an alarm is sent for each event independently of the participant.&lt;/li&gt; &lt;/ul&gt;"/>
<column name="global" idlType="boolean" comment="global/local Alarm"/>
<column name="remove" idlType="boolean" comment="Remove alarm from Service, DDS and DB"/>
</topicType>
<topicType name="Notification" isDataObject="1" routeToClient="1" routeToServer="0" routeInReplay="0">
<column name="id" idlType="long" isKey="1"/>
<column name="operatorId" idlType="long"/>
<column name="eventId" idlType="long" comment="ExerciseEventManagement::EventType"/>
<column name="eventType" idlType="ProjectDataManagement::EventType"/>
<column name="time" idlType="long long"/>
<column name="equipmentNumber" idlType="long"/>
<column name="participantId" idlType="long" isOptional="1"/>
<column name="aesId" idlType="long" isOptional="1"/>
<column name="position" idlType="Core::GeoPosition"/>
<column name="properties" idlType="string" comment="Json-Object: Globale und lokale Borderlines: Global (Type boolean: true = global, false = local) optional Global, FriendlyFire , equipmentNumber, EventTypeId, participantId, aesId"/>
</topicType>
<topicType name="AcknowledgeSOSInAllExercises" isDataObject="1" >
<column name="id" idlType="long" isKey="1"/>
<column name="timestamp" idlType="long long"/>
<column name="incomingTime" idlType="long long"/>
<column name="name" idlType="string"/>
<column name="issi" idlType="long"/>
<column name="operatorId" idlType="long"/>
<column name="comment" idlType="string"/>
<column name="expired" idlType="boolean"/>
</topicType>
</topicTypes>
<functionTopics>
<functionTopic name="EmergencyCallAcknowledge" requestTopicType="EmergencyCallAcknowledgeRequest" replyTopicType="Core::FunctionReply" requestTopicName="EmergencyCallAcknowledgeRequest" replyTopicName="EmergencyCallAcknowledgeReply" routeInReplay="0"/>
<functionTopic name="ConfigureAlarms" requestTopicType="ConfigureAlarmsRequest" replyTopicType="Core::FunctionReply" requestTopicName="ConfigureAlarmsRequest" replyTopicName="ConfigureAlarmsReply" routeInReplay="0"/>
</functionTopics>
<enumerations>
<enumeration name="Dummy">
<attribute key="dummy1" value="1"/>
</enumeration>
</enumerations>
</service>
+97
View File
@@ -0,0 +1,97 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by gr, SLSE, 2015-09-02
*/
#include "dds_handler.h"
#include <DDS/DTS/generic_dts_impl.h>
#include <DDS/DDSConnector/publisher.h>
namespace AlarmManagement
{
DdsHandler::DdsHandler(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
: m_ddsConnector(ddsConnector)
, m_logger(getLogger("DdsHandler"))
{
DEBUG_LOG(m_logger) << "DdsHandler(" << m_ddsConnector->getDomainId() << ") for partition: " << m_ddsConnector->getDefaultPartition();
}
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();
// has to be false, because Snapshot Data Topics can be very large and fragmentation is not supported for batched writers!
m_ddsConnector->enableBatchedWriter(false);
m_topicLockMaster = std::make_shared<Core::TopicLockMaster>(m_ddsConnector);
m_dtcCore = Core::Interface::Dtc::create(m_ddsConnector);
m_dtcCore->enablePartitionTime();
m_dtsAlm = AlarmManagement::Interface::Dts::create(m_ddsConnector);
bool bRet = m_dtsAlm->enableAll();
INFO_LOG(m_logger) << "Dts initialised";
m_dtcAlm = AlarmManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_dtcAlm->enableAcknowledgeSOSInAllExercises();
INFO_LOG(m_logger) << "Dts initialised";
m_ftsAlm = AlarmManagement::Interface::Fts::create(m_ddsConnector);
bRet &= m_ftsAlm->enableAll();
INFO_LOG(m_logger) << "Fts initialised";
m_dtcEem = ExerciseEventManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_dtcEem->enableEvent();
INFO_LOG(m_logger) << "Dtc EEM initialised";
m_ftcMrk = MarkerManagement::Interface::Ftc::create(m_ddsConnector);
bRet &= m_ftcMrk->enableMarkerModify();
INFO_LOG(m_logger) << "Ftc MRK initialised";
m_dtcPpm = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_dtcPpm->enableParticipant();
INFO_LOG(m_logger) << "Dtc PPM initialised";
m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_exmDtc->enableExercise();
m_dtcTab = TrainingAreaBase::Interface::Dtc::create(m_ddsConnector);
bRet &= m_dtcTab->enableEmergencyCall();
INFO_LOG(m_logger) << "Dtc TAB initialised";
m_dtsEmergencyCallPublisher = std::make_shared<DDSConnector::Publisher>(m_ddsConnector->getDomainParticipant(),
DDSConnector::DdsConnector::getPartitionString(IDL::PartitionType::AllExercises));
m_dtsEmergencyCall = std::make_shared<DTS::AcknowledgeSOSInAllExercisesImpl>(m_ddsConnector->getDomainParticipant(),
m_dtsEmergencyCallPublisher->getDdsPublisher(),
DDS::Helper::getShortTopicName(DDS::topic_type_name<AlarmManagement::AcknowledgeSOSInAllExercises>::value()));
INFO_LOG(m_logger) << "Dtc TAB initialised";
return bRet;
}
int DdsHandler::exerciseId() const
{
return m_ddsConnector->getPartitionId();
} // end of init
//-----------------------------------------------------------------------------
qint64 DdsHandler::currentPartitionTime() const
{
return m_dtcCore->partitionTime().timeStamp();
}
} // Namespace AlarmManagement
+72
View File
@@ -0,0 +1,72 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by gr, SLSE, 2015-09-02
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/core_dtc.h>
#include <DDS/Core/topic_lock_master.h>
#include <AlarmManagement/alm_dts.h>
#include <AlarmManagement/alm_dtc.h>
#include <AlarmManagement/alm_fts.h>
#include <AlarmManagement/alm_dts_types.h>
#include <ExerciseEventManagement/eem_dtc.h>
#include <MarkerManagement/mrk_ftc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <TrainingAreaBase/tab_dtc.h>
#include <ExerciseManagement/exm_dtc.h>
#include "logger.h"
namespace AlarmManagement
{
class DdsHandler final
{
public:
explicit DdsHandler(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
~DdsHandler() = default;
bool init(const QStringList &qosFileList = QStringList());
std::shared_ptr<DDSConnector::DdsConnector> dds() const { return m_ddsConnector; }
std::shared_ptr<AlarmManagement::Interface::Dts> dtsAlm() const { return m_dtsAlm;} ///< Pointer to DTS AlarmManagement interface object
std::shared_ptr<AlarmManagement::Interface::Dtc> dtcAlm() const { return m_dtcAlm;} ///< Pointer to DTC AlarmManagement interface object
std::shared_ptr<AlarmManagement::Interface::Fts> ftsAlm() const { return m_ftsAlm;} ///< Pointer to FTS AlarmManagement interface object
std::shared_ptr<ExerciseEventManagement::Interface::Dtc> dtcEem() const { return m_dtcEem;} ///< Pointer to DTC ExerciseEventManagement interface object
std::shared_ptr<MarkerManagement::Interface::Ftc> ftcMrk() const { return m_ftcMrk;}
std::shared_ptr<ParticipantManagement::Interface::Dtc> dtcPpm() const { return m_dtcPpm;} ///< Pointer to DTC Participant Management interface object
std::shared_ptr<TrainingAreaBase::Interface::Dtc> dtcTab() const { return m_dtcTab;} ///< Pointer to DTC TrainingAreaBase Management interface object
std::shared_ptr<DTS::AcknowledgeSOSInAllExercises> publishSOS() const {return m_dtsEmergencyCall;}
std::shared_ptr<ExerciseManagement::Interface::Dtc> dtcExm() const {return m_exmDtc;}
std::shared_ptr<Core::TopicLockMaster> topicLockMaster() const { return m_topicLockMaster; }
int exerciseId() const;
qint64 currentPartitionTime() const;
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<Core::Interface::Dtc> m_dtcCore;
std::shared_ptr<Core::TopicLockMaster> m_topicLockMaster;
std::shared_ptr<AlarmManagement::Interface::Dts> m_dtsAlm; ///< Pointer to DTS AlarmManagement interface object
std::shared_ptr<AlarmManagement::Interface::Dtc> m_dtcAlm; ///< Pointer to DTC AlarmManagement interface object
std::shared_ptr<AlarmManagement::Interface::Fts> m_ftsAlm; ///< Pointer to FTS AlarmManagement interface object
std::shared_ptr<ExerciseEventManagement::Interface::Dtc> m_dtcEem;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
std::shared_ptr<MarkerManagement::Interface::Ftc> m_ftcMrk;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_dtcPpm;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_dtcTab;
std::shared_ptr<DDSConnector::Publisher> m_dtsEmergencyCallPublisher;
std::shared_ptr<DTS::AcknowledgeSOSInAllExercises> m_dtsEmergencyCall;
NGL::INGLLogger &m_logger;
};
}
+404
View File
@@ -0,0 +1,404 @@
/**
* Handler for message data request
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-12
*/
#include "handler_configure_alarms.h"
#include "initialization.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QSet>
#include <QTimer>
#include <ProjectDataManagement/pdm_translation.h>
#include <LtsUtilsCore/json_object.h>
namespace AlarmManagement
{
static AlarmConfig toDds(const Database::AlmConfig &data)
{
AlarmConfig almConfig;
almConfig.id(data.id);
almConfig.operatorId(data.operatorId);
if (data.participantId.isValid())
almConfig.participantId(data.participantId.toInt());
else
almConfig.participantId(0);
almConfig.eventTypeId(data.eventTypeId);
almConfig.messageWindow(data.messageWindow);
almConfig.global(data.global);
almConfig.vulnerabilityState(data.vulnerabilityState);
almConfig.setMarker(data.setMarker);
return almConfig;
}
HandlerConfigureAlarms::HandlerConfigureAlarms(Initialization *mainHandler)
: QObject()
, ConfigureAlarmsRequestHandler(mainHandler->dds()->ftsAlm())
, m_mainHandler(mainHandler)
, m_logger(getLogger("HandlerConfigureAlarms"))
, m_windowIndex(0)
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerConfigureAlarms::~HandlerConfigureAlarms()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerConfigureAlarms::init()
{
INFO_LOG(m_logger) << "Start HandlerConfigureAlarms.";
auto dtcEem = m_mainHandler->dds()->dtcEem();
connect(dtcEem.get(), &ExerciseEventManagement::Interface::Dtc::eventChanged,
this, &HandlerConfigureAlarms::onEventChanged);
// publish data from db-table 'alm_config'
auto cb = [this](const Database::AlmConfig & data)
{
DEBUG_LOG(m_logger) << "restore AlarmConfig from DB data: id=" << data.id
<< " operatorId=" << data.operatorId
<< " participantId=" << data.participantId.toInt()
<< " global=" << data.global
<< " eventTypeId=" << data.eventTypeId
<< " setMarker=" << data.setMarker
<< " messageWindow=" << data.messageWindow
<< " vulnerabilityState=" << data.vulnerabilityState;
m_mainHandler->dds()->dtsAlm()->publish(toDds(data));
m_alarms.insert(data.id, data);
};
if (!m_mainHandler->db()->select(cb))
{
ERROR_LOG(m_logger) << "Error reading data from database 'alm_config'!";
return false;
}
return true;
} // end init()
void HandlerConfigureAlarms::removeAlarmConfig(int operatorId,
int participantId,
int eventTypeId)
{
DEBUG_LOG(m_logger) << "removeAlarmConfig m_alarms.size=" << m_alarms.size()
<< " operatorId=" << operatorId
<< " participantId=" << participantId
<< " eventTypeId=" << eventTypeId;
// Find Key
QMapIterator<int, Database::AlmConfig> i(m_alarms);
while (i.hasNext())
{
i.next();
DEBUG_LOG(m_logger) << "key= " << i.key() << ": "
<< " operatorId=" << i.value().operatorId
<< " participantId=" << i.value().participantId.toInt()
<< " eventTypeId=" << i.value().eventTypeId.underlying();
if ((operatorId == i.value().operatorId)
&& (participantId == i.value().participantId)
&& (eventTypeId == ProjectDataManagement::EventType_def::Unknown || eventTypeId == i.value().eventTypeId.underlying()))
{
const int key = i.key();
DEBUG_LOG(m_logger) << "found alarm to remove: " << key << ", eventTypeId: " << eventTypeId;
m_mainHandler->db()->removeAlmConfig(key);
m_mainHandler->dds()->dtsAlm()->removeAlarmConfigByKey(key);
m_alarms.remove(key); // is ok, QMapIterator work on a copy
}
}
}
void HandlerConfigureAlarms::removeGlobalAlarmConfig(int operatorId)
{
DEBUG_LOG(m_logger) << "removeAlarmConfig m_alarms.size=" << m_alarms.size();
// Find Key
QMapIterator<int, Database::AlmConfig> i(m_alarms);
while (i.hasNext())
{
i.next();
DEBUG_LOG(m_logger) << "key= " << i.key()
<< " participantId=" << i.value().participantId.toInt();
if ((operatorId == i.value().operatorId)
&& (i.value().participantId.toInt()) == 0)
{
const int key = i.key();
DEBUG_LOG(m_logger) << "found alarm to remove: " << key;
m_mainHandler->db()->removeAlmConfig(key);
m_mainHandler->dds()->dtsAlm()->removeAlarmConfigByKey(key);
m_alarms.remove(key); // is ok, QMapIterator work on a copy
}
}
}
//-----------------------------------------------------------------------------
void HandlerConfigureAlarms::onConfigureAlarmsRequest(const rti::core::SampleIdentity &cookie,
const ConfigureAlarmsRequest &request)
{
DEBUG_LOG(m_logger) << "onConfigureAlarmsRequest " << request;
if (request.participantIds().size() == 0)
{
Core::FunctionReplyObject ret(0, tr_lts("dds", "Error adding ConfigureAlarms to database. There were no participants given."));
m_mainHandler->dds()->ftsAlm()->configureAlarmsReply(cookie, ret);
return;
}
if (request.remove())
{
// remove only this one Alarm, not all types of the participant for operator
// in DB,
// from DDS
DEBUG_LOG(m_logger) << "request.remove()";
if (request.participantIds().size() > 0 &&
request.alarms().size() > 0)
{
int participantId = request.participantIds()[0];
int eventTypeId = request.alarms()[0].eventTypeId().underlying();
DEBUG_LOG(m_logger) << "removeAlarmConfig(request.operatorId()=" << request.operatorId()
<< " participantId=" << participantId
<< " eventTypeId=" << eventTypeId;
removeAlarmConfig(request.operatorId(), participantId, eventTypeId);
}
}
else
{
// remove old configuration
for (int participantId : request.participantIds())
{
DEBUG_LOG(m_logger) << "remove old configuration participantId=" << participantId;
removeAlarmConfig(request.operatorId(), participantId, 0);
}
if ( request.global())
{
removeGlobalAlarmConfig(request.operatorId());
}
for (int participantId : request.participantIds())
{
DEBUG_LOG(m_logger) << "participantId: " << participantId;
for (const AlarmManagement::AlarmAction &action : request.alarms())
{
DEBUG_LOG(m_logger) << "action: " << action;
AlarmManagement::AlarmConfig almConfig;
almConfig.operatorId(request.operatorId());
almConfig.participantId(participantId);
almConfig.eventTypeId(action.eventTypeId());
almConfig.messageWindow(action.messageWindow());
almConfig.vulnerabilityState(action.vulnerabilityState());
almConfig.setMarker(action.setMarker());
almConfig.global(request.global());
Database::AlmConfig newConfig;
newConfig.operatorId = request.operatorId();
if (participantId != 0)
newConfig.participantId = participantId;
newConfig.eventTypeId = action.eventTypeId();
newConfig.messageWindow = action.messageWindow();
newConfig.vulnerabilityState = action.vulnerabilityState();
newConfig.setMarker = action.setMarker();
newConfig.global = request.global();
if (!m_mainHandler->db()->insert(newConfig))
{
Core::FunctionReplyObject ret(0, tr_lts("dds", "Error adding ConfigureAlarms to database."));
m_mainHandler->dds()->ftsAlm()->configureAlarmsReply(cookie, ret);
}
DEBUG_LOG(m_logger) << "m_dbAlmConfig.insert id=" << newConfig.id;
almConfig.id(newConfig.id);
m_alarms.insert(newConfig.id, newConfig);
m_mainHandler->dds()->dtsAlm()->publish(almConfig);
}
}
}
m_mainHandler->dds()->ftsAlm()->configureAlarmsReply(cookie, Core::FunctionReplyObject());
} // end onConfigureAlarmsRequest()
//-----------------------------------------------------------------------------
void HandlerConfigureAlarms::onEventChanged(const ExerciseEventManagement::Event &eventData, DataModification modType)
{
DEBUG_LOG(m_logger) << "onParticipantEventChanged participantId=" << eventData.participantId();
if (modType == DataModification::eRemoved ||
modType == DataModification::eModified)
return;
DEBUG_LOG(m_logger) << "onParticipantEventChanged verarbeiten m_alarms.size =" << m_alarms.size();
ExerciseEventManagement::Event eventDatalocal = eventData;
QVector<int> participants;
QSet<int> sentEventToOperator;
// toBool() returns false if attribute does not exist
const QJsonObject eventObject = getEventProperties(eventDatalocal);
const bool friendlyFireBool = eventObject.value(QStringLiteral("FriendlyFire")).toBool();
const int vulnerabilityState = eventObject.value(QStringLiteral("vulnerabilityState")).toInt();
// Schleife ueber jede Konfiguration,
// pruefen, ob eventTypeId == EventTypeId
// Schleife ueber jeden Participant in der Konfiguration
// Vergleich Participant-ID Event mit Konfiguration Event
// Wenn ja, Alarm an operatorId schicken
for (const Database::AlmConfig &alarm : qAsConst(m_alarms))
{
{
/*DEBUG_LOG(m_logger) << " alarmConfigItem.id=" << alarm.id
<< " alarmConfigItem.participantId=" << alarm.participantId
<< " alarmConfigItem.eventTypeId=" << alarm.eventTypeId
<< " eventDatalocal.eventTypeId()=" << eventDatalocal.eventTypeId().underlying()
<< " alarm.operatorId=" << alarm.operatorId
<< " friendyFire =" << friendlyFireBool;*/
if ((alarm.eventTypeId == eventDatalocal.eventTypeId().underlying()) ||
(alarm.eventTypeId == ProjectDataManagement::EventType_def::FriendlyFire && friendlyFireBool))
{
if (friendlyFireBool && eventDatalocal.eventTypeId() == ProjectDataManagement::EventType_def::FireEvent)
eventDatalocal.eventTypeId(ProjectDataManagement::EventType_def::FriendlyFire);
participants.clear();
if (alarm.participantId.isValid())
participants = m_mainHandler->hdlParticipant()->getRecursiveInferiorIds(alarm.participantId.toInt());
else
participants.append(0);
for (int participantId : qAsConst(participants))
{
bool allVulnerabilityStates = alarm.vulnerabilityState == -1;
bool vulnerabilityCheck = vulnerabilityState == alarm.vulnerabilityState;
bool globalAlarm = participantId == 0;
DEBUG_LOG(m_logger) << " allVulnerabilityStates=" << allVulnerabilityStates
<< " vulnerabilityCheck=" << vulnerabilityCheck
<< " participantId=" << participantId
<< " eventDatalocal.participantId()=" << eventDatalocal.participantId();
if (((allVulnerabilityStates || vulnerabilityCheck) &&
(participantId == eventDatalocal.participantId())) || globalAlarm)
{
if (sentEventToOperator.contains(alarm.operatorId))
{
DEBUG_LOG(m_logger) << "Event " << eventDatalocal.eventTypeId() << " already handled for Operator:" << alarm.operatorId;
continue;
}
sentEventToOperator += alarm.operatorId;
if (alarm.messageWindow)
{
DEBUG_LOG(m_logger) << "send Window to Operator:" << alarm.operatorId;
sendWindow(eventDatalocal, alarm);
}
if (alarm.setMarker)
{
DEBUG_LOG(m_logger) << "set Marker to Operator:" << alarm.operatorId;
sendMarker(eventDatalocal, alarm);
}
}
}
}
}
}
} // end onParticipantEventChanged
void HandlerConfigureAlarms::sendWindow(const ExerciseEventManagement::Event &eventData,
const Database::AlmConfig &config)
{
AlarmManagement::Notification wi;
QJsonObject eventObject = getEventProperties(eventData);
QJsonObject obj;
wi.id(m_windowIndex);
wi.operatorId(config.operatorId);
wi.time(eventData.sampleTime());
wi.eventId(eventData.id());
if (eventData.eventTypeId() == ProjectDataManagement::EventType_def::CrossGlobalBorderline
|| eventData.eventTypeId() == ProjectDataManagement::EventType_def::CrossLocalBorderline)
{
if (eventObject.contains(QStringLiteral("Global")))
obj[QStringLiteral("Global")] = eventObject.value(QStringLiteral("Global"));
}
if (eventObject.contains(QStringLiteral("FriendlyFire")))
obj[QStringLiteral("FriendlyFire")] = eventObject.value(QStringLiteral("FriendlyFire"));
if (eventObject.contains(QStringLiteral("AesId")))
wi.aesId(eventObject.value(QStringLiteral("AesId")).toInt());
// AesEvent from the radio:
int originatorType = eventObject[QStringLiteral("OriginatorType")].toInt();
if (originatorType == ProjectDataManagement::StateChangeOriginator_def::AreaEffectSimulation
|| originatorType == ProjectDataManagement::StateChangeOriginator_def::FunctionalArea)
obj[QStringLiteral("aesId")] = eventObject[QStringLiteral("OriginatorId")].toInt();
wi.equipmentNumber(eventData.equipmentNumber());
wi.eventType(eventData.eventTypeId());
wi.participantId(eventData.participantId());
wi.position(eventData.position());
const QByteArray propertiesStr = QJsonDocument(obj).toJson(QJsonDocument::Compact);
wi.properties() = propertiesStr.data();
m_mainHandler->dds()->dtsAlm()->publish(wi);
// take data from dds-bus after 1 min
const auto id = wi.id();
QTimer::singleShot(1 * 60 * 1000, this, [id, this]() { m_mainHandler->dds()->dtsAlm()->removeNotificationByKey(id); });
incrementWindowIndex(); // m_windowIndex++;
} // end sendWindow
QJsonObject HandlerConfigureAlarms::getEventProperties(const ExerciseEventManagement::Event &eventData)
{
return LtsUtils::JsonObject::fromJson(eventData.properties());
}
void HandlerConfigureAlarms::sendMarker(const ExerciseEventManagement::Event &eventData,
const Database::AlmConfig &config)
{
int defaultOffset = 10000;
DEBUG_LOG(m_logger) << "eventProperties =" << eventData.properties();
QString contentStr;
contentStr = QStringLiteral("Equip No.: ");
const int participantId = eventData.participantId();
const int equipNo = m_mainHandler->hdlParticipant()->participantId2EquipNo(participantId);
QJsonObject eventObject = getEventProperties(eventData);
contentStr += QString::number(equipNo);
contentStr += QStringLiteral(" ");
contentStr += ProjectDataManagement::toString(eventData.eventTypeId());
if (eventObject.contains(QStringLiteral("AesName")))
{
contentStr += QStringLiteral(" (");
contentStr += eventObject.value(QStringLiteral("AesName")).toString();
contentStr += QStringLiteral(")");
}
DEBUG_LOG(m_logger) << "contentStr=" << contentStr;
const QByteArray contentArr = contentStr.toUtf8();
MarkerManagement::MarkerDescription ma;
ma.content(contentArr.data());
ma.markerType(MarkerManagement::MarkerType_def::Situation);
MarkerManagement::Marker mcr;
mcr.operatorId(config.operatorId);
mcr.note(ma.content()); // really?
mcr.timeStamp(eventData.sampleTime());
mcr.timeStampOffset(defaultOffset);
mcr.descriptions().push_back(ma);
mcr.position(eventData.position());
mcr.fileContentId(0); // we cannot save an image here
m_mainHandler->dds()->ftcMrk()->markerModifyRequest(mcr, this);
} // end sendMarker
void HandlerConfigureAlarms::onMarkerModifyReply(const Core::FunctionReplyObject &replyObject)
{
Q_UNUSED(replyObject);
INFO_LOG(m_logger) << "************** onMarkerCreateReply";
} // end onMarkerCreateReply
} // namespace AlarmManagement
+91
View File
@@ -0,0 +1,91 @@
/**
* Handler for table-io message data request
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-10-12
*/
#pragma once
#include <QObject>
#include "dds_handler.h"
#include "logger.h"
#include <alm_db.h>
#include <AlarmManagement/alm_fts.h>
#include <AlarmManagement/alm_ftc.h>
#include <AlarmManagement/alm_dtc.h>
#include <AlarmManagement/alm_dts.h>
#include <ExerciseEventManagement/eem_dtc.h>
#include <MarkerManagement/mrk_ftc.h>
#include <handler_participant.h>
namespace AlarmManagement
{
class Initialization;
class HandlerConfigureAlarms final
: public QObject
, protected Interface::Fts::ConfigureAlarmsRequestHandler
, protected MarkerManagement::Interface::Ftc::MarkerModifyReplyHandler
{
Q_OBJECT
public:
explicit HandlerConfigureAlarms(Initialization *mainHandler);
~HandlerConfigureAlarms();
bool init();
void onMarkerModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void incrementWindowIndex() { m_windowIndex++; }
int getWindowIndex() const { return m_windowIndex; }
private Q_SLOTS:
void onEventChanged(const ExerciseEventManagement::Event &eventData, DataModification modType);
private: // methods
void onConfigureAlarmsRequest(const rti::core::SampleIdentity &cookie,
const ConfigureAlarmsRequest &request) override;
void sendWindow(const ExerciseEventManagement::Event &eventData,
const Database::AlmConfig &config);
void sendMarker(const ExerciseEventManagement::Event &eventData,
const Database::AlmConfig &config);
static QJsonObject getEventProperties(const ExerciseEventManagement::Event &eventData);
void removeAlarmConfig(int operatorId, int participantId, int eventTypeId);
void removeGlobalAlarmConfig(int operatorId);
private: // attributes
Initialization *m_mainHandler;
struct AlarmKey
{
int operatorId = 0;
int participantId = 0;
AlarmKey() = default;
explicit AlarmKey(const Database::AlmConfig &db)
: operatorId(db.operatorId)
, participantId(db.participantId.isNull() ? 0 : db.participantId.toInt())
{}
explicit AlarmKey(int operatorId_, int participantId_)
: operatorId(operatorId_)
, participantId(participantId_)
{}
bool operator<(const AlarmKey &o) const
{
if (operatorId != o.operatorId)
return operatorId < o.operatorId;
return participantId < o.participantId;
}
};
QMap<int, Database::AlmConfig> m_alarms; // Key = almConfig.id
NGL::INGLLogger &m_logger;
int m_windowIndex;
};
} // namespace AlarmManagement
+238
View File
@@ -0,0 +1,238 @@
/**
* Handler emergency call from tetra radio
* $Id$
* $HeadURL$
* $Author$
* Created by Astrid Müller, SGAS, 2018-02-23
*/
#include "handler_emergency_call.h"
#include "initialization.h"
#include "handler_configure_alarms.h"
#include "object_emergency_call.h"
#include "alm_db.h"
#include <DDS/DTS/generic_dts.h>
#include <QJsonObject>
#include <QJsonDocument>
#include <QDebug>
namespace AlarmManagement
{
HandlerEmergencyCall::HandlerEmergencyCall(const std::shared_ptr<HandlerConfigureAlarms> &configAlarmHandler,
Initialization *mainHandler)
: QObject()
, EmergencyCallAcknowledgeRequestHandler(mainHandler->dds()->ftsAlm())
, m_mainHandler(mainHandler)
, m_configAlarmHandler(configAlarmHandler)
, m_logger(getLogger("HandlerEmergencyCall"))
{
}
HandlerEmergencyCall::~HandlerEmergencyCall()
{
}
bool HandlerEmergencyCall::init()
{
INFO_LOG(m_logger) << "Start HandlerEmergencyCall.";
auto dtcTab = m_mainHandler->dds()->dtcTab();
connect(dtcTab.get(), &TrainingAreaBase::Interface::Dtc::emergencyCallChanged,
this, &HandlerEmergencyCall::onEmergencyCallChanged);
connect(m_mainHandler->dds()->dtcAlm().get(), &AlarmManagement::Interface::Dtc::acknowledgeSOSInAllExercisesChanged,
this, &HandlerEmergencyCall::onAcknowledgeSOSInExercises);
// create and init Object
bool bRet = true;
auto cbEmergencyCall = [this, &bRet](const Database::AlmEmergencyCall &data)
{
WARNING_LOG(m_logger)<<" ALM_EMERGENCY " << "Reading from DB AlmEmergencyCall with id = " << data.id;
if (!this->isExerciseValidForEmergencyCall() && !data.expired)
return;
ObjectEmergencyCall *ifs(new ObjectEmergencyCall(m_mainHandler));
Core::FunctionReplyObject ret = ifs->createObject(data);
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger) << ret();
bRet = false;
return;
}
m_emergencyObjMap.insert(ifs->getId(), ifs);
};
if (!m_mainHandler->db()->select(cbEmergencyCall) || !bRet)
{
ERROR_LOG(m_logger) <<"Error reading data from database 'alm_emergency_call!";
return false;
}
return true;
}
bool HandlerEmergencyCall::createEmergencyCallObject(const AlarmManagement::AlmEmergencyCall &data)
{
if (!isNewEmergencyCall(data) || std::isnan(data.position().lat()))
return false;
Database::AlmEmergencyCall newData;
newData.id = 0;
newData.operatorId = 0;
newData.sampleTime = data.sampleTime();
newData.incomingTime = data.sampleTime();
newData.expired = false;
newData.issi = data.issi();
newData.position.setX(data.position().lon());
newData.position.setY(data.position().lat());
newData.comment = QStringLiteral("");
newData.name = QStringLiteral("");
ObjectEmergencyCall *ifs(new ObjectEmergencyCall(m_mainHandler, this));
Core::FunctionReplyObject ret = ifs->createObject(newData);
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger)<< ret();
return false;
}
WARNING_LOG(m_logger)<< "Entry createEmergencyCallObject in the DB id: "<<ifs->getData().id<<" issi: "<<ifs->getData().issi<<" expired: "<<ifs->getData().expired;
m_emergencyObjMap.insert(ifs->getId(), ifs);
return true;
}
void HandlerEmergencyCall::onEmergencyCallAcknowledgeRequest(const rti::core::SampleIdentity &cookie, const EmergencyCallAcknowledgeRequest &request)
{
WARNING_LOG(m_logger) << "[onEmergencyCallAcknowledgeRequest] request: " << request;
Core::FunctionReplyObject ret = emergencyCallAcknowledgeRequest(request);
if (ret.error())
WARNING_LOG(m_logger)<< ret();
if (request.expired())
{
AcknowledgeSOSInAllExercises ackInEx;
ackInEx.id(request.id());
ackInEx.timestamp(request.timestamp());
ackInEx.incomingTime(request.incomingTime());
ackInEx.name(request.name());
ackInEx.issi(request.issi());
ackInEx.operatorId(request.operatorId());
ackInEx.comment(request.comment());
ackInEx.expired(request.expired());
WARNING_LOG(m_logger) << "onEmergencyCallAcknowledgeRequest: issi: "<<ackInEx.issi();
m_mainHandler->dds()->publishSOS()->write(ackInEx);
}
m_mainHandler->dds()->ftsAlm()->emergencyCallAcknowledgeReply(cookie,ret);
}
Core::FunctionReplyObject HandlerEmergencyCall::emergencyCallAcknowledgeRequest(const EmergencyCallAcknowledgeRequest &request)
{
ObjectEmergencyCall *ifs = nullptr;
for(auto _ifs : qAsConst(m_emergencyObjMap))
{
auto data = _ifs->getData();
if (data.issi == request.issi()
&& data.incomingTime == request.incomingTime()
&& !data.expired)
{
ifs = _ifs;
break;
}
}
if (ifs == nullptr)
{
WARNING_LOG(m_logger)<<" Couldn't acknowledge SOS with issi: " <<request.issi()<< ": already acknowledged or issi doesn't exist.";
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "already acknowledged or issi(%1) couldn't be found")).arg(request.issi());
}
if (ifs->getData().expired) // if already expired do nothing
{
WARNING_LOG(m_logger) << "SOS Already expired do nothing";
return Core::FunctionReplyObject(ifs->getData().id);
}
WARNING_LOG(m_logger) << "emergencyCallAcknowledgeRequest: issi found!: "<<request.issi()<<" ifs->state: "<<ifs->getData().expired;
Database::AlmEmergencyCall newData(ifs->getData());
newData.expired = true;
newData.issi = request.issi();
newData.sampleTime = request.timestamp();
newData.deleted = request.timestamp();
newData.operatorId = request.operatorId();
newData.name = QString::fromStdString(request.name());
newData.comment = QString::fromStdString(request.comment());
return ifs->updateObject(newData);
}
bool HandlerEmergencyCall::isExerciseValidForEmergencyCall() const
{
const auto &exmDtc = m_mainHandler->dds()->dtcExm();
exmDtc->waitForHistoricalDataExercise();
const ExerciseManagement::Exercise exercise = exmDtc->exercise(m_mainHandler->dds()->exerciseId());
if (exercise.state() != ExerciseManagement::ExerciseState::Started
&& exercise.state() != ExerciseManagement::ExerciseState::Paused)
return false;
return true;
}
bool HandlerEmergencyCall::isNewEmergencyCall(const AlmEmergencyCall &newdata)
{
for(const auto &ifs : qAsConst(m_emergencyObjMap))
{
const auto &data = ifs->getData();
if (data.issi == newdata.issi()
&& data.incomingTime == newdata.incomingTime()
&& !data.expired)
{
WARNING_LOG(m_logger)<<" SOS EXISTS and not yet quit! issi: "<<data.issi<<" incomingTime: "<<data.incomingTime<<" expired: "<<data.expired;
return false;
}
}
return true;
}
void HandlerEmergencyCall::onEmergencyCallChanged(const TrainingAreaBase::EmergencyCall &data, DataModification modType)
{
if (modType == DataModification::eRemoved || !isExerciseValidForEmergencyCall())
return;
AlarmManagement::AlmEmergencyCall emc;
emc.id(0);
emc.operatorId(0); // global
emc.sampleTime(data.sampleTime());
emc.incomingTime(data.sampleTime());
emc.expired(false);
emc.issi(data.equipmentNumber());
Core::GeoPosition position;
position.lat(data.position().lat());
position.lon(data.position().lon());
emc.position(position);
createEmergencyCallObject(emc);
}
void HandlerEmergencyCall::onAcknowledgeSOSInExercises(const AcknowledgeSOSInAllExercises &data, DataModification modType)
{
DEBUG_LOG(m_logger)<< "onAcknowledgeSOSInExercises START ";
Q_UNUSED(modType)
EmergencyCallAcknowledgeRequest request;
request.comment(data.comment());
request.expired(data.expired());
request.id(data.id());
request.issi(data.issi());
request.incomingTime(data.incomingTime());
request.name(data.name());
request.timestamp(data.timestamp());
request.operatorId(data.operatorId());
emergencyCallAcknowledgeRequest(request);
DEBUG_LOG(m_logger)<< "onAcknowledgeEmergencyInExercises END ";
}
} // namespace AlarmManagement
+55
View File
@@ -0,0 +1,55 @@
/**
* Handler emergency call from tetra radio
* $Id$
* $HeadURL$
* $Author$
* Created by Astrid Müller, SGAS, 2018-02-23
*/
#pragma once
#include <QObject>
#include <AlarmManagement/alm_db.h>
#include "dds_handler.h"
#include "logger.h"
#include "alm_fts.h"
namespace AlarmManagement
{
class Initialization;
class HandlerConfigureAlarms;
class ObjectEmergencyCall;
class HandlerEmergencyCall final : public QObject
, protected Interface::Fts::EmergencyCallAcknowledgeRequestHandler
{
Q_OBJECT
public:
explicit HandlerEmergencyCall(const std::shared_ptr<HandlerConfigureAlarms> &configAlarmHandler,
Initialization *mainHandler);
~HandlerEmergencyCall();
bool init();
private:
bool createEmergencyCallObject(const AlmEmergencyCall &data);
void onEmergencyCallAcknowledgeRequest(const rti::core::SampleIdentity &cookie,
const EmergencyCallAcknowledgeRequest &request) override final;
Core::FunctionReplyObject emergencyCallAcknowledgeRequest(const EmergencyCallAcknowledgeRequest &request);
bool isExerciseValidForEmergencyCall() const;
bool isNewEmergencyCall(const AlmEmergencyCall &newdata);
private slots:
void onEmergencyCallChanged(const TrainingAreaBase::EmergencyCall &data, DataModification modType);
void onAcknowledgeSOSInExercises(const AlarmManagement::AcknowledgeSOSInAllExercises &data, DataModification modType);
private:
Initialization *m_mainHandler;
std::shared_ptr<HandlerConfigureAlarms> m_configAlarmHandler;
NGL::INGLLogger &m_logger;
QMap<int, ObjectEmergencyCall *> m_emergencyObjMap; // Key = DangerAreaId
};
} // namespace AlarmManagement
+193
View File
@@ -0,0 +1,193 @@
/**
* Handler for player data
* $Id$
* $HeadURL$
* $Author$
* Created by gr, SLSE, 2017-02-20
*/
#include "handler_participant.h"
#include "settings.h"
using namespace AlarmManagement;
HandlerParticipant::HandlerParticipant(const std::shared_ptr<DdsHandler> &ddsHandler)
: QObject()
, m_ddsHandler(ddsHandler)
, m_logger(getLogger("HandlerParticipant"))
{
} // end of ctor()
//-----------------------------------------------------------------------------
HandlerParticipant::~HandlerParticipant()
{
} // end of dtor()
//-----------------------------------------------------------------------------
bool HandlerParticipant::init()
{
auto dtcPpm = m_ddsHandler->dtcPpm();
connect(dtcPpm.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &HandlerParticipant::onParticipantChanged);
connect(dtcPpm.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
this, &HandlerParticipant::onDynamicParticipantChanged);
// Get 'static' data for all participants
{
auto cb = [this](const ParticipantManagement::Participant & es)
{
onParticipantChanged(es, DataModification::eAdded);
};
if (!dtcPpm->participants(cb))
return false;
}
if (!dtcPpm->enableParticipant())
{
ERROR_LOG(m_logger) << "Could not initialize ParticipantManagement enableParticipant data topic client";
return false;
}
if (!dtcPpm->enableDynamicParticipantData())
{
ERROR_LOG(m_logger) << "Could not initialize ParticipantManagement enableDynamicParticipant data topic client";
return false;
}
// Get 'static' data for all participants (playerdata)
QMap<qint32, ParticipantManagement::Participant> participantsMap;
participantsMap = dtcPpm->participants();
// Player in Referenzmap eintragen
for (auto i = participantsMap.begin(); i != participantsMap.end(); ++i)
{
const ParticipantManagement::Participant &participant = i.value();
if (participant.equipmentNumber().is_set())
{
m_partId2EquipNoMap.insert(participant.id(), participant.equipmentNumber().get());
DEBUG_LOG(m_logger) << "m_partId2EquipNoMap.insert participantId=" << participant.id() << "EquimentNo=" << participant.equipmentNumber().get();
}
else
m_partId2EquipNoMap.insert(participant.id(), 0);
}
// Get 'static' data for all dynamicParticipants (playerdata)
QMap<qint32, ParticipantManagement::DynamicParticipantData> dynamicParticipantsMap = dtcPpm->dynamicParticipantDatas();
// Player in Referenzmap eintragen
for (auto i = dynamicParticipantsMap.begin(); i != dynamicParticipantsMap.end(); ++i)
{
const ParticipantManagement::DynamicParticipantData &dynamicParticipant = i.value();
if (dynamicParticipant.stateHistory().size() > 0)
{
std::vector<ParticipantManagement::StateInformation> states = dynamicParticipant.stateHistory();
ParticipantManagement::StateInformation state = states[0];
int vulnerability = state.vulnerabilityState().underlying();
m_partId2VulnerabilityMap.insert(i.value().participantId(), vulnerability);
DEBUG_LOG(m_logger) << "m_partId2EquipNoMap.insert participantId=" << i.value().participantId() << "vulnerability=" << vulnerability;
}
else
m_partId2VulnerabilityMap.insert(i.value().participantId(), -1);
}
INFO_LOG(m_logger) << "HandlerParticipant::init() finished. ";
return true;
} // end of init()
//-----------------------------------------------------------------------------
void HandlerParticipant::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType)
{
if (modType == DataModification::eRemoved)
{
DEBUG_LOG(m_logger) << "onParticipantChanged - remove: " << data.id();
m_participantHierarchy.remove(data.id());
}
else
{
DEBUG_LOG(m_logger) << "onParticipantChanged:" << data;
m_participantHierarchy.insert(data.id(),
AlarmManagement::HandlerParticipant::ParticipantHierarchy(data));
const int equipNo = data.equipmentNumber().is_set() ? data.equipmentNumber().get() : 0;
m_partId2EquipNoMap.insert(data.id(), equipNo);
}
} // end of onParticipantChanged()
void HandlerParticipant::onDynamicParticipantChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "onDynamicParticipant participantId=" << data.participantId();
if (modType == DataModification::eRemoved)
{
}
else
{
INFO_LOG(m_logger) << "onDynamicParticipant()"
<< " participantId: " << data.participantId();
if (data.stateHistory().size() > 0)
{
std::vector<ParticipantManagement::StateInformation> states = data.stateHistory();
ParticipantManagement::StateInformation state = states[0];
int vulnerability = state.vulnerabilityState().underlying();
m_partId2VulnerabilityMap.insert(data.participantId(), vulnerability);
DEBUG_LOG(m_logger) << "m_partId2EquipNoMap.insert participantId=" << data.participantId()
<< " vulnerability=" << vulnerability;
}
else
m_partId2VulnerabilityMap.insert(data.participantId(), -1);
}
} // end of onParticipantChanged()
QVector<AlarmManagement::HandlerParticipant::ParticipantHierarchy> HandlerParticipant::getInferiorParts(int participantId) const
{
QVector<AlarmManagement::HandlerParticipant::ParticipantHierarchy> retVector;
auto it = m_participantHierarchy.find(participantId);
if (it == m_participantHierarchy.end())
return retVector;
const AlarmManagement::HandlerParticipant::ParticipantHierarchy &participant = it.value();
retVector.reserve(participant.inferiorIds.size() + retVector.size());
retVector.append(participant);
for (int inferiorId : participant.inferiorIds)
retVector.append(getInferiorParts(inferiorId));
return retVector;
}
//-----------------------------------------------------------------------------
QVector<int> HandlerParticipant::getRecursiveInferiorIds(const int participantId) const
{
QVector <int> result;
if (participantId == 0)
{
result.append(participantId);
return result;
}
const QVector<AlarmManagement::HandlerParticipant::ParticipantHierarchy> inferiorParts = HandlerParticipant::getInferiorParts(participantId);
result.reserve(inferiorParts.size());
for (const auto &inferior : inferiorParts)
result.append(inferior.participantId);
return result;
}
int HandlerParticipant::participantId2EquipNo(const int participantId) const
{
int ret = m_partId2EquipNoMap.value(participantId, 0);
INFO_LOG(m_logger) << "participantId2EquipNo participantId=" << participantId
<< " EquipmentNumber=" << ret;
return ret;
} // end of participantId2EquipNo()
int HandlerParticipant::participantId2Vulnerability(const int participantId) const
{
int ret = m_partId2VulnerabilityMap.value(participantId, 0);
INFO_LOG(m_logger) << "m_partId2VulnerabilityMap participantId=" << participantId
<< " Vulnerability=" << ret;
return ret;
} // end of participantId2Vulnerability()
+92
View File
@@ -0,0 +1,92 @@
/**
* Handler for player data
* $Id$
* $HeadURL$
* $Author$
* Created by gr, SLSE, 2017-02-20
*/
#pragma once
#include <QObject>
#include "dds_handler.h"
#include "logger.h"
#include <ParticipantManagement/ppm_dtc.h>
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QVector>
#include <QtCore/QHash>
// AES-Handling for participants outside buildings
namespace AlarmManagement
{
class HandlerParticipant : public QObject
{
Q_OBJECT
public:
struct ParticipantHierarchy
{
int participantId = 0;
int superiorId = 0;
std::vector<int32_t> inferiorIds;
ParticipantHierarchy() = default;
explicit ParticipantHierarchy(const ParticipantManagement::Participant &p)
: participantId(p.id())
, superiorId(p.superiorId())
, inferiorIds(p.inferiorIds())
{}
};
public:
explicit HandlerParticipant(const std::shared_ptr<DdsHandler> &ddsHandler);
~HandlerParticipant();
/**
* Initialize the handler to manage information from participant management
*/
bool init();
QVector<int> getRecursiveInferiorIds(const int participantId) const;
/**
* Get equipment number for the given participant
* @param participantId participant id to get the equipment number for
* @return equipment number or 0 in case of failure
*/
int participantId2EquipNo(const int participantId) const;
/**
* Get vulnerability for the given participant
* @param participantId participant id to get the equipment number for
* @return vulnerability
*/
int participantId2Vulnerability(const int participantId) const;
private Q_SLOTS:
/**
* Handle the signals from 'ParticipantManagement'
*/
void onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType);
void onDynamicParticipantChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType);
private: // methoden
QVector<AlarmManagement::HandlerParticipant::ParticipantHierarchy> getInferiorParts(int participantId) const;
private: // attributes
QMap<int, int> m_partId2EquipNoMap; ///< Reference map participant (player) to equipment number (participantId, equipmentNo)
QMap<int, int> m_partId2VulnerabilityMap; ///< Reference map participant (player) to Vulnerability
QHash<int, ParticipantHierarchy> m_participantHierarchy;
std::shared_ptr<DdsHandler> m_ddsHandler; ///< Pointer to VOM DDS handler
NGL::INGLLogger &m_logger; ///< Interface to the Logging-Facility
};
} // namespace AlarmManagement
+99
View File
@@ -0,0 +1,99 @@
/**
* Initialization for MessageDataManagement
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-09-21
*/
#include "initialization.h"
#include "handler_configure_alarms.h"
#include "handler_emergency_call.h"
#include "handler_participant.h"
#include <ngldb/db_service_handler.h>
#include <DDS/DDSConnector/dds_connector.h>
using namespace AlarmManagement;
Initialization::Initialization()
: m_logger(getLogger("Initialization"))
{
INFO_LOG(m_logger) << "Initialization::Initialization";
} // end of ctor()
//-----------------------------------------------------------------------------
Initialization::~Initialization()
{
m_handlerConfigureAlarms = nullptr;
m_handlerParticipant = nullptr;
m_handlerEmergencyCall = nullptr;
m_dbHandler = nullptr;
m_ddsHandler = nullptr;
INFO_LOG(m_logger) << "Initialization::~Initialization";
} // end of dtor()
//-----------------------------------------------------------------------------
bool Initialization::initialize(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, const QSqlDatabase &sqlDb)
{
INFO_LOG(m_logger) << "Start AlarmManagement Initialization. domainId =" << ddsConnector->getDomainId()
<< " exerciseId=" << ddsConnector->getPartitionId();
// initialize DDS
m_ddsHandler = std::make_shared<DdsHandler>(ddsConnector);
if (!m_ddsHandler->init())
{
ERROR_LOG(m_logger) << "Initialisation DdsHandler failed.";
return false;
}
INFO_LOG(m_logger) << "m_ddsHandler initialized. ";
// initialize database
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(ddsConnector->getPartitionId(), sqlDb))
{
ERROR_LOG(m_logger) << "Initialisation DbHandler failed.";
return false;
}
INFO_LOG(m_logger) << "DbHandler initialized. ";
// create handlers
m_handlerParticipant = std::make_shared<HandlerParticipant>(m_ddsHandler);
m_handlerConfigureAlarms = std::make_shared<HandlerConfigureAlarms>(this);
m_handlerEmergencyCall = std::make_shared<HandlerEmergencyCall>(m_handlerConfigureAlarms, this);
DEBUG_LOG(m_logger) << "initialize successfully finished";
return true;
} // end of init()
//-----------------------------------------------------------------------------
bool Initialization::loadFromDatabase()
{
// initialize handlers
if (!m_handlerParticipant || !m_handlerParticipant->init())
{
ERROR_LOG(m_logger) << "Can not initialize Participant handler";
return false;
}
if (!m_handlerConfigureAlarms || !m_handlerConfigureAlarms->init())
{
ERROR_LOG(m_logger) << "Can not initialize Alarm handler";
return false;
}
if (!m_handlerEmergencyCall || !m_handlerEmergencyCall->init())
{
ERROR_LOG(m_logger) << "Can not initialize EmergencyCall handler";
return false;
}
DEBUG_LOG(m_logger) << "loadFromDatabase successfully finished";
return true;
}
std::shared_ptr<DatabaseInterface> Initialization::db() const
{
return m_dbHandler->db();
}
+57
View File
@@ -0,0 +1,57 @@
/**
* MainHandler for Messaging
* $Id$
* $HeadURL$
* $Author$
* Created by hjl, SLSE, 2016-09-21
*/
#pragma once
#include <memory>
#include <AlarmManagement/alm_db.h>
#include <ngldb/db_service_handler.h>
#include "dds_handler.h"
#include "logger.h"
namespace DDSConnector
{
class DdsConnector;
}
namespace NGLDB
{
template <typename DB> class DbServiceHandler;
}
namespace AlarmManagement
{
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class DdsHandler;
class HandlerConfigureAlarms;
class HandlerEmergencyCall;
class HandlerParticipant;
class ALM_SERVER_EXPORT Initialization
{
public:
Initialization();
~Initialization();
bool initialize(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, const QSqlDatabase &sqlDb);
bool loadFromDatabase();
std::shared_ptr<DdsHandler> dds() const { return m_ddsHandler; }
std::shared_ptr<DatabaseInterface> db() const;
std::shared_ptr<HandlerParticipant> hdlParticipant() const { return m_handlerParticipant; }
private: // attributes
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler; ///< Pointer to DB handler
std::shared_ptr<HandlerConfigureAlarms> m_handlerConfigureAlarms;
std::shared_ptr<HandlerParticipant> m_handlerParticipant; ///< Pointer to handler for all participants
std::shared_ptr<HandlerEmergencyCall> m_handlerEmergencyCall;
NGL::INGLLogger &m_logger; ///< Interface to the Logging-Facility
};
} // namespace MessagingDataManagement
+37
View File
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace AlarmManagement
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "ALM.";
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 AlarmManagement
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for AlarmManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "alm_export.h"
namespace AlarmManagement
{
using LOGGER_T = NGL::INGLLogger &;
ALM_COMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace AlarmManagement
+81
View File
@@ -0,0 +1,81 @@
#include "object_emergency_call.h"
#include "logger.h"
#include "initialization.h"
namespace AlarmManagement{
ObjectEmergencyCall::ObjectEmergencyCall(Initialization *mainHandler, QObject *parent)
: QObject(parent)
, m_mainHandler(mainHandler)
, m_logger(getLogger("ObjectEmergencyCall"))
{
}
ObjectEmergencyCall::~ObjectEmergencyCall()
{
if (m_data.id > 0)
m_mainHandler->dds()->dtsAlm()->removeAlarmConfigByKey(m_data.id);
}
Core::FunctionReplyObject ObjectEmergencyCall::createObject(const Database::AlmEmergencyCall &data)
{
m_data = data;
if (m_data.id <= 0)
{
if (!m_mainHandler->db()->insert(m_data))
return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting Order into database."));
WARNING_LOG(m_logger) << "New EmergencyCall created Id: " << m_data.id;
}
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectEmergencyCall::updateObject(const Database::AlmEmergencyCall &data)
{
if (data.id <= 0)
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Object not valid/blocked"));
m_data = data;
if (!m_mainHandler->db()->update(m_data))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating EmergencyCall into database"));
DEBUG_LOG(m_logger) << "Data changed for Changed Id: " << m_data.id;
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectEmergencyCall::removeObject()
{
if (!m_mainHandler->db()->removeAlmEmergencyCall(m_data.id))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "AlmEmergencyCall %1 can not be removed")).arg(m_data.id);
return Core::FunctionReplyObject(m_data.id);
}
void ObjectEmergencyCall::onPublishData()
{
AlarmManagement::AlmEmergencyCall ddsData;
ddsData.id(m_data.id);
ddsData.sampleTime(m_data.sampleTime);
ddsData.incomingTime(m_data.incomingTime);
ddsData.name(m_data.name.toStdString());
ddsData.issi(m_data.issi);
ddsData.position(Core::GeoPosition2D(m_data.position.x(), m_data.position.y()));
ddsData.expired(m_data.expired);
ddsData.deleted(m_data.deleted);
ddsData.operatorId(m_data.operatorId);
ddsData.comment(m_data.comment.toStdString());
INFO_LOG(m_logger) << "Publish AlmEmergencyCall-data to dds (" << m_data.id << ") ...";
if (!m_mainHandler->dds()->dtsAlm()->publish(ddsData))
ERROR_LOG(m_logger) << "Error publishing AlmEmergencyCall-data to dds";
}
}// namespace AlarmManagement
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <QObject>
#include <dds_handler.h>
#include "logger.h"
#include <DDS/Core/function_reply_object.h>
//Generated
#include <AlarmManagement/alm_db.h>
namespace AlarmManagement {
class Initialization;
class ObjectEmergencyCall : public QObject
{
Q_OBJECT
public:
explicit ObjectEmergencyCall(Initialization *mainHandler, QObject *parent = nullptr);
~ObjectEmergencyCall();
Core::FunctionReplyObject createObject(const Database::AlmEmergencyCall &data);
Core::FunctionReplyObject updateObject(const Database::AlmEmergencyCall &data);
Core::FunctionReplyObject removeObject();
int getId() const { return m_data.id; }
const Database::AlmEmergencyCall &getData() const { return m_data; }
Core::FunctionReplyObject updateObject() { return updateObject(m_data); }
protected Q_SLOTS:
void onPublishData();
private:
Initialization *m_mainHandler;
Database::AlmEmergencyCall m_data;
NGL::INGLLogger &m_logger;
};
}
+42
View File
@@ -0,0 +1,42 @@
/**
* Settings for AlarmManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "settings.h"
#include <QtCore/QSettings>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
namespace AlarmManagement
{
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);
}
} // namespace AlarmManagement
+24
View File
@@ -0,0 +1,24 @@
/**
* Settings for AlarmManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QString>
namespace NGL
{
class Settings;
}
namespace AlarmManagement
{
namespace Settings
{
NGL::Settings *settings();
void setIniFile(const QString &ini);
}
} // namespace AlarmManagement
+467
View File
@@ -0,0 +1,467 @@
#es
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE aes_service.xml)
#
# --- Library svcAesCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
aes_export.h
logger.cpp
logger.h
Utils/icons.cpp
Utils/icons.h
Utils/intersections.cpp
Utils/intersections.h
Utils/tne_algorithmen.h
Utils/tne_algorithmen.cpp
Utils/polygons.h
Utils/polygons.cpp
Utils/polygons.h
Utils/ddsdbconvert.cpp
Utils/ddsdbconvert.h
Utils/aes_helper.cpp
Utils/aes_helper.h
Utils/fire_automaton.cpp
Utils/fire_automaton.h
Utils/firing_timer.cpp
Utils/firing_timer.h
settings.cpp
settings.h
)
# copy aes_predefined_data.sql to share
SET(in_file_sql "aes_predefined_data.sql")
SET(out_dir_sql "${CMAKE_BINARY_DIR}/share/Lts/sql/")
SET(out_file_sql "${out_dir_sql}/${in_file_sql}")
ADD_CUSTOM_COMMAND(OUTPUT ${out_file_sql}
COMMAND ${CMAKE_COMMAND}
ARGS -E make_directory ${out_dir_sql}
COMMAND ${CMAKE_COMMAND}
ARGS -E copy_if_different ${in_file_sql} ${out_file_sql}
DEPENDS ${in_file_sql}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
LIST(APPEND SRCS_common ${in_file_sql} ${out_file_sql})
# Generate aes_enums.idl / aes.idl / aes_enums.cpp/aes_enums.h
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate aes_db_structs.cpp/.h
CODEGEN_DATABASE_STRUCTS(SRCS_common ${xml_FILE})
# Generate aes_db_impl.cpp/.h,
CODEGEN_DATABASE_SQL(SRCS_common ${xml_FILE})
# Generate aes_dao.cpp/.h
CODEGEN_DAO(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from aes_enums.idl / aes.idl
RTIDDSGEN(SRCS_common aes_enums.idl)
RTIDDSGEN(SRCS_common aes.idl)
# Generate aes_dt[s,c]_types.h, aes_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# generate aes_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS HLA svcAesCommon SHARED ${SRCS_common} )
NGL_TARGET_INCLUDE_DIRECTORIES(svcAesCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAesCommon
PRIVATE
RTI::nddscpp2
LtsCoordTrans
PUBLIC
Qt5::Core
Qt5::Gui
Qt5::Xml
ddscore
svcPdmCommon
svcPpmCommon
NGLLogger
nglsettings
ngltools
situationMapElevationMap
Trajectory
ngldb
)
NGL_INSTALL_TARGET(svcAesCommon)
#
# --- Library svcAesClient --------------------------------------------------------------------------------
#
SET(SRCS_client
aes_dtc_impl_custom.cpp
aes_dtc_impl_custom.h
aes_export.h
)
# generate aes_[d|f]tc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
# SV wegen ft unit test
NGL_ADD_LIBRARY(AAR SV HLA WS svcAesClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcAesClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAesClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcAesCommon
nglsettings
ngltools
ddsftc
)
NGL_INSTALL_TARGET(svcAesClient)
#
# --- Library svcAesServer --------------------------------------------------------------------------------
#
SET(SRCS_server
aes_export.h
log4cxx_aes.props
)
# generate aes_[d|f]ts.h, aes_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
#WS for Record Replay plugin stand alone build
NGL_ADD_LIBRARY(AAR SV WS svcAesServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcAesServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcAesServer
PRIVATE
RTI::nddscpp2
ddsconnector
ddsfts
PUBLIC
svcAesCommon
ngldb
)
NGL_INSTALL_TARGET(svcAesServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- RecordReplayPlugin --------------------------------------------------------------
#
CREATE_RECORD_REPLAY_PLUGIN_SHARED(${xml_FILE})
#
# --- RecordReplayPlugin --------------------------------------------------------------
#
IF (TARGET aesRecordReplayPlugin)
TARGET_SOURCES(aesRecordReplayPlugin
PRIVATE
RecordReplay/aes_record_replay_plugin_custom.cpp
RecordReplay/aes_record_replay_plugin_custom.h
)
TARGET_LINK_LIBRARIES(aesRecordReplayPlugin
Qt5::Concurrent
svcAesServer
svcAesClient
)
ENDIF()
#
# --- Executable svcAreaEffectSimulation (sAES) --------------------------------------------------------------
#
LIST(APPEND SRCS_exe
dds_handler.cpp
dds_handler.h
handler_air_support_impact.cpp
handler_air_support_impact.h
handler_base.cpp
handler_base.h
handler_bccontamination.cpp
handler_bccontamination.h
handler_booby_trap.cpp
handler_booby_trap.h
handler_borderline.cpp
handler_borderline.h
handler_charge_administration.cpp
handler_charge_administration.h
handler_charge_effect.cpp
handler_charge_effect.h
handler_danger_area.cpp
handler_danger_area.h
handler_dus_single_mine.cpp
handler_dus_single_mine.h
handler_effect_cluster.h
handler_effect_cluster.cpp
handler_explosion_area.cpp
handler_explosion_area.h
handler_explosion_cluster.cpp
handler_explosion_cluster.h
handler_explosion_single.cpp
handler_explosion_single.h
handler_functional_area.cpp
handler_functional_area.h
handler_gas_pipe.cpp
handler_gas_pipe.h
handler_mine_area.cpp
handler_mine_area.h
handler_mine_corridor.cpp
handler_mine_corridor.h
handler_ncontamination.cpp
handler_ncontamination.h
handler_off_route_mine.cpp
handler_off_route_mine.h
handler_recon_area.cpp
handler_recon_area.h
handler_technical_area.cpp
handler_technical_area.h
handler_effector.cpp
handler_effector.h
handler_trajectory.cpp
handler_trajectory.h
main.cpp
main_handler.cpp
main_handler.h
meteo_data_provider.cpp
meteo_data_provider.h
mine_corridor_creator.cpp
mine_corridor_creator.h
object_air_support_impact.cpp
object_air_support_impact.h
object_base.cpp
object_base.h
object_bccontamination.cpp
object_bccontamination.h
object_booby_trap.cpp
object_booby_trap.h
object_borderline.cpp
object_borderline.h
object_charge_administration.cpp
object_charge_administration.h
object_charge_effect.cpp
object_charge_effect.h
object_danger_area.cpp
object_danger_area.h
object_dus_single_mine.cpp
object_dus_single_mine.h
object_effect_cluster.h
object_effect_cluster.cpp
object_explosion_area.cpp
object_explosion_area.h
object_explosion_cluster.cpp
object_explosion_cluster.h
object_explosion_single.cpp
object_explosion_single.h
object_functional_area.cpp
object_functional_area.h
object_gas_pipe.cpp
object_gas_pipe.h
object_mine_area.cpp
object_mine_area.h
object_mine_area_null.cpp
object_mine_area_null.h
object_mine_corridor.cpp
object_mine_corridor.h
object_ncontamination.cpp
object_ncontamination.h
object_off_route_mine.cpp
object_off_route_mine.h
object_recon_area.cpp
object_recon_area.h
object_technical_area.cpp
object_technical_area.h
object_effector.cpp
object_effector.h
object_trajectory.cpp
object_trajectory.h
special_area_provider.cpp
special_area_provider.h
effector_state_machine.cpp
effector_state_machine.h
./EffectorFiringStrategy/abstract_firing_strategy.cpp
./EffectorFiringStrategy/abstract_firing_strategy.h
./EffectorFiringStrategy/default_firing_strategy.cpp
./EffectorFiringStrategy/default_firing_strategy.h
./EffectorFiringStrategy/dm72_firing_strategy.cpp
./EffectorFiringStrategy/dm72_firing_strategy.h
./EffectorFiringStrategy/firing_strategy_factory.cpp
./EffectorFiringStrategy/firing_strategy_factory.h
./EffectorFiringStrategy/gmlrs_firing_strategy.cpp
./EffectorFiringStrategy/gmlrs_firing_strategy.h
./EffectorFiringStrategy/smart_firing_strategy.h
./EffectorFiringStrategy/smart_firing_strategy.cpp
./TrajectorySplitter/trajectory_splitter.cpp
./TrajectorySplitter/trajectory_splitter.h
)
# Generate aes.sql, aes.xml
CODEGEN_SQL(SRCS_exe ${xml_FILE})
NGL_ADD_SVC_ICON(SRCS_exe)
NGL_ADD_EXECUTABLE(AAR SV svcAreaEffectSimulation ${SRCS_exe})
NGL_TARGET_LINK_LIBRARIES(svcAreaEffectSimulation
svcAesServer
svcCesClient
svcCoreClient
svcMdmClient
svcPdmClient
svcPpmClient
svcTabClient
svcIsmClient
svcExmClient
svcAsmClient
Qt5::Core
Qt5::Gui # QVector3D
RTI::nddscpp2
nglsettings
ProjectConfig
LtsCommandLineParser
LtsCoordTrans
LtsUtilsCore
LtsUtilsDds
LtsElevation
NGLLogger
nglcoreapplication
ddsconnector
ngltools
saaTelegrams
)
NGL_INSTALL_TARGET(svcAreaEffectSimulation)
NGL_INSTALL(AAR SV FILES log4cxx_aes.props DESTINATION bin)
NGL_ADD_TARGET_LINK(svcAreaEffectSimulation sAES)
#
# --- Tests --------------------------------------------------------------
#
MACRO(ADD_FT_TEST type)
IF(TARGET svcAesClient)
IF(NOT TARGET aes_test_base)
NGL_ADD_LIBRARY2(AAR SV NO_TRANSLATION aes_test_base SHARED
ModuleTests/aes_test_base.cpp
ModuleTests/aes_test_base.h
)
NGL_TARGET_LINK_LIBRARIES(aes_test_base
Qt5::Core
Qt5::Test
ddsconnector
LtsCommandLineParser
nglcoreapplication
svcAesClient
LtsCoordTrans
)
ENDIF()
SET(_tgt_name aes_test_${type})
NGL_ADD_EXECUTABLE(AAR SV NO_TRANSLATION ${_tgt_name}
ModuleTests/aes_test_${type}.cpp
ModuleTests/aes_test_${type}.h
)
NGL_TARGET_LINK_LIBRARIES(${_tgt_name}
aes_test_base
)
NGL_INSTALL_TARGET(${_tgt_name} DESTINATION bin/test)
ENDIF()
ENDMACRO()
ADD_FT_TEST(air_support_impact)
ADD_FT_TEST(bccontamination)
ADD_FT_TEST(booby_trap)
ADD_FT_TEST(charge_administration)
ADD_FT_TEST(danger_area)
ADD_FT_TEST(explosion_area)
ADD_FT_TEST(explosion_area_bulk)
ADD_FT_TEST(explosion_single)
ADD_FT_TEST(explosion_cluster)
ADD_FT_TEST(gas_pipe)
ADD_FT_TEST(mine_area)
ADD_FT_TEST(ncontamination)
ADD_FT_TEST(off_route_mine)
NGL_INSTALL(AAR SV FILES log4cxx_aes.props DESTINATION bin/test)
#
# --- Tests --------------------------------------------------------------
#
NGL_ADD_EXECUTABLE(AAR SV NO_TRANSLATION aes_utils_test
Utils/test_main.cpp
)
NGL_TARGET_LINK_LIBRARIES(aes_utils_test
Qt5::Core
Qt5::Widgets
RTI::nddscpp2
svcAesCommon
ProjectConfig
ddsconnector
nglapplication
LtsCoordTrans
)
NGL_INSTALL(AAR SV TARGETS aes_utils_test DESTINATION bin/test)
#
# --- Tests --------------------------------------------------------------
#
NGL_ADD_EXECUTABLE(SV NO_TRANSLATION aes_convert_mine_area
ModuleTests/aes_convert_mine_area.cpp
)
NGL_TARGET_LINK_LIBRARIES(aes_convert_mine_area
Qt5::Core
# Qt5::Widgets
# RTI::nddscpp2
svcAesServer
ProjectConfig
# nglapplication
LtsCoordTrans
ngldb
LtsCommandLineParser
nglcoreapplication
nglsettings
ngltools
)
NGL_INSTALL(SV TARGETS aes_convert_mine_area DESTINATION bin/test)
#
# --- SUBDIRECTORY --------------------------------------------------------------
#
ADD_SUBDIRECTORY(Trajectory)
# Used to pack this specific Module Test in rpm. Please not use this for other tests
ADD_SUBDIRECTORY(ModuleTest_Limits)
#
# --- Tests --------------------------------------------------------------
#
IF(BUILD_TESTING)
ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(UnitTest_FireAutomaton)
ADD_SUBDIRECTORY(TrajectorySplitter)
ADD_SUBDIRECTORY(ModuleTest_EffectCluster)
ADD_SUBDIRECTORY(ModuleTest_ExplosionCluster)
ADD_SUBDIRECTORY(ModuleTest_others)
ENDIF()
NGL_INSTALL(AAR SV FILES log4cxx_aes.props DESTINATION bin)
NGL_INSTALL(AAR SV FILES aes_predefined_data.sql DESTINATION share/Lts/sql)
@@ -0,0 +1,211 @@
/**
* Base Class for Effector Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2025-05-11
*/
#include "abstract_firing_strategy.h"
#include "object_effector.h"
#include "main_handler.h"
#include "handler_charge_effect.h"
#include <ParticipantManagement/ppm.hpp>
#include "Trajectory/trajectory_transform.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <QJsonDocument>
#include <QRandomGenerator>
namespace AreaEffectSimulation
{
AbstractFiringStrategy::AbstractFiringStrategy(ObjectEffector *parent)
: QObject(parent)
, m_effector(parent)
, m_logger(getLogger("FiringStrategy"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
AbstractFiringStrategy::~AbstractFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void AbstractFiringStrategy::calculateDispersionTrajectory()
{
DEBUG_LOG(m_logger) << "calculateDispersionTrajectory";
// 1. Neuen Zielpunkt berechnen
const auto &intendendTrajectory = m_effector->intendendTrajectory();
// sanity check:
if (intendendTrajectory.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of intendendTrajectory not sufficiant, size=" << intendendTrajectory.size();
return;
}
LtsUtils::QPoint3D newTarget = intendendTrajectory.last().position;
const int scatterLength = qRound(m_effector->effectorData().mhpScatterLenght());
const int randomValueX = QRandomGenerator::global()->bounded(-scatterLength, scatterLength + 1);
const int scatterWidth = qRound(m_effector->effectorData().mhpScatterWidth());
const int randomValueY = QRandomGenerator::global()->bounded(-scatterWidth, scatterWidth + 1);
newTarget.setX(newTarget.x() + randomValueX);
newTarget.setY(newTarget.y() + randomValueY);
// 2. Neue Trajektorie berechnen
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(intendendTrajectory, newTarget);
}
std::vector<Core::GeoPositionRelativeTime> AbstractFiringStrategy::buildTrajectoryGeometry(const QVector<TrajectoryPoint> &relativeTrajectory)
{
// sanity check:
if (relativeTrajectory.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of relativeTrajectory not sufficiant, size=" << relativeTrajectory.size();
return {};
}
// setup some vars of effector, which is used as launch position:
Core::GeoPosition effectorPositionGeo = participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate &launchUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(effectorPositionGeo.lon(), effectorPositionGeo.lat());
std::vector<Core::GeoPositionRelativeTime> geometry;
geometry.reserve(relativeTrajectory.size());
// Aehnlich wie Lts/Proxies/TrainingAreaBase/Plugins/AdlerJson/aes_handler.cpp Zeile 365
for (const TrajectoryPoint &trajPntRelPos : qAsConst(relativeTrajectory))
{
DEBUG_LOG(m_logger) << "Flugbahnpunkt: trajPntRelPos :" << trajPntRelPos; // benefit from operator<<:
// // geographic offers nice feature to add an offset:
// const QPointF &trajectoryAbsGeo = launchUtm.geographic(trajPntRelPos);
// but lets stay with trajectoryAbsUtm to enable DEBUG_LOG
const QPointF &trajPntRelPosUtm = trajPntRelPos.position.toPointF(); // still relativ to launch
const LtsCoordTrans::UtmCoordinate trajPntAbsUtm(launchUtm.zone,
launchUtm.x + trajPntRelPosUtm.x(),
launchUtm.y + trajPntRelPosUtm.y());
const QPointF &trajPntAbsGeo = trajPntAbsUtm.geographic();
// Position des Participants mit der m_participantId
const double zValue = effectorPositionGeo.z() + trajPntRelPos.position.z(); // trajPntRelPos.position.z() ist die Hoehe
DEBUG_LOG(m_logger) << "trajPntAbsUtm :" << trajPntAbsUtm << ", trajPntAbsGeo :" << trajPntAbsGeo;
geometry.emplace_back(Core::GeoPositionRelativeTime(trajPntAbsGeo.x(), trajPntAbsGeo.y(), zValue, trajPntRelPos.time));
}
return geometry;
}
TrajectoryTimeAware AbstractFiringStrategy::buildTrajectoryTimeAware(const std::vector<Core::GeoPositionRelativeTime> &trajectoryGeometry, qint64 trajectoryStartTime, int index)
{
// sanity check:
if (trajectoryGeometry.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of trajectoryGeometry not sufficiant, size=" << trajectoryGeometry.size();
return {};
}
TrajectoryTimeAware trajectory;
trajectory.trajectoryStartTime(trajectoryStartTime);
trajectory.index(index); // see SituationMap : charge_effect_to_map ::updateSingleTrajectory()
// [E01562-259] use originatorId, originatorTypeId (instead of aesItemId, aesTypeId)
trajectory.originatorId(m_effector->aesItemId());
trajectory.originatorTypeId(ProjectDataManagement::StateChangeOriginator::ChargeEffect);
trajectory.geometry(trajectoryGeometry);
return trajectory;
}
int AbstractFiringStrategy::sendChargeEffect(const TrajectoryTimeAware &trajectoryTimeAware, const QJsonObject &properties)
{
DEBUG_LOG(m_logger) << "sendChargeEffect";
const auto &trajGeometry = trajectoryTimeAware.geometry();
// sanity check:
if (trajGeometry.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " size of trajectoryTimeAware.geometry not sufficiant, size=" << trajGeometry.size();
return -1;
}
const qint64 trajectoryStartTime = trajectoryTimeAware.trajectoryStartTime();
ChargeEffectModifyRequest chargeEffect;
chargeEffect.id(0);
chargeEffect.effectDuration(3000);
chargeEffect.name("Schuss");
chargeEffect.chargeTypeId(m_effector->effectorData().chargeStruct().chargeTypeId());
chargeEffect.effectStart(trajectoryStartTime + trajectoryTimeAware.geometry().back().relativeTime()); // Einschlag Zeit
chargeEffect.igniterTypeId(m_effector->effectorData().chargeStruct().igniterTypeId());
chargeEffect.gearTypeId(gearTypeId(m_effector->effectorData().chargeStruct().chargeTypeId(), m_effector->participantId()));
chargeEffect.originatorId(m_effector->aesItemId());
chargeEffect.originatorTypeId(ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
chargeEffect.properties(QJsonDocument(properties).toJson().toStdString());
const Core::GeoPosition &impactPosition = trajGeometry.back();
chargeEffect.position(impactPosition); // Impact Position
chargeEffect.trajectoryTimeAware(trajectoryTimeAware);
const auto &reply = m_effector->mainHandler()->chargeEffectHandler()->createChargeEffect(chargeEffect);
DEBUG_LOG(m_logger) << "reply.id=" << reply.id() << ", str:" << reply.str() << ", uniqueId:" << reply.uniqueId();
return reply.id();
}
Core::GeoPosition AbstractFiringStrategy::participantPosition(int participantId)
{
m_effector->mainHandler()->dds()->ppmDtc()->enableParticipantPosition(true);
const ParticipantManagement::ParticipantPosition &pos = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(participantId);
return pos.pos();
}
int32_t AbstractFiringStrategy::gearTypeId(int chargeTypeId, int participantId)
{
DEBUG_LOG(m_logger) << "getGearTypeId"
<< ", chargeTypeId:" << chargeTypeId << ", participantId:" << participantId;
m_effector->mainHandler()->dds()->ppmDtc()->enableDynamicParticipantData(true);
m_effector->mainHandler()->dds()->ppmDtc()->waitForHistoricalDataDynamicParticipantData(1000);
const ParticipantManagement::DynamicParticipantData &dynamicParticipantData = m_effector->mainHandler()->dds()->ppmDtc()->dynamicParticipantData(participantId);
DEBUG_LOG(m_logger) << "gearSize " << dynamicParticipantData.gear().size();
const auto& participantData_gear = dynamicParticipantData.gear();
for (const auto &gear : qAsConst(participantData_gear))
{
DEBUG_LOG(m_logger) << "gear.chargeTypeId(): " << gear.chargeTypeId() << ", chargeTypeId:" << chargeTypeId;
if (gear.chargeTypeId() == chargeTypeId)
{
DEBUG_LOG(m_logger) << "getGearTypeId return: " << gear.gearTypeId();
if (gear.gearTypeId() != 0)
return gear.gearTypeId();
else
return 701; // MALTE XKCD
}
}
return 701; // MALTE XKCD
}
qint64 AbstractFiringStrategy::getStartTime() const
{
const auto trajectoryAndEffectStartTime = m_effector->mainHandler()->dds()->currentPartitionTimeMSec() + 1000;
return trajectoryAndEffectStartTime;
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,71 @@
/**
* Base Class for Effector Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
/*
* Diese Implementierung des Strategie- und Factory-Patterns ist ein Schritt, die Logik der Erzeugung der Trajektorien und der ChargeEffects von dem Effektor zu trennen.
* Aus zeitlichen Gründen ist die SW-technische Abhängigkeit Strategie und Effektor bidirektional.
*
* Bei zukünftigen Implementierungen sollte diese Abhängigkeit nur in einer Richtung bestehen.
* Oder anders gesagt: Die Strategie sollte möglichst UNABHÄNGIG (von ihren Vater) sein.
*
* Vorteile waeren:
* - Strategie kann unabhängig erstellt, gestestet und vielfältiger/flexibler ein gesetzt werden.
* - Es ist besser erkennbar, WAS die Strategie genau macht, da Ein- und Ausgangsdaten klar definiert sind.
*/
#pragma once
#include <AreaEffectSimulation/logger.h>
#include <QObject>
#include <Core/core.hpp>
#include <QJsonObject>
namespace Core
{
class GeoPosition;
}
namespace AreaEffectSimulation
{
class ObjectEffector;
class TrajectoryPoint;
class TrajectoryTimeAware;
class AbstractFiringStrategy : public QObject
{
Q_OBJECT
public:
explicit AbstractFiringStrategy(ObjectEffector *parent);
virtual ~AbstractFiringStrategy();
virtual void doFiring() = 0;
Q_SIGNALS:
// Has to be emitted in doFiring() implementation!
void signalFired(int participantId, int aesItemId, const QVector<int> &chargeEffectIds);
protected:
// methods:
void calculateDispersionTrajectory();
std::vector<Core::GeoPositionRelativeTime> buildTrajectoryGeometry(const QVector<TrajectoryPoint> &trajectory);
TrajectoryTimeAware buildTrajectoryTimeAware(const std::vector<Core::GeoPositionRelativeTime> &trajectoryGeometry, qint64 curTime, int index = 0);
int sendChargeEffect(const TrajectoryTimeAware &trajectoryTimeAware, const QJsonObject &properties = QJsonObject());
Core::GeoPosition participantPosition(int participantId);
int32_t gearTypeId(int chargeTypeId, int participantId);
qint64 getStartTime() const;
// attributes:
ObjectEffector *m_effector {nullptr};
QVector<TrajectoryPoint> m_transformedTrajectory;
LOGGER_T &m_logger;
}; // class ObjectEffector
} // namespace AreaEffectSimulation
@@ -0,0 +1,47 @@
/**
* Default Effector Firing Strategy (RRohr)
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#include "default_firing_strategy.h"
#include "object_effector.h"
namespace AreaEffectSimulation
{
DefaultFiringStrategy::DefaultFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
DEBUG_LOG(m_logger) << "using DefaultFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
DefaultFiringStrategy::~DefaultFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void DefaultFiringStrategy::doFiring()
{
calculateDispersionTrajectory();
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
DEBUG_LOG(m_logger) << "created ChargeEffectId: " << chargeEffectId;
}
} //namespace AreaEffectSimulation
@@ -0,0 +1,28 @@
/**
* Default Effector Firing Strategy (RRohr)
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace AreaEffectSimulation
{
class DefaultFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit DefaultFiringStrategy(ObjectEffector *effector);
virtual ~DefaultFiringStrategy();
void doFiring() override;
}; // class DefaultFiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,190 @@
/**
* Effector Firing Strategy for DM72
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-21
*/
#include "dm72_firing_strategy.h"
#include "main_handler.h"
#include "object_effector.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <Trajectory/trajectory_transform.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QTransform>
namespace AreaEffectSimulation
{
Dm72FiringStrategy::Dm72FiringStrategy(ObjectEffector *effector)
: AbstractFiringStrategy(effector)
{
init();
DEBUG_LOG(m_logger) << "using Dm72FiringStrategy";
}
void Dm72FiringStrategy::doFiring()
{
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(m_effector->intendendTrajectory(), m_impactPoints.at(m_impactPointIndex++));
if (m_impactPointIndex >= m_impactPoints.size())
m_impactPointIndex = 0;
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
QJsonObject props{{QStringLiteral("effectDurationMsec"), m_effectDurationMsec}};
const int chargeEffectId = sendChargeEffect(trajTimeAware, props);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
}
void Dm72FiringStrategy::init()
{
using namespace AdlerJson;
using namespace LtsCoordTrans;
m_impactPointIndex = 0;
m_impactPoints.clear();
bool bOk;
WirkmittelFeuerkommandoRakete wirkmittel;
const QJsonObject properties = QJsonDocument::fromJson(QByteArray::fromStdString(m_effector->effectorData().properties())).object();
const Core::GeoPosition geoStartPos = participantPosition(m_effector->participantId());
const UtmCoordinate utmStartPos = UtmCoordinate::fromGeographic(geoStartPos.lon(), geoStartPos.lat());
wirkmittelFeuerkommandoRaketeFromJson(properties, wirkmittel, bOk);
using UtmWithHeight = std::pair<UtmCoordinate, int>;
m_effectDurationMsec = getFeuerkommandoRaketeEffectDurationMsec(properties);
QVector<UtmWithHeight> tempUtmTreffpunkte;
tempUtmTreffpunkte.reserve(wirkmittel.geschoss.anzahl);
for (const Treffpunkt &treffpunkt : qAsConst(wirkmittel.treffpunkte))
{
const UtmCoordinate utmTreffpunkt = UtmCoordinate::fromGeographic(treffpunkt.koordinate.koordinate.longitude * deg2rad(),
treffpunkt.koordinate.koordinate.latitude * deg2rad(),
utmStartPos.zone);
for (int anzahlCounter = 1; anzahlCounter <= treffpunkt.anzahl; ++anzahlCounter)
tempUtmTreffpunkte.append({utmTreffpunkt, treffpunkt.koordinate.hoehe});
}
const QVector<QPointF> mineLayingCentroidDeviations = mineLayingCentroids();
int deviationIndex = 0;
for (const UtmWithHeight &tempUtmTreffpunkt : tempUtmTreffpunkte)
{
const QPointF &deviation = mineLayingCentroidDeviations.at(deviationIndex++);
const LtsUtils::QPoint3D impactPoint(tempUtmTreffpunkt.first.x - utmStartPos.x + deviation.x(),
tempUtmTreffpunkt.first.y - utmStartPos.y + deviation.y(),
tempUtmTreffpunkt.second);
m_impactPoints.append(impactPoint);
if (deviationIndex >= mineLayingCentroidDeviations.size())
deviationIndex = 0;
}
DEBUG_LOG(m_logger) << "Got " << m_impactPoints.size() << " impact points";
}
QVector<QPointF> Dm72FiringStrategy::mineLayingCentroids() const
{
using namespace LtsCoordTrans;
const int chargeTypeId = m_effector->effectorData().chargeStruct().chargeTypeId();
const ProjectDataManagement::ChargeType &chargeType = m_effector->mainHandler()->dds()->pdmDtc()->chargeType(chargeTypeId);
const QJsonObject chargeTypeProperties = QJsonDocument::fromJson(QByteArray::fromStdString(chargeType.properties())).object();
const QJsonObject mineLaying = chargeTypeProperties.value(QStringLiteral("mineLaying")).toObject();
const QJsonArray mineLayingPattern = mineLaying.value(QStringLiteral("pattern")).toArray();
/* QMap is used here because it sort its entries by key.
* Since each pattern has an id and we want the pattern sorted by id, adding them to a QMap
* automatically sorts the pattern.
*/
QMap<int, QPointF> centroidsMap;
const UtmCoordinate mhpUtm = UtmCoordinate::fromGeographic(m_effector->effectorData().mhpPosition().lon(),
m_effector->effectorData().mhpPosition().lat());
const double rotationAngle = angleToMhpUtm(mhpUtm.toPoint());
QTransform rotation;
rotation.rotate(rotationAngle);
for (const QJsonValue &patternValue: mineLayingPattern)
{
const QJsonObject patternObject = patternValue.toObject();
const int patternId = patternObject.value(QStringLiteral("id")).toInt();
const QJsonArray patternPositionArray = patternObject.value(QStringLiteral("positions")).toArray();
int sumNorthing = 0;
int sumEasting = 0;
for (const QJsonValue &positionValue : patternPositionArray)
{
const QJsonObject positionObject = positionValue.toObject();
const int northing = positionObject.value(QStringLiteral("northing")).toInt();
const int easting = positionObject.value(QStringLiteral("easting")).toInt();
sumNorthing += northing;
sumEasting += easting;
}
QPointF centroid(static_cast<double>(sumEasting) / patternPositionArray.size(), static_cast<double>(sumNorthing) / patternPositionArray.size());
centroid = rotation.map(centroid);
centroidsMap.insert(patternId, centroid);
}
if (centroidsMap.isEmpty())
{
WARNING_LOG(m_logger) << "Could not read mine laying patterns from DM72 ammunition properties for creation of DM72 trajectories.";
return { QPointF(0.0, 0.0) }; // We have to return at least one QPointF(0.0, 0.0) to ensure later DM72 functionality
}
int vectorIndex = 0;
QVector<QPointF> mineLayingCentroids;
mineLayingCentroids.reserve(centroidsMap.size());
for (auto it = centroidsMap.constBegin(); it != centroidsMap.constEnd(); ++it)
mineLayingCentroids.insert(vectorIndex++, it.value()); // insert with index ensures sorting
return mineLayingCentroids;
}
double Dm72FiringStrategy::angleToMhpUtm(const QPointF &mhpUtm) const
{
const ParticipantManagement::ParticipantPosition &ownPosition = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate ownPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(ownPosition.pos().lon(), ownPosition.pos().lat());
const double angle = QLineF(mhpUtm, ownPositionUtm.toPoint()).angle();
double northAngle = 90 - angle;
northAngle = std::fmod(northAngle, 360.0);
if (northAngle < 0.0)
northAngle += 360.0;
DEBUG_LOG(m_logger) << "angleToMhpUtm:"
<< "mhpUtm=" << mhpUtm
<< ", ownPositionUtm=" << ownPositionUtm.toPoint()
<< ", angle=" << angle
<< ", northAngle=" << northAngle;
return northAngle;
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,40 @@
/**
* Effector Firing Strategy for DM72
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-21
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace LtsUtils
{
class QPoint3D;
}
namespace AreaEffectSimulation
{
class Dm72FiringStrategy : public AbstractFiringStrategy
{
Q_OBJECT
public:
explicit Dm72FiringStrategy(ObjectEffector *effector);
void doFiring() override final;
private: // methods
void init();
QVector<QPointF> mineLayingCentroids() const;
double angleToMhpUtm(const QPointF &mhpUtm) const;
private: // attributes
QVector<LtsUtils::QPoint3D> m_impactPoints;
int m_impactPointIndex {0};
int m_effectDurationMsec = 3 * 60 * 60 * 1000; // default 3h
}; // class Dm72FiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,64 @@
/**
* Namespace and function for creating a Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#include "firing_strategy_factory.h"
#include "default_firing_strategy.h"
#include "dm72_firing_strategy.h"
#include "gmlrs_firing_strategy.h"
#include "smart_firing_strategy.h"
#include "main_handler.h"
#include "object_effector.h"
namespace AreaEffectSimulation
{
namespace FiringStrategyFactory
{
AbstractFiringStrategy *createFiringStrategy(ObjectEffector *parent)
{
LOGGER_T &logger = getLogger("FiringStrategyFactory");
TRACE_LOG(logger) << LOGFUNCTION << " START";
if (!parent)
return nullptr;
const ChargeStruct &cs = parent->effectorData().chargeStruct();
DEBUG_LOG(logger) << LOGFUNCTION << " ChargeStruct = " << cs;
// GMLRS
if (parent->mainHandler()->dds()->pdmDtc()->isGmlrs(cs.chargeTypeId()))
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new GmlrsFiringStrategy(parent);
}
// SMArt
const bool bIsSmart = parent->mainHandler()->dds()->pdmDtc()->isSmart(cs.chargeTypeId(), cs.igniterTypeId(), cs.igniterFunctionId());
if (bIsSmart)
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new SmartFiringStrategy(parent);
}
// DM72
if (parent->mainHandler()->dds()->pdmDtc()->isDm72(cs.chargeTypeId()))
{
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new Dm72FiringStrategy(parent);
}
TRACE_LOG(logger) << LOGFUNCTION << " END";
return new DefaultFiringStrategy(parent);
}
} //namespace FiringFactory
} //namespace AreaEffectSimulation
@@ -0,0 +1,22 @@
/**
* Namespace and function for creating a Firing Strategy
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-11
*/
#pragma once
namespace AreaEffectSimulation
{
class AbstractFiringStrategy;
class ObjectEffector;
namespace FiringStrategyFactory
{
AbstractFiringStrategy *createFiringStrategy(ObjectEffector *parent);
} //namespace FiringFactory
} //namespace AreaEffectSimulation
@@ -0,0 +1,87 @@
/**
* Effector Firing Strategy for GMLRS
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-12
*/
#include "gmlrs_firing_strategy.h"
#include "object_effector.h"
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <Trajectory/trajectory_transform.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QJsonDocument>
#include <QJsonObject>
namespace AreaEffectSimulation
{
GmlrsFiringStrategy::GmlrsFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
init();
DEBUG_LOG(m_logger) << "using GmlrsFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
GmlrsFiringStrategy::~GmlrsFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void GmlrsFiringStrategy::doFiring()
{
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(m_effector->intendendTrajectory(), m_impactPoints.at(m_impactPointIndex++));
if (m_impactPointIndex >= m_impactPoints.size())
m_impactPointIndex = 0;
const qint64 trajectoryStartTime = getStartTime();
const auto &geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto &trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
}
void GmlrsFiringStrategy::init()
{
using namespace AdlerJson;
m_impactPointIndex = 0;
m_impactPoints.clear();
const QJsonObject &properties = QJsonDocument::fromJson(QByteArray::fromStdString(m_effector->effectorData().properties())).object();
const Core::GeoPosition &geoStartPos = participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate &utmStartPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoStartPos.lon(), geoStartPos.lat());
bool bOk = false;
WirkmittelFeuerkommandoRakete wirkmittel;
wirkmittelFeuerkommandoRaketeFromJson(properties, wirkmittel, bOk);
for (const Treffpunkt &treffpunkt : qAsConst(wirkmittel.treffpunkte))
{
const LtsCoordTrans::UtmCoordinate utmTreffpunkt =
LtsCoordTrans::UtmCoordinate::fromGeographic(treffpunkt.koordinate.koordinate.longitude * LtsCoordTrans::deg2rad(),
treffpunkt.koordinate.koordinate.latitude * LtsCoordTrans::deg2rad());
const LtsUtils::QPoint3D relImpactPoint(utmTreffpunkt.x - utmStartPos.x,
utmTreffpunkt.y - utmStartPos.y,
treffpunkt.koordinate.hoehe);
for (int anzahlCounter = 1; anzahlCounter <= treffpunkt.anzahl; ++anzahlCounter)
m_impactPoints.append(relImpactPoint);
}
DEBUG_LOG(m_logger) << "Got " << m_impactPoints.size() << " impact points";
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,40 @@
/**
* Effector Firing Strategy for GMLRS
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-12
*/
#pragma once
#include "abstract_firing_strategy.h"
#include <LtsUtilsCore/qpoint3d.h>
namespace Core
{
class RelativePosition;
}
namespace AreaEffectSimulation
{
class GmlrsFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit GmlrsFiringStrategy(ObjectEffector *effector);
virtual ~GmlrsFiringStrategy();
void doFiring() override;
private: // methods
void init();
private: // attributes
QVector<LtsUtils::QPoint3D> m_impactPoints;
int m_impactPointIndex {0};
}; // class GmlrsFiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,102 @@
/**
* Effector Firing Strategy for SMart
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-05-20
*/
#include "smart_firing_strategy.h"
#include "object_effector.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <TrajectorySplitter/trajectory_splitter.h>
#include <QJsonDocument>
#include <QJsonObject>
namespace AreaEffectSimulation
{
SmartFiringStrategy::SmartFiringStrategy(ObjectEffector *effector)
: BaseClass(effector)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
DEBUG_LOG(m_logger) << "using SmartFiringStrategy";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
SmartFiringStrategy::~SmartFiringStrategy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void SmartFiringStrategy::doFiring()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// by default, following is done
// - calculateDispersionTrajectory
// - calc new target
// - calc matching trajectory
// (intendendTrajectory -> m_transformedTrajectory)
// - sendChargeEffect
// - emit signalFired
// for SMArt, calculateDispersionTrajectory is not necessary
// But we have to split the tow-in-one trajectory:
const qint64 trajectoryStartTime = getStartTime();
//
// first check, if trajectory is a tow-in-one trajectory:
//
// convert from intendendTrajectory (relativ) to TRAJECTORY_TYPE (absolute)
// The TrajectorySplitter needs TRAJECTORY_TYPE
// using TRAJECTORY_TYPE = std::vector<Core::GeoPositionRelativeTime>;
const auto &intendendTrajectory = m_effector->intendendTrajectory();
const auto &trajGeometry = buildTrajectoryGeometry(intendendTrajectory);
// split and avoid copying:
const TrajectorySplitter<std::vector<Core::GeoPositionRelativeTime>> splitter(trajGeometry);
const auto &trajGeo1 = splitter.getTrajectory1();
const auto &trajGeo2 = splitter.getTrajectory2();
DEBUG_LOG(m_logger) << "splitter: " << " intendendTrajectory.size = " << intendendTrajectory.size();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeometry.size = " << trajGeometry.size();
DEBUG_LOG(m_logger) << "splitter: " << " rawTrajectorySize = " << splitter.rawTrajectorySize();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeo1 = " << trajGeo1.size();
DEBUG_LOG(m_logger) << "splitter: " << " trajGeo2 = " << trajGeo2.size();
//
// and second, process both Trajectories/Impacts
//
// ToDo: Check, how oftem signalFired should be fired?
QVector<int> chargeEffectIds;
if (trajGeo1.size() > 2)
{
const auto &trajTimeAware = buildTrajectoryTimeAware(trajGeo1, trajectoryStartTime, 0);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
chargeEffectIds << chargeEffectId;
DEBUG_LOG(m_logger) << "Trajectory1 - created ChargeEffectId: " << chargeEffectId;
}
if (trajGeo2.size() > 2)
{
const auto &trajTimeAware = buildTrajectoryTimeAware(trajGeo2, trajectoryStartTime, 1);
const int chargeEffectId = sendChargeEffect(trajTimeAware);
chargeEffectIds << chargeEffectId;
DEBUG_LOG(m_logger) << "Trajectory2 - created ChargeEffectId: " << chargeEffectId;
}
if (chargeEffectIds.size())
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), chargeEffectIds);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,37 @@
/**
* Effector Firing Strategy for SMart
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-05-20
*/
#pragma once
#include "abstract_firing_strategy.h"
namespace Core
{
class RelativePosition;
}
namespace AreaEffectSimulation
{
class SmartFiringStrategy final : public AbstractFiringStrategy
{
using BaseClass = AbstractFiringStrategy;
Q_OBJECT
public:
explicit SmartFiringStrategy(ObjectEffector *effector);
virtual ~SmartFiringStrategy();
void doFiring() override;
private: // methods
private: // attributes
}; // class SmartFiringStrategy
} // namespace AreaEffectSimulation
@@ -0,0 +1,38 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
effect_cluster_module_test.cpp
effect_cluster_module_test.h
)
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesModuleTestEffectCluster ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesModuleTestEffectCluster
NGLLogger
nglcoreapplication
LtsCommandLineParser
LtsCoordinate
Qt5::Core
Qt5::Test
ddsconnector
svcAesCommon
svcAesClient
svcBldClient
svcExmClient
svcIsmClient
svcPdmClient
svcPpmClient
svcTabServer
)
# ADD_TEST(NAME aesModuleTestExplosionCluster COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
#NGL_INSTALL(TESTING SV FILES EffectClusterModifyRequest_ESG.spl DESTINATION bin)
NGL_INSTALL(SV FILES EffectClusterModifyRequest_ESG.spl DESTINATION bin)
@@ -0,0 +1,631 @@
<!DOCTYPE Samples>
<Samples>
<Sample>
<TopicName>LTS_EffectClusterModifyRequest</TopicName>
<TypeName>AreaEffectSimulation::EffectClusterModifyRequest</TypeName>
<Pause>1000</Pause>
<DdsData topic="LTS_EffectClusterModifyRequest" type="AreaEffectSimulation::EffectClusterModifyRequest" count="17">
<id>8</id>
<name>eb511ab7d8414b88af248fd32ef03285</name>
<element>eb511ab7d8414b88af248fd32ef03285</element>
<effectStart/>
<effectDuration>0</effectDuration>
<effectSourceId>1</effectSourceId>
<area type="AreaEffectSimulation::Area" count="4">
<cornerBottomLeft type="Core::GeoPosition2D" count="2">
<lon>0.201951</lon>
<lat>0.915259</lat>
</cornerBottomLeft>
<width>50</width>
<height>50</height>
<rotation>0</rotation>
</area>
<explosionGeometry>4</explosionGeometry>
<tla>0</tla>
<effectors count="1">
<effectors.1>
<angleOfFall>0</angleOfFall>
<participantId>240</participantId>
<flightTime>39526.3</flightTime>
<peakHeight>2002</peakHeight>
<interval>0</interval>
<chargeStruct type="AreaEffectSimulation::ChargeStruct" count="3">
<chargeTypeId>367</chargeTypeId>
<igniterTypeId>1</igniterTypeId>
<igniterFunctionId>2</igniterFunctionId>
</chargeStruct>
<chargeAmount>2</chargeAmount>
<mhpScatterLenght>3.08314e-316</mhpScatterLenght>
<mhpScatterWidth>8.76302e-315</mhpScatterWidth>
<mhpScatterLengthDetonationHeight>9.69057e-316</mhpScatterLengthDetonationHeight>
<mhpScatterWidthDetonationHeight>8.76639e-315</mhpScatterWidthDetonationHeight>
<mhpRotation>-45.4373</mhpRotation>
<mhpPosition type="Core::GeoPosition" count="3">
<lon>0.201958</lon>
<lat>0.915263</lat>
<z>88</z>
</mhpPosition>
<trajectory count="95">
<trajectory.1>
<lon>0</lon>
<lat>0</lat>
<z>85</z>
<relativeTime>0</relativeTime>
</trajectory.1>
<trajectory.2>
<lon>41.6079</lon>
<lat>-39.6154</lat>
<z>168.375</z>
<relativeTime>0</relativeTime>
</trajectory.2>
<trajectory.3>
<lon>82.7348</lon>
<lat>-78.773</lat>
<z>249.195</z>
<relativeTime>0</relativeTime>
</trajectory.3>
<trajectory.4>
<lon>123.367</lon>
<lat>-117.46</lat>
<z>327.472</z>
<relativeTime>1000</relativeTime>
</trajectory.4>
<trajectory.5>
<lon>169.188</lon>
<lat>-161.086</lat>
<z>413.849</z>
<relativeTime>1000</relativeTime>
</trajectory.5>
<trajectory.6>
<lon>214.339</lon>
<lat>-204.075</lat>
<z>496.976</z>
<relativeTime>2000</relativeTime>
</trajectory.6>
<trajectory.7>
<lon>258.83</lon>
<lat>-246.435</lat>
<z>576.935</z>
<relativeTime>2000</relativeTime>
</trajectory.7>
<trajectory.8>
<lon>302.659</lon>
<lat>-288.166</lat>
<z>653.787</z>
<relativeTime>3000</relativeTime>
</trajectory.8>
<trajectory.9>
<lon>345.82</lon>
<lat>-329.26</lat>
<z>727.59</z>
<relativeTime>3000</relativeTime>
</trajectory.9>
<trajectory.10>
<lon>388.317</lon>
<lat>-369.722</lat>
<z>798.415</z>
<relativeTime>3000</relativeTime>
</trajectory.10>
<trajectory.11>
<lon>430.153</lon>
<lat>-409.555</lat>
<z>866.336</z>
<relativeTime>4000</relativeTime>
</trajectory.11>
<trajectory.12>
<lon>471.335</lon>
<lat>-448.764</lat>
<z>931.431</z>
<relativeTime>4000</relativeTime>
</trajectory.12>
<trajectory.13>
<lon>511.87</lon>
<lat>-487.359</lat>
<z>993.78</z>
<relativeTime>5000</relativeTime>
</trajectory.13>
<trajectory.14>
<lon>551.768</lon>
<lat>-525.346</lat>
<z>1053.46</z>
<relativeTime>5000</relativeTime>
</trajectory.14>
<trajectory.15>
<lon>591.038</lon>
<lat>-562.736</lat>
<z>1110.56</z>
<relativeTime>5000</relativeTime>
</trajectory.15>
<trajectory.16>
<lon>629.691</lon>
<lat>-599.538</lat>
<z>1165.15</z>
<relativeTime>6000</relativeTime>
</trajectory.16>
<trajectory.17>
<lon>672.451</lon>
<lat>-640.25</lat>
<z>1223.66</z>
<relativeTime>6000</relativeTime>
</trajectory.17>
<trajectory.18>
<lon>714.459</lon>
<lat>-680.247</lat>
<z>1279.22</z>
<relativeTime>7000</relativeTime>
</trajectory.18>
<trajectory.19>
<lon>755.733</lon>
<lat>-719.544</lat>
<z>1331.91</z>
<relativeTime>7000</relativeTime>
</trajectory.19>
<trajectory.20>
<lon>796.291</lon>
<lat>-758.16</lat>
<z>1381.86</z>
<relativeTime>8000</relativeTime>
</trajectory.20>
<trajectory.21>
<lon>836.152</lon>
<lat>-796.112</lat>
<z>1429.17</z>
<relativeTime>8000</relativeTime>
</trajectory.21>
<trajectory.22>
<lon>875.334</lon>
<lat>-833.417</lat>
<z>1473.94</z>
<relativeTime>8000</relativeTime>
</trajectory.22>
<trajectory.23>
<lon>913.857</lon>
<lat>-870.096</lat>
<z>1516.26</z>
<relativeTime>9000</relativeTime>
</trajectory.23>
<trajectory.24>
<lon>951.743</lon>
<lat>-906.167</lat>
<z>1556.24</z>
<relativeTime>9000</relativeTime>
</trajectory.24>
<trajectory.25>
<lon>989.01</lon>
<lat>-941.65</lat>
<z>1593.96</z>
<relativeTime>10000</relativeTime>
</trajectory.25>
<trajectory.26>
<lon>1029.72</lon>
<lat>-980.41</lat>
<z>1633.33</z>
<relativeTime>10000</relativeTime>
</trajectory.26>
<trajectory.27>
<lon>1069.72</lon>
<lat>-1018.49</lat>
<z>1670.15</z>
<relativeTime>11000</relativeTime>
</trajectory.27>
<trajectory.28>
<lon>1109.04</lon>
<lat>-1055.93</lat>
<z>1704.52</z>
<relativeTime>11000</relativeTime>
</trajectory.28>
<trajectory.29>
<lon>1147.71</lon>
<lat>-1092.75</lat>
<z>1736.55</z>
<relativeTime>11000</relativeTime>
</trajectory.29>
<trajectory.30>
<lon>1185.76</lon>
<lat>-1128.98</lat>
<z>1766.34</z>
<relativeTime>12000</relativeTime>
</trajectory.30>
<trajectory.31>
<lon>1223.22</lon>
<lat>-1164.65</lat>
<z>1793.99</z>
<relativeTime>12000</relativeTime>
</trajectory.31>
<trajectory.32>
<lon>1260.12</lon>
<lat>-1199.78</lat>
<z>1819.58</z>
<relativeTime>13000</relativeTime>
</trajectory.32>
<trajectory.33>
<lon>1296.49</lon>
<lat>-1234.41</lat>
<z>1843.2</z>
<relativeTime>13000</relativeTime>
</trajectory.33>
<trajectory.34>
<lon>1335.91</lon>
<lat>-1271.94</lat>
<z>1867</z>
<relativeTime>14000</relativeTime>
</trajectory.34>
<trajectory.35>
<lon>1374.77</lon>
<lat>-1308.94</lat>
<z>1888.6</z>
<relativeTime>14000</relativeTime>
</trajectory.35>
<trajectory.36>
<lon>1413.1</lon>
<lat>-1345.43</lat>
<z>1908.1</z>
<relativeTime>14000</relativeTime>
</trajectory.36>
<trajectory.37>
<lon>1450.94</lon>
<lat>-1381.46</lat>
<z>1925.57</z>
<relativeTime>15000</relativeTime>
</trajectory.37>
<trajectory.38>
<lon>1488.32</lon>
<lat>-1417.05</lat>
<z>1941.1</z>
<relativeTime>15000</relativeTime>
</trajectory.38>
<trajectory.39>
<lon>1525.3</lon>
<lat>-1452.26</lat>
<z>1954.75</z>
<relativeTime>16000</relativeTime>
</trajectory.39>
<trajectory.40>
<lon>1561.9</lon>
<lat>-1487.11</lat>
<z>1966.58</z>
<relativeTime>16000</relativeTime>
</trajectory.40>
<trajectory.41>
<lon>1598.17</lon>
<lat>-1521.64</lat>
<z>1976.64</z>
<relativeTime>16000</relativeTime>
</trajectory.41>
<trajectory.42>
<lon>1634.14</lon>
<lat>-1555.89</lat>
<z>1984.97</z>
<relativeTime>17000</relativeTime>
</trajectory.42>
<trajectory.43>
<lon>1669.86</lon>
<lat>-1589.89</lat>
<z>1991.63</z>
<relativeTime>17000</relativeTime>
</trajectory.43>
<trajectory.44>
<lon>1705.35</lon>
<lat>-1623.69</lat>
<z>1996.63</z>
<relativeTime>18000</relativeTime>
</trajectory.44>
<trajectory.45>
<lon>1740.66</lon>
<lat>-1657.31</lat>
<z>2000</z>
<relativeTime>18000</relativeTime>
</trajectory.45>
<trajectory.46>
<lon>1775.83</lon>
<lat>-1690.79</lat>
<z>2001.77</z>
<relativeTime>19000</relativeTime>
</trajectory.46>
<trajectory.47>
<lon>1810.9</lon>
<lat>-1724.18</lat>
<z>2001.94</z>
<relativeTime>19000</relativeTime>
</trajectory.47>
<trajectory.48>
<lon>1845.89</lon>
<lat>-1757.5</lat>
<z>2000.51</z>
<relativeTime>19000</relativeTime>
</trajectory.48>
<trajectory.49>
<lon>1880.86</lon>
<lat>-1790.79</lat>
<z>1997.5</z>
<relativeTime>20000</relativeTime>
</trajectory.49>
<trajectory.50>
<lon>1915.83</lon>
<lat>-1824.09</lat>
<z>1992.87</z>
<relativeTime>20000</relativeTime>
</trajectory.50>
<trajectory.51>
<lon>1950.85</lon>
<lat>-1857.44</lat>
<z>1986.63</z>
<relativeTime>21000</relativeTime>
</trajectory.51>
<trajectory.52>
<lon>1985.96</lon>
<lat>-1890.86</lat>
<z>1978.74</z>
<relativeTime>21000</relativeTime>
</trajectory.52>
<trajectory.53>
<lon>2021.19</lon>
<lat>-1924.41</lat>
<z>1969.18</z>
<relativeTime>21000</relativeTime>
</trajectory.53>
<trajectory.54>
<lon>2056.58</lon>
<lat>-1958.1</lat>
<z>1957.91</z>
<relativeTime>22000</relativeTime>
</trajectory.54>
<trajectory.55>
<lon>2092.18</lon>
<lat>-1991.99</lat>
<z>1944.87</z>
<relativeTime>22000</relativeTime>
</trajectory.55>
<trajectory.56>
<lon>2128.01</lon>
<lat>-2026.11</lat>
<z>1930.02</z>
<relativeTime>23000</relativeTime>
</trajectory.56>
<trajectory.57>
<lon>2164.13</lon>
<lat>-2060.5</lat>
<z>1913.29</z>
<relativeTime>23000</relativeTime>
</trajectory.57>
<trajectory.58>
<lon>2200.56</lon>
<lat>-2095.19</lat>
<z>1894.62</z>
<relativeTime>23000</relativeTime>
</trajectory.58>
<trajectory.59>
<lon>2237.36</lon>
<lat>-2130.22</lat>
<z>1873.91</z>
<relativeTime>24000</relativeTime>
</trajectory.59>
<trajectory.60>
<lon>2271.16</lon>
<lat>-2162.41</lat>
<z>1853.24</z>
<relativeTime>24000</relativeTime>
</trajectory.60>
<trajectory.61>
<lon>2305.33</lon>
<lat>-2194.94</lat>
<z>1830.75</z>
<relativeTime>25000</relativeTime>
</trajectory.61>
<trajectory.62>
<lon>2339.89</lon>
<lat>-2227.84</lat>
<z>1806.34</z>
<relativeTime>25000</relativeTime>
</trajectory.62>
<trajectory.63>
<lon>2374.89</lon>
<lat>-2261.16</lat>
<z>1779.93</z>
<relativeTime>26000</relativeTime>
</trajectory.63>
<trajectory.64>
<lon>2410.34</lon>
<lat>-2294.92</lat>
<z>1751.41</z>
<relativeTime>26000</relativeTime>
</trajectory.64>
<trajectory.65>
<lon>2446.3</lon>
<lat>-2329.15</lat>
<z>1720.68</z>
<relativeTime>26000</relativeTime>
</trajectory.65>
<trajectory.66>
<lon>2482.79</lon>
<lat>-2363.9</lat>
<z>1687.61</z>
<relativeTime>27000</relativeTime>
</trajectory.66>
<trajectory.67>
<lon>2516.11</lon>
<lat>-2395.63</lat>
<z>1655.75</z>
<relativeTime>27000</relativeTime>
</trajectory.67>
<trajectory.68>
<lon>2549.93</lon>
<lat>-2427.82</lat>
<z>1621.78</z>
<relativeTime>28000</relativeTime>
</trajectory.68>
<trajectory.69>
<lon>2584.26</lon>
<lat>-2460.51</lat>
<z>1585.61</z>
<relativeTime>28000</relativeTime>
</trajectory.69>
<trajectory.70>
<lon>2619.13</lon>
<lat>-2493.71</lat>
<z>1547.11</z>
<relativeTime>28000</relativeTime>
</trajectory.70>
<trajectory.71>
<lon>2654.58</lon>
<lat>-2527.47</lat>
<z>1506.15</z>
<relativeTime>29000</relativeTime>
</trajectory.71>
<trajectory.72>
<lon>2690.64</lon>
<lat>-2561.79</lat>
<z>1462.59</z>
<relativeTime>29000</relativeTime>
</trajectory.72>
<trajectory.73>
<lon>2723.22</lon>
<lat>-2592.82</lat>
<z>1421.57</z>
<relativeTime>30000</relativeTime>
</trajectory.73>
<trajectory.74>
<lon>2756.33</lon>
<lat>-2624.34</lat>
<z>1378.26</z>
<relativeTime>30000</relativeTime>
</trajectory.74>
<trajectory.75>
<lon>2789.99</lon>
<lat>-2656.38</lat>
<z>1332.55</z>
<relativeTime>31000</relativeTime>
</trajectory.75>
<trajectory.76>
<lon>2824.22</lon>
<lat>-2688.98</lat>
<z>1284.3</z>
<relativeTime>31000</relativeTime>
</trajectory.76>
<trajectory.77>
<lon>2859.05</lon>
<lat>-2722.14</lat>
<z>1233.38</z>
<relativeTime>31000</relativeTime>
</trajectory.77>
<trajectory.78>
<lon>2894.51</lon>
<lat>-2755.9</lat>
<z>1179.62</z>
<relativeTime>32000</relativeTime>
</trajectory.78>
<trajectory.79>
<lon>2930.63</lon>
<lat>-2790.29</lat>
<z>1122.88</z>
<relativeTime>32000</relativeTime>
</trajectory.79>
<trajectory.80>
<lon>2962.79</lon>
<lat>-2820.91</lat>
<z>1070.65</z>
<relativeTime>33000</relativeTime>
</trajectory.80>
<trajectory.81>
<lon>2995.5</lon>
<lat>-2852.05</lat>
<z>1015.86</z>
<relativeTime>33000</relativeTime>
</trajectory.81>
<trajectory.82>
<lon>3028.77</lon>
<lat>-2883.73</lat>
<z>958.393</z>
<relativeTime>34000</relativeTime>
</trajectory.82>
<trajectory.83>
<lon>3062.63</lon>
<lat>-2915.97</lat>
<z>898.099</z>
<relativeTime>34000</relativeTime>
</trajectory.83>
<trajectory.84>
<lon>3097.11</lon>
<lat>-2948.8</lat>
<z>834.825</z>
<relativeTime>34000</relativeTime>
</trajectory.84>
<trajectory.85>
<lon>3132.22</lon>
<lat>-2982.23</lat>
<z>768.404</z>
<relativeTime>35000</relativeTime>
</trajectory.85>
<trajectory.86>
<lon>3167.99</lon>
<lat>-3016.29</lat>
<z>698.657</z>
<relativeTime>35000</relativeTime>
</trajectory.86>
<trajectory.87>
<lon>3199.21</lon>
<lat>-3046.01</lat>
<z>636.077</z>
<relativeTime>36000</relativeTime>
</trajectory.87>
<trajectory.88>
<lon>3230.96</lon>
<lat>-3076.24</lat>
<z>570.777</z>
<relativeTime>36000</relativeTime>
</trajectory.88>
<trajectory.89>
<lon>3263.25</lon>
<lat>-3106.99</lat>
<z>502.616</z>
<relativeTime>37000</relativeTime>
</trajectory.89>
<trajectory.90>
<lon>3296.12</lon>
<lat>-3138.28</lat>
<z>431.443</z>
<relativeTime>37000</relativeTime>
</trajectory.90>
<trajectory.91>
<lon>3329.58</lon>
<lat>-3170.14</lat>
<z>357.096</z>
<relativeTime>37000</relativeTime>
</trajectory.91>
<trajectory.92>
<lon>3363.65</lon>
<lat>-3202.58</lat>
<z>279.399</z>
<relativeTime>38000</relativeTime>
</trajectory.92>
<trajectory.93>
<lon>3398.36</lon>
<lat>-3235.63</lat>
<z>198.163</z>
<relativeTime>38000</relativeTime>
</trajectory.93>
<trajectory.94>
<lon>3433.74</lon>
<lat>-3269.31</lat>
<z>113.182</z>
<relativeTime>39000</relativeTime>
</trajectory.94>
<trajectory.95>
<lon>3444.05</lon>
<lat>-3279.13</lat>
<z>88</z>
<relativeTime>39000</relativeTime>
</trajectory.95>
</trajectory>
</effectors.1>
</effectors>
<firePurposeId>2</firePurposeId>
<partyId>4</partyId>
<participantId>239</participantId>
<esmParticipantIds count="0"/>
<externalId>7</externalId>
<effectMarkingInTa>1</effectMarkingInTa>
<properties>{"effectSource":1,"elemente":1,"feuerkampfReferenz":"eb511ab7d8414b88af248fd32ef03285","firingType":1,"ladung":"DM16","ladungsNummer":3,"partyId":4,"subTyp":0,"typ":0,"useReceived":true}</properties>
</DdsData>
</Sample>
</Samples>
@@ -0,0 +1,942 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
#include "effect_cluster_module_test.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <nglapplication/nglcoreapplication.h>
// AES
#include <AreaEffectSimulation/Utils/polygons.h>
#include <AreaEffectSimulation/aes_dtc.h>
// PPM
#include "ParticipantManagement/ppm_ftc_types.h"
#include "TrainingAreaBase/tab.hpp"
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QTimer>
#include <QVector>
#include <QXmlStreamReader>
#include <iostream>
using namespace AreaEffectSimulation;
using namespace LtsCoordTrans;
static QString s_REQUEST_SPL_FILE = QStringLiteral("EffectClusterModifyRequest_ESG.spl");
//
// some helper functions
//
template <typename T> dds::core::optional<T> toOptional(T inVal) { return inVal != T() ? dds::core::optional<T>(inVal) : dds::core::optional<T>(); }
template <typename T> dds::core::optional<T> optional() { return dds::core::optional<T>(); }
// mathematical positive north -> mathematical negativ, east = 0°, 0,01 degree in 1/100° -> factor 100
static inline int compassToMathAngleForSfm(double compassDegree)
{
int ret = qRound((90.0 - compassDegree) * 100.0);
while (ret < 0)
ret += 36000;
while (ret > 36000)
ret -= 36000;
return ret;
}
double distanceMeter(const LtsCoordTrans::UtmCoordinate &utmCoord1, const LtsCoordTrans::UtmCoordinate &utmCoord2)
{
double dist = LtsCoordTrans::distance(utmCoord1, utmCoord2);
return dist;
}
double distanceMeter(const Core::GeoPosition &geoPos, const LtsCoordTrans::UtmCoordinate &utmCoord)
{
const LtsCoordTrans::UtmCoordinate &utmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos.lon(), geoPos.lat());
return distanceMeter(utmPos, utmCoord);
}
double distanceMeter(const Core::GeoPosition2D &geoPos2D, const LtsCoordTrans::UtmCoordinate &utmCoord)
{
const LtsCoordTrans::UtmCoordinate &utmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos2D.lon(), geoPos2D.lat());
return distanceMeter(utmPos, utmCoord);
}
ModuleTestEffectCluster::ModuleTestEffectCluster(int domainId, int exerciseId)
: m_domainId(domainId)
, m_exerciseId(exerciseId)
, m_logger(getLogger("ModuleTestEffectCluster"))
{
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
connect(m_timer,
&QTimer::timeout,
this,
[this]()
{
DEBUG_LOG(m_logger) << "Wirkfeldplanung";
doEffectClusterModifyRequest();
});
}
void ModuleTestEffectCluster::onEffectClusterModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onEffectClusterModifyReply: " << m_replyError << ", aesItemId=" << replyObject.id();
m_aesItemId = replyObject.id();
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onEffectClusterModifyReply: " << replyObject.translated();
}
void ModuleTestEffectCluster::onAcknowledgeExternalAesItemReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onAcknowledgeExternalAesItemReply: " << m_replyError;
sendTabExFire();
// AES::EffectState von ES_Received -> ES_Created
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onAcknowledgeExternalAesItemReply: " << replyObject.translated();
}
void ModuleTestEffectCluster::onModifyPartyDataReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onModifyPartyDataReply: " << m_replyError << " " << replyObject.additionalInfo();
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onModifyPartyDataReply: " << replyObject.translated();
if (!m_participantChangeRequestDone)
{
participantChangeRequest();
m_participantChangeRequestDone = true;
}
}
void ModuleTestEffectCluster::onModifyParticipantDataReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " \n\ngot reply - onModifyParticipantDataReply error: " << m_replyError << ", additionalInfo: " << replyObject.additionalInfo();
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onModifyParticipantDataReply: " << replyObject.translated();
DEBUG_LOG(m_logger) << "onModifyParticipantDataReply => Set Participant Pos";
QJsonArray jsonArray = QJsonDocument::fromJson(QByteArray::fromStdString(replyObject.additionalInfo())).object().value(QLatin1String("participants")).toArray();
DEBUG_LOG(m_logger) << "available_in_exercise jsonDocument.size()=" << jsonArray.size();
// ParticipantId aus dem Result bestimmen, keine Unit
// Dieser einfache Test arbeitet mit einem Participant
for (auto it = jsonArray.constBegin(); it != jsonArray.constEnd(); ++it)
{
int participantId = it->toInt();
DEBUG_LOG(m_logger) << "participantId=" << participantId;
ParticipantManagement::Participant participant = m_ppmDtc->participant(participantId);
if (m_ppmDtc->type(participant.typeId()).typeClass().underlying() != ParticipantManagement::TypeClass::Unit)
{
DEBUG_LOG(m_logger) << "m_participant1.id=" << participantId;
m_participant1.id(participantId); // wichtig zum loeschen
DEBUG_LOG(m_logger) << "m_participantId keine Unit=" << participantId;
setParticipantPos(participantId);
setEquipmentGearCharge(participantId);
// Wirkfeldplanung
if (!m_timer->isActive())
m_timer->start(500);
}
else
{
DEBUG_LOG(m_logger) << "m_participant0.id=" << participantId;
m_participant0.id(participantId); // wichtig zum loeschen
}
}
}
void ModuleTestEffectCluster::onAesItemViewChanged(const AesItemView &aesItemView, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
AreaEffectSimulation::AesType_def::type aesType = aesItemView.aesTypeId().underlying();
if (aesType != AreaEffectSimulation::AesType_def::AtEffectCluster)
return;
if (aesItemView.aesItemId() != m_aesItemId)
return;
DEBUG_LOG(m_logger) << " got onAesItemViewChanged: "
<< ", aesItemId=" << aesItemView.aesItemId() << ", aesType=" << aesType << ", effectStateId=" << aesItemView.effectStateId();
QCOMPARE(aesItemView.effectStateId(), m_effectStates[m_effectStateCounter]);
m_effectStateCounter++;
if (aesItemView.effectStateId() == AreaEffectSimulation::AesEffectState::EsReceived)
{
DEBUG_LOG(m_logger) << "sendAcknowledgeExternalAesItemRequest aesItemId=" << aesItemView.aesItemId();
sendAcknowledgeExternalAesItemRequest(aesItemView.aesItemId());
}
}
void ModuleTestEffectCluster::sendTabExFire()
{
DEBUG_LOG(m_logger) << "sendTabExFire";
TrainingAreaBase::EX_Fire exFire;
exFire.effectorId(m_effectorId); // Kenner z.B B1
exFire.identifier(m_identifier.toStdString()); // Verein z.B 451100
exFire.reference(m_reference.toStdString()); // UUID Feuerkampf
exFire.equipmentNumber(m_participant1.id() * -1);
exFire.sampleTime(QDateTime::currentMSecsSinceEpoch() + 500);
m_tabDts->publish(exFire);
}
void ModuleTestEffectCluster::onEffectsChanged(const Effects &effects, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
AreaEffectSimulation::AesType_def::type aesType = effects.aesTypeId().underlying();
DEBUG_LOG(m_logger) << " got onEffectsChanged - m_data: " << m_data.id() << ", aesType=" << aesType;
++m_counterDds.cntEffectClusterEffects;
m_effects = effects;
DEBUG_LOG(m_logger) << " got onEffectsChanged - m_effects: " << m_effects;
}
void ModuleTestEffectCluster::onChargeEffectChanged(const ChargeEffect &chargeEffect, DataModification modType)
{
DEBUG_LOG(m_logger) << "onChargeEffectChanged state=" << chargeEffect.state();
if (modType == DataModification::eRemoved)
return;
if (chargeEffect.state() == AreaEffectSimulation::AesEffectState::EsActivated)
{
// Compare Position
// mhpPosition.lon(0.201958);
// mhpPosition.lat(0.915263);
// mhpPosition.z(88.0);
double lon = qRound64(chargeEffect.position().lon() * 100000) / 100000.0; // Nur die ersten 5 Nachkommastellen beruecksichtigen
double lat = qRound64(chargeEffect.position().lat() * 100000) / 100000.0;
QCOMPARE(lon, 0.20196);
QCOMPARE(lat, 0.91526);
QCOMPARE(chargeEffect.position().z(), 88.0);
}
else if (chargeEffect.state() == AreaEffectSimulation::AesEffectState::EsExpired)
participantDeleteRequest();
DEBUG_LOG(m_logger) << " got onEffectsChanged - m_effects: " << m_effects;
}
void ModuleTestEffectCluster::onTrajectoryTimeAwareChanged(const TrajectoryTimeAware &data, DataModification modType)
{
Q_UNUSED(modType);
if (data.id() == -1)
return;
++m_counterDds.cntTrajectory;
DEBUG_LOG(m_logger) << " got TrajectoryTimeAwareChanged - data: " << data.id() << ", cntTrajectory=" << m_counterDds.cntTrajectory;
}
void ModuleTestEffectCluster::dumpCnt()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " Counter: "
<< " cntEffectCluster = " << m_counterDds.cntEffectCluster << ", cntAtEffectCluster = " << m_counterDds.cntAtEffectCluster
<< ", cntGunToTargetLine = " << m_counterDds.cntAtEffectClusterGunToTargetLine << ", cntClusterEffects = " << m_counterDds.cntEffectClusterEffects
<< ", cntTrajectory = " << m_counterDds.cntTrajectory;
}
bool ModuleTestEffectCluster::waitForData(const std::function<bool()> &func, int retryCount)
{
// Maybe give QTRY_VERIFY2 or QTRY_VERIFY2_WITH_TIMEOUT a chance
// Or better QTRY_IMPL ??
// QTRY_IMPL(m_replyError == false, 1);
// wait for data
retryCount *= 10;
for (int i = 0; i < retryCount; ++i)
{
TRACE_LOG(m_logger) << " waitForData - i: " << i;
if (func())
return true;
QTest::qWait(1000);
}
return func();
}
EffectCluster ModuleTestEffectCluster::getEcForTneVerification(const QString &eaName, double rotation)
{
AreaEffectSimulation::EffectCluster eaData;
eaData.id(0);
eaData.effectStart((time(nullptr) + 30) * 1000); // msec
QString name(eaName);
name += QString::number(eaData.effectStart().get());
eaData.name(name.toStdString());
eaData.effectDuration(10000); // 10 seconds
// eaData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
// eaData.chargeStruct().igniterTypeId(s_IgniterTypeProximity);
eaData.partyId(2);
eaData.participantId(7);
// SubStruct FlightCurve
FlightCurve fc;
fc.requestedRelativePeakHeight(100);
// siehe auch: GUEZE-2311 Verifikation des Algorithmus bzgl. MTPs zw. TNE und EXCON.
const double height = 100; // Y
const double width = 40; // X
const double rot = rotation;
// In Kontext GUEZE-2311 werden die MTPs unabhängig von eigentliche WirkfeldGeometrie an die TNE übermittelt.
// Um in der Zentrale den gewünschten MTP zu erhalten, muss das Wirkfeld entsprechend angelegt werden:
LtsCoordTrans::UtmCoordinate cp = LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 97000 78000"));
QPointF bottomLeft(Polygons::centerPoint2BottomLeft(cp.geographic(), width, height, rot));
DEBUG_LOG(m_logger) << " bottomLeft" << bottomLeft;
eaData.area().cornerBottomLeft(Core::GeoPosition(bottomLeft.x(), bottomLeft.y(), 0.0));
eaData.area().height(height);
eaData.area().width(width);
eaData.area().rotation(rot);
eaData.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry_def::EgArea);
eaData.tla(0); // aka zop
eaData.externalId(0);
return eaData;
}
std::vector<Core::RelativePositionRelativeTime> ModuleTestEffectCluster::parseTrajectory(const QString &filePath)
{
// EffectClusterModifyRequest
// AreaEffectSimulation::EffectorData
// std::vector< Core::GeoPositionRelativeTime >& trajectory
// GeoPositionRelativeTime ( lon, lat, z, relativeTime)
QFile file(filePath);
std::vector<Core::RelativePositionRelativeTime> points;
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return points;
QXmlStreamReader xml(&file);
while (!xml.atEnd() && !xml.hasError())
{
xml.readNext();
// Wir suchen nach Start-Elementen, die mit "trajectory." beginnen
if (xml.isStartElement() && xml.name().toString().startsWith(QStringLiteral("trajectory.")))
{
Core::RelativePositionRelativeTime p;
// Wir lesen die Unterelemente (lon, lat, z, relativeTime)
while (!(xml.isEndElement() && xml.name().toString().startsWith(QStringLiteral("trajectory."))))
{
xml.readNext();
if (xml.isStartElement())
{
QString name = xml.name().toString();
QString val = xml.readElementText();
if (name == QStringLiteral("lon"))
p.offsetX(val.toDouble());
else if (name == QStringLiteral("lat"))
p.offsetY(val.toDouble());
else if (name == QStringLiteral("z"))
p.offsetZ(val.toDouble());
else if (name == QStringLiteral("relativeTime"))
p.relativeTime(val.toDouble());
}
}
points.push_back(p);
DEBUG_LOG(m_logger) << "Punkt gelesen: offsetY" << p.offsetX() << ", offsetY" << p.offsetY() << ", offsetZ" << p.offsetZ() << ", time=" << p.relativeTime();
}
}
if (xml.hasError())
{
DEBUG_LOG(m_logger) << "XML Fehler:" << xml.errorString();
}
DEBUG_LOG(m_logger) << "file.close()";
file.close();
DEBUG_LOG(m_logger) << "return points";
return points;
}
int ModuleTestEffectCluster::sendAcknowledgeExternalAesItemRequest(int aesItemId)
{
DEBUG_LOG(m_logger) << "sendAcknowledgeExternalAesItemRequest aesItemId=" << aesItemId;
int ret = m_aesFtc->acknowledgeExternalAesItemRequest(AcknowledgeExternalAesItemRequest(aesItemId), this);
return ret;
}
int ModuleTestEffectCluster::doEffectClusterModifyRequest()
{
EffectCluster eaData;
QString dateTimeString = QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yyyy hh:mm"));
m_reference = dateTimeString;
if (eaData.effectStart().is_set())
INFO_LOG(m_logger) << " effectClusterModifyRequest with effectStart: " << QDateTime::fromMSecsSinceEpoch(eaData.effectStart().get()).toString();
else
INFO_LOG(m_logger) << " effectClusterModifyRequest with effectStart: not set ";
FlightCurve fc;
// if (eaData.flightCurve().is_set())
// fc = eaData.flightCurve().get();
QJsonObject obj;
obj[QStringLiteral("effectSource")] = 1;
obj[QStringLiteral("elemente")] = 1;
obj[QStringLiteral("feuerkampfReferenz")] = dateTimeString;
obj[QStringLiteral("firingType")] = 1;
obj[QStringLiteral("ladung")] = QStringLiteral("DM16");
obj[QStringLiteral("ladungsNummer")] = 3;
obj[QStringLiteral("partyId")] = 4;
obj[QStringLiteral("subTyp")] = 0;
obj[QStringLiteral("typ")] = 0;
obj[QStringLiteral("useReceived")] = true;
obj[QStringLiteral("effectSource")] = ProjectDataManagement::EffectSource::ES_ADLER;
QString propertiesStr = QString::fromStdString(QJsonDocument(obj).toJson(QJsonDocument::Compact).toStdString());
eaData.properties(propertiesStr.toStdString());
AreaEffectSimulation::ChargeStruct chargeStruct;
chargeStruct.chargeTypeId(367);
chargeStruct.igniterTypeId(1);
chargeStruct.igniterFunctionId(2);
Core::GeoPosition mhpPosition;
mhpPosition.lon(0.201958);
mhpPosition.lat(0.915263);
mhpPosition.z(88.0);
Core::GeoPosition2D cornerBottomLeft;
cornerBottomLeft.lon(0.201951);
cornerBottomLeft.lat(0.915259);
AreaEffectSimulation::Area area;
area.cornerBottomLeft(cornerBottomLeft);
area.width(50);
area.height(50);
area.rotation(0);
eaData.area(area);
eaData.explosionGeometry(AreaEffectSimulation::AesExplosionGeometry::EgPoint);
std::vector<Core::RelativePositionRelativeTime> trajectory = parseTrajectory(s_REQUEST_SPL_FILE);
AreaEffectSimulation::EffectorData effector;
effector.angleOfFall(0);
effector.participantId(m_participant1.id()); // Participant
DEBUG_LOG(m_logger) << "effector.participantId(m_participant1.id()=" << m_participant1.id();
effector.flightTime(39526.3);
effector.peakHeight(2002.0);
effector.interval(0);
effector.chargeStruct(chargeStruct);
effector.chargeAmount(1);
effector.mhpScatterLenght(3.08314e-316);
effector.mhpScatterWidth(8.76302e-315);
effector.mhpScatterLengthDetonationHeight(9.69057e-316);
effector.mhpScatterWidthDetonationHeight(8.76638e-315);
effector.mhpRotation(-45.4373);
effector.mhpPosition(mhpPosition);
effector.trajectoryRelative(trajectory);
DEBUG_LOG(m_logger) << "effector";
std::vector<AreaEffectSimulation::EffectorData> effectors;
effectors.push_back(effector);
DEBUG_LOG(m_logger) << "Nach effector";
eaData.id(0);
eaData.effectSourceId(ProjectDataManagement::EffectSource::ES_ADLER);
eaData.firePurposeId(2);
eaData.partyId(4);
eaData.participantId(m_participant0.id()); // Unit
DEBUG_LOG(m_logger) << "eaData.participantId(m_participant0.id()=" << m_participant0.id();
eaData.externalId(7);
eaData.effectMarkingInTa(1);
eaData.name(dateTimeString.toStdString());
eaData.element(dateTimeString.toStdString());
DEBUG_LOG(m_logger) << "Vor send effectClusterModifyRequest";
int ret = m_aesFtc->effectClusterModifyRequest(EffectClusterModifyRequest(eaData.id(),
eaData.name(),
eaData.element(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.area(),
eaData.explosionGeometry(), // AimPoint, Radial, Area, Line, Point
eaData.tla(), // ZielOrtungsGenauigkeit
effectors,
eaData.firePurposeId(), // FirePurpose: blenden, niederhalten, vernichten
eaData.partyId(),
eaData.participantId(),
{}, // esmParticipantIds
eaData.externalId(),
eaData.effectMarkingInTa(),
eaData.properties()),
this);
DEBUG_LOG(m_logger) << "send effectClusterModifyRequest ret=" << ret;
return ret;
}
void ModuleTestEffectCluster::waitForEffectStates(int lineNumber, int durPrep, int durAct, int durExp)
{
// expected condition:
if (!waitForData([this]() { return m_replyError == false; }, 1))
{
ERROR_LOG(m_logger) << "Error on request"
<< ", lineNumber = " << lineNumber;
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsPrepared...";
if (!waitForData([this]() { return m_data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsPrepared; }, durPrep))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsPrepared of explosion cluster with id " << m_data.id() << ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsActivated...";
if (!waitForData([this]() { return m_data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated; }, durAct))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsActivated of explosion cluster with id " << m_data.id() << ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsExpired...";
if (!waitForData([this]() { return m_data.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired; }, durExp))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsExpired of explosion cluster with id " << m_data.id() << ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
}
void ModuleTestEffectCluster::disconnectFromParty()
{
DEBUG_LOG(m_logger) << "PPM::ModifyPartyDataRequest";
ParticipantManagement::Party party;
party.name("GREEN");
party.role(ParticipantManagement::Role::Neutral);
party.color(-16711936);
party.fadingColor(-6553701);
ModifyPartyDataRequest req;
req.id(m_partyId);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(true);
req.sampleTime(QDateTime::currentMSecsSinceEpoch());
const HlaForceIdentifierEnum hlaId = HlaForceIdentifierEnum::Neutral_1;
req.hlaForceIdentifier(hlaId);
QString properties = QStringLiteral("{\"Adler\": {\"wsId\": 3, \"SaaName\": \"UI Test\", \"assetId\": -1, \"saaName\": \"SAA1@GUEZ.DE\", \"szenarName\": \"UI Test\", "
"\"isSaaConnected\": 1, \"saaConnectedState\": 2}}");
req.properties(properties.toStdString());
if (!m_ppmFtc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
// failExit();
}
}
void ModuleTestEffectCluster::connectToParty()
{
DEBUG_LOG(m_logger) << "PPM::ModifyPartyDataRequest";
ParticipantManagement::Party party;
party.name("GREEN");
party.role(ParticipantManagement::Role::Neutral);
party.color(-16711936);
party.fadingColor(-6553701);
ModifyPartyDataRequest req;
req.id(m_partyId);
req.name(party.name());
req.color(party.color());
req.fadingColor(party.fadingColor());
req.role(party.role());
req.isUsedBy(true);
req.sampleTime(QDateTime::currentMSecsSinceEpoch());
const HlaForceIdentifierEnum hlaId = HlaForceIdentifierEnum::Neutral_1;
req.hlaForceIdentifier(hlaId);
QString properties = QStringLiteral("{\"Adler\": {\"wsId\": 3, \"SaaName\": \"UI Test\", \"assetId\": -1, \"saaName\": \"SAA1@GUEZ.DE\", \"szenarName\": \"UI Test\", "
"\"isSaaConnected\": 2, \"isVerbund\": true, \"saaConnectedState\": 4}}");
req.properties(properties.toStdString());
if (!m_ppmFtc->modifyPartyDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
// failExit();
}
}
void ModuleTestEffectCluster::participantChangeRequest()
{
DEBUG_LOG(m_logger) << "PPM::participantChangerequest";
m_participant0.id(0);
m_participant0.partyId(4);
m_participant0.hierarchyNumber(1);
m_participant0.typeId(182);
m_participant0.externalId(0);
m_participant0.symbol(QStringLiteral("AP6D|CMPL|10011000001308000000").toStdString());
m_participant0.name(QStringLiteral("451200").toStdString());
m_participant0.identification(QStringLiteral("").toStdString());
m_participant0.designator(QStringLiteral("451200").toStdString()), m_participant0.tacticalIdentifier(QStringLiteral("451200").toStdString());
m_participant0.info(QStringLiteral("").toStdString());
m_participant0.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
m_participant0.isSimulated(false);
m_participant0.isDeleted(false);
m_participant0.properties(QStringLiteral("").toStdString());
m_participant0.modId(1138783559);
m_participant0.superiorModId(0);
m_participant1.id(0);
m_participant1.partyId(4);
m_participant1.hierarchyNumber(1);
m_participant1.typeId(188);
m_participant1.externalId(0);
m_participant1.symbol(QStringLiteral("AP6D|CMPL|10011500001114020000").toStdString());
m_participant1.name(QStringLiteral("C3").toStdString());
m_participant1.identification(QStringLiteral("").toStdString());
m_participant1.designator(QStringLiteral("C3").toStdString()), m_participant1.tacticalIdentifier(QStringLiteral("451200").toStdString());
m_participant1.info(QStringLiteral("").toStdString());
m_participant1.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
m_participant1.isSimulated(false);
m_participant1.isDeleted(false);
m_participant1.properties(QStringLiteral("").toStdString());
m_participant1.modId(1390608527);
m_participant1.superiorModId(1138783559);
ModifyParticipantDataRequest req;
std::vector<ParticipantManagement::ParticipantData> data;
data.push_back(m_participant0);
data.push_back(m_participant1);
req.requestId(548288706);
req.data(data);
if (!m_ppmFtc->modifyParticipantDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
// failExit();
}
}
void ModuleTestEffectCluster::participantDeleteRequest()
{
DEBUG_LOG(m_logger) << "PPM::participantDeleteRequest";
m_participant0.isDeleted(true);
m_participant1.isDeleted(true);
ModifyParticipantDataRequest req;
std::vector<ParticipantManagement::ParticipantData> data;
data.push_back(m_participant0);
data.push_back(m_participant1);
req.requestId(548288707);
req.data(data);
if (!m_ppmFtc->modifyParticipantDataRequest(req, this))
{
ERROR_LOG(m_logger) << "Error creating party data";
// failExit();
}
}
void ModuleTestEffectCluster::setParticipantPos(int participantId)
{
DEBUG_LOG(m_logger) << "setupParticipants";
TrainingAreaBase::TabPosition position;
Core::GeoPosition geoPos;
TrainingAreaBase::EquipmentPosition pos;
geoPos.lon(0.20110363);
geoPos.lat(0.91579597);
geoPos.z(0.0);
DEBUG_LOG(m_logger) << " initTestCase_data1 ...";
pos.equipmentNumber(participantId * -1); // ParticipantId
pos.sampleTime(QDateTime::currentMSecsSinceEpoch());
position.reportingSource(Core::PositionReportingSource::SetByOperator);
position.position(geoPos);
pos.position(position);
m_tabDts->publish(pos);
}
void ModuleTestEffectCluster::setEquipmentGearCharge(int participantId)
{
DEBUG_LOG(m_logger) << "setupEquipmentGearCharge";
TrainingAreaBase::EquipmentGearChargeState egs;
TrainingAreaBase::GearCharge charge;
std::vector<TrainingAreaBase::GearCharge> charges;
egs.equipmentNumber(participantId * -1); // ParticipantId
egs.sampleTime(QDateTime::currentMSecsSinceEpoch());
charges.resize(7);
charge.gearId(138);
charge.gearTypeId(801);
charge.chargeTypeId(367);
charge.remain(50);
charges.push_back(charge); // 0
charge.chargeTypeId(366);
charges.push_back(charge); // 1
charge.chargeTypeId(361);
charges.push_back(charge); // 2
charge.chargeTypeId(362);
charges.push_back(charge); // 3
charge.chargeTypeId(363);
charges.push_back(charge); // 4
charge.chargeTypeId(364);
charges.push_back(charge); // 5
charge.chargeTypeId(365);
charges.push_back(charge); // 6
egs.charge(charges);
m_tabDts->publish(egs);
}
void ModuleTestEffectCluster::onPartyChanged(const ParticipantManagement::Party &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "onPartyChanged:" << data;
// Remove Participant:
if (modType == DataModification::eRemoved)
{
return;
}
if (data.id() != m_partyId)
{
return;
}
DEBUG_LOG(m_logger) << "onPartyChanged id= " << data.id();
if (m_ppmDtc->saaConnectedState(data.id()) == ParticipantManagement::ConnectState::Connected)
{
DEBUG_LOG(m_logger) << "onPartyChanged saaConnectedState == connected";
}
else if (m_ppmDtc->saaConnectedState(data.id()) == ParticipantManagement::ConnectState::Disconnected)
{
DEBUG_LOG(m_logger) << "onPartyChanged saaConnectedState == disconnected => connectToParty";
connectToParty();
}
}
//
// context of textCases
//
void ModuleTestEffectCluster::initTestCase()
{
//
// DdsConnector
//
DEBUG_LOG(m_logger) << "DDSConnector: m_domainId " << m_domainId << ", m_exerciseId " << m_exerciseId;
QFile file(s_REQUEST_SPL_FILE);
const bool bFileExists = file.exists();
QVERIFY2(bFileExists, "ERROR, SPL file does not exists in current path");
// cmd
m_ddsConnector.reset(new DDSConnector::DdsConnector(m_domainId, IDL::PartitionType::Exercise, m_exerciseId));
bool bInit = m_ddsConnector->init();
QVERIFY2(bInit, "ERROR, ddsConnector.init failed!!");
if (!bInit)
{
ERROR_LOG(m_logger) << " ERROR, ddsConnector.init failed!!";
return;
}
INFO_LOG(m_logger) << " ddsConnector.init successfull";
// cmd
m_ddsConnector->initPublisherSubscriber();
INFO_LOG(m_logger) << " ddsConnector.initPublisherSubscriber done";
//
// Aes
//
// cmd
m_aesFtc = AreaEffectSimulation::Interface::Ftc::create(m_ddsConnector);
QVERIFY2(m_aesFtc, "ERROR, create m_aesFtc failed!!");
bool bRet = m_aesFtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
// cmd
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_aesDtc, "ERROR, create m_aesDtc failed!!");
bRet = bRet && m_aesDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
m_aesFtc->enableAll();
m_aesDtc->enableAesItemView();
m_aesDtc->enableChargeEffect();
DEBUG_LOG(m_logger) << " waitForHistoricalDataAesItemView... ";
m_aesDtc->waitForHistoricalDataAesItemView(2000);
DEBUG_LOG(m_logger) << " waitForHistoricalDataAesItemView... Done";
QMap<qint32, AreaEffectSimulation::AesItemView> aesItemViewsCont = m_aesDtc->aesItemViews();
DEBUG_LOG(m_logger) << " aesItemViewsCont.size() " << aesItemViewsCont.size();
//
// PDM
//
m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_pdmDtc, "ERROR, create m_pdmDtc failed!!");
//
// ISM
//
m_ismDtc = InfrastructureManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_ismDtc, "ERROR, create m_ismDtc failed!!");
bRet = bRet && m_ismDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
//
// EXM ExerciseManagement
//
m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_exmDtc, "ERROR, create m_exmDtc failed!!");
bRet = bRet && m_exmDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll m_exmDtc failed!!");
m_exmDtc->waitForHistoricalDataExercise(2000);
DEBUG_LOG(m_logger) << " waitForHistoricalDataExercise... Done";
const ExerciseManagement::Exercise &exercise = m_exmDtc->exercise(m_exerciseId);
QVERIFY2(exercise.id() == m_exerciseId, "ERROR, exercise failed!!");
m_traingAreaId = exercise.trainingAreaId();
DEBUG_LOG(m_logger) << " ExerciseManagement"
<< ", m_traingAreaId = " << m_traingAreaId;
//
// PPM ParticipantManagement
//
//
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_ppmDtc, "ERROR, create m_ppmDtc failed!!");
bRet = bRet && m_ppmDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll m_ppmDtc failed!!");
m_ppmFtc = ParticipantManagement::Interface::Ftc::create(m_ddsConnector);
QVERIFY2(m_ppmFtc, "ERROR, create m_ppmFtc failed!!");
bRet = bRet && m_ppmFtc->enableAll();
m_ppmFtc->enableSetSimulatedPosition();
m_ppmFtc->enableModifyParticipantData();
QVERIFY2(bRet, "ERROR, m_ppmFtc enableAll failed!!");
m_tabDts = TrainingAreaBase::Interface::Dts::create(m_ddsConnector);
QVERIFY2(m_tabDts, "ERROR, create m_tabDts failed!!");
bRet = bRet && m_tabDts->enableAll();
QVERIFY2(bRet, "ERROR, m_tabDts enableAll failed!!");
DEBUG_LOG(m_logger) << "disconnectFromParty";
disconnectFromParty();
// connectToParty();
// connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
// this, &ModuleTestEffectCluster::onParticipantChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::partyChanged, this, &ModuleTestEffectCluster::onPartyChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged, this, &ModuleTestEffectCluster::onAesItemViewChanged, Qt::UniqueConnection);
connect(m_aesDtc.get(),
&AreaEffectSimulation::Interface::Dtc::trajectoryTimeAwareChanged,
this,
&ModuleTestEffectCluster::onTrajectoryTimeAwareChanged,
Qt::UniqueConnection);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::effectsChanged, this, &ModuleTestEffectCluster::onEffectsChanged, Qt::UniqueConnection);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::chargeEffectChanged, this, &ModuleTestEffectCluster::onChargeEffectChanged, Qt::UniqueConnection);
// spend some for the eventLoop to "consume" existing topics/samples:
for (int i = 0; i < 10; ++i)
QTest::qWait(1000);
}
void ModuleTestEffectCluster::init()
{
// init will be called before each test function is executed.
m_counterDds.reset();
}
void ModuleTestEffectCluster::cleanupTestCase()
{
// finish last (dds) calls:
waitForData([]() { return false; }, 10);
INFO_LOG(m_logger) << " ddsConnector destroy...";
m_ddsConnector.reset();
INFO_LOG(m_logger) << " ddsConnector destroy...DONE";
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("ModuleTest_ExplosionCluster", "log4cxx_aes.props");
// aesModuleTestExplosionCluster -v2 test_long -e42 -d139
// ^^^ ^^^^ // no space!!!
// aesModuleTestExplosionCluster -v2 test_long -e42
// aesModuleTestExplosionCluster -v2 test_long -e42
// aesModuleTestExplosionCluster test_long -e42
// aesModuleTestExplosionCluster -e42
CmdLineParser options;
options.setUnitModuleTestMode(true);
std::cout << " consider 'aesModuleTestEffectCluster -e42' " << std::endl;
if (!options.init(true)) // showHelp , exitOnBadOption
return -1;
if (options.exerciseId() <= 0)
{
fprintf(stderr, "ExerciseId not set!\n");
return -2;
}
std::cout << " DomainId = " << options.domainId() << " - ExerciseId = " << options.exerciseId() << std::endl;
if (options.isSet(QStringLiteral("exerciseid")))
--argc;
if (options.isSet(QStringLiteral("domainid")))
--argc;
const QStringList &slPositionalArgs = options.positionalArguments();
for (const QString &item : qAsConst(slPositionalArgs))
std::cout << " item = " << item << std::endl;
ModuleTestEffectCluster tc(options.domainId(), options.exerciseId());
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,192 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
// Aufruf: ./aesModuleTestEffectCluster -e42
// Mehr Debug-Ausgaben:
// Aufruf: ./aesModuleTestEffectCluster -v2 -e42
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <LtsCoordinate/lts_coordinate.h>
#include <AreaEffectSimulation/aes_ftc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/logger.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <ExerciseManagement/exm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <TrainingAreaBase/tab_dts.h>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <memory>
using namespace AreaEffectSimulation; // JUST FOR LOGGER_T
using namespace ParticipantManagement;
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(DomainId|ExerciseId|NoVersion))
{
}
};
class ModuleTestEffectCluster : public QObject
, protected AreaEffectSimulation::Interface::Ftc::EffectClusterModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::AcknowledgeExternalAesItemReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyPartyDataReplyHandler
, protected ParticipantManagement::Interface::Ftc::ModifyParticipantDataReplyHandler
{
Q_OBJECT
public:
ModuleTestEffectCluster(int domainId, int exerciseId);
protected:
void onEffectClusterModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onAcknowledgeExternalAesItemReply(const Core::FunctionReplyObject &replyObject) override;
void onModifyPartyDataReply(const Core::FunctionReplyObject &replyObject) override;
void onModifyParticipantDataReply(const Core::FunctionReplyObject &replyObject) override;
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &aesItemView, DataModification modType);
void onEffectsChanged(const AreaEffectSimulation::Effects &effects, DataModification modType);
void onChargeEffectChanged(const ChargeEffect &chargeEffect, DataModification modType);
void onTrajectoryTimeAwareChanged(const AreaEffectSimulation::TrajectoryTimeAware &data, DataModification modType);
// pre-condition
void disconnectFromParty();
void connectToParty();
void participantChangeRequest();
void participantDeleteRequest();
void setParticipantPos(int participantId);
void setEquipmentGearCharge(int participantId);
// helper
bool waitForData(const std::function<bool()> &func, int retryCount = 10);
EffectCluster getEcForTneVerification(const QString &eaName = QStringLiteral("ModuleTest_TneVerification__"), double rotation = 0);
int doEffectClusterModifyRequest();
int doExplosionClusterSingleEffectOnlyModifyRequest(const ExplosionClusterSingleEffectOnlyRequest &request);
std::vector< Core::RelativePositionRelativeTime > parseTrajectory(const QString &filePath);
int sendAcknowledgeExternalAesItemRequest(int aesItemId);
void sendTabExFire();
void waitForEffectStates(int lineNumber, int durPrep = 5, int durAct = 60, int durExp = 30);
private Q_SLOTS:
//
// TestCases
//
// void initTestCase_data(); // will be called to create a global test data table.
void initTestCase(); // will be called before the first test function is executed.
void init(); // will be called before each test function is executed.
void cleanupTestCase();
private:
void onPartyChanged(const ParticipantManagement::Party &data, DataModification modType);
void dumpCnt();
private:
int m_domainId{-1};
int m_exerciseId{-1};
int m_traingAreaId{-1};
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
int32_t m_partyId{4};
ParticipantManagement::ParticipantData m_participant0; // Unit
ParticipantManagement::ParticipantData m_participant1; // Participant
const ProjectDataManagement::L27_EffectorId m_effectorId{ProjectDataManagement::L27_EffectorId::C3};
QString m_reference;
const QString m_identifier{QStringLiteral("451200")};
int m_aesItemId{-1};
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
std::shared_ptr<TrainingAreaBase::Interface::Dts> m_tabDts;
EffectCluster m_data;
Effects m_effects;
long m_objectRequestedTimePointMsec{-1};
bool m_replyError{false};
QTimer *m_timer;
bool m_participantChangeRequestDone{false};
const QVector<AreaEffectSimulation::AesEffectState> m_effectStates = {AesEffectState::EsReceived,
AesEffectState::EsCreated,
AesEffectState::EsActivated,
AesEffectState::EsExpired};
int m_effectStateCounter = 0;
struct counterDds
{
// ExplosionCluster
int cntEffectCluster{0};
// AesItemView
int cntAtEffectCluster{0};
int cntAtEffectClusterGunToTargetLine{0};
// Effects
int cntEffectClusterEffects{0};
// Trajectory
int cntTrajectory{0};
void reset()
{
cntEffectCluster = 0;
cntAtEffectCluster = 0;
cntAtEffectClusterGunToTargetLine = 0;
cntEffectClusterEffects = 0;
cntTrajectory = 0;
}
}
m_counterDds;
LOGGER_T m_logger;
};
@@ -0,0 +1,34 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
explosion_cluster_module_test.cpp
explosion_cluster_module_test.h
)
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesModuleTestExplosionCluster ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesModuleTestExplosionCluster
NGLLogger
nglcoreapplication
LtsCommandLineParser
LtsCoordinate
Qt5::Core
Qt5::Test
ddsconnector
svcAesCommon
svcAesClient
svcBldClient
svcExmClient
svcIsmClient
svcPdmClient
svcPpmClient
)
# ADD_TEST(NAME aesModuleTestExplosionCluster COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,244 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <LtsCoordinate/lts_coordinate.h>
#include <AreaEffectSimulation/aes_ftc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/logger.h>
#include <Plugins/Buildings/bld_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <ExerciseManagement/exm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <memory>
using namespace AreaEffectSimulation; // JUST FOR LOGGER_T
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(DomainId|ExerciseId|NoVersion))
{
}
};
class ModuleTestExplosionCluster : public QObject
, protected ParticipantManagement::Interface::Ftc::SetSimulatedPositionReplyHandler
, protected ParticipantManagement::Interface::Ftc::SetSimulatedParticipantFormationReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterOcModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterSingleEffectOnlyModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterPauseResumeReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::TargetPositionsModifyReplyHandler
, protected Buildings::Interface::Ftc::SetBuildingStatesReplyHandler
{
Q_OBJECT
public:
ModuleTestExplosionCluster(int domainId, int exerciseId);
protected:
void onSetSimulatedPositionReply(const Core::FunctionReplyObject &replyObject) override;
void onSetSimulatedParticipantFormationReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterSingleEffectOnlyModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterOcModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterPauseResumeReply(const Core::FunctionReplyObject &replyObject) override;
void onTargetPositionsModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onSetBuildingStatesReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType);
void onBuildingStateChanged(const Buildings::BuildingState &bldgState, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &aesItemView, DataModification modType);
void onEffectsChanged(const AreaEffectSimulation::Effects &effects, DataModification modType);
void onTrajectoryTimeAwareChanged(const AreaEffectSimulation::TrajectoryTimeAware &data, DataModification modType);
// pre-condition
void setupParticipants();
bool checkParticipant_5();
bool checkParticipant_6();
bool checkParticipant_89();
bool checkParticipant_99();
void resetBldgs();
int getBldgId(const QString &bldgName) const;
// helper
bool waitForData(const std::function<bool()> &func, int retryCount = 10);
ExplosionCluster getEcForTneVerification(const QString &eaName = QStringLiteral("ModuleTest_TneVerification__"), double rotation = 0);
ExplosionCluster getEcFor1104(const QString &eaName = QStringLiteral("ModuleTest_ec__"));
ExplosionCluster getEcFor1106(const QString &eaName = QStringLiteral("ModuleTest_ec__"));
ExplosionCluster getEcForUBAHN(const QString &eaName = QStringLiteral("ModuleTest_ec__"));
ExplosionCluster getEcFor3101(const QString &eaName = QStringLiteral("ModuleTest_ec__"));
int doExplosionClusterModifyRequest(const ExplosionCluster &eaData);
int doExplosionClusterSingleEffectOnlyModifyRequest(const ExplosionClusterSingleEffectOnlyRequest &request);
int doExplosionClusterOcModifyRequest(const ExplosionClusterOcModifyRequest &request);
int doExplosionClusterTargetPositionsModify(const TargetPositionsModifyRequest &request);
void waitForEffectStates(int lineNumber, int durPrep = 5, int durAct = 60, int durExp = 30);
private Q_SLOTS:
//
// TestCases
//
// void initTestCase_data(); // will be called to create a global test data table.
void initTestCase(); // will be called before the first test function is executed.
void init(); // will be called before each test function is executed.
// void cleanup(); // will be called after each test function is executed.
void dumpCnt();
void dumpBldgStates();
bool setFilterForBldgState(const QString &bldgName);
bool checkSingleEffects(uint idx, const LtsCoordTrans::LtsCoordinate &coord, const AreaEffectSimulation::AesZPlacement zPlace
, const QString &context, double maxDist3D = 1);
bool checkBldgStates(const QVector<int> &damagePercent, const QString &context);
bool isKanaeleAssigned();
void test_justSetupParticipants();
void test_TneVerificationBremen_00();
void test_TneVerificationBremen_01();
void test_TneVerificationBremen_02();
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
void test_bldg_1104_Proximity();
void test_bldg_1104_Impact();
void test_bldg_1104_Delay();
void test_bldg_1106_Proximity();
void test_bldg_1106_Impact();
void test_bldg_1106_Delay();
void test_bldg_UBAHN_Proximity();
void test_bldg_UBAHN_Impact();
void test_bldg_UBAHN_Delay();
void test_bldg_3101_Proximity();
void test_bldg_3101_Impact();
void test_bldg_3101_Delay();
void test_remove_effect_start();
void test_create_bulk(); // has to be call explicitly called by user
void test_point_geometry_bldg_1104();
void test_point_geometry_bldg_1104_SMArt_Proximity(); // NT, Jan 2025: since SMArt seems to be 'not implememented yet', test case fails
// ExplosionCluster 'OC' / 'ABT'
void test_bldg_1104_OC();
// ExplosionCluster 'Light' (without trajectory)
void test_point_geometry_bldg_1104_light();
void test_point_geometry_bldg_1104_single_effect_only(); // Von HLA
void test_bldg_1104_effects_only(); // Von ABT
// VULCANO
void test_vulcano_bldg_1104();
void test_vulcano_bldg_1104_withTargetModify();
void test_vulcano_bldg_1104_withTargetModify2();
// will be called after the last test function was executed:
void cleanupTestCase(); // will be called after the last test function was executed.
public:
static bool m_enable_create_bulk;
private:
int m_domainId{-1};
int m_exerciseId{-1};
int m_traingAreaId{-1};
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<Buildings::Interface::Ftc> m_bldFtc;
std::shared_ptr<Buildings::Interface::Dtc> m_bldDtc;
// std::shared_ptr<InfrastructureManagement::Interface::Ftc> m_ismFtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
ExplosionCluster m_data;
Effects m_effects;
long m_objectRequestedTimePointMsec{-1};
AesItemView m_aesItemViewSingleExplosions;
bool m_replyError{false};
QMap<int, QString> m_bldgIdToName;
int m_bldgIdToFilter{-1};
std::vector<Buildings::BuildingState> m_buildingStateVec;
struct counterDds
{
// ExplosionCluster
int cntExplosionCluster{0};
// AesItemView
int cntAtExplosionCluster{0};
int cntAtExplosionClusterGunToTargetLine{0};
// Effects
int cntExplosionClusterEffects{0};
// Trajectory
int cntTrajectory{0};
void reset()
{
cntExplosionCluster = 0;
cntAtExplosionCluster = 0;
cntAtExplosionClusterGunToTargetLine = 0;
cntExplosionClusterEffects = 0;
cntTrajectory = 0;
}
}
m_counterDds;
LOGGER_T m_logger;
};
@@ -0,0 +1,33 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
limits_module_test.cpp
limits_module_test.h
)
NGL_ADD_EXECUTABLE(SV aesModuleTestLimits ${LinkedSources} ${UnitTestSources} )
NGL_TARGET_LINK_LIBRARIES(aesModuleTestLimits
NGLLogger
nglcoreapplication
LtsCommandLineParser
LtsCoordTrans
Qt5::Core
Qt5::Test
ddsconnector
svcAesCommon
svcAesClient
svcIsmClient
svcExmClient
svcPpmClient
svcPdmClient
)
NGL_INSTALL(SV TARGETS aesModuleTestLimits DESTINATION bin/test)
# ADD_TEST(NAME aesModuleTestBorders COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,263 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <AreaEffectSimulation/aes_ftc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/logger.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <ExerciseManagement/exm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <memory>
using namespace AreaEffectSimulation; // JUST FOR LOGGER_T
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(DomainId | ExerciseId | NoVersion))
{
}
};
class ModuleTestLimits : public QObject
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineAreaRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::NContaminationModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::NContaminationRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BCContaminationModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BCContaminationRemoveReplyHandler
, protected ParticipantManagement::Interface::Ftc::SetSimulatedPositionReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ChargeAdministrationModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::TechnicalAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::FunctionalAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::FunctionalAreaRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::TechnicalAreaRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaRemoveReplyHandler
{
Q_OBJECT
public:
ModuleTestLimits(int domainId, int exerciseId);
protected:
void onExplosionClusterModifyReply(const Core::FunctionReplyObject &replyObject) override;
// helper
bool waitForData(const std::function<bool()> &func, int retryCount = 10);
ExplosionClusterModifyRequest getEc(const Core::GeoPosition2D& cornerBottomLeft);
ExplosionClusterModifyRequest getAs(); //AirSupport
ExplosionAreaModifyRequest getEa();
ExplosionSingleModifyRequest getEs();
MineAreaModifyRequest getMa();
NContaminationModifyRequest getNBC();
OffRouteMineModifyRequest getORM();
BoobyTrapModifyRequest getBT();
BCContaminationModifyRequest getBCC();
FunctionalAreaModifyRequest getFA();
TechnicalAreaModifyRequest getTA();
private Q_SLOTS:
//
// TestCases
//
// void initTestCase_data(); // will be called to create a global test data table.
void initTestCase(); // will be called before the first test function is executed.
void init(); // will be called before each test function is executed.
// void cleanup(); // will be called before each test function is executed.
void cleanup();
//ExplosionArea and Single is currently not used in GÜZ
// void testExplosionAreaCentral();
// void testExplosionAreaDecentral();
// void testExplosionSingleCentral();
// void testExplosionSingleDecentral();
void testExplosionClusterCentral();
void testExplosionClusterDecentral();
void testAirSupports(); //Always central
void testMineAreaCentral();
void testMineAreaDecentral();
void testNBCCentral();
void testNBCDecentral();
void testOffRouteMines(); //Always central
void testBoobyTraps(); //Always central
void testBCCCentral();
void testBCCDecentral();
void testFADecentral();
void testFACentral();
// void testTADecentral(); //Only in AHTC
// void testTACentral();
void cleanupTestCase(); // will be called after the last test function was executed.
private:
void setupParticipant();
void setupCharges();
void deleteAesAreas();
public:
static bool m_noDelete;
private:
int m_domainId{-1};
int m_exerciseId{-1};
int m_traingAreaId{-1};
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
int s_counter{0};
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
ExplosionCluster m_data;
long m_objectRequestedTimePointMsec{-1};
AesItemView m_aesItemViewSingleExplosions;
bool m_replyError{false};
QMap<int, QString> m_bldgIdToName;
int m_maxExplosionsCentral = 200;
int m_maxExplosionsDecentral = 50;
int m_maxMineAreasCentral = 100;
int m_maxMineAreasDecentral = 50;
int m_maxNBCCentral = 100;
int m_maxNBCDecentral = 10;
int m_maxOffRouteMines = 100;
int m_maxBoobyTraps = 150;
int m_maxSafePzAbwSafe = 10;
int m_maxFunctionalAreaCentral = 50;
int m_maxFunctionalAreaDec = 10;
int m_maxTechnicalAreaCentral = 100;
int m_maxTechnicalAreaDecentral = 10;
int m_maxAirSupports = 100;
InfrastructureManagement::SpecialArea m_specialArea;
Core::GeoPosition2D m_centralPoint;
Core::GeoPosition2D m_decentralPoint;
std::vector<Core::GeoPosition2D> m_segmentsCentral;
std::vector<Core::GeoPosition2D> m_segmentsDecentral;
Core::GeoPosition2D m_partPos;
const Core::GeoPosition2D m_centralBremen = Core::GeoPosition2D(0.154634, 0.926053);
const Core::GeoPosition2D m_centralAltmark = Core::GeoPosition2D(0.201526, 0.914231);
const Core::GeoPosition2D m_decentralBremen = Core::GeoPosition2D(0.156305, 0.92683);
const Core::GeoPosition2D m_decentralAltmark = Core::GeoPosition2D(0.202014, 0.915328);
const std::vector<Core::GeoPosition2D> m_segmentsCentralBremen = {{0.154634, 0.926053}, {0.154635, 0.926055} };
const std::vector<Core::GeoPosition2D> m_segmentsDecentralBremen = {{0.15609399, 0.92610527}, {0.15580772, 0.92628852}};
const std::vector<Core::GeoPosition2D> m_segmentsCentralAltmark = {{0.201412, 0.914215}, {0.201757, 0.914225} };
const std::vector<Core::GeoPosition2D> m_segmentsDecentralAltmark = {{0.201799, 0.914842}, {0.201965, 0.914806} };
const Core::GeoPosition2D m_partPosAltmark = Core::GeoPosition2D(0.202168, 0.914311);
const Core::GeoPosition2D m_partPosBremen = Core::GeoPosition2D(0.156482, 0.925844);
struct counterDds
{
// ExplosionCluster
int cntExplosionCluster{0};
// AesItemView
int cntAtExplosionCluster{0};
int cntAtExplosionClusterGunToTargetLine{0};
// Effects
int cntExplosionClusterEffects{0};
// Trajectory
int cntTrajectory{0};
void reset()
{
cntExplosionCluster = 0;
cntAtExplosionCluster = 0;
cntAtExplosionClusterGunToTargetLine = 0;
cntExplosionClusterEffects = 0;
cntTrajectory = 0;
}
}
m_counterDds;
LOGGER_T m_logger;
public:
void onMineAreaModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onExplosionAreaModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onExplosionClusterRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onNContaminationModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onOffRouteMineModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onBoobyTrapModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onNContaminationRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onOffRouteMineRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onBoobyTrapRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onMineAreaRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onBCContaminationModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onBCContaminationRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
// SetSimulatedPositionReplyHandler interface
void onSetSimulatedPositionReply(const Core::FunctionReplyObject &reply) override
{
m_replyError = reply.error();
DEBUG_LOG(m_logger) << " got reply - onSetSimulatedPositionReply : " << m_replyError;
if (!reply.str().empty())
ERROR_LOG(m_logger) << " got reply - onExplosionClusterModifyReply: " << reply.translated();
}
// ChargeAdministrationModifyReplyHandler interface
void onChargeAdministrationModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onTechnicalAreaModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onFunctionalAreaModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onTechnicalAreaRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onFunctionalAreaRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onExplosionSingleModifyReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onExplosionAreaRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
void onExplosionSingleRemoveReply(const Core::FunctionReplyObject &reply) override { Q_UNUSED(reply);}
};
@@ -0,0 +1,32 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
aes_others_module_test.h
aes_others_module_test.cpp
)
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesModuleTestOthers ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesModuleTestOthers
NGLLogger
nglcoreapplication
LtsCommandLineParser
LtsCoordinate
Qt5::Core
Qt5::Test
ddsconnector
svcAesCommon
svcAesClient
svcExmClient
svcPdmClient
svcPpmClient
)
# ADD_TEST(NAME aesModuleTestExplosionCluster COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
@@ -0,0 +1,828 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_others_module_test.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCoordTrans/lts_coord_trans.h>
// AES
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/Utils/polygons.h>
#include <QTimer>
#include <QDebug>
#include <iostream>
using namespace AreaEffectSimulation;
using namespace LtsCoordTrans;
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 371;
static const int s_ChargeTypeId2 = 372;
static const int s_TraingAreaId_Altmark = 2;
static const int s_TraingAreaId_Bremen = 6;
//
// some helper functions
//
template<typename T>
dds::core::optional<T> toOptional(T inVal)
{
return inVal != T() ? dds::core::optional<T>(inVal) : dds::core::optional<T>();
}
template<typename T>
dds::core::optional<T> optional()
{
return dds::core::optional<T>();
}
double distanceMeter(const LtsCoordTrans::UtmCoordinate &utmCoord1, const LtsCoordTrans::UtmCoordinate &utmCoord2)
{
double dist = LtsCoordTrans::distance(utmCoord1, utmCoord2);
qCritical() << " dist" << dist;
return dist;
}
double distanceMeter(const Core::GeoPosition &geoPos, const LtsCoordTrans::UtmCoordinate &utmCoord)
{
const LtsCoordTrans::UtmCoordinate &utmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos.lon(), geoPos.lat());
return distanceMeter(utmPos, utmCoord);
}
double distanceMeter(const Core::GeoPosition2D &geoPos2D, const LtsCoordTrans::UtmCoordinate &utmCoord)
{
const LtsCoordTrans::UtmCoordinate &utmPos = LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos2D.lon(), geoPos2D.lat());
return distanceMeter(utmPos, utmCoord);
}
ModuleTestAesOthers::ModuleTestAesOthers(int domainId, int exerciseId)
: m_domainId(domainId)
, m_exerciseId(exerciseId)
, m_logger(getLogger("ModuleTestAesOthers"))
{
}
void ModuleTestAesOthers::onSetSimulatedPositionReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onSetSimulatedPositionReply : " << m_replyError;
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onExplosionClusterModifyReply: " << replyObject.translated();
}
void ModuleTestAesOthers::onBoobyTrapModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onBoobyTrapModifyReply : " << m_replyError;
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onBoobyTrapModifyReply: " << replyObject.translated();
}
void ModuleTestAesOthers::onBoobyTrapRemoveReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << " got reply - onBoobyTrapRemoveReply : " << m_replyError;
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onBoobyTrapRemoveReply: " << replyObject.translated();
}
void ModuleTestAesOthers::onOffRouteMineModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_replyError = replyObject.error();
DEBUG_LOG(m_logger) << " got reply - onOffRouteMineModifyReply : " << m_replyError;
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onOffRouteMineModifyReply: " << replyObject.translated();
}
void ModuleTestAesOthers::onOffRouteMineRemoveReply(const Core::FunctionReplyObject &replyObject)
{
DEBUG_LOG(m_logger) << " got reply - onOffRouteMineRemoveReply : " << m_replyError;
if (!replyObject.str().empty())
ERROR_LOG(m_logger) << " got reply - onOffRouteMineRemoveReply: " << replyObject.translated();
}
void ModuleTestAesOthers::boobyTrapChanged(const BoobyTrap &data, DataModification modType)
{
Q_UNUSED(modType);
if (data.id() == -1)
return;
++m_counterDds.cntBoobyTrap;
m_dataBoobyTrap = data;
qint64 startTimeMsec = -1;
if (m_dataBoobyTrap.effectStart().is_set())
startTimeMsec = m_dataBoobyTrap.effectStart().get();
DEBUG_LOG(m_logger) << " got changed - m_dataBoobyTrap: " << m_dataBoobyTrap.id() << ", state=" << m_dataBoobyTrap.effectStateId()
<< ", startTimeMsec=" << startTimeMsec
<< ", cntBoobyTrap=" << m_counterDds.cntBoobyTrap;
TRACE_LOG(m_logger) << " got changed - m_dataBoobyTrap: " << m_dataBoobyTrap;
}
void ModuleTestAesOthers::offRouteMineChanged(const OffRouteMine &data, DataModification modType)
{
Q_UNUSED(modType);
if (data.id() == -1)
return;
++m_counterDds.cntOffRouteMine;
m_dataOffRouteMine = data;
qint64 startTimeMsec = -1;
if (m_dataOffRouteMine.effectStart().is_set())
startTimeMsec = m_dataOffRouteMine.effectStart().get();
DEBUG_LOG(m_logger) << " got changed - m_dataOffRouteMine: " << m_dataOffRouteMine.id() << ", state=" << m_dataOffRouteMine.effectStateId()
<< ", startTimeMsec=" << startTimeMsec
<< ", cntOffRouteMine=" << m_counterDds.cntOffRouteMine;
TRACE_LOG(m_logger) << " got changed - m_dataOffRouteMine: " << m_dataOffRouteMine;
}
void ModuleTestAesOthers::onAesItemViewChanged(const AesItemView &aesItemView, DataModification modType)
{
if (modType == DataModification::eRemoved)
return;
AreaEffectSimulation::AesType_def::type aesType = aesItemView.aesTypeId().underlying();
DEBUG_LOG(m_logger) << " got onAesItemViewChanged, aesType=" << aesType;
switch (aesType)
{
case AreaEffectSimulation::AesType_def::AtBoobyTrap:
case AreaEffectSimulation::AesType_def::AtBoobyTrapSecondary:
case AreaEffectSimulation::AesType_def::AtOffRouteMine:
case AreaEffectSimulation::AesType_def::AtOffRouteMineSecondary:
++m_counterDds.cntItemView;
break;
default:
break;
}
}
void ModuleTestAesOthers::dumpCnt()
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " Counter: "
<< " cntBoobyTrap = " << m_counterDds.cntBoobyTrap
<< " cntOffRouteMine = " << m_counterDds.cntOffRouteMine
<< ", cntItemView = " << m_counterDds.cntItemView;
}
bool ModuleTestAesOthers::waitForData(const std::function<bool ()> &func, int retryCount)
{
// Maybe give QTRY_VERIFY2 or QTRY_VERIFY2_WITH_TIMEOUT a chance
// Or better QTRY_IMPL ??
// QTRY_IMPL(m_replyError == false, 1);
// wait for data
retryCount *= 10;
for (int i = 0; i < retryCount; ++i)
{
DEBUG_LOG(m_logger) << " waitForData - i: " << i;
if (func())
return true;
QTest::qWait(100);
}
return func();
}
void ModuleTestAesOthers::waitForEffectStatesBT(int lineNumber, int durPrep, int durAct, int durExp)
{
// expected condition:
if (!waitForData([this]() { return m_replyError == false; }, 1))
{
ERROR_LOG(m_logger) << "Error on request" << ", lineNumber = " << lineNumber;
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsPrepared...";
if (!waitForData([this]() { return m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsPrepared;}, durPrep))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsPrepared of m_dataBoobyTrap with id " << m_dataBoobyTrap.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsActivated...";
if (!waitForData([this]() { return m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated;}, durAct))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsActivated of m_dataBoobyTrapr with id " << m_dataBoobyTrap.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
if (durExp > 0)
{
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsExpired...";
if (!waitForData([this]() { return m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired;}, durExp))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsExpired of m_dataBoobyTrap with id " << m_dataBoobyTrap.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
}
}
void ModuleTestAesOthers::waitForEffectStatesORM(int lineNumber, int durPrep, int durAct, int durExp)
{
// expected condition:
if (!waitForData([this]() { return m_replyError == false; }, 1))
{
ERROR_LOG(m_logger) << "Error on request" << ", lineNumber = " << lineNumber;
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsPrepared...";
if (!waitForData([this]() { return m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsPrepared;}, durPrep))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsPrepared of m_dataOffRouteMine with id " << m_dataOffRouteMine.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsActivated...";
if (!waitForData([this]() { return m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated;}, durAct))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsActivated of m_dataOffRouteMine with id " << m_dataOffRouteMine.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
if (durExp > 0)
{
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for EsExpired...";
if (!waitForData([this]() { return m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired;}, durExp))
{
ERROR_LOG(m_logger) << "Timeout waiting for EsExpired of m_dataOffRouteMine with id " << m_dataOffRouteMine.id()
<< ", lineNumber = " << lineNumber;
QVERIFY2(false, "ERROR, exit");
return;
}
}
}
void ModuleTestAesOthers::setupParticipants()
{
INFO_LOG(m_logger) << " setupParticipant ...";
QVERIFY2(m_ppmFtc, "ERROR, no m_ppmFtc");
{
ParticipantManagement::SetSimulatedPosition req;
{
req.participantId(18); // according testDB
req.onlyOutdatedParticipants(false);
const LtsCoordTrans::UtmCoordinate &utmPos =
LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32UPD7690018100")); // 32U PD 76900 18100
QPointF pos = utmPos.geographic();
req.position(Core::GeoPosition(pos.x(), pos.y(), 0.0));
INFO_LOG(m_logger) << " setSimulatedPositionRequest ...";
m_replyError = true;
m_ppmFtc->setSimulatedPositionRequest(req, this);
// expected condition:
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error on request";
}
}
}
INFO_LOG(m_logger) << " setSimulatedPositionRequest ...DONE";
}
bool ModuleTestAesOthers::checkParticipant_18()
{
// soll: QStringLiteral("32UPD7690018000")); // 32U PD 76900 18000
const int partId = 18;
const ParticipantManagement::ParticipantPosition &pos = m_ppmDtc->participantPosition(partId);
QString mgrsStr = LtsCoordTrans::mgrsFromGeographic(pos.pos().lon(), pos.pos().lat());
INFO_LOG(m_logger) << " for partId = '" << partId << "', we got position: " << mgrsStr;
return mgrsStr == QStringLiteral("32U PD 76900 18100");
}
bool ModuleTestAesOthers::tryRemoveOldBoobyTraps()
{
m_aesDtc->waitForHistoricalDataBoobyTrap(5000);
const auto &btMap = m_aesDtc->boobyTraps();
for (const auto &bt : btMap)
{
qCritical() << " bt.name" << bt.name().c_str();
int ret = m_aesFtc->boobyTrapRemoveRequest(Core::RemoveElementRequest(bt.id()));
qCritical() << " bt.name -> " << ret;
}
return true;
}
int ModuleTestAesOthers::doBoobyTrapModifyRequest(const BoobyTrap &btData)
{
BoobyTrapModifyRequest req(btData.id(),
btData.name(),
btData.effectDuration(),
btData.chargeStruct().chargeTypeId(),
btData.impactPosition(),
btData.impactRadius(),
btData.triggerRadius(),
btData.rotationAngle(),
btData.centralAngle(),
btData.effectStart(),
btData.partyId()
);
int ret = m_aesFtc->boobyTrapModifyRequest(req, this);
return ret;
}
int ModuleTestAesOthers::doOffRouteMineModifyRequest(const OffRouteMine &offRouteMine)
{
OffRouteMineModifyRequest req(offRouteMine.id(),
offRouteMine.name(),
offRouteMine.effectDuration(),
offRouteMine.chargeStruct().chargeTypeId(),
offRouteMine.area(),
offRouteMine.impactRadius(),
offRouteMine.effectStart(),
offRouteMine.partyId()
);
int ret = m_aesFtc->offRouteMineModifyRequest(req, this);
return ret;
}
int ModuleTestAesOthers::doMoveParticipant(int participantId, const QString &newPositionMGRS)
{
ParticipantManagement::SetSimulatedPosition req;
{
req.participantId(participantId); // according testDB
req.onlyOutdatedParticipants(false);
const LtsCoordTrans::UtmCoordinate &utmPos =
LtsCoordTrans::UtmCoordinate::fromMgrs(newPositionMGRS); // 32U PD 76900 18100
QPointF pos = utmPos.geographic();
req.position(Core::GeoPosition(pos.x(), pos.y(), 0.0));
INFO_LOG(m_logger) << " setSimulatedPositionRequest ...";
m_replyError = true;
m_ppmFtc->setSimulatedPositionRequest(req, this);
// expected condition:
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error on request";
}
}
INFO_LOG(m_logger) << " doMoveParticipant ...DONE";
return true;
}
//
// context of textCases
//
void ModuleTestAesOthers::initTestCase()
{
//
// DdsConnector
//
NOTICE_LOG(m_logger) << "DDSConnector: m_domainId " << m_domainId << ", m_exerciseId " << m_exerciseId;
// cmd
m_ddsConnector.reset(new DDSConnector::DdsConnector(m_domainId, IDL::PartitionType::Exercise, m_exerciseId));
bool bInit = m_ddsConnector->init();
QVERIFY2(bInit, "ERROR, ddsConnector.init failed!!");
if (!bInit)
{
ERROR_LOG(m_logger) << " ERROR, ddsConnector.init failed!!";
return;
}
INFO_LOG(m_logger) << " ddsConnector.init successfull";
// cmd
m_ddsConnector->initPublisherSubscriber();
INFO_LOG(m_logger) << " ddsConnector.initPublisherSubscriber done";
//
// Aes
//
// cmd
m_aesFtc = AreaEffectSimulation::Interface::Ftc::create(m_ddsConnector);
QVERIFY2(m_aesFtc, "ERROR, create m_aesFtc failed!!");
bool bRet = m_aesFtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
// cmd
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_aesDtc, "ERROR, create m_aesDtc failed!!");
bRet = bRet && m_aesDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
std::cout << " waitForHistoricalDataAesItemView... " << std::endl;
m_aesDtc->waitForHistoricalDataBoobyTrap(2000);
std::cout << " waitForHistoricalDataAesItemView... Done" << std::endl;
std::cout << " waitForHistoricalDataAesItemView... " << std::endl;
m_aesDtc->waitForHistoricalDataAesItemView(2000);
std::cout << " waitForHistoricalDataAesItemView... Done" << std::endl;
QMap<qint32, AreaEffectSimulation::AesItemView> aesItemViewsCont = m_aesDtc->aesItemViews();
std::cout << " aesItemViewsCont.size() " << aesItemViewsCont.size() << std::endl;
//
// PDM
//
// m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(m_ddsConnector);
// QVERIFY2(m_pdmDtc, "ERROR, create m_pdmDtc failed!!");
// bRet = bRet && m_pdmDtc->enableBuildingChargeEffect();
// QVERIFY2(bRet, "ERROR, enableBuildingChargeEffect failed!!");
// m_pdmDtc->waitForHistoricalDataBuildingChargeEffect(2000);
// std::cout << " waitForHistoricalDataBuildingChargeEffect... Done" << std::endl;
//
// EXM ExerciseManagement
//
m_exmDtc = ExerciseManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_exmDtc, "ERROR, create m_exmDtc failed!!");
bRet = bRet && m_exmDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
m_exmDtc->waitForHistoricalDataExercise(2000);
std::cout << " waitForHistoricalDataExercise... Done" << std::endl;
const ExerciseManagement::Exercise &exercise = m_exmDtc->exercise(m_exerciseId);
QVERIFY2(exercise.id() == m_exerciseId, "ERROR, exercise failed!!");
m_traingAreaId = exercise.trainingAreaId();
std::cout << " ExerciseManagement"
<< ", m_traingAreaId = " << m_traingAreaId << std::endl;
//
// PPM ParticipantManagement
//
//
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
QVERIFY2(m_ppmDtc, "ERROR, create m_ppmDtc failed!!");
bRet = bRet && m_ppmDtc->enableAll();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
m_ppmFtc = ParticipantManagement::Interface::Ftc::create(m_ddsConnector);
QVERIFY2(m_ppmFtc, "ERROR, create m_ppmFtc failed!!");
bRet = bRet && m_ppmFtc->enableAll();
m_ppmFtc->enableSetSimulatedPosition();
QVERIFY2(bRet, "ERROR, enableAll failed!!");
// Geschuetz
setupParticipants();
// connect at the end to skip unwanted
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
this, &ModuleTestAesOthers::boobyTrapChanged, Qt::UniqueConnection);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::offRouteMineChanged,
this, &ModuleTestAesOthers::offRouteMineChanged, Qt::UniqueConnection);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &ModuleTestAesOthers::onAesItemViewChanged, Qt::UniqueConnection);
// spend some for the eventLoop to "consume" existing topics/samples:
for (int i = 0; i < 10; ++i)
QTest::qWait(100);
}
void ModuleTestAesOthers::init()
{
// init will be called before each test function is executed.
m_counterDds.reset();
setupParticipants();
}
void ModuleTestAesOthers::test_justSetupParticipants()
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " START";
setupParticipants();
NOTICE_LOG(m_logger) << LOGFUNCTION << " END";
}
//
// 1104
//
void ModuleTestAesOthers::test_boobyTrap_01()
{
if (m_traingAreaId != s_TraingAreaId_Altmark)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " skip testcase due to m_traingAreaId = " << m_traingAreaId;
return;
}
NOTICE_LOG(m_logger) << LOGFUNCTION << " START, m_traingAreaId = " << m_traingAreaId;
QVERIFY2(checkParticipant_18(), "ERROR, checkParticipant_5 failed!!");
AreaEffectSimulation::BoobyTrap bt;
bt.id(0);
bt.name(QString(QStringLiteral("ModuleTest_BoobyTrap01")).toStdString());
bt.chargeStruct().chargeTypeId(252);
bt.effectDuration(60000);
bt.partyId(2);
bt.impactRadius(10);
bt.triggerRadius(5);
// Type1 == SchAbwMi ohne rotationAngle, centralAngle
// Type2 == SchAbwRiMi mit rotationAngle, centralAngle
LtsCoordinate lts = LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0);
bt.impactPosition(lts.geoPosition2D());
bt.effectStart((time(nullptr) + 10) * 1000); // msec
m_replyError = true;
m_dataBoobyTrap = AreaEffectSimulation::BoobyTrap();
int ret = doBoobyTrapModifyRequest(bt);
QVERIFY2(ret != 0, "ERROR, doBoobyTrapModifyRequest failed!!");
waitForEffectStatesBT(__LINE__, 5, 15, 0); // do _not_ wait for expired!
QVERIFY2(m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated, "ERROR, effectStateId failed!!");
dumpCnt();
QVERIFY2(m_counterDds.cntBoobyTrap == 2, "ERROR, cnt failed!!"); // EsPrepared, EsActivated,
QVERIFY2(m_counterDds.cntItemView == 4, "ERROR, cnt failed!!"); //
// Move Participant to trigger area, maybe also 3D scene marker:
doMoveParticipant(18, QStringLiteral("32U PD 77002 18112"));
// wait...
waitForData([]() { return false; }, 2);
doMoveParticipant(18, QStringLiteral("32U PD 77002 18103"));
// wait until expired:
waitForData([this]() { return m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired; }, 60);
QVERIFY2(m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired, "ERROR, effectStateId failed!!");
// wait to let it disappear:
waitForData([]() { return false; }, 15);
NOTICE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ModuleTestAesOthers::test_boobyTrap_02_IMPL(const QString &name,
const LtsCoordinate &impactCoord,
double rotationAngle,
const LtsCoordinate &particpantCoord)
{
if (m_traingAreaId != s_TraingAreaId_Altmark)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " skip testcase due to m_traingAreaId = " << m_traingAreaId;
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " START, m_traingAreaId = " << m_traingAreaId
<< ", name =" << name << "'"
<< ", impactCoord= " << impactCoord.mgrs()
<< ", rotationAngle = " << rotationAngle
<< ", particpantCoord = " << particpantCoord.mgrs();
QVERIFY2(checkParticipant_18(), "ERROR, checkParticipant_5 failed!!");
AreaEffectSimulation::BoobyTrap bt;
bt.id(0);
bt.name(name.toStdString());
bt.chargeStruct().chargeTypeId(252);
bt.effectDuration(600000);
bt.partyId(2);
bt.impactRadius(10);
bt.triggerRadius(5);
bt.impactPosition(impactCoord.geoPosition());
// Type1 == SchAbwMi ohne rotationAngle, centralAngle
// Type2 == SchAbwRiMi mit rotationAngle, centralAngle
bt.rotationAngle(toOptional(rotationAngle)); // Q1: 30
bt.centralAngle(toOptional(90.0));
bt.effectStart((time(nullptr) + 10) * 1000); // msec
m_replyError = true;
m_dataBoobyTrap = AreaEffectSimulation::BoobyTrap();
int ret = doBoobyTrapModifyRequest(bt);
QVERIFY2(ret != 0, "ERROR, doBoobyTrapModifyRequest failed!!");
waitForEffectStatesBT(__LINE__, 5, 15, 0); // do _not_ wait for expired!
QVERIFY2(m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated, "ERROR, m_dataBoobyTrap effectStateId failed!!");
dumpCnt();
QVERIFY2(m_counterDds.cntBoobyTrap == 2, "ERROR, cnt failed!!"); // EsPrepared, EsActivated,
QVERIFY2(m_counterDds.cntItemView == 4, "ERROR, cnt failed!!"); //
// Move Participant to trigger area, maybe also 3D scene marker:
// 1. in the near of area:
doMoveParticipant(18, QStringLiteral("32U PD 77002 18112"));
// wait...
waitForData([]() { return false; }, 2);
// 2. move IN the area:
doMoveParticipant(18, particpantCoord.mgrs());
// wait until expired:
waitForData([this]() { return m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired; }, 60);
QVERIFY2(m_dataBoobyTrap.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired, "ERROR, effectStateId failed!!");
// wait to let it disappear:
waitForData([]() { return false; }, 15);
INFO_LOG(m_logger) << LOGFUNCTION << " END"
<< ", name =" << name << "'"
<< ", impactCoord= " << impactCoord.mgrs()
<< ", rotationAngle = " << rotationAngle
<< ", particpantCoord = " << particpantCoord.mgrs();
}
void ModuleTestAesOthers::test_boobyTrap_02_Q1()
{
test_boobyTrap_02_IMPL(QStringLiteral("ModuleTest_BoobyTrap02_Q1"),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0),
30+(0*90),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77002 18102")), 0.0));
}
void ModuleTestAesOthers::test_boobyTrap_02_Q2()
{
test_boobyTrap_02_IMPL(QStringLiteral("ModuleTest_BoobyTrap02_Q2"),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0),
30+(1*90),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77002 18098")), 0.0));
}
void ModuleTestAesOthers::test_boobyTrap_02_Q3()
{
test_boobyTrap_02_IMPL(QStringLiteral("test_boobyTrap_02_Q3"),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0),
30+(2*90),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 76998 18098")), 0.0));
}
void ModuleTestAesOthers::test_boobyTrap_02_Q4()
{
test_boobyTrap_02_IMPL(QStringLiteral("test_boobyTrap_02_Q4"),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0),
30+(3*90),
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 76998 18102")), 0.0));
}
void ModuleTestAesOthers::test_offRouteMine_02()
{
if (m_traingAreaId != s_TraingAreaId_Altmark)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " skip testcase due to m_traingAreaId = " << m_traingAreaId;
return;
}
INFO_LOG(m_logger) << LOGFUNCTION << " START, m_traingAreaId = " << m_traingAreaId;
QVERIFY2(checkParticipant_18(), "ERROR, checkParticipant_5 failed!!");
// offRouteMine1 = 253; // OHNE impactRadius
// offRouteMine2 = 254; // MIT impactRadius
AreaEffectSimulation::OffRouteMine orm;
orm.id(0);
orm.name(QString(QStringLiteral("test_offRouteMine_02")).toStdString());
orm.chargeStruct().chargeTypeId(254);
orm.effectDuration(600000);
orm.partyId(2);
orm.impactRadius(10); // 254 -> MIT impactRadius -> mit 3DSphere/Kugel
const LtsCoordinate &coord =
LtsCoordinate::fromUtmCoordinate(UtmCoordinate::fromMgrs(QStringLiteral("32U PD 77000 18100")), 0.0);
orm.area().cornerBottomLeft(coord.geoPosition());
orm.area().height(50);
orm.area().width(1);
orm.area().rotation(85);
orm.effectStart((time(nullptr) + 10) * 1000); // msec
m_replyError = true;
m_dataOffRouteMine = AreaEffectSimulation::OffRouteMine();
int ret = doOffRouteMineModifyRequest(orm);
QVERIFY2(ret != 0, "ERROR, doOffRouteMineModifyRequest failed!!");
waitForEffectStatesORM(__LINE__, 5, 15, 0); // do _not_ wait for expired!
QVERIFY2(m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated, "ERROR, effectStateId failed!!");
dumpCnt();
QVERIFY2(m_counterDds.cntOffRouteMine == 2, "ERROR, cnt failed!!"); // EsPrepared, EsActivated,
QVERIFY2(m_counterDds.cntItemView == 4, "ERROR, cnt failed!!"); //
// Move Participant to trigger area, maybe also 3D scene marker:
// 1. in the near of area:
doMoveParticipant(18, QStringLiteral("32U PD 77002 18112"));
// wait...
waitForData([]() { return false; }, 2);
// 2. move OVER the area:
doMoveParticipant(18, QStringLiteral("32U PD 77020 18095"));
// wait until expired:
waitForData([this]() { return m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired; }, 60);
QVERIFY2(m_dataOffRouteMine.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsExpired, "ERROR, m_dataOffRouteMine effectStateId failed!!");
// wait to let it disappear:
waitForData([]() { return false; }, 15);
INFO_LOG(m_logger) << LOGFUNCTION << " END" << ", name =" << orm.name() << "'";
}
void ModuleTestAesOthers::cleanupTestCase()
{
// finish last (dds) calls:
waitForData([]() { return false; }, 10);
INFO_LOG(m_logger) << " ddsConnector destroy...";
m_ddsConnector.reset();
INFO_LOG(m_logger) << " ddsConnector destroy...DONE";
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("ModuleTest_ExplosionCluster", "log4cxx_aes.props");
// aesModuleTestAesOthers -v2 test_long -e42 -d139
// ^^^ ^^^^ // no space!!!
// aesModuleTestAesOthers -v2 test_long -e42
// aesModuleTestAesOthers -v2 test_long -e42
// aesModuleTestAesOthers test_long -e42
// aesModuleTestAesOthers -e42
CmdLineParser options;
options.setUnitModuleTestMode(true);
if (!options.init(true)) // showHelp , exitOnBadOption
return -1;
if (options.exerciseId() <= 0)
{
fprintf(stderr, "ExerciseId not set!\n");
return -2;
}
std::cout << " DomainId = " << options.domainId() << " - ExerciseId = " << options.exerciseId() << std::endl;
if (options.isSet(QStringLiteral("exerciseid")))
--argc;
if (options.isSet(QStringLiteral("domainid")))
--argc;
const QStringList &slPositionalArgs = options.positionalArguments();
for (const QString &item : qAsConst(slPositionalArgs))
std::cout << " item = " << item << std::endl;
// // enable_create_bulk only if it´s explicitly called by user:
// ModuleTestAesOthers::m_enable_create_bulk = slPositionalArgs.contains(QStringLiteral("test_create_bulk"));
// std::cout << " enable_create_bulk = " << ModuleTestAesOthers::m_enable_create_bulk << std::endl;
ModuleTestAesOthers tc(options.domainId(), options.exerciseId());
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,174 @@
/**
* @RDE_HEADER@
* ModuleTest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <LtsCoordinate/lts_coordinate.h>
#include <AreaEffectSimulation/aes_ftc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/logger.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <ExerciseManagement/exm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <memory>
using namespace AreaEffectSimulation; // JUST FOR LOGGER_T
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(DomainId | ExerciseId | NoVersion))
{
}
};
class ModuleTestAesOthers : public QObject
, protected ParticipantManagement::Interface::Ftc::SetSimulatedPositionReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineRemoveReplyHandler
{
Q_OBJECT
public:
ModuleTestAesOthers(int domainId, int exerciseId);
protected:
void onSetSimulatedPositionReply(const Core::FunctionReplyObject &replyObject) override;
void onBoobyTrapModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onBoobyTrapRemoveReply(const Core::FunctionReplyObject &replyObject) override;
void onOffRouteMineModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onOffRouteMineRemoveReply(const Core::FunctionReplyObject &replyObject) override;
void boobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType);
void offRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &aesItemView, DataModification modType);
// pre-condition
void setupParticipants();
bool checkParticipant_18();
bool tryRemoveOldBoobyTraps();
// helper
bool waitForData(const std::function<bool()> &func, int retryCount = 10);
void waitForEffectStatesBT(int lineNumber, int durPrep = 5, int durAct = 60, int durExp = 30);
void waitForEffectStatesORM(int lineNumber, int durPrep = 5, int durAct = 60, int durExp = 30);
int doMoveParticipant(int participantId, const QString &newPositionMGRS);
int doBoobyTrapModifyRequest(const BoobyTrap &btData);
int doOffRouteMineModifyRequest(const OffRouteMine &offRouteMine);
void test_boobyTrap_02_IMPL(const QString &name,
const LtsCoordTrans::LtsCoordinate &impactCoord,
double rotationAngle,
const LtsCoordTrans::LtsCoordinate &particpantCoord);
private Q_SLOTS:
//
// TestCases
//
// void initTestCase_data(); // will be called to create a global test data table.
void initTestCase(); // will be called before the first test function is executed.
void init(); // will be called before each test function is executed.
// void cleanup(); // will be called after each test function is executed.
void dumpCnt();
void test_justSetupParticipants();
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
// !!! assume ONE elevationMap: Altmark_dt1.tif !!!
// boobyTrap: VLad1, VLad2
void test_boobyTrap_01();
void test_boobyTrap_02_Q1();
void test_boobyTrap_02_Q2();
void test_boobyTrap_02_Q3();
void test_boobyTrap_02_Q4();
// offRoute: SchAbwMi, SchAbwRiMi
// // void test_offRouteMine_01(); // not implemeneted yet
void test_offRouteMine_02();
// will be called after the last test function was executed:
void cleanupTestCase(); // will be called after the last test function was executed.
public:
static bool m_enable_create_bulk;
private:
int m_domainId{-1};
int m_exerciseId{-1};
int m_traingAreaId{-1};
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_aesFtc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
BoobyTrap m_dataBoobyTrap;
OffRouteMine m_dataOffRouteMine;
bool m_replyError{false};
struct counterDds
{
// AesItem
int cntBoobyTrap{0};
int cntOffRouteMine{0};
// AesItemView
int cntItemView{0};
void reset()
{
cntBoobyTrap = 0;
cntOffRouteMine = 0;
cntItemView = 0;
}
}
m_counterDds;
LOGGER_T m_logger;
};
@@ -0,0 +1,267 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_convert_mine_area.h"
#include <QTimer>
#include <QLineF>
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <ngldb/dbtransaction.h>
#include <ngldb/nsqlquery.h>
#include <ngldb/dbconnection.h>
#include <ngldb/sql_query_janitor.h>
#include <QtSql/QSqlError>
#include <QVariant>
#include <AreaEffectSimulation/aes_db_structs.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <nglsettings/nglsettings_connections.h>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
namespace AreaEffectSimulation
{
ConvertMineArea::ConvertMineArea(QObject *parent)
: QObject(parent)
, m_query(nullptr)
, m_exerciseId(-1)
, m_bEmitMessageOnError(false)
, m_bCanHandleSchemas(true)
, m_ownDatabaseConnection(true)
, m_logger(getLogger(QStringLiteral("ConvertMineArea")))
{}
bool ConvertMineArea::init(int exerciseId)
{
m_exerciseId = exerciseId;
NGL::Settings *s = new NGL::Settings(NGL::findProjectConfigFile());
const QString dbUser(QStringLiteral("ets_exm"));
const QString dbPasswd(QStringLiteral("ets11de"));
const QString hostname(s->databaseHostname());
const QString driver(s->databaseDriver());
const QString dbname(s->databaseName());
bool ok = initDB(dbUser, dbPasswd, driver, hostname, dbname);
if (!ok)
{
ERROR_LOG(m_logger) << "Error opening db, exiting..";
return false;
}
ERROR_LOG(m_logger) << "Database open";
return true;
}
bool ConvertMineArea::initDB(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName)
{
m_ownDatabaseConnection = true;
m_dbConnection = DBConnection::open(m_logger, dbUser, dbPasswd, dbDriver, dbHost, dbName, schemaName());
return initDBInternal();
}
bool ConvertMineArea::initDBInternal()
{
QSqlDriver *driver = m_dbConnection.driver();
if (!driver)
{
WARNING_LOG(m_logger) << "No SQL driver loaded - no database available!";
return false;
}
const QString driverName = m_dbConnection.driverName();
DEBUG_LOG(m_logger) << "Connected to database " << m_dbConnection.databaseName() << "@" << m_dbConnection.hostName()
<< ", schema: " << schemaName() << ", used driver: " << driverName;
if (!m_dbConnection.isOpen())
{
WARNING_LOG(m_logger) << "Database connection is no open - can not initialize database interface";
return false;
}
if (driverName == QLatin1String("QSQLITE"))
m_bCanHandleSchemas = false;
m_query = new NSqlQuery(m_dbConnection, m_logger);
return true;
}
QString ConvertMineArea::schemaName() const
{
if (!m_bCanHandleSchemas)
return QString();
if (m_exerciseId <= 0)
return QStringLiteral("aes");
return DBCommon::getExerciseSchemaName(m_exerciseId);
}
QString ConvertMineArea::dbUser() const
{
if (m_exerciseId > 0)
return QStringLiteral("ets_ex_00");
else
return QStringLiteral("ets_") + schemaName();
}
//
// MineArea
//
QString ConvertMineArea::getTableName(const QString &tableName) const
{
if (!m_bCanHandleSchemas)
return tableName;
if (m_exerciseId > 0)
return DBCommon::getExerciseTableName(tableName, m_exerciseId);
return QStringLiteral("aes.") + tableName;
}
bool ConvertMineArea::tryConvertMineArea()
{
const QString tableName1(getTableName(QStringLiteral("aes_mine_area")));
const QString stmt1(QStringLiteral("SELECT id, density FROM ") + tableName1);
m_query->prepare(stmt1);
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
QMap<int,double> densityMap;
while (m_query->next())
{
int i = 0;
int aesId = m_query->value(i++).toInt();
double density = m_query->value(i++).toInt();
densityMap.insert(aesId, density);
}
const QString tableName(getTableName(QStringLiteral("aes_mine_area_segment")));
const QString stmt(QStringLiteral("SELECT id, mine_area_id, element_number, start_position_x, start_position_y, rotation_angle, mine_count"
" FROM ") + tableName);
m_query->prepare(stmt);
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
return false;
}
Database::AesMineAreaSegment data;
QVector<Database::AesMineAreaSegment> vecMineArea;
while (m_query->next())
{
int i = 0;
data.id = m_query->value(i++).toInt();
data.mineAreaId = m_query->value(i++).toInt();
data.elementNumber = m_query->value(i++).toInt();
data.startPosition.rx() = m_query->value(i++).toDouble();
data.startPosition.ry() = m_query->value(i++).toDouble();
double rotationAngle = m_query->value(i++).toDouble();
int mineCount = m_query->value(i++).toInt();
LtsCoordTrans::UtmCoordinate startPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(data.startPosition);
QLineF line;
line.setP1({startPositionUtm.x, startPositionUtm.y});
line.setLength(mineCount / densityMap.value(data.mineAreaId));
line.setAngle(rotationAngle);
QPointF p2 = line.p2();
LtsCoordTrans::UtmCoordinate endPositionUtm = startPositionUtm;
endPositionUtm.x = p2.x();
endPositionUtm.y = p2.y();
data.endPosition = endPositionUtm.geographic();
DEBUG_LOG(m_logger) << "aesId: " << data.mineAreaId << ", id: " << data.id;
DEBUG_LOG(m_logger) << "EndpositionUtm: x: " << endPositionUtm.x << ", y: " << endPositionUtm.y;
DEBUG_LOG(m_logger) << "Geographic: x: " << data.endPosition.x() << " y: " << data.endPosition.y();
vecMineArea.append(data);
}
const QString stmt3(QStringLiteral("UPDATE ") + tableName +
QStringLiteral(" SET"
" end_position_x = :end_position_x, end_position_y = :end_position_y"
" WHERE id = :id"));
if (!m_query->prepare(stmt3))
{
m_lastError = m_query->lastError();
ERROR_LOG(m_logger) << "lastError: " << m_lastError.text();
return false;
}
for ( const Database::AesMineAreaSegment &mineAreaSegment : vecMineArea)
{
m_query->bindValue(QStringLiteral(":end_position_x"), mineAreaSegment.endPosition.x());
m_query->bindValue(QStringLiteral(":end_position_y"), mineAreaSegment.endPosition.y());
m_query->bindValue(QStringLiteral(":id"), mineAreaSegment.id);
if (!m_query->exec(m_bEmitMessageOnError))
{
m_lastError = m_query->lastError();
ERROR_LOG(m_logger) << "lastError: " << m_lastError.text();
return false;
}
}
return true;
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
}
int exerciseId() const { return isSet(QLatin1String("e")) ? value(QLatin1String("e")).toInt() : -1; }
};
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("ConvertMineArea", 0 );
CmdLineParser options;
if (!options.init())
return -1;
AreaEffectSimulation::ConvertMineArea convMa(nullptr);
if (!convMa.init(options.exerciseId()))
{
ERROR_LOG(app.baseLogger()) << "Initialisation failed.";
return -2;
}
if (convMa.tryConvertMineArea())
ERROR_LOG(app.baseLogger()) << "Conversion for Exercise " << options.exerciseId() << " successful!";
else
ERROR_LOG(app.baseLogger()) << "Conversion failed.";
}
@@ -0,0 +1,63 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
#include <QSqlDriver>
#include <QSqlDatabase>
#include <QtSql/QSqlError>
#include <AreaEffectSimulation/aes_enums.hpp>
#include <AreaEffectSimulation/aes_db.h>
#include <AreaEffectSimulation/logger.h>
#include <ngldb/nsqlquery.h>
namespace AreaEffectSimulation
{
class ConvertMineArea : public QObject
{
Q_OBJECT
public:
explicit ConvertMineArea(QObject *parent);
bool init(int exerciseId);
bool tryConvertMineArea();
QString getTableName(const QString &tableName) const;
bool initDB(const QString &dbUser,
const QString &dbPasswd,
const QString &dbDriver,
const QString &dbHost,
const QString &dbName);
bool initDBInternal();
QString schemaName() const;
QString dbUser() const;
const QSqlDatabase &database() const { return m_dbConnection; }
void setEmitMessageOnError(bool bEmit) ;
QSqlError getLastError() const;
private:
QSqlDatabase m_dbConnection;
NSqlQuery *m_query;
QSqlError m_lastError;
int m_exerciseId;
bool m_bEmitMessageOnError;
bool m_bCanHandleSchemas;
bool m_ownDatabaseConnection;
LOGGER_T m_logger;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,227 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_air_support_impact.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
// single fire charge types = area fire
static const int s_ChargeTypeId1 = 471;
static const int s_ChargeTypeId2 = 472;
TestAirSupportImpact::TestAirSupportImpact(QObject *parent)
: TestBase("Test.ExplosionSingle", parent)
{}
bool TestAirSupportImpact::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataExplosionSingle();
m_asData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::airSupportImpactChanged,
this, &TestAirSupportImpact::airSupportImpactChanged);
return true;
}
bool TestAirSupportImpact::checkBaseData(const AreaEffectSimulation::AirSupportImpact &a,
const AreaEffectSimulation::AirSupportImpact &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}
return true;
}
//
// ExplosionSingle
//
void TestAirSupportImpact::tryCreate()
{
m_asData = AreaEffectSimulation::AirSupportImpact();
AreaEffectSimulation::AirSupportImpact asData;
asData.id(0);
asData.name("chris");
asData.effectStart((time(nullptr) + 4) * 1000); // msec
asData.chargeStruct().chargeTypeId(345863487);
asData.impactPosition(Core::GeoPosition2D(0.15525, 0.926080));
asData.partyId(7);
asData.participantId(1234);
asData.externalId(1);
asData.flightTime(123);
INFO_LOG(m_logger) << "trying to send explosionSingleCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->airSupportImpactModifyRequest(
AirSupportImpactModifyRequest(
0,
asData.name(),
asData.effectStart(),
asData.chargeStruct().chargeTypeId(),
asData.impactPosition(),
asData.partyId(),
asData.participantId(),
asData.flightTime(),
asData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
}
// now with correct charge type
m_bExpectedFailure = false;
m_expectedAesId = -1;
asData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
m_bIsCreatedRequest = true;
if (!m_ftc->airSupportImpactModifyRequest(
AirSupportImpactModifyRequest(
0,
asData.name(),
asData.effectStart(),
asData.chargeStruct().chargeTypeId(),
asData.impactPosition(),
asData.partyId(),
asData.participantId(),
asData.flightTime(),
asData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_asData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object";
failExit();
return;
}
if (!checkBaseData(m_asData, asData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestAirSupportImpact::tryModify);
INFO_LOG(m_logger) << "tryCreateExplosionSingle successful";
}
void TestAirSupportImpact::tryModify()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_asData.name("chris2");
m_asData.effectStart(m_asData.effectStart().get() + 2000);
m_asData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_asData.impactPosition(Core::GeoPosition2D(0.15525, 0.92607));
m_asData.partyId(8);
m_asData.participantId(1235);
m_asData.flightTime(321);
const AreaEffectSimulation::AirSupportImpact asData(m_asData);
m_asData = AreaEffectSimulation::AirSupportImpact();
m_asData.id(asData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->airSupportImpactModifyRequest(
AirSupportImpactModifyRequest(
asData.id(),
asData.name(),
asData.effectStart(),
asData.chargeStruct().chargeTypeId(),
asData.impactPosition(),
asData.partyId(),
asData.participantId(),
asData.flightTime(),
asData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_asData.chargeStruct().chargeTypeId() == s_ChargeTypeId2;}))
{
ERROR_LOG(m_logger) << "Error updating single explosion object";
failExit();
return;
}
if (!checkBaseData(m_asData, asData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestAirSupportImpact::waitExpire);
INFO_LOG(m_logger) << "tryModifyExplosionSingle successful";
}
void TestAirSupportImpact::waitExpire()
{
INFO_LOG(m_logger) << "waiting until explosion single expires";
if (!waitForData([this]() { return m_asData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of explosion single with id " << m_asData.id();
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "waitExpireExplosionSingle successful";
}
void TestAirSupportImpact::airSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType)
{
if (m_asData.id() == -1)
return;
onAesChanged(data, modType, m_asData);
}
void TestAirSupportImpact::onAirSupportImpactModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestAirSupportImpact::onAirSupportImpactRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestAirSupportImpact; });
}
@@ -0,0 +1,43 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestAirSupportImpact final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::AirSupportImpactModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::AirSupportImpactRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestAirSupportImpact(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreate(); }
private Q_SLOTS:
void tryCreate();
void tryModify();
void waitExpire();
protected Q_SLOTS:
void airSupportImpactChanged(const AreaEffectSimulation::AirSupportImpact &data, DataModification modType);
protected:
void onAirSupportImpactModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onAirSupportImpactRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::AirSupportImpact &a,
const AreaEffectSimulation::AirSupportImpact &b) const;
private:
AreaEffectSimulation::AirSupportImpact m_asData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,206 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_base.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QTimer>
#include <QtTest/QTest>
namespace AreaEffectSimulation
{
TestBase::TestBase(const char *loggerName, QObject *parent)
: QObject(parent)
, m_expectedAesId(-1)
, m_bIsCreatedRequest(false)
, m_bExpectedFailure(false)
, m_replyError(false)
, m_bTestsSuccessful(true)
, m_testId(0)
, m_aesTypeName(QString::fromLatin1(loggerName).splitRef(QLatin1Char('.')).last().toString())
, m_logger(getLogger(loggerName))
{}
bool TestBase::init(int domainId, int exerciseId, int testId)
{
INFO_LOG(m_logger) << "init begin";
m_testId = testId;
// initialize DDS
m_ddsConnector.reset(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Exercise, exerciseId));
if (!m_ddsConnector->init())
return false;
m_ddsConnector->initPublisherSubscriber();
INFO_LOG(m_logger) << "init create ftc";
m_ftc = AreaEffectSimulation::Interface::Ftc::create(m_ddsConnector);
INFO_LOG(m_logger) << "init init ftc";
bool bRet = m_ftc->enableAll();
m_dtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsConnector);
bRet = bRet && m_dtc->enableAll();
INFO_LOG(m_logger) << "init end - bRet = " << bRet;
return bRet;
}
bool TestBase::waitForData(const std::function<bool()> &func)
{
// wait for data
for (int i = 0; i < 100; i++)
{
if (func())
return true;
QTest::qWait(100);
}
return func();
}
bool TestBase::checkAesItem(const AreaEffectSimulation::AesItem &a,
const AreaEffectSimulation::AesItem &b) const
{
if (a.name() != b.name())
{
WARNING_LOG(m_logger) << "name does not match: " << a.name() << " - " << b.name();
return false;
}
if (a.effectStart() != b.effectStart())
{
WARNING_LOG(m_logger) << "effectStart does not match: " << a.effectStart() << " - " << b.effectStart();
return false;
}
if (a.chargeStruct().chargeTypeId() != b.chargeStruct().chargeTypeId())
{
WARNING_LOG(m_logger) << "chargeTypeId does not match: " << a.chargeStruct().chargeTypeId() << " - " << b.chargeStruct().chargeTypeId();
return false;
}
if (a.partyId() != b.partyId())
{
WARNING_LOG(m_logger) << "partyId does not match: " << a.partyId() << " - " << b.partyId();
return false;
}
if (a.participantId() != b.participantId())
{
WARNING_LOG(m_logger) << "participantId does not match: " << a.participantId() << " - " << b.participantId();
return false;
}
return true;
}
void TestBase::failExit()
{
ERROR_LOG(m_logger) << "fail exit!";
m_bTestsSuccessful = false;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
void TestBase::onAesCreateReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got create " << m_aesTypeName << " reply with id " << replyObject.id()
<< ", error = " << replyObject.error()
<< " and errorString = '" << replyObject.translated() << "'";
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got create " << m_aesTypeName << " reply error: " << replyObject();
else
m_expectedAesId = replyObject.id(); // only works as expected because reply is sent before publishing data occours
}
void TestBase::onAesModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_expectedAesId != replyObject.id())
return;
INFO_LOG(m_logger) << "Got modify " << m_aesTypeName << " reply with id " << replyObject.id()
<< ", error = " << replyObject.error()
<< " and errorString = '" << replyObject.translated() << "'";
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got modify " << m_aesTypeName << " reply error: " << replyObject();
else
m_expectedAesId = replyObject.id();
}
void TestBase::onAesRemoveReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got remove " << m_aesTypeName << " reply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got remove " << m_aesTypeName << " reply error: " << replyObject();
else
m_expectedAesId = replyObject.id();
}
void TestBase::onAesTriggerReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got trigger " << m_aesTypeName << " reply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got remove " << m_aesTypeName << " reply error: " << replyObject();
else
m_expectedAesId = replyObject.id();
}
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
addOption( { QStringList() << QLatin1String("t") << QLatin1String("testId")
, QLatin1String("TestId.")
, QLatin1String("Default: 0")
, QLatin1String("0")
} );
}
int testId() const { return isSet(QLatin1String("t")) ? value(QLatin1String("t")).toInt() : 0; }
};
// ------------------------------------------------------------------
int mainBase(int argc, char **argv,
const std::function<TestBase*()> &createFunc)
{
NGLCoreApplication app(argc, argv);
app.createLogger("AES", "log4cxx_aes.props");
CmdLineParser options;
if (!options.init())
return -1;
if (options.exerciseId() <= 0)
{
fprintf(stderr, "ExerciseId not set!\n");
return -2;
}
int iRet = -1;
{
std::unique_ptr <TestBase> test(createFunc());
if (!test->init(options.domainId(), options.exerciseId(), options.testId()))
return iRet;
QTimer::singleShot(0, test.get(), &TestBase::startTest);
STARTUP_LOG(app.baseLogger());
app.exec();
iRet = test->testsSuccessful() ? 0 : 1;
fprintf(stderr, "%d\n", iRet);
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,83 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_ftc.h>
#include <AreaEffectSimulation/aes_dtc.h>
#include <AreaEffectSimulation/logger.h>
#include <QObject>
#include <DDS/DDSConnector/dds_connector.h>
#include <memory>
#include <AreaEffectSimulation/aes_test_base_export.h>
namespace AreaEffectSimulation
{
class AES_TEST_BASE_EXPORT TestBase : public QObject
{
Q_OBJECT
protected:
explicit TestBase(const char *loggerName, QObject *parent);
public:
bool testsSuccessful() const { return m_bTestsSuccessful; }
virtual bool init(int domainId, int exerciseId, int testId);
public Q_SLOTS:
virtual void startTest() = 0;
protected:
static bool waitForData(const std::function<bool()> &func);
bool checkAesItem(const AreaEffectSimulation::AesItem &a,
const AreaEffectSimulation::AesItem &b) const;
void failExit();
void onAesCreateReply(const Core::FunctionReplyObject &replyObject);
void onAesModifyReply(const Core::FunctionReplyObject &replyObject);
void onAesRemoveReply(const Core::FunctionReplyObject &replyObject);
void onAesTriggerReply(const Core::FunctionReplyObject &replyObject);
template <typename T>
void onAesChanged(const T &dataIn, DataModification modType, T &dataOut)
{
INFO_LOG(m_logger) << "Got " << m_aesTypeName << " with mod type " << (int)modType
<< ", id: " << dataIn.id() << ", chargeType: " << dataIn.chargeStruct().chargeTypeId()
<< ", effectState: " << dataIn.effectStateId();
if (m_expectedAesId == dataIn.id())
{
DEBUG_LOG(m_logger) << "Expected aes id - updating data for " << m_aesTypeName;
dataOut = dataIn;
}
if (modType == DataModification::eRemoved && dataOut.id() == dataIn.id())
{
DEBUG_LOG(m_logger) << "Removed " << m_aesTypeName << " with id " << dataIn.id();
m_expectedAesId = dataIn.id();
}
}
protected:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<AreaEffectSimulation::Interface::Ftc> m_ftc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_dtc;
int m_expectedAesId; //!< contains the id of the created aes
bool m_bIsCreatedRequest;
bool m_bExpectedFailure;
bool m_replyError;
bool m_bTestsSuccessful;
int m_testId;
QString m_aesTypeName;
LOGGER_T m_logger;
};
int AES_TEST_BASE_EXPORT mainBase(int argc, char **argv,
const std::function<TestBase*()> &createFunc);
} // namespace AreaEffectSimulation
@@ -0,0 +1,331 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_bccontamination.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 341;
static const int s_ChargeTypeId2 = 342;
TestBCContamination::TestBCContamination(QObject *parent)
: TestBase("Test.BCContamination", parent)
{}
bool TestBCContamination::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataBCContamination();
m_bcData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged,
this, &TestBCContamination::bCContaminationChanged);
return true;
}
bool TestBCContamination::checkBaseData(const AreaEffectSimulation::BCContamination &a,
const AreaEffectSimulation::BCContamination &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}
if (a.impactRadius() != b.impactRadius())
{
WARNING_LOG(m_logger) << "impactRadius does not match: " << a.impactRadius() << " - " << b.impactRadius();
return false;
}
return true;
}
//
// BCContamination
//
void TestBCContamination::tryCreateBCContamination()
{
m_bcData = AreaEffectSimulation::BCContamination();
AreaEffectSimulation::BCContamination bcData;
bcData.id(0);
bcData.name("chris");
bcData.effectStart((time(nullptr) + 4) * 1000); // msec
bcData.effectDuration(10234); // msec
bcData.chargeStruct().chargeTypeId(345863487);
bcData.impactPosition(Core::GeoPosition2D(0.16, 0.92));
bcData.impactRadius(100); // 100m
bcData.partyId(2);
bcData.participantId(1234);
bcData.upperOffset(6.0);
bcData.lowerOffset(-1000.0);
INFO_LOG(m_logger) << "trying to send BCContaminationCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
if(!m_ftc->bCContaminationModifyRequest(
BCContaminationModifyRequest(
0,
bcData.name(),
bcData.effectStart(),
bcData.effectDuration(),
bcData.chargeStruct().chargeTypeId(),
bcData.impactPosition(),
bcData.impactRadius(),
AesWeatherSource::AwsStatic,
bcData.partyId(),
bcData.participantId(),
dds::core::optional<double>(), // propagationDirection
dds::core::optional<double>(), // windSpeed
dds::core::optional<Core::GeoPosition2D>(), // weatherPosition
dds::core::optional<rti::core::int64>(), // weatherTimestamp
dds::core::optional<long long>(), // externalId
bcData.upperOffset(),
bcData.lowerOffset(),
dds::core::optional<bool>()),
this))
{
ERROR_LOG(m_logger) << "Error during BCContaminationCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating bc contamination object (try with error)";
failExit();
return;
}
// now with correct charge type
m_bExpectedFailure = false;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
bcData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
if (!m_ftc->bCContaminationModifyRequest(
BCContaminationModifyRequest(
0,
bcData.name(),
bcData.effectStart(),
bcData.effectDuration(),
bcData.chargeStruct().chargeTypeId(),
bcData.impactPosition(),
bcData.impactRadius(),
AesWeatherSource::AwsStatic,
bcData.partyId(),
bcData.participantId(),
dds::core::optional<double>(),
dds::core::optional<double>(),
dds::core::optional<Core::GeoPosition2D>(),
dds::core::optional<rti::core::int64>(),
dds::core::optional<long long>(),
bcData.upperOffset(),
bcData.lowerOffset(),
dds::core::optional<bool>()),
this))
{
ERROR_LOG(m_logger) << "Error during BCContaminationCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_bcData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating bc contamination object";
failExit();
return;
}
if (!checkBaseData(m_bcData, bcData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestBCContamination::tryModifyBCContamination);
INFO_LOG(m_logger) << "tryCreateBCContamination successful";
}
void TestBCContamination::tryModifyBCContamination()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_bcData.name("chris2");
m_bcData.effectStart(m_bcData.effectStart().get() + 2000);
m_bcData.effectDuration(3200); // msec
m_bcData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_bcData.impactPosition(Core::GeoPosition2D(0.17, 0.93));
m_bcData.partyId(3);
m_bcData.participantId(1235);
m_bcData.upperOffset(6.0);
m_bcData.lowerOffset(false);
const AreaEffectSimulation::BCContamination bcData(m_bcData);
m_bcData = AreaEffectSimulation::BCContamination();
m_bcData.id(bcData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->bCContaminationModifyRequest(
BCContaminationModifyRequest(
bcData.id(),
bcData.name(),
bcData.effectStart(),
bcData.effectDuration(),
bcData.chargeStruct().chargeTypeId(),
bcData.impactPosition(),
bcData.impactRadius(),
AesWeatherSource::AwsStatic,
bcData.partyId(),
bcData.participantId(),
dds::core::optional<double>(),
dds::core::optional<double>(),
dds::core::optional<Core::GeoPosition2D>(),
dds::core::optional<rti::core::int64>(),
dds::core::optional<rti::core::int64>(),
bcData.upperOffset(),
bcData.lowerOffset(),
dds::core::optional<bool>()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_bcData.chargeStruct().chargeTypeId() == s_ChargeTypeId2;}))
{
ERROR_LOG(m_logger) << "Error updating bc contamination object";
failExit();
return;
}
if (!checkBaseData(m_bcData, bcData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestBCContamination::waitExpireBCContamination);
INFO_LOG(m_logger) << "tryModifyBCContamination successful";
}
void TestBCContamination::waitExpireBCContamination()
{
INFO_LOG(m_logger) << "waiting until bc contamination expires";
if (!waitForData([this]() { return m_bcData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of bc contamination with id " << m_bcData.id();
failExit();
return;
}
QTimer::singleShot(0, this, &TestBCContamination::tryRemoveBCContamination);
INFO_LOG(m_logger) << "waitExpireBCContamination successful";
}
void TestBCContamination::tryRemoveBCContamination()
{
INFO_LOG(m_logger) << "tryRemoveBCContamination start";
AreaEffectSimulation::BCContamination bcData(m_bcData);
bcData.name("RemoveTest");
bcData.effectStart(0); // not set
m_bcData = AreaEffectSimulation::BCContamination();
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
bcData.upperOffset(6.0);
bcData.lowerOffset(false);
if (!m_ftc->bCContaminationModifyRequest(
BCContaminationModifyRequest(
0,
bcData.name(),
bcData.effectStart(),
bcData.effectDuration(),
bcData.chargeStruct().chargeTypeId(),
bcData.impactPosition(),
bcData.impactRadius(),
AesWeatherSource::AwsStatic,
bcData.partyId(),
bcData.participantId(),
dds::core::optional<double>(),
dds::core::optional<double>(),
dds::core::optional<Core::GeoPosition2D>(),
dds::core::optional<rti::core::int64>(),
dds::core::optional<rti::core::int64>(),
bcData.upperOffset(),
bcData.lowerOffset(),
dds::core::optional<bool>()),
this))
{
ERROR_LOG(m_logger) << "Error during bCContaminationModifyRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_bcData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating bc contamination object";
failExit();
return;
}
m_expectedAesId = -1;
if (!m_ftc->bCContaminationRemoveRequest(Core::RemoveElementRequest(m_bcData.id()), this))
{
ERROR_LOG(m_logger) << "Error during BCContaminationRemoveRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_bcData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error removing bc contamination object";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryRemoveBCContamination successful";
}
void TestBCContamination::bCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType)
{
if (m_bcData.id() == -1)
return;
onAesChanged(data, modType, m_bcData);
}
void TestBCContamination::onBCContaminationModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestBCContamination::onBCContaminationRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestBCContamination; });
}
@@ -0,0 +1,44 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestBCContamination final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::BCContaminationModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BCContaminationRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestBCContamination(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateBCContamination(); }
private Q_SLOTS:
void tryCreateBCContamination();
void tryModifyBCContamination();
void waitExpireBCContamination();
void tryRemoveBCContamination();
protected Q_SLOTS:
void bCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType);
protected:
void onBCContaminationModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onBCContaminationRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::BCContamination &a,
const AreaEffectSimulation::BCContamination &b) const;
private:
AreaEffectSimulation::BCContamination m_bcData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,323 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_booby_trap.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 251;
static const int s_ChargeTypeId2 = 252;
TestBoobyTrap::TestBoobyTrap(QObject *parent)
: TestBase("Test.BoobyTrap", parent)
{}
bool TestBoobyTrap::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataBoobyTrap();
m_btData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
this, &TestBoobyTrap::boobyTrapChanged);
return true;
}
bool TestBoobyTrap::checkBaseData(const AreaEffectSimulation::BoobyTrap &a,
const AreaEffectSimulation::BoobyTrap &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}
if (a.impactRadius() != b.impactRadius())
{
WARNING_LOG(m_logger) << "impactRadius does not match: " << a.impactRadius() << " - " << b.impactRadius();
return false;
}
return true;
}
//
// BoobyTrap
//
void TestBoobyTrap::tryCreateBoobyTrap()
{
m_btData = AreaEffectSimulation::BoobyTrap();
AreaEffectSimulation::BoobyTrap btData;
btData.id(0);
btData.name("BoobyTrap1");
btData.effectStart((time(nullptr) + 4) * 1000); // msec
btData.effectDuration(10234); // msec
btData.chargeStruct().chargeTypeId(3456789);
btData.impactPosition(Core::GeoPosition2D(0.1547, 0.926));
btData.impactRadius(800); // 100m
btData.triggerRadius(200);
btData.rotationAngle(45);
btData.centralAngle(90);
btData.partyId(2);
//btData.participantId(1234);
INFO_LOG(m_logger) << "trying to send BoobyTrapCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
if (!m_ftc->boobyTrapModifyRequest(
BoobyTrapModifyRequest(
0,
btData.name(),
btData.effectDuration(),
btData.chargeStruct().chargeTypeId(),
btData.impactPosition(),
btData.impactRadius(),
btData.triggerRadius(),
btData.rotationAngle().get(),
btData.centralAngle().get(),
btData.effectStart().get(),
btData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during BoobyTrapCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating booby trap object (try with error)";
failExit();
return;
}
// now with correct charge type
m_bExpectedFailure = false;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
btData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
if (!m_ftc->boobyTrapModifyRequest(
BoobyTrapModifyRequest(
0,
btData.name(),
btData.effectDuration(),
btData.chargeStruct().chargeTypeId(),
btData.impactPosition(),
btData.impactRadius(),
btData.triggerRadius(),
btData.rotationAngle().get(),
btData.centralAngle().get(),
btData.effectStart().get(),
btData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during BoobyTrapCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_btData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating booby trap object";
failExit();
return;
}
if (!checkBaseData(m_btData, btData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestBoobyTrap::tryModifyBoobyTrap);
INFO_LOG(m_logger) << "tryCreateBoobyTrap successful";
}
void TestBoobyTrap::tryModifyBoobyTrap()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_btData.name("Booby Trap 2");
m_btData.effectStart(m_btData.effectStart().get() + 2000);
m_btData.effectDuration(3200); // msec
m_btData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_btData.impactPosition(Core::GeoPosition2D(0.1547, 0.926));
m_btData.partyId(3);
//m_btData.participantId(1235);
const AreaEffectSimulation::BoobyTrap btData(m_btData);
m_btData = AreaEffectSimulation::BoobyTrap();
m_btData.id(btData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->boobyTrapModifyRequest(
BoobyTrapModifyRequest(
btData.id(),
btData.name(),
btData.effectDuration(),
btData.chargeStruct().chargeTypeId(),
btData.impactPosition(),
btData.impactRadius(),
btData.triggerRadius(),
btData.rotationAngle().get(),
btData.centralAngle().get(),
btData.effectStart().get(),
btData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_btData.chargeStruct().chargeTypeId() == s_ChargeTypeId2;}))
{
ERROR_LOG(m_logger) << "Error updating booby trap object";
failExit();
return;
}
if (!checkBaseData(m_btData, btData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestBoobyTrap::tryTriggerBoobyTrap);
INFO_LOG(m_logger) << "tryModifyBoobyTrap successful";
}
void TestBoobyTrap::tryTriggerBoobyTrap()
{
INFO_LOG(m_logger) << "trigger booby trap -> expires";
BoobyTrapTriggerRequest req;
req.id(m_btData.id());
req.triggered(true);
if (!m_ftc->boobyTrapTriggerRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting trigger data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestBoobyTrap::waitExpireBoobyTrap);
INFO_LOG(m_logger) << "waitExpireBoobyTrap successful";
}
void TestBoobyTrap::waitExpireBoobyTrap()
{
INFO_LOG(m_logger) << "waiting until booby trap expires";
if (!waitForData([this]() { return m_btData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of booby trap with id " << m_btData.id();
failExit();
return;
}
//QTimer::singleShot(0, this, &TestBoobyTrap::tryRemoveBoobyTrap);
INFO_LOG(m_logger) << "waitExpireBoobyTrap successful";
}
void TestBoobyTrap::tryRemoveBoobyTrap()
{
INFO_LOG(m_logger) << "tryRemoveBoobyTrap start";
AreaEffectSimulation::BoobyTrap btData(m_btData);
btData.name("RemoveTest");
btData.effectStart(0); // not set
m_btData = AreaEffectSimulation::BoobyTrap();
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
if (!m_ftc->boobyTrapModifyRequest(
BoobyTrapModifyRequest(
0,
btData.name(),
btData.effectDuration(),
btData.chargeStruct().chargeTypeId(),
btData.impactPosition(),
btData.impactRadius(),
btData.triggerRadius(),
btData.rotationAngle().get(),
btData.centralAngle().get(),
btData.effectStart().get(),
btData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during BoobyTrapModifyRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_btData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating booby trap object";
failExit();
return;
}
m_expectedAesId = -1;
if (!m_ftc->boobyTrapRemoveRequest(Core::RemoveElementRequest(m_btData.id()), this))
{
ERROR_LOG(m_logger) << "Error during BoobyTrapRemoveRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_btData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error removing booby trap object";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryRemoveBoobyTrap successful";
}
void TestBoobyTrap::boobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType)
{
if (m_btData.id() == -1)
return;
onAesChanged(data, modType, m_btData);
}
void TestBoobyTrap::onBoobyTrapModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestBoobyTrap::onBoobyTrapRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
void TestBoobyTrap::onBoobyTrapTriggerReply(const Core::FunctionReplyObject &replyObject)
{
onAesTriggerReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestBoobyTrap; });
}
@@ -0,0 +1,47 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestBoobyTrap final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::BoobyTrapTriggerReplyHandler
{
Q_OBJECT
public:
explicit TestBoobyTrap(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateBoobyTrap(); }
private Q_SLOTS:
void tryCreateBoobyTrap();
void tryModifyBoobyTrap();
void tryTriggerBoobyTrap();
void waitExpireBoobyTrap();
void tryRemoveBoobyTrap();
protected Q_SLOTS:
void boobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType);
protected:
void onBoobyTrapModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onBoobyTrapRemoveReply(const Core::FunctionReplyObject &replyObject) override;
void onBoobyTrapTriggerReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::BoobyTrap &a,
const AreaEffectSimulation::BoobyTrap &b) const;
private:
AreaEffectSimulation::BoobyTrap m_btData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,241 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_charge_administration.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
static const int s_ChargeAmount = 600;
TestChargeAdministration::TestChargeAdministration(QObject *parent)
: TestBase("Test.ChargeAdministration", parent)
{
}
bool TestChargeAdministration::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_testId = testId;
m_caData.id(-1);
DEBUG_LOG(m_logger) << "Test Id = " << m_testId;
m_dtc->waitForHistoricalDataChargeAdministration();
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::chargeAdministrationChanged, this, &TestChargeAdministration::chargeAdministrationChanged);
return true;
}
bool TestChargeAdministration::checkBaseData(const AreaEffectSimulation::ChargeAdministration &a, const AreaEffectSimulation::ChargeAdministration &b) const
{
if (a.amount() != b.amount())
{
WARNING_LOG(m_logger) << "Amount does not match: " << a.amount() << " - " << b.amount();
return false;
}
if (a.planned() != b.planned())
{
WARNING_LOG(m_logger) << "Planned does not match: " << a.planned() << " - " << b.planned();
return false;
}
if (a.chargeTypeId() != b.chargeTypeId())
{
WARNING_LOG(m_logger) << "ChargeTypeId does not match: " << a.chargeTypeId() << " - " << b.chargeTypeId();
return false;
}
if (a.partyId() != b.partyId())
{
WARNING_LOG(m_logger) << "PartyId does not match: " << a.partyId() << " - " << b.partyId();
return false;
}
if (a.used() != b.used())
{
WARNING_LOG(m_logger) << "Used does not match: " << a.used() << " - " << b.used();
return false;
}
return true;
}
//
// ChargeAdministration
//
void TestChargeAdministration::tryCreateChargeAdministration()
{
m_caData = AreaEffectSimulation::ChargeAdministration();
m_expectedAesId = -1;
AreaEffectSimulation::ChargeAdministration esData;
esData.chargeTypeId(471);
esData.partyId(2);
esData.amount(450);
esData.effectSourceId(ProjectDataManagement::EffectSource::ES_Internal);
INFO_LOG(m_logger) << "trying to send ChargeAdministrationCreateRequest()";
m_replyError = false;
m_bIsCreatedRequest = true;
// amountChangedByOperator=true
if (!m_ftc->chargeAdministrationModifyRequest(ChargeAdministrationModifyRequest(0,
esData.chargeTypeId(),
esData.partyId(),
esData.effectSourceId(),
esData.amount(),
true),
this))
{
ERROR_LOG(m_logger) << "Error during ChargeAdministrationCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_caData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating charge admin object";
failExit();
return;
}
if (!checkBaseData(m_caData, esData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestChargeAdministration::tryModifyChargeAdministration);
INFO_LOG(m_logger) << "tryCreateChargeAdministration successful";
}
void TestChargeAdministration::tryModifyChargeAdministration()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_caData.chargeTypeId(471);
m_caData.partyId(2);
m_caData.amount(600);
const AreaEffectSimulation::ChargeAdministration esData(m_caData);
m_caData = AreaEffectSimulation::ChargeAdministration();
m_caData.id(esData.id());
m_bIsCreatedRequest = false;
// amountChangedByOperator=true
if (!m_ftc->chargeAdministrationModifyRequest(ChargeAdministrationModifyRequest(esData.id(),
esData.chargeTypeId(),
esData.partyId(),
esData.effectSourceId(),
esData.amount(),
true),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_caData.amount() == s_ChargeAmount; }))
{
ERROR_LOG(m_logger) << "Error updating charge admin object";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryModifyChargeAdministration successful";
}
//
// ChargeAdministration
//
void TestChargeAdministration::chargeAdministrationChanged(const AreaEffectSimulation::ChargeAdministration &data, DataModification modType)
{
if (m_caData.id() < 0)
return;
INFO_LOG(m_logger) << "Got ChargeAdministrationData with mod type " << (int)modType << ", id: " << data.id() << ", name: " << data.chargeTypeId()
<< ", amount: " << data.amount();
if ((modType == DataModification::eAdded && m_caData.id() == 0) || (modType == DataModification::eModified && m_caData.id() == data.id()))
{
DEBUG_LOG(m_logger) << "** Updating m_caData";
m_caData = data;
}
}
void TestChargeAdministration::onChargeAdministrationModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestChargeAdministration::tryCreateCA()
{
tryCreateChargeAdministration(371, 3, 1383);
tryCreateChargeAdministration(371, 2, 1382);
tryCreateChargeAdministration(372, 3, 1393);
tryCreateChargeAdministration(372, 2, 1392);
tryCreateChargeAdministration(381, 3, 9999);
tryCreateChargeAdministration(382, 3, 9999);
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
void TestChargeAdministration::tryCreateChargeAdministration(int chargeTypeId, int partyId, int amount)
{
m_caData = AreaEffectSimulation::ChargeAdministration();
AreaEffectSimulation::ChargeAdministration esData;
esData.chargeTypeId(chargeTypeId);
esData.partyId(partyId);
esData.amount(amount);
esData.amountChangedByOperator(true);
INFO_LOG(m_logger) << "trying to send ChargeAdministrationCreateRequest(int,int,int)";
m_replyError = false;
m_bIsCreatedRequest = true;
// amountChangedByOperator=true
if (!m_ftc->chargeAdministrationModifyRequest(ChargeAdministrationModifyRequest(0,
esData.chargeTypeId(),
esData.partyId(),
esData.effectSourceId(),
esData.amount(),
true),
this))
{
ERROR_LOG(m_logger) << "Error during ChargeAdministrationCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_caData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating charge admin object";
failExit();
return;
}
if (!checkBaseData(m_caData, esData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
INFO_LOG(m_logger) << "tryCreateChargeAdministration successful";
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestChargeAdministration; });
}
@@ -0,0 +1,41 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestChargeAdministration final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::ChargeAdministrationModifyReplyHandler
{
Q_OBJECT
public:
explicit TestChargeAdministration(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
bool checkBaseData(const AreaEffectSimulation::ChargeAdministration &a,
const AreaEffectSimulation::ChargeAdministration &b) const;
public Q_SLOTS:
void startTest() override { if (m_testId == 0) tryCreateChargeAdministration(); else tryCreateCA();}
private Q_SLOTS:
void tryCreateChargeAdministration();
void tryCreateCA();
void tryCreateChargeAdministration(int, int, int);
void tryModifyChargeAdministration();
protected Q_SLOTS:
void chargeAdministrationChanged(const AreaEffectSimulation::ChargeAdministration &data, DataModification modType);
protected:
void onChargeAdministrationModifyReply(const Core::FunctionReplyObject &replyObject) override;
private:
AreaEffectSimulation::ChargeAdministration m_caData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,245 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_danger_area.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
const std::string TestDangerArea::s_name = "chris2";
TestDangerArea::TestDangerArea(QObject *parent)
: TestBase("Test.DangerArea", parent)
{}
bool TestDangerArea::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataDangerArea();
m_daData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::dangerAreaChanged,
this, &TestDangerArea::dangerAreaChanged);
return true;
}
bool TestDangerArea::checkBaseData(const AreaEffectSimulation::DangerArea &a,
const AreaEffectSimulation::DangerArea &b) const
{
if (a.name() != b.name())
{
WARNING_LOG(m_logger) << "Name does not macht: " << a.name() << " - " << b.name();
return false;
}
if (a.effectStart() != b.effectStart())
{
WARNING_LOG(m_logger) << "StartTime does not match: " << a.effectStart() << " - " << b.effectStart();
return false;
}
for (size_t i = 0; i<a.segments().size() && i<b.segments().size(); ++i)
{
if (a.segments()[i] != b.segments()[i])
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.segments()[i] << " - " << b.segments()[i];
return false;
}
}
return true;
}
//
// DangerArea
//
void TestDangerArea::tryCreateDangerArea()
{
m_daData = AreaEffectSimulation::DangerArea();
AreaEffectSimulation::DangerArea esData;
esData.id(0);
esData.name("chris");
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.sampleTime(time(nullptr) *1000);
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.17001, 0.93001));
segments.emplace_back(Core::GeoPosition2D(0.17002, 0.93002));
segments.emplace_back(Core::GeoPosition2D(0.17003, 0.93003));
INFO_LOG(m_logger) << "trying to send dangerAreaCreateRequest()";
m_replyError = false;
m_bIsCreatedRequest = true;
if (!m_ftc->dangerAreaModifyRequest(
DangerAreaModifyRequest(
0,
esData.name(),
0, // Effect Duration
311, // Charge Type Id -> 311 = Danger Area Dummy
esData.effectStart(),
segments,
dds::core::optional<int32_t>(), // Party Id
dds::core::optional<int32_t>(), // Player Id
dds::core::optional<int32_t>() // associatedAesId
),
this))
{
ERROR_LOG(m_logger) << "Error during dangerAreaCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_daData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating danger area object";
failExit();
return;
}
if (!checkBaseData(m_daData, esData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestDangerArea::tryModifyDangerArea);
INFO_LOG(m_logger) << "tryCreateDangerArea successful";
}
void TestDangerArea::tryModifyDangerArea()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_daData.name(s_name);
m_daData.effectStart(time(nullptr)+ 6 * 1000);
const AreaEffectSimulation::DangerArea esData(m_daData);
m_daData = AreaEffectSimulation::DangerArea();
m_daData.id(esData.id());
// resize to 2 elements
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.17004, 0.93004));
segments.emplace_back(Core::GeoPosition2D(0.17004, 0.93005));
m_bIsCreatedRequest = false;
if (!m_ftc->dangerAreaModifyRequest(
DangerAreaModifyRequest(
esData.id(),
esData.name(),
0, // Effect Duration
311, // Charge Type Id -> 311 = Danger Area Dummy
esData.effectStart(),
segments,
dds::core::optional<int32_t>(), // Party Id
dds::core::optional<int32_t>(), // Player Id
dds::core::optional<int32_t>() // associatedAesId
),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_daData.segments().size() == 2;}))
{
ERROR_LOG(m_logger) << "Error updating danger area object";
failExit();
return;
}
// increase to 4 elements
segments.resize(4);
segments[0] = Core::GeoPosition2D(0.17004, 0.93004);
segments[1] = Core::GeoPosition2D(0.17004, 0.93005);
segments[2] = Core::GeoPosition2D(0.17005, 0.93006);
segments[3] = Core::GeoPosition2D(0.17006, 0.93007);
if (!m_ftc->dangerAreaModifyRequest(
DangerAreaModifyRequest(
esData.id(),
esData.name(),
0, // Effect Duration
311, // Charge Type Id -> 311 = Danger Area Dummy
esData.effectStart(),
segments,
dds::core::optional<int32_t>(), // Party Id
dds::core::optional<int32_t>(), // Player Id
dds::core::optional<int32_t>() // associatedAesId
),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_daData.segments().size() == 4;}))
{
ERROR_LOG(m_logger) << "Error updating danger area object";
failExit();
return;
}
QTimer::singleShot(0, this, &TestDangerArea::tryRemoveDangerArea);
INFO_LOG(m_logger) << "tryModifyDangerArea successful";
}
void TestDangerArea::tryRemoveDangerArea()
{
m_expectedAesId = -1;
if (!m_ftc->dangerAreaRemoveRequest(
Core::RemoveElementRequest( m_daData.id()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_daData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error removing bc contamination object " << m_daData.id() << " != " << m_expectedAesId;
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryRemoveDangerArea successful";
}
//
// DangerArea
//
void TestDangerArea::dangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification modType)
{
if (m_daData.id() == -1)
return;
onAesChanged(data, modType, m_daData);
}
void TestDangerArea::onDangerAreaModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestDangerArea::onDangerAreaRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestDangerArea; });
}
@@ -0,0 +1,44 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestDangerArea final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::DangerAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::DangerAreaRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestDangerArea(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
bool checkBaseData(const AreaEffectSimulation::DangerArea &a,
const AreaEffectSimulation::DangerArea &b) const;
public Q_SLOTS:
void startTest() override { tryCreateDangerArea(); }
private Q_SLOTS:
void tryCreateDangerArea();
void tryModifyDangerArea();
void tryRemoveDangerArea();
protected Q_SLOTS:
void dangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification modType);
protected:
void onDangerAreaModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onDangerAreaRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
AreaEffectSimulation::DangerArea m_daData;
static const std::string s_name;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,428 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_explosion_area.h"
#include <QCoreApplication>
#include <QTimer>
#include <QTest>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 371;
static const int s_ChargeTypeId2 = 372;
TestExplosionArea::TestExplosionArea(QObject *parent)
: TestBase("Test.ExplosionArea", parent)
{}
bool TestExplosionArea::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataExplosionArea();
m_eaData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
this, &TestExplosionArea::explosionAreaChanged);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
this, &TestExplosionArea::triggeredImpactsChanged);
return true;
}
bool TestExplosionArea::checkBaseData(const AreaEffectSimulation::ExplosionArea &a,
const AreaEffectSimulation::ExplosionArea &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.area() != b.area())
{
WARNING_LOG(m_logger) << "area does not match: " << a.area() << " - " << b.area();
return false;
}
if (a.cannonCount() != b.cannonCount())
{
WARNING_LOG(m_logger) << "cannonCount does not match: " << a.cannonCount() << " - " << b.cannonCount();
return false;
}
if (a.cannonRounds() != b.cannonRounds())
{
WARNING_LOG(m_logger) << "cannonCount does not match: " << a.cannonCount() << " - " << b.cannonCount();
return false;
}
return true;
}
void TestExplosionArea::tryCreateExplosionArea()
{
INFO_LOG(m_logger) << "tryCreateExplosionArea start";
m_eaData = AreaEffectSimulation::ExplosionArea();
AreaEffectSimulation::ExplosionArea eaData;
eaData.id(0);
eaData.name("af1");
eaData.effectStart((time(nullptr) + 4) * 1000); // msec
eaData.effectDuration(10000); // 10 seconds
eaData.chargeStruct().chargeTypeId(345863487);
eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.16, 0.92), 50, 60, 70));
eaData.partyId(2);
eaData.participantId(1234);
eaData.cannonCount(1);
eaData.cannonRounds(2);
eaData.externalId(2);
INFO_LOG(m_logger) << "trying to send explosionAreaCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->explosionAreaModifyRequest(
ExplosionAreaModifyRequest(
0,
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.chargeStruct().chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
eaData.cannonCount(),
eaData.cannonRounds(),
eaData.fireRequestTime(),
eaData.altitude(),
eaData.purpose(),
eaData.effectMarkingInTa(),
eaData.externalId()
),
this))
{
ERROR_LOG(m_logger) << "Error during explosionAreaCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating area explosion object (try with error)";
failExit();
return;
}
// now with correct charge type
m_bExpectedFailure = false;
m_expectedAesId = -1;
eaData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
m_bIsCreatedRequest = true;
if (!m_ftc->explosionAreaModifyRequest(
ExplosionAreaModifyRequest(
0,
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.chargeStruct().chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
eaData.cannonCount(),
eaData.cannonRounds(),
eaData.fireRequestTime(),
eaData.altitude(),
eaData.purpose(),
eaData.effectMarkingInTa(),
eaData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionAreaCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating area explosion object";
failExit();
return;
}
if (!checkBaseData(m_eaData, eaData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionArea::tryModifyExplosionArea);
INFO_LOG(m_logger) << "tryCreateExplosionArea successful";
}
void TestExplosionArea::tryModifyExplosionArea()
{
INFO_LOG(m_logger) << "tryModifyExplosionArea start";
m_eaData.name("af2");
m_eaData.effectStart(m_eaData.effectStart().get() + 3000);
m_eaData.effectDuration(9000); // 9 sec
m_eaData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.165, 0.921), 50, 60, 70));
m_eaData.partyId(3);
m_eaData.participantId(1235);
m_eaData.cannonCount(2);
m_eaData.cannonRounds(2);
m_eaData.effectMarkingInTa(true);
const AreaEffectSimulation::ExplosionArea eaData(m_eaData);
m_eaData = AreaEffectSimulation::ExplosionArea();
m_eaData.id(eaData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->explosionAreaModifyRequest(
ExplosionAreaModifyRequest(
eaData.id(),
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.chargeStruct().chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
eaData.cannonCount(),
eaData.cannonRounds(),
eaData.fireRequestTime(),
eaData.altitude(),
eaData.purpose(),
eaData.effectMarkingInTa(),
eaData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.chargeStruct().chargeTypeId() == s_ChargeTypeId2; }))
{
ERROR_LOG(m_logger) << "Error updating area explosion object";
failExit();
return;
}
if (!checkBaseData(m_eaData, eaData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionArea::tryWaitForActivationExplosionArea);
INFO_LOG(m_logger) << "tryModifyExplosionArea successful";
}
void TestExplosionArea::tryWaitForActivationExplosionArea()
{
INFO_LOG(m_logger) << "tryWaitForActivationExplosionArea start";
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
DEBUG_LOG(m_logger) << "Effect State = " << m_eaData.effectStateId();
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionArea::tryPauseResumeExplosionArea);
INFO_LOG(m_logger) << "tryWaitForActivationExplosionArea end";
}
void TestExplosionArea::tryPauseResumeExplosionArea()
{
INFO_LOG(m_logger) << "tryPauseResumeExplosionArea start";
// try to resume but is already active -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error waiting for reply error while switching to pause of area explosion";
failExit();
return;
}
// and now pause
m_bExpectedFailure = false;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsPaused; }))
{
ERROR_LOG(m_logger) << "Error while waiting for pause of area explosion";
failExit();
return;
}
// pause again -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
failExit();
return;
}
// and resume execution
m_bExpectedFailure = false;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
INFO_LOG(m_logger) << "tryPauseResumeExplosionArea end";
QTimer::singleShot(0, this, &TestExplosionArea::checkTriggeredExplosions);
}
void TestExplosionArea::checkTriggeredExplosions()
{
INFO_LOG(m_logger) << "checkTriggeredExplosions start";
// first we should wait for Activation
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
// attn: msb is bit 7
checkTriggeredExplosionsHelper(0x70); // 0b01110000
checkTriggeredExplosionsHelper(0x30); // 0b00110000
checkTriggeredExplosionsHelper(0x10); // 0b00010000
checkTriggeredExplosionsHelper(0x00); // 0b00000000
// last step: wait for expiry
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired; }))
{
ERROR_LOG(m_logger) << "Error while waiting for expiring of area explosion";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "checkTriggeredExplosions end";
}
void TestExplosionArea::checkTriggeredExplosionsHelper(int bitfield)
{
// since we've 9 seconds duration and the first hit is at 0, every 2.5sec should be a new TriggeredExplosions telegram
QElapsedTimer et;
et.start();
if (!waitForData([this, bitfield]() { return m_tiData.bitfield()[0] == bitfield; }))
{
ERROR_LOG(m_logger) << "Error while waiting for triggered explosison telegram with bitfield 0x" << QByteArray::number(bitfield, 16);
failExit();
return;
}
// 500ms delay through dds allowed :)
if (et.elapsed() > 3000)
{
ERROR_LOG(m_logger) << "Something went wrong - waited more than 3 seconds for next impact trigger";
failExit();
}
}
void TestExplosionArea::explosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType)
{
if (m_eaData.id() == -1)
return;
onAesChanged(data, modType, m_eaData);
}
void TestExplosionArea::triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
{
if (m_eaData.id() != data.aesItemId() || modType == DataModification::eRemoved)
return;
INFO_LOG(m_logger) << "Got TriggeredImpacts data - bitfield[0] = 0x" << QByteArray::number(static_cast<int>(data.bitfield()[0]), 16);
m_tiData = data;
}
void TestExplosionArea::onExplosionAreaModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestExplosionArea::onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got pause/resume reply explosion area with id " << replyObject.id()
<< ", error = " << replyObject.error()
<< " and errorString = '" << replyObject.translated() << "'";
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got pause/resume reply explosion area error: " << replyObject();
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestExplosionArea; });
}
@@ -0,0 +1,48 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestExplosionArea final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaPauseResumeReplyHandler
{
Q_OBJECT
public:
explicit TestExplosionArea(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateExplosionArea(); }
private Q_SLOTS:
void tryCreateExplosionArea();
void tryModifyExplosionArea();
void tryWaitForActivationExplosionArea();
void tryPauseResumeExplosionArea();
void checkTriggeredExplosions();
void checkTriggeredExplosionsHelper(int bitfield);
protected Q_SLOTS:
void explosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType);
void triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType);
protected:
void onExplosionAreaModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::ExplosionArea &a,
const AreaEffectSimulation::ExplosionArea &b) const;
private:
AreaEffectSimulation::ExplosionArea m_eaData;
AreaEffectSimulation::TriggeredImpacts m_tiData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,411 @@
/**
* @RDE_HEADER@
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_explosion_area_bulk.h"
#include <QCoreApplication>
#include <QTimer>
#include <QTest>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 371;
static const int s_ChargeTypeId2 = 372;
TestExplosionAreaBulk::TestExplosionAreaBulk(QObject *parent)
: TestBase("Test.ExplosionAreaBulk", parent)
{}
bool TestExplosionAreaBulk::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataExplosionArea();
m_eaData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
this, &TestExplosionAreaBulk::explosionAreaChanged);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
this, &TestExplosionAreaBulk::triggeredImpactsChanged);
return true;
}
bool TestExplosionAreaBulk::checkBaseData(const AreaEffectSimulation::ExplosionArea &a,
const AreaEffectSimulation::ExplosionArea &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.area() != b.area())
{
WARNING_LOG(m_logger) << "area does not match: " << a.area() << " - " << b.area();
return false;
}
if (a.cannonCount() != b.cannonCount())
{
WARNING_LOG(m_logger) << "cannonCount does not match: " << a.cannonCount() << " - " << b.cannonCount();
return false;
}
if (a.cannonRounds() != b.cannonRounds())
{
WARNING_LOG(m_logger) << "cannonCount does not match: " << a.cannonCount() << " - " << b.cannonCount();
return false;
}
return true;
}
void TestExplosionAreaBulk::tryCreateExplosionArea()
{
const int count = 100;
const int effectDurationMesc = 60 * 1000; // 10 seconds
const double lonOffset = 0.0001;
for (int i=0; i<count;++i)
{
QString objName = QString(QStringLiteral("af_bulk_%1")).arg(i);
INFO_LOG(m_logger) << "tryCreateExplosionArea start, objName=" << objName;
m_eaData = AreaEffectSimulation::ExplosionArea();
AreaEffectSimulation::ExplosionArea eaData;
eaData.id(0);
eaData.name(objName.toStdString().c_str());
eaData.effectStart((time(nullptr) + 4) * 1000); // msec
eaData.effectDuration(effectDurationMesc); // 10 seconds
eaData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
// Bremen : 0.155 ; 0.926
eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.15 + (lonOffset*i), 0.926), 500, 600, 70));
eaData.partyId((i%3) + 2);
eaData.participantId(1234);
eaData.cannonCount(10);
eaData.cannonRounds(10);
eaData.externalId(2);
INFO_LOG(m_logger) << "trying to send explosionAreaCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->explosionAreaModifyRequest(
ExplosionAreaModifyRequest(
0,
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.chargeStruct().chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
eaData.cannonCount(),
eaData.cannonRounds(),
eaData.fireRequestTime(),
eaData.altitude(),
eaData.purpose(),
eaData.effectMarkingInTa(),
eaData.externalId()
),
this))
{
ERROR_LOG(m_logger) << "Error during explosionAreaCreateRequest()";
failExit();
return;
}
// if (!waitForData([this]() { return m_eaData.id() == m_expectedAesId; }))
// {
// ERROR_LOG(m_logger) << "Error creating area explosion object";
// failExit();
// return;
// }
// if (!checkBaseData(m_eaData, eaData))
// {
// ERROR_LOG(m_logger) << "Creation data does not match published data";
// failExit();
// return;
// }
INFO_LOG(m_logger) << "tryCreateExplosionArea successful objName=" << objName;
} //for
QTimer::singleShot(0, this, &TestExplosionAreaBulk::tryWaitForActivationExplosionArea);
}
void TestExplosionAreaBulk::tryModifyExplosionArea()
{
INFO_LOG(m_logger) << "tryModifyExplosionArea start";
m_eaData.name("af2");
m_eaData.effectStart(m_eaData.effectStart().get() + 3000);
m_eaData.effectDuration(9000); // 9 sec
m_eaData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.165, 0.921), 50, 60, 70));
m_eaData.partyId(3);
m_eaData.participantId(1235);
m_eaData.cannonCount(2);
m_eaData.cannonRounds(2);
m_eaData.effectMarkingInTa(true);
const AreaEffectSimulation::ExplosionArea eaData(m_eaData);
m_eaData = AreaEffectSimulation::ExplosionArea();
m_eaData.id(eaData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->explosionAreaModifyRequest(
ExplosionAreaModifyRequest(
eaData.id(),
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.chargeStruct().chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
eaData.cannonCount(),
eaData.cannonRounds(),
eaData.fireRequestTime(),
eaData.altitude(),
eaData.purpose(),
eaData.effectMarkingInTa(),
eaData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.chargeStruct().chargeTypeId() == s_ChargeTypeId2; }))
{
ERROR_LOG(m_logger) << "Error updating area explosion object";
failExit();
return;
}
if (!checkBaseData(m_eaData, eaData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionAreaBulk::tryWaitForActivationExplosionArea);
INFO_LOG(m_logger) << "tryModifyExplosionArea successful";
}
void TestExplosionAreaBulk::tryWaitForActivationExplosionArea()
{
INFO_LOG(m_logger) << "tryWaitForActivationExplosionArea start";
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
DEBUG_LOG(m_logger) << "Effect State = " << m_eaData.effectStateId();
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionAreaBulk::tryPauseResumeExplosionArea);
INFO_LOG(m_logger) << "tryWaitForActivationExplosionArea end";
}
void TestExplosionAreaBulk::tryPauseResumeExplosionArea()
{
INFO_LOG(m_logger) << "tryPauseResumeExplosionArea start";
// try to resume but is already active -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error waiting for reply error while switching to pause of area explosion";
failExit();
return;
}
// and now pause
m_bExpectedFailure = false;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsPaused; }))
{
ERROR_LOG(m_logger) << "Error while waiting for pause of area explosion";
failExit();
return;
}
// pause again -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
failExit();
return;
}
// and resume execution
m_bExpectedFailure = false;
if (!m_ftc->explosionAreaPauseResumeRequest(
ExplosionAreaPauseResumeRequest(
m_eaData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionAreaPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
INFO_LOG(m_logger) << "tryPauseResumeExplosionArea end";
QTimer::singleShot(0, this, &TestExplosionAreaBulk::checkTriggeredExplosions);
}
void TestExplosionAreaBulk::checkTriggeredExplosions()
{
INFO_LOG(m_logger) << "checkTriggeredExplosions start";
// first we should wait for Activation
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
// attn: msb is bit 7
checkTriggeredExplosionsHelper(0x70); // 0b01110000
checkTriggeredExplosionsHelper(0x30); // 0b00110000
checkTriggeredExplosionsHelper(0x10); // 0b00010000
checkTriggeredExplosionsHelper(0x00); // 0b00000000
// last step: wait for expiry
if (!waitForData([this]() { return m_eaData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired; }))
{
ERROR_LOG(m_logger) << "Error while waiting for expiring of area explosion";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "checkTriggeredExplosions end";
}
void TestExplosionAreaBulk::checkTriggeredExplosionsHelper(int bitfield)
{
// since we've 9 seconds duration and the first hit is at 0, every 2.5sec should be a new TriggeredExplosions telegram
QElapsedTimer et;
et.start();
if (!waitForData([this, bitfield]() { return m_tiData.bitfield()[0] == bitfield; }))
{
ERROR_LOG(m_logger) << "Error while waiting for triggered explosison telegram with bitfield 0x" << QByteArray::number(bitfield, 16);
failExit();
return;
}
// 500ms delay through dds allowed :)
if (et.elapsed() > 3000)
{
ERROR_LOG(m_logger) << "Something went wrong - waited more than 3 seconds for next impact trigger";
failExit();
}
}
void TestExplosionAreaBulk::explosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType)
{
if (m_eaData.id() == -1)
return;
onAesChanged(data, modType, m_eaData);
}
void TestExplosionAreaBulk::triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
{
if (m_eaData.id() != data.aesItemId() || modType == DataModification::eRemoved)
return;
if (data.bitfield().size() > 0)
INFO_LOG(m_logger) << "Got TriggeredImpacts data - bitfield[0] = 0x" << QByteArray::number(static_cast<int>(data.bitfield()[0]), 16);
m_tiData = data;
}
void TestExplosionAreaBulk::onExplosionAreaModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestExplosionAreaBulk::onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got pause/resume reply explosion area with id " << replyObject.id()
<< ", error = " << replyObject.error()
<< " and errorString = '" << replyObject.translated() << "'";
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got pause/resume reply explosion area error: " << replyObject();
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestExplosionAreaBulk; });
}
@@ -0,0 +1,49 @@
/**
* @RDE_HEADER@
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestExplosionAreaBulk final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionAreaPauseResumeReplyHandler
{
Q_OBJECT
public:
explicit TestExplosionAreaBulk(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateExplosionArea(); }
private Q_SLOTS:
void tryCreateExplosionArea();
void tryModifyExplosionArea();
void tryWaitForActivationExplosionArea();
void tryPauseResumeExplosionArea();
void checkTriggeredExplosions();
void checkTriggeredExplosionsHelper(int bitfield);
protected Q_SLOTS:
void explosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType);
void triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType);
protected:
void onExplosionAreaModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionAreaPauseResumeReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::ExplosionArea &a,
const AreaEffectSimulation::ExplosionArea &b) const;
private:
AreaEffectSimulation::ExplosionArea m_eaData;
AreaEffectSimulation::TriggeredImpacts m_tiData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,479 @@
/**
* @RDE_HEADER@
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_explosion_cluster.h"
#include <ProjectDataManagement/pdm.hpp>
#include <QCoreApplication>
#include <QTimer>
#include <QTest>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 371;
static const int s_ChargeTypeId2 = 372;
static const int s_IgniterTypeProximity = 101;
static const int s_IgniterTypeImpact = 102;
TestExplosionCluster::TestExplosionCluster(QObject *parent)
: TestBase("Test.ExplosionCluster", parent)
{}
bool TestExplosionCluster::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataExplosionCluster();
m_ecData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionClusterChanged,
this, &TestExplosionCluster::explosionClusterChanged);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::triggeredImpactsChanged,
this, &TestExplosionCluster::triggeredImpactsChanged);
return true;
}
bool TestExplosionCluster::checkBaseData(const AreaEffectSimulation::ExplosionCluster &a,
const AreaEffectSimulation::ExplosionCluster &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.area() != b.area())
{
WARNING_LOG(m_logger) << "area does not match: " << a.area() << " - " << b.area();
return false;
}
return true;
}
void TestExplosionCluster::tryCreateExplosionCluster()
{
INFO_LOG(m_logger) << "tryCreateExplosionCluster start";
m_ecData = AreaEffectSimulation::ExplosionCluster();
AreaEffectSimulation::ExplosionCluster eaData;
eaData.id(0);
eaData.effectStart((time(nullptr) + 30) * 1000); // msec
QString name(QStringLiteral("ec__"));
name += QString::number(eaData.effectStart().get());
eaData.name(name.toStdString());
eaData.effectDuration(10000); // 10 seconds
eaData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
eaData.chargeStruct().igniterTypeId(s_IgniterTypeImpact);
// 32 U MD 93860 79903 -> 0.155484041, 0.92622146
// eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202560, 0.91611), 200, 50, 20)); // width, height, rotation
// eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202561, 0.916111), 200, 50, 20)); // width, height, rotation
// eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202562, 0.916112), 200, 50, 20)); // width, height, rotation
// eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202564, 0.916112), 200, 50, 20)); // width, height, rotation
// eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202562, 0.916115), 200, 50, 20)); // width, height, rotation
eaData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.202562, 0.916114), 30, 20, 20)); // width, height, rotation
eaData.mhPoints({{0.2025611, 0.9161111}, {0.2025611, 0.9161111}, {0.2025611, 0.9161111}}); // 3
eaData.partyId(3);
eaData.participantId(5);
eaData.externalId(0);
INFO_LOG(m_logger) << "trying to send explosionClusterCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
int igniterTypeId = s_IgniterTypeImpact;
if ((m_testId >= 0) && (m_testId <= 2) )
igniterTypeId = m_testId;
INFO_LOG(m_logger) << "m_testId " << m_testId << ", igniterType = " << igniterTypeId;
const std::string& properties = "{}";
if (!m_ftc->explosionClusterModifyRequest(
ExplosionClusterModifyRequest(
0,
eaData.name(),
eaData.element(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.effectSourceId(),
eaData.area(),
AreaEffectSimulation::AesExplosionGeometry_def::EgArea,
0, // zog
eaData.mhPoints().size(), // mhpCount;
dds::core::optional<double>(), // mhpRotation for ExplosionClusterOcModify only
3, // impactCount
{ 12345, // eaData.chargeTypeId(),
igniterTypeId, // eaData.igniterTypeId(),
0 // eaData.igniterFunctionId()
},
3, // firePurpose 3=Blenden mit Wirkung
// 25, // firing_elevation_angle
123, // peakHeight,
10 * 1000, // flightDuration [msec],
eaData.partyId(),
eaData.participantId(),
{}, // esmParticipantIds
eaData.externalId(),
-1, // eaData.launchVelocity(),
-1, // eaData.launchAngleDeg(),
-1, // eaData.dropAngle(),
-1, // eaData.tapTime(), // msec since epoch
-1, // eaData.lcAttack(), // fka lcPrf
-1, // eaData.lcTermination(),
-1, // eaData.cloudBase(), // Wolkenuntergrenze
eaData.effectMarkingInTa(),
properties),
this))
{
ERROR_LOG(m_logger) << "Error during explosionClusterCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating area explosion object (try with error)";
failExit();
return;
}
failExit();
/*
// now with correct charge type
m_bExpectedFailure = false;
m_expectedAesId = -1;
eaData.chargeTypeId(s_ChargeTypeId1);
m_bIsCreatedRequest = true;
if (!m_ftc->explosionClusterModifyRequest(
ExplosionClusterModifyRequest(
0,
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
1, // eaData.cannonCount(),
1, // eaData.cannonRounds(),
123,// eaData.fireRequestTime(),
// eaData.altitude(),
// eaData.purpose(),
// eaData.effectMarkingInTa(),
eaData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionClusterCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ecData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating area explosion object";
failExit();
return;
}
if (!checkBaseData(m_ecData, eaData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
// QTimer::singleShot(0, this, &TestExplosionCluster::tryModifyExplosionCluster);
INFO_LOG(m_logger) << "tryCreateExplosionCluster successful";
*/
}
/*
void TestExplosionCluster::tryModifyExplosionCluster()
{
INFO_LOG(m_logger) << "tryModifyExplosionCluster start";
m_ecData.name("af2");
m_ecData.effectStart(m_ecData.effectStart().get() + 3000);
m_ecData.effectDuration(9000); // 9 sec
m_ecData.chargeTypeId(s_ChargeTypeId2);
// 32 U MD 93860 79903 -> 0.155484041, 0.92622146
m_ecData.area(AreaEffectSimulation::Area(Core::GeoPosition2D(0.155484041, 0.92622146), 50, 60, 70));
waitForData
m_ecData.partyId(3);
m_ecData.participantId(5);
// m_ecData.cannonCount(2);
// m_ecData.cannonRounds(2);
m_ecData.effectMarkingInTa(true);
const AreaEffectSimulation::ExplosionCluster eaData(m_ecData);
m_ecData = AreaEffectSimulation::ExplosionCluster();
m_ecData.id(eaData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->explosionClusterModifyRequest(
ExplosionClusterModifyRequest(
eaData.id(),
eaData.name(),
eaData.effectStart(),
eaData.effectDuration(),
eaData.chargeTypeId(),
eaData.area(),
eaData.partyId(),
eaData.participantId(),
1,// eaData.cannonCount(),
1, // eaData.cannonRounds(),
123,// eaData.fireRequestTime(),
123,// eaData.fireRequestTime(),
eaData.subAreaCount(),
// eaData.altitude(),
// eaData.purpose(),
// eaData.effectMarkingInTa(),
eaData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_ecData.chargeTypeId() == s_ChargeTypeId2; }))
{
ERROR_LOG(m_logger) << "Error updating area explosion object";
failExit();
return;
}
if (!checkBaseData(m_ecData, eaData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionCluster::tryWaitForActivationExplosionCluster);
INFO_LOG(m_logger) << "tryModifyExplosionCluster successful";
}
*/
void TestExplosionCluster::tryWaitForActivationExplosionCluster()
{
INFO_LOG(m_logger) << "tryWaitForActivationExplosionCluster start";
if (!waitForData([this]() { return m_ecData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
DEBUG_LOG(m_logger) << "Effect State = " << m_ecData.effectStateId();
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionCluster::tryPauseResumeExplosionCluster);
INFO_LOG(m_logger) << "tryWaitForActivationExplosionCluster end";
}
void TestExplosionCluster::tryPauseResumeExplosionCluster()
{
INFO_LOG(m_logger) << "tryPauseResumeExplosionCluster start";
// try to resume but is already active -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionClusterPauseResumeRequest(
ExplosionClusterPauseResumeRequest(
m_ecData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionClusterPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error waiting for reply error while switching to pause of area explosion";
failExit();
return;
}
// and now pause
m_bExpectedFailure = false;
if (!m_ftc->explosionClusterPauseResumeRequest(
ExplosionClusterPauseResumeRequest(
m_ecData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionClusterPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_ecData.effectStateId() == AreaEffectSimulation::AesEffectState::EsPaused; }))
{
ERROR_LOG(m_logger) << "Error while waiting for pause of area explosion";
failExit();
return;
}
// pause again -> expected error
m_replyError = false;
m_bExpectedFailure = true;
if (!m_ftc->explosionClusterPauseResumeRequest(
ExplosionClusterPauseResumeRequest(
m_ecData.id(),
false,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionClusterPauseResumeRequest(false)";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
failExit();
return;
}
// and resume execution
m_bExpectedFailure = false;
if (!m_ftc->explosionClusterPauseResumeRequest(
ExplosionClusterPauseResumeRequest(
m_ecData.id(),
true,
dds::core::optional<rti::core::int64>(),
dds::core::optional<int32_t>(),
dds::core::optional<int32_t>()),
this))
{
ERROR_LOG(m_logger) << "Error calling explosionClusterPauseResumeRequest(true)";
failExit();
return;
}
if (!waitForData([this]() { return m_ecData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
INFO_LOG(m_logger) << "tryPauseResumeExplosionCluster end";
QTimer::singleShot(0, this, &TestExplosionCluster::checkTriggeredExplosions);
}
void TestExplosionCluster::checkTriggeredExplosions()
{
INFO_LOG(m_logger) << "checkTriggeredExplosions start";
// first we should wait for Activation
if (!waitForData([this]() { return m_ecData.effectStateId() == AreaEffectSimulation::AesEffectState::EsActivated; }))
{
ERROR_LOG(m_logger) << "Error while waiting for activation of area explosion";
failExit();
return;
}
// attn: msb is bit 7
checkTriggeredExplosionsHelper(0x70); // 0b01110000
checkTriggeredExplosionsHelper(0x30); // 0b00110000
checkTriggeredExplosionsHelper(0x10); // 0b00010000
checkTriggeredExplosionsHelper(0x00); // 0b00000000
// last step: wait for expiry
if (!waitForData([this]() { return m_ecData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired; }))
{
ERROR_LOG(m_logger) << "Error while waiting for expiring of area explosion";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "checkTriggeredExplosions end";
}
void TestExplosionCluster::checkTriggeredExplosionsHelper(int bitfield)
{
// since we've 9 seconds duration and the first hit is at 0, every 2.5sec should be a new TriggeredExplosions telegram
QElapsedTimer et;
et.start();
if (!waitForData([this, bitfield]() { return m_tiData.bitfield()[0] == bitfield; }))
{
ERROR_LOG(m_logger) << "Error while waiting for triggered explosison telegram with bitfield 0x" << QByteArray::number(bitfield, 16);
failExit();
return;
}
// 500ms delay through dds allowed :)
if (et.elapsed() > 3000)
{
ERROR_LOG(m_logger) << "Something went wrong - waited more than 3 seconds for next impact trigger";
failExit();
}
}
void TestExplosionCluster::explosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType)
{
if (m_ecData.id() == -1)
return;
onAesChanged(data, modType, m_ecData);
}
void TestExplosionCluster::triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType)
{
if (m_ecData.id() != data.aesItemId() || modType == DataModification::eRemoved)
return;
INFO_LOG(m_logger) << "Got TriggeredImpacts data - bitfield[0] = 0x" << QByteArray::number(static_cast<int>(data.bitfield()[0]), 16);
m_tiData = data;
}
void TestExplosionCluster::onExplosionClusterModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestExplosionCluster::onExplosionClusterPauseResumeReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got pause/resume reply explosion area with id " << replyObject.id()
<< ", error = " << replyObject.error()
<< " and errorString = '" << replyObject.translated() << "'";
m_replyError = replyObject.error();
if (m_replyError && !m_bExpectedFailure)
ERROR_LOG(m_logger) << "Got pause/resume reply explosion area error: " << replyObject();
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestExplosionCluster; });
}
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestExplosionCluster final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionClusterPauseResumeReplyHandler
{
Q_OBJECT
public:
explicit TestExplosionCluster(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateExplosionCluster(); }
private Q_SLOTS:
void tryCreateExplosionCluster();
// void tryModifyExplosionCluster();
void tryWaitForActivationExplosionCluster();
void tryPauseResumeExplosionCluster();
void checkTriggeredExplosions();
void checkTriggeredExplosionsHelper(int bitfield);
protected Q_SLOTS:
void explosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType);
void triggeredImpactsChanged(const AreaEffectSimulation::TriggeredImpacts &data, DataModification modType);
protected:
void onExplosionClusterModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionClusterPauseResumeReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::ExplosionCluster &a,
const AreaEffectSimulation::ExplosionCluster &b) const;
private:
AreaEffectSimulation::ExplosionCluster m_ecData;
AreaEffectSimulation::TriggeredImpacts m_tiData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,357 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_explosion_single.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
// single fire charge types = area fire
static const int s_ChargeTypeId1 = 371;
static const int s_ChargeTypeId2 = 372;
TestExplosionSingle::TestExplosionSingle(QObject *parent)
: TestBase("Test.ExplosionSingle", parent)
{}
bool TestExplosionSingle::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataExplosionSingle();
m_esData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
this, &TestExplosionSingle::explosionSingleChanged);
return true;
}
bool TestExplosionSingle::checkBaseData(const AreaEffectSimulation::ExplosionSingle &a,
const AreaEffectSimulation::ExplosionSingle &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}
return true;
}
//
// ExplosionSingle
//
void TestExplosionSingle::tryCreateExplosionSingle()
{
m_esData = AreaEffectSimulation::ExplosionSingle();
AreaEffectSimulation::ExplosionSingle esData;
esData.id(0);
esData.name("chris");
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.chargeStruct().chargeTypeId(345863487);
esData.impactPosition(Core::GeoPosition2D(0.16, 0.92));
esData.partyId(7);
esData.participantId(1234);
esData.externalId(1);
esData.effectMarkingInTa(false);
INFO_LOG(m_logger) << "trying to send explosionSingleCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->explosionSingleModifyRequest(
ExplosionSingleModifyRequest(
0,
esData.name(),
esData.effectStart(),
esData.effectSourceId(),
esData.chargeStruct().chargeTypeId(),
esData.impactPosition(),
esData.partyId(),
esData.participantId(),
esData.fireRequestTime(),
esData.altitude(),
esData.purpose(),
esData.effectMarkingInTa(),
esData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
}
// now with correct charge type
m_bExpectedFailure = false;
m_expectedAesId = -1;
esData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
esData.partyId(2);
m_bIsCreatedRequest = true;
if (!m_ftc->explosionSingleModifyRequest(
ExplosionSingleModifyRequest(
0,
esData.name(),
esData.effectStart(),
esData.effectSourceId(),
esData.chargeStruct().chargeTypeId(),
esData.impactPosition(),
esData.partyId(),
esData.participantId(),
esData.fireRequestTime(),
esData.altitude(),
esData.purpose(),
esData.effectMarkingInTa(),
esData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_esData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object";
failExit();
return;
}
if (!checkBaseData(m_esData, esData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionSingle::tryModifyExplosionSingle);
INFO_LOG(m_logger) << "tryCreateExplosionSingle successful";
}
void TestExplosionSingle::tryModifyExplosionSingle()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_esData.name("chris2");
m_esData.effectStart(m_esData.effectStart().get() + 2000);
m_esData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_esData.impactPosition(Core::GeoPosition2D(0.17, 0.93));
m_esData.partyId(3);
m_esData.participantId(1235);
const AreaEffectSimulation::ExplosionSingle esData(m_esData);
m_esData = AreaEffectSimulation::ExplosionSingle();
m_esData.id(esData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->explosionSingleModifyRequest(
ExplosionSingleModifyRequest(
esData.id(),
esData.name(),
esData.effectStart(),
esData.effectSourceId(),
esData.chargeStruct().chargeTypeId(),
esData.impactPosition(),
esData.partyId(),
esData.participantId(),
esData.fireRequestTime(),
esData.altitude(),
esData.purpose(),
esData.effectMarkingInTa(),
esData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_esData.chargeStruct().chargeTypeId() == s_ChargeTypeId2;}))
{
ERROR_LOG(m_logger) << "Error updating single explosion object";
failExit();
return;
}
if (!checkBaseData(m_esData, esData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestExplosionSingle::waitExpireExplosionSingle);
INFO_LOG(m_logger) << "tryModifyExplosionSingle successful";
}
void TestExplosionSingle::waitExpireExplosionSingle()
{
INFO_LOG(m_logger) << "waiting until explosion single expires";
if (!waitForData([this]() { return m_esData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of explosion single with id " << m_esData.id();
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "waitExpireExplosionSingle successful";
}
void TestExplosionSingle::explosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
{
if (m_esData.id() == -1)
return;
onAesChanged(data, modType, m_esData);
}
void TestExplosionSingle::onExplosionSingleModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestExplosionSingle::onExplosionSingleRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
//
// TestExplosionSingleTestVWM
//
TestExplosionSingleTestVWM::TestExplosionSingleTestVWM(QObject *parent)
: TestExplosionSingle(parent)
{
}
void TestExplosionSingleTestVWM::tryCreateExplosionSingleVWM(const std::string &esName,
int ammoId,
const Core::GeoPosition2D & position)
{
INFO_LOG(m_logger) << "tryCreateExplosionSingleVWM"
<< " name ='" << esName << "'"
<< " ammoId = " << ammoId
<< " position = " << position;
m_esData = AreaEffectSimulation::ExplosionSingle();
AreaEffectSimulation::ExplosionSingle esData;
esData.id(0);
esData.name(esName);
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.chargeStruct().chargeTypeId(ammoId);
esData.impactPosition(position);
esData.partyId(3); // 3 -> blau
esData.participantId(1234);
esData.externalId(1);
esData.effectMarkingInTa(false);
INFO_LOG(m_logger) << "trying to send explosionSingleCreateRequest()";
m_replyError = false;
m_bIsCreatedRequest = true;
m_bExpectedFailure = false;
m_expectedAesId = -1;
if (!m_ftc->explosionSingleModifyRequest(
ExplosionSingleModifyRequest(
0,
esData.name(),
esData.effectStart(),
esData.effectSourceId(),
esData.chargeStruct().chargeTypeId(),
esData.impactPosition(),
esData.partyId(),
esData.participantId(),
esData.fireRequestTime(),
esData.altitude(),
esData.purpose(),
esData.effectMarkingInTa(),
esData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
// TestBase::onAesCreateReply should update m_expectedAesId:
if (!waitForData([this]() { return m_esData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating single explosion object (try with error)";
}
INFO_LOG(m_logger) << "got answer, m_replyError=" << m_replyError
<< " m_expectedAesId=" << m_expectedAesId;
std::cerr << "got answer, m_replyError=" << m_replyError
<< " m_expectedAesId=" << m_expectedAesId << std::endl;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
void TestExplosionSingleTestVWM::onExplosionSingleCreateReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
// LOGGING: log4cxx_aes.props -> ctc_aes.log
// find name, ammoId, position
std::string name;
int ammoId = -1;
Core::GeoPosition2D position;
{
for (int i=1; i<argc; ++i)
{
if (strcmp(argv[i], "name") == 0)
name = argv[i+1];
if (strcmp(argv[i], "ammoId") == 0)
ammoId = atoi(argv[i+1]);
if (strcmp(argv[i], "x") == 0)
position.lon(atof(argv[i+1]));
if (strcmp(argv[i], "y") == 0)
position.lat(atof(argv[i+1]));
}
}
if (!name.empty())
{
std::cerr << " name = '" << name << "'"
<< " ammoId = '" << ammoId
<< " position = '" << position << std::endl;
std::function<AreaEffectSimulation::TestBase*()> createFunc = [name, ammoId, position]() {
auto ptr = new AreaEffectSimulation::TestExplosionSingleTestVWM;
ptr->setName(name);
ptr->setAmmoId(ammoId);
ptr->setPosition(position);
return ptr;
};
return AreaEffectSimulation::mainBase(argc, argv, createFunc);
} else {
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestExplosionSingle; });
}
}
@@ -0,0 +1,78 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestExplosionSingle : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::ExplosionSingleRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestExplosionSingle(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateExplosionSingle(); }
private Q_SLOTS:
void tryCreateExplosionSingle();
void tryModifyExplosionSingle();
void waitExpireExplosionSingle();
protected Q_SLOTS:
void explosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType);
protected:
void onExplosionSingleModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onExplosionSingleRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::ExplosionSingle &a,
const AreaEffectSimulation::ExplosionSingle &b) const;
protected:
AreaEffectSimulation::ExplosionSingle m_esData;
};
class TestExplosionSingleTestVWM final : public TestExplosionSingle
{
Q_OBJECT
public:
explicit TestExplosionSingleTestVWM(QObject *parent = nullptr);
void setName(const std::string &name) { m_name = name; }
void setAmmoId(int ammoId) { m_ammoId = ammoId; }
void setPosition(const Core::GeoPosition2D &position) { m_position = position; }
public Q_SLOTS:
void startTest() override
{
tryCreateExplosionSingleVWM(m_name, m_ammoId, m_position);
}
private Q_SLOTS:
void tryCreateExplosionSingleVWM(const std::string &esName = "Norbert",
int ammoId = 371,
const Core::GeoPosition2D &position = Core::GeoPosition2D(0.202603, 0.916049)
);
void onExplosionSingleCreateReply(const Core::FunctionReplyObject &replyObject);
private:
std::string m_name;
int m_ammoId{-1};
Core::GeoPosition2D m_position;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,128 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_gas_pipe.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
TestGasPipe::TestGasPipe(QObject *parent)
: TestBase("Test.GasPipe", parent)
, m_refPos(LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32UMD8000090000")))
{}
bool TestGasPipe::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
tryCreateGasPipe();
m_dtc->waitForHistoricalDataGasPipe();
m_esData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::gasPipeChanged,
this, &TestGasPipe::gasPipeChanged);
return true;
}
bool TestGasPipe::checkBaseData(const AreaEffectSimulation::GasPipe &a,
const AreaEffectSimulation::GasPipe &b) const
{
if (!checkAesItem(a, b))
return false;
/*if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}*/
return true;
}
static inline void setPos(std::vector<Core::GeoPosition2D> &cont, const QPointF &pos)
{
cont.emplace_back(Core::GeoPosition2D(pos.x(), pos.y()));
}
//
// GasPipe
//
void TestGasPipe::tryCreateGasPipe()
{
AreaEffectSimulation::GasPipeModifyRequest gp;
//AreaEffectSimulation::GasPipe gp;
gp.id(0);
gp.name("GasPipe");
gp.partyId(1);
setPos(gp.segments(), m_refPos.geographic(3, 3));
setPos(gp.segments(), m_refPos.geographic(4, 4));
setPos(gp.segments(), m_refPos.geographic(5, 3));
DEBUG_LOG(m_logger) << LOGFUNCTION << " GasPipe created. id: " << gp.id();
DEBUG_LOG(m_logger) << "request GasPipe";
if (!m_ftc->gasPipeModifyRequest( gp, this))
{
ERROR_LOG(m_logger) << "Error during explosionSingleCreateRequest()";
failExit();
return;
}
}
void TestGasPipe::tryModifyGasPipe()
{
INFO_LOG(m_logger) << "tryModifyGasPipe successful";
}
void TestGasPipe::waitExpireGasPipe()
{
INFO_LOG(m_logger) << "waiting until explosion single expires";
if (!waitForData([this]() { return m_esData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of explosion single with id " << m_esData.id();
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "waitExpireGasPipe successful";
}
void TestGasPipe::gasPipeChanged(const AreaEffectSimulation::GasPipe &data, DataModification modType)
{
if (m_esData.id() == -1)
return;
onAesChanged(data, modType, m_esData);
}
void TestGasPipe::onGasPipeModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestGasPipe::onGasPipeRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestGasPipe; });
}
@@ -0,0 +1,46 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QPointF>
namespace AreaEffectSimulation
{
class TestGasPipe final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::GasPipeModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::GasPipeRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestGasPipe(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateGasPipe(); }
private Q_SLOTS:
void tryCreateGasPipe();
void tryModifyGasPipe();
void waitExpireGasPipe();
protected Q_SLOTS:
void gasPipeChanged(const AreaEffectSimulation::GasPipe &data, DataModification modType);
protected:
void onGasPipeModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onGasPipeRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::GasPipe &a,
const AreaEffectSimulation::GasPipe &b) const;
private:
AreaEffectSimulation::GasPipe m_esData;
LtsCoordTrans::UtmCoordinate m_refPos;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,734 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_mine_area.h"
#include "Utils/polygons.h"
#include "qjsonobject.h"
#include <QCoreApplication>
#include <QJsonDocument>
#include <QTest>
#include <QTimer>
#include <LtsCoordTrans/lts_coord_trans.h>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 53; // Virtual AP
static const int s_ChargeTypeId2 = 40; // DUS AP
static const int s_ChargeTypeId3 = 213; // DUS-Mine
static const int s_ChargeTypeId4 = 240; // Concrete-Mine
TestMineArea::TestMineArea(QObject *parent)
: TestBase("Test.MineArea", parent)
{
}
bool TestMineArea::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataMineArea();
m_maData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::mineAreaChanged, this, &TestMineArea::mineAreaChanged);
return true;
}
void TestMineArea::startTest()
{
setSegmentForTesMineData();
const auto effectClusterList = m_dtc->effectClusters();
m_referencedAesItemId = 0;
for (const auto &ec : effectClusterList)
{
m_referencedAesItemId = ec.id();
}
if (m_testId <= 0)
tryCreateMineArea();
else
{
m_currentMineId = 12;
// Dus Mine Test
if (m_testId == 10)
tryCreateMineArea2(s_ChargeTypeId3);
// Concrete Mine
if (m_testId == 11)
tryCreateMineArea2(s_ChargeTypeId4);
if (m_testId == 12)
tryCreateMineArea2(215, 0.109); // Virt. Mine
if (m_testId == 15)
testMineData_11(); // Virt. Mine
if (m_testId == 16)
testAddMineData_11(); // Virt. Mine
if (m_testId == 17)
testMineData_21(); // Virt. Mine
if (m_testId == 18)
testAddMineData_21(); // Virt. Mine
}
}
bool TestMineArea::checkBaseData(const AreaEffectSimulation::MineArea &a, const AreaEffectSimulation::MineArea &b) const
{
if (!checkAesItem(a, b))
return false;
for (size_t i = 0; i < a.segments().size() && i < b.segments().size(); ++i)
{
if (a.segments()[i] != b.segments()[i])
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.segments()[i] << " - " << b.segments()[i];
return false;
}
}
return true;
}
//
// MineArea
//
void TestMineArea::tryCreateMineArea()
{
ERROR_LOG(m_logger) << " >>>> create Mine Area...";
m_maData = AreaEffectSimulation::MineArea();
AreaEffectSimulation::MineArea esData;
esData.id(0);
esData.name("chris");
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.chargeStruct().chargeTypeId(345863487); // Todo: Is this intended?
esData.effectDuration(400 * 1000);
esData.density(0.2);
esData.width(7.0);
esData.partyId(7);
esData.participantId(1234);
esData.layingProcedureType(AreaEffectSimulation::AesMaLayingProcedureType_def::LptArtillery);
esData.isLaidHidden(true);
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.17001, 0.93001));
segments.emplace_back(Core::GeoPosition2D(0.17002, 0.93002));
segments.emplace_back(Core::GeoPosition2D(0.17003, 0.93003));
const double distance = 100.0;
INFO_LOG(m_logger) << "trying to send mineAreaCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->mineAreaModifyRequest(MineAreaModifyRequest(0,
esData.name(),
esData.effectStart(),
esData.effectDuration(),
esData.effectSourceId(),
esData.density(),
esData.width(),
{esData.chargeStruct().chargeTypeId(), 0, 0},
segments,
esData.partyId(),
esData.participantId(),
esData.layingProcedureType(),
esData.isLaidHidden(),
esData.effectMarkingInTa(),
true,
distance,
esData.externalId(),
true),
this))
{
ERROR_LOG(m_logger) << "Error during mineAreaCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating mine area object (try with error)";
failExit();
return;
}
// now with correct charge type
m_bExpectedFailure = false;
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
esData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
if (!m_ftc->mineAreaModifyRequest(MineAreaModifyRequest(esData.id(),
esData.name(),
esData.effectStart(),
esData.effectDuration(),
esData.effectSourceId(),
esData.density(),
esData.width(),
{esData.chargeStruct().chargeTypeId(), 0, 0},
segments,
esData.partyId(),
esData.participantId(),
esData.layingProcedureType(),
esData.isLaidHidden(),
esData.effectMarkingInTa(),
true,
distance,
esData.externalId(),
true),
this))
{
ERROR_LOG(m_logger) << "Error during mineAreaCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_maData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating mine area object";
failExit();
return;
}
if (!checkBaseData(m_maData, esData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestMineArea::tryModifyMineArea);
INFO_LOG(m_logger) << "tryCreateMineArea successful";
}
// test Mine Data
void TestMineArea::testMineData_11()
{
if (m_referencedAesItemId == 0)
{
ERROR_LOG(m_logger) << " TESTING ADD MINE DATA WILL NOT BE POSSIBLE. CREATE A EFFECT CLUSTER BEFORE STARTING THIS TEST";
return;
}
if (!m_ftc->isMineAreaReferenceModifyEnabled())
{
if (!m_ftc->enableMineAreaReferenceModify())
{
ERROR_LOG(m_logger) << "Error updating mine area object";
failExit();
return;
}
}
QVector<Database::AesMineAreaSegment> dbsegments;
int count = 0;
std::vector<Core::GeoPosition2D> addVec;
addVec.push_back({0.154111, 0.925984});
addVec.push_back({0.154111, 0.925937});
addVec.push_back({0.154137, 0.925984});
addVec.push_back({0.154137, 0.925937});
qCritical() << " ---> newSegments Pos count: " << addVec.size();
std::vector<MineData> mineData;
count = 60;
QDateTime date = QDateTime::currentDateTime().addDays(7);
for (const auto &pos : addVec)
{
MineData m;
// auto dt = QDateTime::fromMSecsSinceEpoch(date.toMSecsSinceEpoch());
m.appearenceTime(date.toMSecsSinceEpoch());
m.armingTime(date.addDays(count).toMSecsSinceEpoch());
m.position() = pos;
count += 6;
mineData.push_back(m);
}
qCritical() << " ---> final mineAreaReferenceModifyRequest ";
m_replyError = false;
m_bExpectedFailure = false;
m_bIsCreatedRequest = true;
if (!m_ftc->mineAreaReferenceModifyRequest(MineAreaReferenceModifyRequest(m_referencedAesItemId, 216, m_wirkzeitMsec, mineData)))
{
ERROR_LOG(m_logger) << "Error during mineAreaReferenceModifyRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error creating mine area from mine data with referenced Id: " << m_referencedAesItemId;
failExit();
return;
}
// QTimer::singleShot(3000, this, &TestMineArea::testAddMineData_11);
}
void TestMineArea::testAddMineData_11()
{
if (m_referencedAesItemId == 0)
{
ERROR_LOG(m_logger) << " TESTING ADD MINE DATA WILL NOT BE POSSIBLE. CREATE A EFFECT CLUSTER BEFORE STARTING THIS TEST";
return;
}
if (!m_ftc->enableMineAreaReferenceModify())
{
ERROR_LOG(m_logger) << "Error updating mine area object";
failExit();
return;
}
QVector<Database::AesMineAreaSegment> dbsegments;
int count = 0;
std::vector<Core::GeoPosition2D> addVec;
addVec.push_back({0.154111, 0.925937});
addVec.push_back({0.154111, 0.925900});
addVec.push_back({0.154137, 0.925937});
addVec.push_back({0.154137, 0.925900});
qCritical() << " ---> newSegments Pos count: " << addVec.size();
std::vector<MineData> mineData;
count = 60;
QDateTime date = QDateTime::currentDateTime().addDays(7);
for (const auto &pos : addVec)
{
MineData m;
// auto dt = QDateTime::fromMSecsSinceEpoch(date.toMSecsSinceEpoch());
m.appearenceTime(date.toMSecsSinceEpoch());
m.armingTime(date.addDays(count).toMSecsSinceEpoch());
m.position() = pos;
count += 6;
mineData.push_back(m);
}
qCritical() << " ---> final mineAreaReferenceModifyRequest ";
if (!m_ftc->mineAreaReferenceModifyRequest(MineAreaReferenceModifyRequest(m_referencedAesItemId, 216, m_wirkzeitMsec, mineData)))
{
ERROR_LOG(m_logger) << "Error during mineAreaReferenceModifyRequest()";
failExit();
return;
}
}
void TestMineArea::testMineData_21()
{
if (m_referencedAesItemId == 0)
{
ERROR_LOG(m_logger) << " TESTING ADD MINE DATA WILL NOT BE POSSIBLE. CREATE A EFFECT CLUSTER BEFORE STARTING THIS TEST";
return;
}
if (!m_ftc->enableMineAreaReferenceModify())
{
ERROR_LOG(m_logger) << "Error updating mine area object";
failExit();
return;
}
QVector<Database::AesMineAreaSegment> dbsegments;
int count = 0;
std::vector<Core::GeoPosition2D> addVec;
addVec.push_back({0.153900, 0.925950});
addVec.push_back({0.153825, 0.925905});
addVec.push_back({0.153850, 0.925890});
addVec.push_back({0.153929, 0.925937});
qCritical() << " ---> newSegments Pos count: " << addVec.size();
std::vector<MineData> mineData;
count = 60;
QDateTime date = QDateTime::currentDateTime().addDays(7);
for (const auto &pos : addVec)
{
MineData m;
// auto dt = QDateTime::fromMSecsSinceEpoch(date.toMSecsSinceEpoch());
m.appearenceTime(date.toMSecsSinceEpoch());
m.armingTime(date.addDays(count).toMSecsSinceEpoch());
m.position() = pos;
count += 6;
mineData.push_back(m);
}
qCritical() << " ---> final mineAreaReferenceModifyRequest ";
if (!m_ftc->mineAreaReferenceModifyRequest(MineAreaReferenceModifyRequest(m_referencedAesItemId, 216, m_wirkzeitMsec, mineData)))
{
ERROR_LOG(m_logger) << "Error during mineAreaReferenceModifyRequest()";
failExit();
return;
}
// QTimer::singleShot(3000, this, &TestMineArea::testAddMineData_21);
}
void TestMineArea::testAddMineData_21()
{
if (m_referencedAesItemId == 0)
{
ERROR_LOG(m_logger) << " TESTING ADD MINE DATA WILL NOT BE POSSIBLE. CREATE A EFFECT CLUSTER BEFORE STARTING THIS TEST";
return;
}
if (!m_ftc->enableMineAreaReferenceModify())
{
ERROR_LOG(m_logger) << "Error updating mine area object";
failExit();
return;
}
QVector<Database::AesMineAreaSegment> dbsegments;
int count = 0;
std::vector<Core::GeoPosition2D> addVec;
addVec.push_back({0.153900, 0.925937});
addVec.push_back({0.153955, 0.925937});
addVec.push_back({0.153955, 0.925920});
addVec.push_back({0.153900, 0.925920});
qCritical() << " ---> newSegments Pos count: " << addVec.size();
std::vector<MineData> mineData;
count = 60;
QDateTime date = QDateTime::currentDateTime().addDays(7);
for (const auto &pos : addVec)
{
MineData m;
// auto dt = QDateTime::fromMSecsSinceEpoch(date.toMSecsSinceEpoch());
m.appearenceTime(date.toMSecsSinceEpoch());
m.armingTime(date.addDays(count).toMSecsSinceEpoch());
m.position() = pos;
count += 6;
mineData.push_back(m);
}
qCritical() << " ---> final mineAreaReferenceModifyRequest ";
if (!m_ftc->mineAreaReferenceModifyRequest(MineAreaReferenceModifyRequest(m_referencedAesItemId, 216, m_wirkzeitMsec, mineData)))
{
ERROR_LOG(m_logger) << "Error during mineAreaReferenceModifyRequest()";
failExit();
return;
}
}
void TestMineArea::setSegmentForTesMineData()
{
std::vector<Core::GeoPosition2D> segments;
// (form == 1)
{
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.154136, 0.926118));
segments.emplace_back(Core::GeoPosition2D(0.154136, 0.926231));
m_mapSegmentForm.insert(1, segments);
}
// (form == 2)
{
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.154215, 0.926000));
segments.emplace_back(Core::GeoPosition2D(0.15345, 0.926231));
m_mapSegmentForm.insert(1, segments);
}
// (form == 3)
{
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.153581, 0.926173));
segments.emplace_back(Core::GeoPosition2D(0.154500, 0.926126));
m_mapSegmentForm.insert(3, segments);
}
}
void TestMineArea::tryModifyMineArea()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_maData.name("chris2");
m_maData.effectStart(m_maData.effectStart().get() + 2000);
m_maData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_maData.effectDuration(4 * 1000);
m_maData.density(0.2);
m_maData.width(7.0);
m_maData.partyId(8);
m_maData.participantId(1235);
m_maData.layingProcedureType(AreaEffectSimulation::AesMaLayingProcedureType_def::LptArtillery);
m_maData.isLaidHidden(false);
const AreaEffectSimulation::MineArea esData(m_maData);
m_maData = AreaEffectSimulation::MineArea();
m_maData.id(esData.id());
m_maData.effectMarkingInTa(true);
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.17004, 0.93004));
segments.emplace_back(Core::GeoPosition2D(0.17004, 0.93005));
const double distance = 100.0;
m_bIsCreatedRequest = false;
if (!m_ftc->mineAreaModifyRequest(MineAreaModifyRequest(esData.id(),
esData.name(),
esData.effectStart(),
esData.effectDuration(),
esData.effectSourceId(),
esData.density(),
esData.width(),
{esData.chargeStruct().chargeTypeId(), 0, 0},
segments,
esData.partyId(),
esData.participantId(),
esData.layingProcedureType(),
esData.isLaidHidden(),
esData.effectMarkingInTa(),
true,
distance,
esData.externalId(),
true),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_maData.chargeStruct().chargeTypeId() == s_ChargeTypeId2; }))
{
ERROR_LOG(m_logger) << "Error updating mine area object";
failExit();
return;
}
// No testing, segments won't match!
// if (!checkBaseData(m_maData, esData))
// {
// ERROR_LOG(m_logger) << "Modify data does not match published data";
// failExit();
// return;
// }
QTimer::singleShot(0, this, &TestMineArea::tryCreateAnotherMineArea);
INFO_LOG(m_logger) << "tryModifyMineArea successful";
}
//
// MineArea
//
void TestMineArea::tryCreateAnotherMineArea()
{
INFO_LOG(m_logger) << "trying to create another area request";
AreaEffectSimulation::MineArea esData;
esData.id(0);
esData.name("chris");
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
esData.effectDuration(4 * 1000);
esData.density(0.2);
esData.width(7.0);
esData.partyId(7);
esData.participantId(1234);
esData.layingProcedureType(AreaEffectSimulation::AesMaLayingProcedureType_def::LptArtillery);
esData.isLaidHidden(false);
std::vector<Core::GeoPosition2D> segments;
segments.emplace_back(Core::GeoPosition2D(0.17004, 0.93004));
segments.emplace_back(Core::GeoPosition2D(0.17005, 0.93004));
const double distance = 100.0;
INFO_LOG(m_logger) << "trying to send mineAreaCreateRequest()";
m_replyError = true;
m_bIsCreatedRequest = true;
if (!m_ftc->mineAreaModifyRequest(MineAreaModifyRequest(0,
esData.name(),
esData.effectStart(),
esData.effectDuration(),
esData.effectSourceId(),
esData.density(),
esData.width(),
{esData.chargeStruct().chargeTypeId(), 0, 0},
segments,
esData.partyId(),
esData.participantId(),
esData.layingProcedureType(),
esData.isLaidHidden(),
esData.effectMarkingInTa(),
true,
distance,
esData.externalId(),
true),
this))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error creating mine area object (try with error)";
failExit();
return;
}
QTimer::singleShot(0, this, &TestMineArea::tryTriggerDusMineInBoth);
INFO_LOG(m_logger) << "tryCreateMineArea successful";
}
void TestMineArea::tryCreateMineArea2(int chargeTypeId, double density)
{
m_maData = AreaEffectSimulation::MineArea();
AreaEffectSimulation::MineArea esData;
esData.id(0);
esData.name("1337Test");
esData.effectStart((time(nullptr) + 4) * 1000); // msec
esData.chargeStruct().chargeTypeId(chargeTypeId);
esData.effectDuration(400 * 1000);
esData.density(density);
esData.width(7.0);
esData.partyId(7);
esData.participantId(1234);
esData.layingProcedureType(AreaEffectSimulation::AesMaLayingProcedureType_def::LptArtillery);
esData.isLaidHidden(false);
std::vector<Core::GeoPosition2D> segments;
/* Erzeugung der GeoKoordinaten fuer den guez-spezifischen zentralen Bereich (Sueden)
QPointF pos = LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UPD 74600 14100"));
ERROR_LOG(m_logger) << "pos x: " << pos.x() << ", pos y: " << pos.y();
segments.emplace_back(Core::GeoPosition2D(pos.x(), pos.y()));
pos = LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UPD 77500 14200"));
ERROR_LOG(m_logger) << "pos x: " << pos.x() << ", pos y: " << pos.y();
segments.emplace_back(Core::GeoPosition2D(pos.x(), pos.y()));
pos = LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UPD 76100 09150"));
ERROR_LOG(m_logger) << "pos x: " << pos.x() << ", pos y: " << pos.y();
segments.emplace_back(Core::GeoPosition2D(pos.x(), pos.y()));
pos = LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UPD 73550 09450"));
ERROR_LOG(m_logger) << "pos x: " << pos.x() << ", pos y: " << pos.y();
segments.emplace_back(Core::GeoPosition2D(pos.x(), pos.y()));
*/
segments.emplace_back(Core::GeoPosition2D(0.15401, 0.92501));
segments.emplace_back(Core::GeoPosition2D(0.15502, 0.92702));
segments.emplace_back(Core::GeoPosition2D(0.15603, 0.93003));
const double distance = 100.0;
INFO_LOG(m_logger) << "trying to send mineAreaCreateRequest()";
m_replyError = false;
m_bExpectedFailure = false;
m_bIsCreatedRequest = true;
if (!m_ftc->mineAreaModifyRequest(MineAreaModifyRequest(0,
esData.name(),
esData.effectStart(),
esData.effectDuration(),
esData.effectSourceId(),
esData.density(),
esData.width(),
{esData.chargeStruct().chargeTypeId(), 0, 0},
segments,
esData.partyId(),
esData.participantId(),
esData.layingProcedureType(),
esData.isLaidHidden(),
esData.effectMarkingInTa(),
true,
distance,
esData.externalId(),
true),
this))
{
ERROR_LOG(m_logger) << "Error during mineAreaCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error creating mine area object with chargeTypeId " << chargeTypeId;
failExit();
return;
}
}
void TestMineArea::tryTriggerDusMineInBoth()
{
AreaEffectSimulation::MineTriggeredRequest mtr;
mtr.mineAreaId(0); // DUS does not send mine area ID!
mtr.mineId(123);
mtr.position(Core::GeoPosition2D(0.170041, 0.930041));
mtr.chargeTypeId(48);
INFO_LOG(m_logger) << "trying to send mineTriggeredRequest()";
m_replyError = true;
if (!m_ftc->mineTriggeredRequest(MineTriggeredRequest(mtr.mineAreaId(), mtr.mineId(), mtr.position().get(), mtr.chargeTypeId().get()), this))
{
ERROR_LOG(m_logger) << "Error during mineTriggeredRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == false; }))
{
ERROR_LOG(m_logger) << "Error triggering mine (try with error)";
failExit();
return;
}
QTimer::singleShot(0, this, &TestMineArea::waitExpireMineArea);
INFO_LOG(m_logger) << "tryTriggerDusMineInBoth successful";
}
void TestMineArea::waitExpireMineArea()
{
INFO_LOG(m_logger) << "waiting until mine area expires";
if (!waitForData([this]() { return m_maData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired; }))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of mine area with id " << m_maData.id();
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "waitExpireMineArea successful";
}
void TestMineArea::mineAreaChanged(const AreaEffectSimulation::MineArea &data, DataModification modType)
{
if (m_maData.id() == -1)
return;
onAesChanged(data, modType, m_maData);
}
void TestMineArea::onMineAreaModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestMineArea::onMineAreaRemoveReply(const Core::FunctionReplyObject &replyObject) { onAesRemoveReply(replyObject); }
void TestMineArea::onMineResetReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got mine reset with id " << replyObject.id() << " and errorString " << replyObject.translated();
m_replyError = replyObject.error();
if (m_replyError)
{
ERROR_LOG(m_logger) << "Got mine reset reply error: " << replyObject();
}
}
void TestMineArea::onMineTriggeredReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Got mine triggered with id " << replyObject.id() << " and errorString " << replyObject.translated();
m_replyError = replyObject.error();
if (m_replyError)
{
ERROR_LOG(m_logger) << "Got mine triggered reply error: " << replyObject();
}
}
void TestMineArea::onMineAreaReferenceModifyReply(const Core::FunctionReplyObject &replyObject)
{
INFO_LOG(m_logger) << "Modify Mine Area with reference replyObj.id: " << replyObject.id() << " and errorString " << replyObject.translated();
m_replyError = replyObject.error();
if (m_replyError)
{
ERROR_LOG(m_logger) << "Got mine modify reply error: " << replyObject();
}
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestMineArea; });
}
@@ -0,0 +1,64 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestMineArea final
: public TestBase
, protected AreaEffectSimulation::Interface::Ftc::MineAreaModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineAreaRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineResetReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineTriggeredReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::MineAreaReferenceModifyReplyHandler
{
Q_OBJECT
public:
explicit TestMineArea(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override;
private Q_SLOTS:
void tryCreateMineArea();
void testMineData_11();
void testAddMineData_11();
void testMineData_21();
void testAddMineData_21();
void setSegmentForTesMineData();
void tryModifyMineArea();
void waitExpireMineArea();
void tryTriggerDusMineInBoth();
void tryCreateAnotherMineArea();
void tryCreateMineArea2(int chargeTypeId, double density = 0.2);
protected Q_SLOTS:
void mineAreaChanged(const AreaEffectSimulation::MineArea &data, DataModification modType);
protected:
void onMineAreaModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onMineAreaRemoveReply(const Core::FunctionReplyObject &replyObject) override;
void onMineResetReply(const Core::FunctionReplyObject &replyObject) override;
void onMineTriggeredReply(const Core::FunctionReplyObject &replyObject) override;
void onMineAreaReferenceModifyReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::MineArea &a, const AreaEffectSimulation::MineArea &b) const;
private:
AreaEffectSimulation::MineArea m_maData;
QMap<int, std::vector<Core::GeoPosition2D>> m_mapSegmentForm;
int m_referencedAesItemId = 0;
int32_t m_wirkzeitMsec = 3 * 10 * 1000; // 30 sec
int m_currentMineId = 0;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,320 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_ncontamination.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 321;
TestNContamination::TestNContamination(QObject *parent)
: TestBase("Test.NContamination", parent)
{}
bool TestNContamination::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataNContamination();
m_ncData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged,
this, &TestNContamination::nContaminationChanged);
return true;
}
bool TestNContamination::checkBaseData(const AreaEffectSimulation::NContamination &a,
const AreaEffectSimulation::NContamination &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.impactPosition() != b.impactPosition())
{
WARNING_LOG(m_logger) << "impactPosition does not match: " << a.impactPosition() << " - " << b.impactPosition();
return false;
}
return true;
}
//
// NContamination
//
void TestNContamination::tryCreateNContamination()
{
m_ncData = AreaEffectSimulation::NContamination();
AreaEffectSimulation::NContamination ncData;
ncData.id(0);
ncData.name("ncont1");
ncData.effectStart((time(nullptr) + 4) * 1000); // msec
ncData.effectDuration(10000); // 10 sec
ncData.chargeStruct().chargeTypeId(345863487);
ncData.impactPosition(Core::GeoPosition2D(0.16, 0.92));
ncData.borderLengthInner(1000); // 1km
ncData.borderLengthOuter(2000); // 2km
ncData.impactRadius(500); // 500m
ncData.spanAngle(31);
ncData.rotationAngle(57);
ncData.detonationValue(1000); // kt, only cosmetical
ncData.partyId(7);
ncData.participantId(1234);
INFO_LOG(m_logger) << "trying to send NContaminationCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
if (!m_ftc->nContaminationModifyRequest(
NContaminationModifyRequest(
0,
ncData.name(),
ncData.effectStart(),
ncData.effectDuration(),
ncData.chargeStruct().chargeTypeId(),
ncData.impactPosition(),
ncData.borderLengthInner(),
ncData.borderLengthOuter(),
ncData.impactRadius(),
ncData.spanAngle(),
ncData.rotationAngle(),
ncData.detonationValue(),
ncData.weatherSource(),
ncData.partyId(),
ncData.participantId(),
ncData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during NContaminationCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating ncontamination object (try with error)";
failExit();
return;
}
m_bExpectedFailure = false;
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
// now with correct charge type
ncData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
if (!m_ftc->nContaminationModifyRequest(
NContaminationModifyRequest(
0,
ncData.name(),
ncData.effectStart(),
ncData.effectDuration(),
ncData.chargeStruct().chargeTypeId(),
ncData.impactPosition(),
ncData.borderLengthInner(),
ncData.borderLengthOuter(),
ncData.impactRadius(),
ncData.spanAngle(),
ncData.rotationAngle(),
ncData.detonationValue(),
ncData.weatherSource(),
ncData.partyId(),
ncData.participantId(),
ncData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during NContaminationCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ncData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating ncontamination object";
failExit();
return;
}
if (!checkBaseData(m_ncData, ncData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestNContamination::tryModifyNContamination);
INFO_LOG(m_logger) << "tryCreateNContamination successful";
}
void TestNContamination::tryModifyNContamination()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_ncData.name("ncont2");
m_ncData.effectStart((time(nullptr) + 4) * 1000); // msec
m_ncData.effectDuration(3000); // 3 sec
m_ncData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
m_ncData.impactPosition(Core::GeoPosition2D(0.162, 0.925));
m_ncData.borderLengthInner(1100); // 1.1km
m_ncData.borderLengthOuter(1800); // 1.8km
m_ncData.impactRadius(600); // 600m
m_ncData.spanAngle(14);
m_ncData.rotationAngle(187);
m_ncData.detonationValue(2000); // kt, only cosmetical
m_ncData.partyId(3);
m_ncData.participantId(1235);
const AreaEffectSimulation::NContamination ncData(m_ncData);
m_ncData = AreaEffectSimulation::NContamination();
m_ncData.id(ncData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->nContaminationModifyRequest(
NContaminationModifyRequest(
ncData.id(),
ncData.name(),
ncData.effectStart(),
ncData.effectDuration(),
ncData.chargeStruct().chargeTypeId(),
ncData.impactPosition(),
ncData.borderLengthInner(),
ncData.borderLengthOuter(),
ncData.impactRadius(),
ncData.spanAngle(),
ncData.rotationAngle(),
ncData.detonationValue(),
ncData.weatherSource(),
ncData.partyId(),
ncData.participantId(),
ncData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_ncData.partyId() == 3;}))
{
ERROR_LOG(m_logger) << "Error updating ncontamination object";
failExit();
return;
}
if (!checkBaseData(m_ncData, ncData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestNContamination::waitExpireNContamination);
INFO_LOG(m_logger) << "tryModifyNContamination successful";
}
void TestNContamination::waitExpireNContamination()
{
INFO_LOG(m_logger) << "waiting until ncontamination expires";
if (!waitForData([this]() { return m_ncData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of ncontamination with id " << m_ncData.id();
failExit();
return;
}
QTimer::singleShot(0, this, &TestNContamination::tryRemoveNContamination);
INFO_LOG(m_logger) << "waitExpireNContamination successful";
}
void TestNContamination::tryRemoveNContamination()
{
INFO_LOG(m_logger) << "tryRemoveNContamination start";
AreaEffectSimulation::NContamination ncData(m_ncData);
ncData.name("RemoveTest");
ncData.effectStart(0); // not set
m_ncData = AreaEffectSimulation::NContamination();
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
if (!m_ftc->nContaminationModifyRequest(
NContaminationModifyRequest(
0,
ncData.name(),
ncData.effectStart(),
ncData.effectDuration(),
ncData.chargeStruct().chargeTypeId(),
ncData.impactPosition(),
ncData.borderLengthInner(),
ncData.borderLengthOuter(),
ncData.impactRadius(),
ncData.spanAngle(),
ncData.rotationAngle(),
ncData.detonationValue(),
ncData.weatherSource(),
ncData.partyId(),
ncData.participantId(),
ncData.externalId()),
this))
{
ERROR_LOG(m_logger) << "Error during NContaminationCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ncData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating ncontamination object";
failExit();
return;
}
m_expectedAesId = -1;
if (!m_ftc->nContaminationRemoveRequest(Core::RemoveElementRequest(m_ncData.id()), this))
{
ERROR_LOG(m_logger) << "Error during NContaminationRemoveRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ncData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error removing ncontamination object";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryRemoveNContamination successful";
}
void TestNContamination::nContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType)
{
if (m_ncData.id() == -1)
return;
onAesChanged(data, modType, m_ncData);
}
void TestNContamination::onNContaminationModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestNContamination::onNContaminationRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestNContamination; });
}
@@ -0,0 +1,44 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestNContamination final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::NContaminationModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::NContaminationRemoveReplyHandler
{
Q_OBJECT
public:
explicit TestNContamination(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateNContamination(); }
private Q_SLOTS:
void tryCreateNContamination();
void tryModifyNContamination();
void waitExpireNContamination();
void tryRemoveNContamination();
protected Q_SLOTS:
void nContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType);
protected:
void onNContaminationModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onNContaminationRemoveReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::NContamination &a,
const AreaEffectSimulation::NContamination &b) const;
private:
AreaEffectSimulation::NContamination m_ncData;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,313 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_test_off_route_mine.h"
#include <QCoreApplication>
#include <QTest>
#include <QTimer>
namespace AreaEffectSimulation
{
// must be in pdm.pdm_charge_type
static const int s_ChargeTypeId1 = 253;
static const int s_ChargeTypeId2 = 254;
TestOffRouteMine::TestOffRouteMine(QObject *parent)
: TestBase("Test.OffRouteMine", parent)
{}
bool TestOffRouteMine::init(int domainId, int exerciseId, int testId)
{
if (!TestBase::init(domainId, exerciseId, testId))
return false;
m_dtc->waitForHistoricalDataOffRouteMine();
m_ormData.id(-1);
connect(m_dtc.get(), &AreaEffectSimulation::Interface::Dtc::offRouteMineChanged,
this, &TestOffRouteMine::offRouteMineChanged);
return true;
}
bool TestOffRouteMine::checkBaseData(const AreaEffectSimulation::OffRouteMine &a,
const AreaEffectSimulation::OffRouteMine &b) const
{
if (!checkAesItem(a, b))
return false;
if (a.area().cornerBottomLeft() != b.area().cornerBottomLeft())
{
WARNING_LOG(m_logger) << "cornerBottomLeft does not match: " << a.area().cornerBottomLeft() << " - " << b.area().cornerBottomLeft();
return false;
}
if (a.impactRadius() != b.impactRadius())
{
WARNING_LOG(m_logger) << "impactRadius does not match: " << a.impactRadius() << " - " << b.impactRadius();
return false;
}
return true;
}
//
// OffRouteMine
//
void TestOffRouteMine::tryCreateOffRouteMine()
{
m_ormData = AreaEffectSimulation::OffRouteMine();
AreaEffectSimulation::OffRouteMine ormData;
ormData.id(0);
ormData.name("OffRouteMine1");
ormData.effectStart((time(nullptr) + 4) * 1000); // msec
ormData.effectDuration(10234); // msec
ormData.chargeStruct().chargeTypeId(3456789);
ormData.area().cornerBottomLeft(Core::GeoPosition2D(0.154, 0.926));
ormData.area().width(1000);//(20);
ormData.area().height(200);//(15);
ormData.area().rotation(0.0);//(20.5);
ormData.impactRadius(100); // 100m
ormData.partyId(2);
//ormData.participantId(1234);
INFO_LOG(m_logger) << "trying to send OffRouteMineCreateRequest()";
m_replyError = false;
m_bExpectedFailure = true;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
if (!m_ftc->offRouteMineModifyRequest(
OffRouteMineModifyRequest(
0,
ormData.name(),
ormData.effectDuration(),
ormData.chargeStruct().chargeTypeId(),
ormData.area(),
ormData.impactRadius().get(),
ormData.effectStart().get(),
ormData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during OffRouteMineCreateRequest()";
failExit();
return;
}
// since the chargeTypeId is not valid, we should get an error
if (!waitForData([this]() { return m_replyError == true; }))
{
ERROR_LOG(m_logger) << "Error creating off route mine object (try with error)";
failExit();
return;
}
// now with correct charge type
m_bExpectedFailure = false;
m_bIsCreatedRequest = true;
m_expectedAesId = -1;
ormData.chargeStruct().chargeTypeId(s_ChargeTypeId1);
if (!m_ftc->offRouteMineModifyRequest(
OffRouteMineModifyRequest(
0,
ormData.name(),
ormData.effectDuration(),
ormData.chargeStruct().chargeTypeId(),
ormData.area(),
ormData.impactRadius().get(),
ormData.effectStart().get(),
ormData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during OffRouteMineCreateRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ormData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating off route mine object";
failExit();
return;
}
if (!checkBaseData(m_ormData, ormData))
{
ERROR_LOG(m_logger) << "Creation data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestOffRouteMine::tryModifyOffRouteMine);
INFO_LOG(m_logger) << "tryCreateOffRouteMine successful";
}
void TestOffRouteMine::tryModifyOffRouteMine()
{
INFO_LOG(m_logger) << "trying to send modify request";
m_ormData.name("OffRouteMine 2");
m_ormData.effectStart(m_ormData.effectStart().get() + 2000);
m_ormData.effectDuration(3200); // msec
m_ormData.chargeStruct().chargeTypeId(s_ChargeTypeId2);
m_ormData.area().cornerBottomLeft(Core::GeoPosition2D(0.154, 0.926));
m_ormData.area().width(1000);//(50);
m_ormData.area().height(200);//(25);
m_ormData.area().rotation(90);//(20.5);
m_ormData.partyId(3);
//m_ormData.participantId(1235);
const AreaEffectSimulation::OffRouteMine ormData(m_ormData);
m_ormData = AreaEffectSimulation::OffRouteMine();
m_ormData.id(ormData.id());
m_bIsCreatedRequest = false;
if (!m_ftc->offRouteMineModifyRequest(
OffRouteMineModifyRequest(
ormData.id(),
ormData.name(),
ormData.effectDuration(),
ormData.chargeStruct().chargeTypeId(),
ormData.area(),
ormData.impactRadius().get(),
ormData.effectStart().get(),
ormData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
return;
}
if (!waitForData([this]() { return m_ormData.chargeStruct().chargeTypeId() == s_ChargeTypeId2;}))
{
ERROR_LOG(m_logger) << "Error updating off route mine object";
failExit();
return;
}
if (!checkBaseData(m_ormData, ormData))
{
ERROR_LOG(m_logger) << "Modify data does not match published data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestOffRouteMine::tryTriggerOffRouteMine);
INFO_LOG(m_logger) << "tryModifyOffRouteMine successful";
}
void TestOffRouteMine::tryTriggerOffRouteMine()
{
INFO_LOG(m_logger) << "trigger off route mine -> expires";
OffRouteMineTriggerRequest req;
req.id(m_ormData.id());
req.triggered(true);
if (!m_ftc->offRouteMineTriggerRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting trigger data";
failExit();
return;
}
QTimer::singleShot(0, this, &TestOffRouteMine::waitExpireOffRouteMine);
INFO_LOG(m_logger) << "waitExpireBoobyTrap successful";
}
void TestOffRouteMine::waitExpireOffRouteMine()
{
INFO_LOG(m_logger) << "waiting until off route mine expires";
if (!waitForData([this]() { return m_ormData.effectStateId() == AreaEffectSimulation::AesEffectState::EsExpired;}))
{
ERROR_LOG(m_logger) << "Timeout waiting for expiry of off route mine with id " << m_ormData.id();
failExit();
return;
}
//QTimer::singleShot(0, this, &TestOffRouteMine::tryRemoveOffRouteMine);
INFO_LOG(m_logger) << "waitExpireOffRouteMine successful";
}
void TestOffRouteMine::tryRemoveOffRouteMine()
{
INFO_LOG(m_logger) << "tryRemoveOffRouteMine start";
AreaEffectSimulation::OffRouteMine ormData(m_ormData);
ormData.name("RemoveTest");
ormData.effectStart(0); // not set
m_ormData = AreaEffectSimulation::OffRouteMine();
m_expectedAesId = -1;
m_bIsCreatedRequest = true;
if (!m_ftc->offRouteMineModifyRequest(
OffRouteMineModifyRequest(
0,
ormData.name(),
ormData.effectDuration(),
ormData.chargeStruct().chargeTypeId(),
ormData.area(),
ormData.impactRadius().get(),
ormData.effectStart().get(),
ormData.partyId().get()),
this))
{
ERROR_LOG(m_logger) << "Error during OffRouteMineModifyRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ormData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error creating off route mine object";
failExit();
return;
}
m_expectedAesId = -1;
if (!m_ftc->offRouteMineRemoveRequest(Core::RemoveElementRequest(m_ormData.id()), this))
{
ERROR_LOG(m_logger) << "Error during OffRouteMineRemoveRequest()";
failExit();
return;
}
if (!waitForData([this]() { return m_ormData.id() == m_expectedAesId; }))
{
ERROR_LOG(m_logger) << "Error removing off route mine object";
failExit();
return;
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
INFO_LOG(m_logger) << "tryRemoveOffRouteMine successful";
}
void TestOffRouteMine::offRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType)
{
if (m_ormData.id() == -1)
return;
onAesChanged(data, modType, m_ormData);
}
void TestOffRouteMine::onOffRouteMineModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (m_bIsCreatedRequest)
onAesCreateReply(replyObject);
else
onAesModifyReply(replyObject);
}
void TestOffRouteMine::onOffRouteMineRemoveReply(const Core::FunctionReplyObject &replyObject)
{
onAesRemoveReply(replyObject);
}
void TestOffRouteMine::onOffRouteMineTriggerReply(const Core::FunctionReplyObject &replyObject)
{
onAesTriggerReply(replyObject);
}
} // namespace AreaEffectSimulation
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
return AreaEffectSimulation::mainBase(argc, argv, []() { return new AreaEffectSimulation::TestOffRouteMine; });
}
@@ -0,0 +1,47 @@
/**
* AES Function Topic Client test executable
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "aes_test_base.h"
namespace AreaEffectSimulation
{
class TestOffRouteMine final : public TestBase
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineModifyReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineRemoveReplyHandler
, protected AreaEffectSimulation::Interface::Ftc::OffRouteMineTriggerReplyHandler
{
Q_OBJECT
public:
explicit TestOffRouteMine(QObject *parent = nullptr);
bool init(int domainId, int exerciseId, int testId) override;
public Q_SLOTS:
void startTest() override { tryCreateOffRouteMine(); }
private Q_SLOTS:
void tryCreateOffRouteMine();
void tryModifyOffRouteMine();
void tryTriggerOffRouteMine();
void waitExpireOffRouteMine();
void tryRemoveOffRouteMine();
protected Q_SLOTS:
void offRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType);
protected:
void onOffRouteMineModifyReply(const Core::FunctionReplyObject &replyObject) override;
void onOffRouteMineRemoveReply(const Core::FunctionReplyObject &replyObject) override;
void onOffRouteMineTriggerReply(const Core::FunctionReplyObject &replyObject) override;
private:
bool checkBaseData(const AreaEffectSimulation::OffRouteMine &a,
const AreaEffectSimulation::OffRouteMine &b) const;
private:
AreaEffectSimulation::OffRouteMine m_ormData;
};
} // namespace AreaEffectSimulation
Binary file not shown.
@@ -0,0 +1,73 @@
https://devhub.defence-elec.de/jira/browse/E01562-259
Kontext:
Die TrajectoryTimeAware besitzt Verweise auf seinen "Verursacher" ("parent")
Urspruenglich wurden diese mit den Attributen "aesItemId" & "aesTypeId" implementiert - wie es bei AES zu der Zeit typisch war.
Dieser Verweise soll auf "originatorId," "originatorTypeId" umgestellt werden (zwecks groesserer Flexibilitaet)
ToDo:
Code komplett auf die Nutzung von "originatorId," "originatorTypeId" umstellen
"aesItemId" & "aesTypeId" aus der aes_service.xml entfernen
"aesItemId" & "aesTypeId" im EA entfernen
Verifikation:
"aesItemId" & "aesTypeId" sind komplett entfernt.
Rahmenbedingungen:
Die meisten AesObjekte haben eine uebergeordnet eindeutige Id.
Ausnahme: Derzeit ChargeEffect.
Konsequenz: Eine ChargeEffect.id kann gleich sein mit Contamination.id, EffectCluster.id oder ...!
StateChangeOriginator::Aes bzw. StateChangeOriginator::ChargeEffect erlaubt dennoch eine Unterscheidung.
Als AesType sind u.a. vorgesehen:
- AtExplosionClusterSingleTrjajectory
- AtEffectClusterSingleTrajectory
Erzeuger des Topic TrajectoryTimeAware:
- [B] object_effect_cluster
- aesItemId,
- aesTypeID = AtEffectClusterSingleTrajectory
- neu: OrigId = aesItemId
- neu: OrigId = StateChangeOriginator::Aes
- [C] object_effector -> charge_effect
- aesItemId = parent
- aesTypeId = AtEffectClusterSingleTrajectory
- origId = chargeEffectId,
- origType=StateChangeOriginator::ChargeEffect
- [A] object_explosion_cluster
- aesItemId,
- aesTypeID = AtExplosionClusterSingleTrajectory
- neu: OrigId = aesItemid
- neu: OrigId = StateChangeOriginator::Aes
Abnehmer
- [C] SitMap: charge_effect_to_map
- beschraenkt sich auf StateChangeOriginator::ChargeEffect
- Neu: keine Anpassung erforderlich
- [B] SitMap: effect_cluster_to_map
nicht relevant, Gtl wird via topic AesItemView uebertragen.
- [A] SitMap: explosion_cluster_to_map
- beschraenkt sich: AtExplosionClusterSingleTrajectory
- Neu: beschraenkt sich auf StateChangeOriginator::aes - doch dann werden
Dialoge:
- [A] EditDialog: aes_explosion_edit.cpp
- bezieht sich auf ExplosionCluster
- beschraenkt sich auf die AesItemId
- Neu: beschraenkt sich auf StateChangeOriginator::AES und origId/aesItemId.
- [B;C] EditDialog: aes_explosion_edit_adler_standalone
- bezieht sich auf EffectCluster
- beschraenkt sich auf die AesItemId
- Neu: beschraenkt sich auf StateChangeOriginator::ChargeEffect und origId/aesItemId.
@@ -0,0 +1,509 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_record_replay_plugin_custom.h"
#include <QElapsedTimer>
static bool OPTIMIZE = true; // GUEZE-3521 "Überarbeitung des Replays zur Performance Steigerung"
namespace AreaEffectSimulation {
RecordReplayPluginCustom::RecordReplayPluginCustom()
: m_currentReplayTime(0)
{
}
RecordReplayPluginCustom::~RecordReplayPluginCustom()
{
}
bool RecordReplayPluginCustom::initDts(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector, int exerciseId, bool bCanHandleSchemas, NSqlQuery &query)
{
RecordReplayPlugin::initDts(ddsConnector, exerciseId, bCanHandleSchemas, query);
m_ddsConnector = ddsConnector;
m_ddsDts = AreaEffectSimulation::Interface::Dts::create(m_ddsConnector);
m_ddsDts->enableAll();
//const qint64 startTime = time(nullptr) * 1000LL;
DEBUG_LOG(m_logger) << "RecordReplayPluginCustom::initDts successful";
return true;
}
bool RecordReplayPluginCustom::setReplayStartTime(NSqlQuery &q, qint64 startTime)
{
QElapsedTimer timer;
QElapsedTimer timer2;
timer2.start();
INFO_LOG(m_logger) << "setReplayStartTime " << startTime;
m_currentReplayTime = 0;
timer.start();
if (!readTrajectoryTimeAwareAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readTrajectoryTimeAwareAt " << startTime << " DONE";
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readTrajectoryTimeAwareAt " << startTime << " before: " << m_trajectoryTimeAwareMap.count();
if (OPTIMIZE)
{
auto copyTW = m_trajectoryTimeAwareMap;
for (const auto &key : m_trajectoryTimeAwareMap.keys())
{
for (const auto &valie : copyTW.values(key))
{
if (valie.trajectoryStartTime() < startTime - 210*1000)
{
m_trajectoryTimeAwareMap.remove(key, valie);
}
}
}
}
DEBUG_LOG(m_logger) << "setReplayStartTime-readTrajectoryTimeAwareAt " << startTime << " after: " << m_trajectoryTimeAwareMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readAirSupportImpactAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readAirSupportImpactAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readAirSupportImpactAt " << startTime << " before: " << m_airSupportImpactMap.count();
removeOld(m_airSupportImpactMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readAirSupportImpactAt " << startTime << " after: " << m_airSupportImpactMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readBCContaminationAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readBCContaminationAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readBCContaminationAt " << startTime << " before: " << m_bCContaminationMap.count();
removeOld(m_bCContaminationMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readBCContaminationAt " << startTime << " after: " << m_bCContaminationMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readBoobyTrapAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readBoobyTrapAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readBoobyTrapAt " << startTime << " before: " << m_boobyTrapMap.count();
removeOld(m_boobyTrapMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readBoobyTrapAt " << startTime << " after: " << m_boobyTrapMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readBorderlineAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readBorderlineAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readBorderlineAt " << startTime << " before: " << m_borderlineMap.count();
removeOld(m_borderlineMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readBorderlineAt " << startTime << " after: " << m_borderlineMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
if (!readChargeAdministrationAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
timer.restart();
if (!readDangerAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDangerAreaAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readDangerAreaAt " << startTime << " before: " << m_dangerAreaMap.count();
removeOld(m_dangerAreaMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readDangerAreaAt " << startTime << " after: " << m_dangerAreaMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
if (!readDusMineStateAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
if (!readDusSingleMineAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
timer.restart();
if (!readExplosionAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readExplosionAreaAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionAreaAt " << startTime << " before: " << m_explosionAreaMap.count();
removeOld(m_explosionAreaMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionAreaAt " << startTime << " after: " << m_explosionAreaMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readExplosionClusterAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readExplosionClusterAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionClusterAt " << startTime << " before: " << m_explosionClusterMap.count();
removeOld(m_explosionClusterMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionClusterAt " << startTime << " after: " << m_explosionClusterMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readExplosionSingleAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readExplosionSingleAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionSingleAt " << startTime << " before: " << m_explosionSingleMap.count();
removeOld(m_explosionSingleMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readExplosionSingleAt " << startTime << " after: " << m_explosionSingleMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
if (!readFunctionalAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
if (!readGasPipeAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readGasPipeAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readGasPipeAt " << startTime << " before: " << m_gasPipeMap.count();
removeOld(m_gasPipeMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readGasPipeAt " << startTime << " after: " << m_gasPipeMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readMineAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readMineAreaAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readMineAreaAt " << startTime << " before: " << m_mineAreaMap.count();
removeOld(m_mineAreaMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readMineAreaAt " << startTime << " after: " << m_mineAreaMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readMineCorridorAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readMineCorridorAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readMineCorridorAt " << startTime << " before: " << m_mineCorridorMap.count();
removeOld(m_mineCorridorMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readMineCorridorAt " << startTime << " after: " << m_mineCorridorMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readNContaminationAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readNContaminationAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readNContaminationAt " << startTime << " before: " << m_nContaminationMap.count();
removeOld(m_nContaminationMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readNContaminationAt " << startTime << " after: " << m_nContaminationMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
if (!readOffRouteMineAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readOffRouteMineAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readOffRouteMineAt " << startTime << " before: " << m_offRouteMineMap.count();
removeOld(m_offRouteMineMap, startTime);
DEBUG_LOG(m_logger) << "setReplayStartTime-readOffRouteMineAt " << startTime << " after: " << m_offRouteMineMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
if (!readReconAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readDynamicParticipantDataAt " << startTime << " DONE";
if (!readTechnicalAreaAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readTechnicalAreaAt " << startTime << " DONE";
if (!readEffectClusterAt(q, startTime))
{
ERROR_LOG(m_logger) << "setReplayStartTime-readEffectClusterAt " << startTime << " failed";
return false;
}
INFO_LOG(m_logger) << "setReplayStartTime-readEffectClusterAt " << startTime << " DONE";
if (!readChargeEffectAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readChargeEffectAt " << startTime << " DONE";
timer.restart();
if (!readTriggeredImpactsAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readTriggeredImpactsAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readTriggeredImpactsAt " << startTime << " before: " << m_triggeredImpactsMap.count();
if (OPTIMIZE)
{
auto copyTI = m_triggeredImpactsMap;
for (const auto &key : m_triggeredImpactsMap.keys())
{
for (const auto &valie : copyTI.values(key))
{
if (valie.sampleTime() < startTime - 210*1000)
m_triggeredImpactsMap.remove(key, valie);
}
}
}
DEBUG_LOG(m_logger) << "setReplayStartTime-readTriggeredImpactsAt " << startTime << " after: " << m_triggeredImpactsMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
//take aes item view last
timer.restart();
if (!readAesItemViewAt(q, startTime)) return false;
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
//manual remove_
timer.restart();
if (!readEffectsAt(q, startTime)) return false;
INFO_LOG(m_logger) << "setReplayStartTime-readEffectsAt " << startTime << " DONE";
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readEffectsAt " << startTime << " before: " << m_effectsMap.count();
if (OPTIMIZE)
{
//find missing aes item vies?!
auto copy = m_effectsMap;
for (const auto &key : copy.keys())
{
for (const auto &valie : copy.values(key))
{
if( m_explosionClusterKeys.find(valie.aesItemId()) != m_explosionClusterKeys.end())
{
m_effectsMap.remove(key, valie);
continue;
}
}
}
}
DEBUG_LOG(m_logger) << "setReplayStartTime-readEffectsAt " << startTime << " after: " << m_effectsMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
timer.restart();
DEBUG_LOG(m_logger) << "setReplayStartTime-readAesItemViewAt " << startTime << " before: " << m_aesItemViewMap.count();
if (OPTIMIZE)
{
// find missing aes item views?!
auto copy = m_aesItemViewMap;
for (const auto &key : m_aesItemViewMap.keys())
{
for (const auto &value : copy.values(key))
{
if (value.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsCreated
|| value.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsFired
|| value.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsReceived
|| value.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsPrepared
|| value.effectStateId() == AreaEffectSimulation::AesEffectState_def::EsActivated
)
continue;
// check regarding _Explosion_Cluster
{
static QVector<AesType_def::type> relavantAesTypeIds = {AesType_def::AtExplosionClusterGunToTargetLine, AesType_def::AtExplosionClusterSingleTrajectory};
if (relavantAesTypeIds.contains(value.aesTypeId().underlying()))
{
if (m_explosionClusterKeys.find(value.aesItemId()) != m_explosionClusterKeys.end() ) //contains not available until cpp 20
continue;
}
}
// E01562-E343 EffectCluster: GunToTargetLines fehlen im Replay&Snapshot
{
static QVector<AesType_def::type> relavantAesTypeIds = {AesType_def::AtEffectClusterGunToTargetLine, AesType_def::AtEffectClusterGunToTargetLine};
if (relavantAesTypeIds.contains(value.aesTypeId().underlying()))
{
if (m_effectClusterKeys.find(value.aesItemId()) != m_effectClusterKeys.end() ) //contains not available until cpp 20
continue;
}
}
if (value.effectStateId() == AesEffectState_def::EsRemoved || value.effectStart() < startTime - 210*1000)
{
m_aesItemViewMap.remove(key, value);
}
}
}
}
DEBUG_LOG(m_logger) << "setReplayStartTime-readAesItemViewAt " << startTime << " after: " << m_aesItemViewMap.count();
DEBUG_LOG(m_logger) << "took " << timer.elapsed();
DEBUG_LOG(m_logger) << "done db work took " << timer2.elapsed();
return true;
}
template<typename T>
void RecordReplayPluginCustom::removeOld(QMultiMap<long long, T> &map, qint64 desiredTime)
{
if (!OPTIMIZE)
return;
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);
continue;
}
// keep planned
if (!inner.effectStart().is_set())
continue;
// Remove to old ones
if (inner.effectStart().get()+inner.effectDuration() < desiredTime - 210*1000 )
map.remove(item, inner);
}
}
}
bool RecordReplayPluginCustom::ddsTopicsFromDatabase(NSqlQuery &q, qint64 fromTime, qint64 toTime)
{
INFO_LOG(m_logger) << "ddsTopicsFromDatabase " << fromTime << " to " << toTime;
// if (!readParticipant(q, fromTime, toTime)) return false;
bool ret = RecordReplayPlugin::ddsTopicsFromDatabase(q, fromTime, toTime);
DEBUG_LOG(m_logger) << "ddsTopicsFromDatabase ret: " << ret;
return ret;
}
bool RecordReplayPluginCustom::publishData(qint64 desiredTime)
{
INFO_LOG(m_logger) << "publishData " << desiredTime;
m_currentReplayTime = desiredTime;
QElapsedTimer timer;
QElapsedTimer timer2;
timer2.start();
timer.start();
DEBUG_LOG(m_logger) << "dispose map =" << m_aesItemViewDisposedMap.size();
DEBUG_LOG(m_logger) << "send map =" << m_aesItemViewMap.size();
if (!publishAesItemView(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishAesItemView took " << timer.elapsed();
timer.restart();
if (!publishAirSupportImpact(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishAirSupportImpact took " << timer.elapsed();
timer.restart();
if (!publishBCContamination(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishBCContamination took " << timer.elapsed();
timer.restart();
if (!publishBoobyTrap(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishBoobyTrap took " << timer.elapsed();
timer.restart();
if (!publishBorderline(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishBorderline took " << timer.elapsed();
timer.restart();
if (!publishChargeAdministration(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishChargeAdministration took " << timer.elapsed();
timer.restart();
if (!publishDangerArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishDangerArea took " << timer.elapsed();
timer.restart();
if (!publishDusMineState(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishDusMineState took " << timer.elapsed();
timer.restart();
if (!publishDusSingleMine(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishDusSingleMine took " << timer.elapsed();
timer.restart();
if (!publishEffects(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishEffects took " << timer.elapsed();
timer.restart();
if (!publishExplosionArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishExplosionArea took " << timer.elapsed();
timer.restart();
if (!publishExplosionCluster(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishExplosionCluster took " << timer.elapsed();
timer.restart();
if (!publishExplosionSingle(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishExplosionSingle took " << timer.elapsed();
timer.restart();
if (!publishFunctionalArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishFunctionalArea took " << timer.elapsed();
timer.restart();
if (!publishGasPipe(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishGasPipe took " << timer.elapsed();
timer.restart();
if (!publishMineArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishMineArea took " << timer.elapsed();
timer.restart();
if (!publishMineCorridor(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishMineCorridor took " << timer.elapsed();
timer.restart();
if (!publishNContamination(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishNContamination took " << timer.elapsed();
timer.restart();
if (!publishOffRouteMine(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishOffRouteMine took " << timer.elapsed();
timer.restart();
if (!publishReconArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishReconArea took " << timer.elapsed();
timer.restart();
if (!publishTechnicalArea(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishTechnicalArea took " << timer.elapsed();
timer.restart();
if (!publishEffectCluster(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishEffectCluster took " << timer.elapsed();
timer.restart();
if (!publishChargeEffect(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishtChargeEffect took " << timer.elapsed();
timer.restart();
if (!publishTrajectoryTimeAware(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishTrajectoryTimeAware took " << timer.elapsed();
timer.restart();
if (!publishTriggeredImpacts(desiredTime)) return false;
DEBUG_LOG(m_logger) << "publishTriggeredImpacts took " << timer.elapsed();
timer.restart();
//bool ret = RecordReplayPlugin::publishData(desiredTime);
//DEBUG_LOG(m_logger) << "publish ret: " << ret;
DEBUG_LOG(m_logger) << "publish totally took " << timer2.elapsed();
return true;
}
bool RecordReplayPluginCustom::waitForAsyncOperations(qint64 desiredTime)
{
Q_UNUSED(desiredTime);
bool ret = RecordReplayPlugin::waitForAsyncOperations(desiredTime);
DEBUG_LOG(m_logger) << "waitForAsyncOperations ret: " << ret;
return ret;
}
void RecordReplayPluginCustom::clearData()
{
RecordReplayPlugin::clearData();
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,48 @@
/**
* @RDE_HEADER@
* Custom record/replay functions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/aes_record_replay_plugin.h>
#include <AreaEffectSimulation/aes_dts.h>
// fixup include path on 42.3 -> missing interface
#include <../../Common/PluginInterfaces/record_replay_plugin.h>
namespace AreaEffectSimulation
{
class AESRECORDREPLAYPLUGIN_EXPORT RecordReplayPluginCustom final : public RecordReplayPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "special:3:recordreplay.aes")
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;
private:
template<typename T>
void removeOld(QMultiMap<long long int , T> &map, qint64 desiredTime);
private:
std::shared_ptr<AreaEffectSimulation::Interface::Dts> m_ddsDts;
qint64 m_currentReplayTime;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,38 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
NGL_ADD_LIBRARY2(WS SV HLA Trajectory SHARED
trajectory_generator.cpp
trajectory_generator.h
trajectory_helper.cpp
trajectory_helper.h
trajectory_generator_vulcano.cpp
trajectory_generator_vulcano.h
trajectory_point.h
trajectory_transform.cpp
trajectory_transform.h
../logger.cpp
../logger.h
)
NGL_TARGET_INCLUDE_DIRECTORIES(Trajectory
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(Trajectory
PRIVATE
NGLLogger
AsmDataPluginInterface
ExmDataPluginInterface
PUBLIC
LtsElevation
LtsUtilsCore
LtsCoordTrans
Qt5::Core
Qt5::Gui
)
ADD_SUBDIRECTORY(UnitTest_Trajectory)
@@ -0,0 +1,36 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(LinkedSources
log4cxxTrajectoryUnitTest.props
)
SET(UnitTestSources
trajectory_unit_test.cpp
trajectory_unit_test.h
)
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestTrajectory ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestTrajectory
NGLLogger
nglcoreapplication
svcAesCommon
Qt5::Core
Qt5::Gui
Qt5::Test)
ADD_TEST(NAME aesUnitTestTrajectory COMMAND $<TARGET_FILE:aesUnitTestTrajectory>)
SET(LOG4CXX_PROPS_FILE log4cxxTrajectoryUnitTest.props)
ADD_CUSTOM_COMMAND(TARGET aesUnitTestTrajectory POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $<TARGET_FILE_DIR:aesUnitTestTrajectory>/${LOG4CXX_PROPS_FILE}
)
ENDIF()
@@ -0,0 +1,129 @@
log4j.reset=true
log4j.rootLogger=INFO, file, stdout
# SituationMap
###########################################################
#log4j.logger.SituationMap.SitMapUnitTest=DEBUG
#log4j.logger.SituationMap.SitMap=WARN
log4j.logger.SituationMap.LoggingTest=ERROR
log4j.logger.SituationMap.LoggingTest.Chan1=INFO
log4j.logger.SituationMap.LoggingTest.Chan2=ERROR
#log4j.logger.SituationMap.MapImpl=WARN
#log4j.logger.SituationMap.LayerFactoryImpl=INFO
#log4j.logger.SituationMap.CoordTranslationFunctorImpl=INFO
#log4j.logger.SituationMap.GridImpl=WARN
#log4j.logger.SituationMap.ObjectFactory=WARN
#log4j.logger.SituationMap.IdGenerator=TRACE
#log4j.logger.SituationMap.ObjectContainer=TRACE
#log4j.logger.SituationMap.ObjectContainerCTC=TRACE
#log4j.logger.SituationMap.ViewshedImpl=INFO
#log4j.logger.SituationMap.ViewshedBase=INFO
#log4j.logger.SituationMap.SymbolDictionary=WARN
#log4j.logger.SituationMap.SymbolDictionaryImpl=WARN
#log4j.logger.SituationMap.Sidc2525Delta=WARN
#log4j.logger.SituationMap.AbstractMapObjectImpl=INFO
#log4j.logger.SituationMap.AbstractEffectSimulationImpl=INFO
#log4j.logger.SituationMap.OriginTranslated=INFO
#log4j.logger.SituationMap.PositionsTranslated=INFO
#log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=INFO
#log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=INFO
#log4j.logger.SituationMap.BasicPathObjectImpl=INFO
#log4j.logger.SituationMap.IndicationContainer=INFO
#log4j.logger.SituationMap.AreaFireImpl=INFO
#log4j.logger.SituationMap.BiologicalAgentImpl=INFO
#log4j.logger.SituationMap.BuildingOutlineImpl=INFO
#log4j.logger.SituationMap.ChemicalAgentImpl=INFO
#log4j.logger.SituationMap.DangerAreaImpl=INFO
#log4j.logger.SituationMap.FunctionalAreaImpl=INFO
#log4j.logger.SituationMap.HitLineImpl=INFO
#log4j.logger.SituationMap.MineImpl=INFO
#log4j.logger.SituationMap.NuclearAgentImpl=INFO
#log4j.logger.SituationMap.ParticipantImpl=INFO
#log4j.logger.SituationMap.SingleFireImpl=INFO
#log4j.logger.SituationMap.AbstractGraphicalObject=WARN
#log4j.logger.SituationMap.GraphicalObjectEllipse=WARN
#log4j.logger.SituationMap.GraphicalObjectPath=WARN
#log4j.logger.SituationMap.GraphicalObjectMultiPath=WARN
#log4j.logger.SituationMap.GraphicalObjectPictureMarker=WARN
#log4j.logger.SituationMap.GraphicalObjectText=WARN
#log4j.logger.SituationMap.TacticalAppearance=INFO
#log4j.logger.SituationMap.MessageBase=INFO
# Appnd_File
###########################################################
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMap.log
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 %-5p %c{1} |#| %m (%-.30F:%L) %n
#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %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
#
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,83 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QCoreApplication>
#include <QtTest/QTest>
#include <NGLLogger/INGLLogger.h>
using LOGGER_T = NGL::INGLLogger&;
class UnitTestTrajectory : public QObject
{
Q_OBJECT
public:
UnitTestTrajectory();
private Q_SLOTS:
// void init();
void test_copy_long_vector();
void test_duration();
void test_calcDuration();
void test_calcDuration2();
void test_calcPeak();
void test_calcPeak2();
void test_calcPeak3_continuity();
void test_calcTimeToPeakAndBack();
void test_calcDuration_realistic40km();
void test_calcDuration_differentDistances();
void test_calcDuration_downToZero();
// for context AirSupport:
void test_realStartPoint_data();
void test_realStartPoint();
// generateTrajectoryBiedziak
void test_generateTrajectoryBiedziak();
// Vulcano:
void test_vulcano_beta_Abschusswinkel();
void test_vulcano_apogaeumCosmos();
void test_vulcano_apogaeum_data();
void test_vulcano_apogaeum();
void test_vulcano_apogaeumStrich_data();
void test_vulcano_apogaeumStrich();
void test_vulcano_tap_data();
void test_vulcano_tap();
void test_vulcano_ballisticCurve_data();
void test_vulcano_ballisticCurve();
void test_vulcano_trajectory_data();
void test_vulcano_trajectory();
// void cleanup();
private:
LOGGER_T m_logger;
};
@@ -0,0 +1,383 @@
/**
* @RDE_HEADER@
* LTS - logging facility
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2023-11-08
*/
#include "trajectory_generator.h"
#include "trajectory_generator_vulcano.h"
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Plugins/AssetManagement/asm_data_plugin_interface.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#ifdef NEVER // for csv file
# include <QFile>
# include <QDateTime>
# include <QTextStream>
#endif // NEVER
#include <cmath>
namespace AreaEffectSimulation
{
static TrajectoryGenerator *s_trajectoryGenerator = nullptr;
TrajectoryGenerator::TrajectoryGenerator()
: m_logger(getLogger(QStringLiteral("TrajectoryGenerator")))
{
}
TrajectoryGenerator *TrajectoryGenerator::getInstance(int exerciseId,
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc)
{
// Be aware: ONE TrajectoryGenerator per process, not per exercise
// That feature has to be added!
// In that case, have a look at LtsElevationManager::createInstance() and createInstanceForTrainingAreaId()
if (!s_trajectoryGenerator)
{
LOGGER_T logger = getLogger(QStringLiteral("TrajectoryGenerator"));
s_trajectoryGenerator = new TrajectoryGenerator();
qAddPostRoutine(&TrajectoryGenerator::cleanup);
// assign LtsElevationManager:
LtsElevation::LTS_ELEVATION_MANAGER_TYPE ltsElevationManager = LtsElevation::LtsElevationManager::createInstance(exerciseId, exmDtc, asmDtc);
#ifndef LTS_DEBUG
Q_ASSERT_X(ltsElevationManager, "LtsElevationManager", "createInstance failed");
#endif // LTS_DEBUG
if (ltsElevationManager)
{
ltsElevationManager->waitForInitialization();
DEBUG_LOG(logger) << LOGFUNCTION << ", ltsElevationManager->waitForInitialization() ... DONE, isValid = " << ltsElevationManager->isValid();
}
s_trajectoryGenerator->initElevation(ltsElevationManager);
}
return s_trajectoryGenerator;
}
TrajectoryGenerator *TrajectoryGenerator::getInstance()
{
if (!s_trajectoryGenerator)
{
s_trajectoryGenerator = new TrajectoryGenerator();
qAddPostRoutine(&TrajectoryGenerator::cleanup);
}
return s_trajectoryGenerator;
}
void TrajectoryGenerator::cleanup()
{
delete s_trajectoryGenerator;
}
void TrajectoryGenerator::initElevation(LtsElevation::LtsElevationManager *ltsElevationManager)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_ltsElevationManager = ltsElevationManager;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LtsElevation::LtsElevationManager *TrajectoryGenerator::getElevationManager()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return m_ltsElevationManager;
}
bool TrajectoryGenerator::hasElevationManager() const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return m_ltsElevationManager;
}
// Ideal ballistic curve, but with time values:
bool TrajectoryGenerator::generateTrajectoryBiedziak(const LtsUtils::QPoint3D &originatorPosition_geo,
const LtsUtils::QPoint3D &idealHit_geo,
double elevation_angle_degree,
TrajectoryGeoPosition &trajectoryOut)
{
return generateTrajectoryBiedziak<std::vector<Core::GeoPosition>>(originatorPosition_geo,
idealHit_geo,
elevation_angle_degree,
trajectoryOut);
}
//
// Vulcano
//
bool TrajectoryGenerator::generateTrajectoryVulcano(const QPoint3D &originatorPosition_geo, const QPoint3D &idealHit_geo,
double v0, double betaRad,
double alphaRad, double tapTimeSec,
TrajectoryGeneratorVulcano::Options options,
TrajectoryGeoPositionRelTime &trajectoryTimeAwareOut,
Core::GeoPosition &idealTapAbsoluteGeoOut,
QString *reasonOut)
{
LOGGER_T logger = getLogger(QStringLiteral("TrajectoryGenerator"));
// use zone of idealHit:
INFO_LOG(logger) << " orgPos geo " << originatorPosition_geo;
INFO_LOG(logger) << " iHPos geo " << idealHit_geo;
const LtsCoordTrans::UtmCoordinate &idealHitUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(idealHit_geo.toPointF());
const LtsCoordTrans::UtmCoordinate &originUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(originatorPosition_geo.toPointF(), idealHitUtm.zone);
INFO_LOG(logger) << " originUtm " << originUtm.mgrs();
INFO_LOG(logger) << " idealHitUtm " << idealHitUtm.mgrs();
TRAJECTORY_METRIC trajectoryMetricOut;
const QPoint3D origin_metric (originUtm.x, originUtm.y, originatorPosition_geo.z());
const QPoint3D idealHit_metric(idealHitUtm.x, idealHitUtm.y, idealHit_geo.z());
double vGlidingOut = 1;
double vFallingOut = 1;
TrajectoryGeneratorVulcano::getSpeedPerPhase(v0, betaRad, vGlidingOut, vFallingOut);
QString localReasonOut;
QPoint3D idealTapAbsoluteOut;
const bool success =
TrajectoryGeneratorVulcano::trajectoryVulcano(origin_metric, idealHit_metric,
v0, betaRad,
vGlidingOut,
vFallingOut, alphaRad, tapTimeSec,
options,
trajectoryMetricOut,
idealTapAbsoluteOut,
&localReasonOut);
if (!localReasonOut.isEmpty())
WARNING_LOG(logger) << " trajectoryVulcano failed, reasonOut = '" << localReasonOut << "'";
//
// for documentation, it may be helpful to write the trajectoryMetric to a csv file
//
#ifdef NEVER
{
static QString filePath(QStringLiteral("/tmp/trajectory_dump.csv"));
QFile data(filePath);
QTextStream output(&data);
if (data.open(QFile::WriteOnly | QIODevice::Append |QFile::Truncate))
{
output.setRealNumberPrecision(3);
output.setRealNumberNotation(QTextStream::RealNumberNotation::FixedNotation);
output << "TrajectoryVulcano at: " << QDateTime::currentDateTime().toString(QStringLiteral("yyyy.MM.dd hh:mm::ss")) << "\n";
output << "v0: " << v0 << "\n";
output << "betaDeg: " << betaRad*LtsCoordTrans::rad2deg() << "\n";
output << "vGlidingOut: " << vGlidingOut << "\n";
output << "vFallingOut: " << vFallingOut << "\n";
output << "alphaDeg: " << alphaRad*LtsCoordTrans::rad2deg() << "\n";
output << "tapTimeSec: " << tapTimeSec << "\n";
const double distance = (origin_metric - idealHit_metric).length();
output << "distance: " << distance << "\n";
output << "x_rel ; y_rel ; z ; relativeTime" << "\n";
if (!trajectoryMetricOut.isEmpty())
{
QPoint4D startPoint = trajectoryMetricOut.first();
for (const QPoint4D &p4D : qAsConst(trajectoryMetricOut))
{
output << p4D.x() - startPoint.x() << ";"
<< p4D.y() - startPoint.y() << ";"
<< p4D.z() << ";"
<< p4D.time() << ";"
<< "\n";
} // for
}
}
}
#endif // NEVER
//
//
//
// convert metric to geo/wgs84
{
LtsCoordTrans::UtmCoordinate tapUtm(idealHitUtm.zone, idealTapAbsoluteOut.x(), idealTapAbsoluteOut.y());
const QPointF &tapGeo = tapUtm.geographic();
idealTapAbsoluteGeoOut = Core::GeoPosition(tapGeo.x(), tapGeo.y(), idealTapAbsoluteOut.z());
}
trajectoryTimeAwareOut.clear();
trajectoryTimeAwareOut.reserve(trajectoryMetricOut.size());
for (const QPoint4D& pnt4d : qAsConst(trajectoryMetricOut))
{
LtsCoordTrans::UtmCoordinate pntUtm(idealHitUtm.zone, pnt4d.x(), pnt4d.y());
const QPointF &pntGeo = pntUtm.geographic();
// !!! relativeTime is in msec!!!
trajectoryTimeAwareOut.emplace_back( Core::GeoPositionRelativeTime(pntGeo.x(), pntGeo.y(), pnt4d.z(), pnt4d.time()*1000.0) );
}
if (reasonOut)
*reasonOut = localReasonOut;
return success;
}
//
// real implementation
//
inline void addItemTo(std::vector<Core::GeoPosition> &cont,
double x, double y, double z)
{
cont.emplace_back(Core::GeoPosition(x, y, z));
}
template<typename CONT_T>
bool TrajectoryGenerator::generateTrajectoryBiedziak(const LtsUtils::QPoint3D &originatorPosition_geo,
const LtsUtils::QPoint3D &impactPosition_geo,
double elevation_angle_degree,
CONT_T &trajectoryOut)
{
// use zone of impact:
INFO_LOG(m_logger) << " oPosition geo " << originatorPosition_geo.x() << " - " << originatorPosition_geo.y();
INFO_LOG(m_logger) << " iPosition geo " << impactPosition_geo.x() << " - " << impactPosition_geo.y();
const LtsCoordTrans::UtmCoordinate &iPosition = LtsCoordTrans::UtmCoordinate::fromGeographic(impactPosition_geo.toPointF());
const LtsCoordTrans::UtmCoordinate &oPosition = LtsCoordTrans::UtmCoordinate::fromGeographic(originatorPosition_geo.toPointF(), iPosition.zone);
INFO_LOG(m_logger) << " oPosition " << oPosition.mgrs();
INFO_LOG(m_logger) << " iPosition " << iPosition.mgrs();
const double distance = sqrt(pow(iPosition.x - oPosition.x, 2.) + pow(iPosition.y - oPosition.y, 2.));
double oHeight = originatorPosition_geo.z();
double iHeight = impactPosition_geo.z();
if (std::isnan(oHeight) && m_ltsElevationManager)
{
oHeight = m_ltsElevationManager ? m_ltsElevationManager->elevation(originatorPosition_geo.x(), originatorPosition_geo.y()) : 0;
if (oHeight < 300)
oHeight = 100;
INFO_LOG(m_logger) << " oPosition " << oPosition << " has no zValue provided (NaN), update via ltsElevationManager...";
}
if (std::isnan(iHeight) && m_ltsElevationManager)
{
iHeight = m_ltsElevationManager ? m_ltsElevationManager->elevation(impactPosition_geo.x(), impactPosition_geo.y()) : 0;
if (iHeight < 300)
iHeight = 100;
INFO_LOG(m_logger) << " iPosition " << iPosition << " has no zValue provided (NaN), update via ltsElevationManager.. ";
}
INFO_LOG(m_logger) << " oPosition " << oPosition << " ; oHeight=" << oHeight;
INFO_LOG(m_logger) << " iPosition " << iPosition << " ; iHeight=" << iHeight;
// z-Values:
const QVector<double> &zValues = this->trajectoryBiedziak(distance, iHeight - oHeight, elevation_angle_degree);
if (zValues.isEmpty())
return false;
trajectoryOut.clear();
const QPointF &oPositionGeo = oPosition.geographic();
addItemTo(trajectoryOut,
oPositionGeo.x(), oPositionGeo.y(), oHeight);
const QPointF &step = (QPointF(iPosition.x, iPosition.y) - QPointF(oPosition.x, oPosition.y)) / (zValues.size() - 1);
QPointF runner;
for (int i = 1; i < zValues.size(); ++i)
{
runner += step;
const QPointF &p2D = oPosition.geographic(runner);
addItemTo(trajectoryOut,
p2D.x(), p2D.y(), oHeight + zValues[i]); // x, y, z
} // for
//// DEBUG_LOG(m_logger) << string4poly(geo);
return true;
}
QVector<double> TrajectoryGenerator::trajectoryBiedziak(const double &distance,
const double &heightOffset,
double elevation_angle_degree,
int steps)
{
const double coefLinear = tan(elevation_angle_degree * LtsCoordTrans::deg2rad());
const double coefSquare = (heightOffset - distance * coefLinear) / pow(distance, 2.0);
const double delta = distance / steps;
double x = 0.;
QVector<double> yValues; // later interpreted as z-Values
yValues.reserve(steps);
yValues << 0.0;
for (int i = 0; i < steps; ++i)
{
x += delta;
yValues << (coefLinear * x + coefSquare * pow(x, 2.));
} // for
return yValues;
}
const QString TrajectoryGenerator::string4poly(const QPair<QPolygonF, QVector<double> > &pair)
{
if (pair.first.isEmpty())
return (QStringLiteral("Empty Polygon!"));
int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(pair.first[0]).zone;
QStringList pointSl;
pointSl.reserve(pair.first.size());
for (int i = 0; i < pair.first.size(); ++i)
pointSl << QString(QStringLiteral("utm=%1 h=%2")).arg(LtsCoordTrans::UtmCoordinate::fromGeographic(pair.first[i], zone).mgrs(), QString::number(pair.second[i], 'f', 1));
return QStringLiteral("%1 Values (%2)").arg(pair.first.count()).arg(pointSl.join(QStringLiteral(" # ")));
}
//const QString TrajectoryGenerator::string4poly(const TrajectoryPoint3D &trajectory)
//{
// if (trajectory.isEmpty())
// return (QStringLiteral("Empty Polygon!"));
// const Point3D &p3D = trajectory.first();
// int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(p3D.toPointF()).zone;
// QStringList pointSl;
// pointSl.reserve(trajectory.size());
// for (int i = 0; i < trajectory.size(); ++i)
// pointSl << QString(QStringLiteral("utm=%1 h=%2")).arg(LtsCoordTrans::UtmCoordinate::fromGeographic(p3D.toPointF(), zone).mgrs(), QString::number(p3D.z(), 'f', 1));
// return QStringLiteral("%1 Values (%2)").arg(trajectory.count()).arg(pointSl.join(QStringLiteral(" # ")));
//}
} // namespace AreaEffectSimulation
@@ -0,0 +1,115 @@
/**
* @RDE_HEADER@
* TrajectoryGenerator
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2023-11-08
*/
#pragma once
#include "../logger.h"
#include "trajectory_generator_vulcano.h"
#include <Core/core.hpp> // Lts/DDS/Core/core.hpp for Core::GeoPosition
#include <LtsElevation/lts_elevation_manager.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <Trajectory/trajectory_export.h>
#include <QObject>
#include <QPair>
#include <QPolygonF>
#include <vector>
// forward
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
} // namespace ExerciseManagement
namespace AssetManagement
{
namespace Interface
{
class Dtc;
}
} // namespace AssetManagement
namespace AreaEffectSimulation
{
// version 1
using TrajectoryGeoPosition = std::vector<Core::GeoPosition>;
// version 2
using TrajectoryGeoPositionRelTime = std::vector<Core::GeoPositionRelativeTime>;
using QPoint3D = LtsUtils::QPoint3D;
//
// class TrajectoryGenerator
//
class TRAJECTORY_EXPORT TrajectoryGenerator : public QObject
{
public:
/*QT_DEPRECATED*/ static TrajectoryGenerator *getInstance(int exerciseId,
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc);
static TrajectoryGenerator *getInstance();
static void cleanup();
void initElevation(LtsElevation::LtsElevationManager *ltsElevationManager);
LtsElevation::LtsElevationManager *getElevationManager();
bool hasElevationManager() const;
~TrajectoryGenerator() = default;
/*! \name Ideal trajectory using Core::GeoPosition at return trajectory ... */
//@{
bool generateTrajectoryBiedziak(const LtsUtils::QPoint3D &originatorPosition_geo,
const LtsUtils::QPoint3D &idealHit_geo,
double elevation_angle_degree, // aka beta
TrajectoryGeoPosition &trajectoryOut);
//@}
static bool generateTrajectoryVulcano(const QPoint3D &originatorPosition_geo,
const QPoint3D &idealHit_geo,
double v0,
double betaRad, // Abschussgeschwindigkeit, -winkel ( betaRad ~ elevation_angle_degree
double alphaRad,
double tapTimeSec, // Fallwinkel, -geschwindigkeit, -zeit
TrajectoryGeneratorVulcano::Options options,
TrajectoryGeoPositionRelTime &trajectoryTimeAwareOut,
Core::GeoPosition &idealTapAbsoluteGeoOut,
QString *reasonOut);
protected:
/*! \name Ideal trajectory */
template <typename CONT_T>
bool generateTrajectoryBiedziak(const LtsUtils::QPoint3D &originatorPosition_geo,
const LtsUtils::QPoint3D &impactPosition_geo,
double elevation_angle_degree,
CONT_T &trajectoryOut);
protected:
explicit TrajectoryGenerator();
public:
// For UnitTest:
friend class UnitTestTrajectoryHelper;
// calculate the z-Values of the trajectory:
static QVector<double> trajectoryBiedziak(const double &distance, const double &heightOffset, double elevation_angle_degree, int steps = 72);
static const QString string4poly(const QPair<QPolygonF, QVector<double>> &pair);
LtsElevation::LtsElevationManager *m_ltsElevationManager{nullptr};
LOGGER_T m_logger;
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,676 @@
/**
* @RDE_HEADER@
* LTS - logging facility
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2023-11-08
*/
#include "trajectory_generator_vulcano.h"
#include <QObject>
#include <QPointF>
namespace AreaEffectSimulation
{
//#define G 9.80665
#define G 9.81
inline void addItemLocal(TRAJECTORY_METRIC &trajectoryRef, const QPoint3D &point, double time = 0.0)
{
trajectoryRef.push_back(QPoint4D(point.x(), point.y(), point.z(), time));
}
LOGGER_T getLocalLogger()
{
return getLogger(QStringLiteral("TrajectoryGeneratorVulcano"));
}
bool TrajectoryGeneratorVulcano::trajectoryVulcano(const QPoint3D &origin, const QPoint3D &idealHit,
double v0, double betaRad,
double vGliding,
double vFalling, double alphaRad, double tapTimeSec,
TrajectoryGeneratorVulcano::Options options,
TRAJECTORY_METRIC &trajectoryOut,
QPoint3D &idealTapAbsoluteOut,
QString *reasonOut)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << LOGFUNCTION
<< ", origin " << origin << ", idealHit " << idealHit
<< ", v0=" << v0 << ", betaDeg=" << betaRad*LtsCoordTrans::rad2deg()
<< ", vGliding=" << vGliding
<< ", vFalling=" << vFalling << ", alphaDeg=" << alphaRad*LtsCoordTrans::rad2deg() << ", tapTimeSec=" << tapTimeSec
<< ", options " << options;
idealTapAbsoluteOut = QPoint3D();
// OptRasterTapHit
//
// very simple check:
//
const QVector2D dist = QVector2D(idealHit.toPointF() - origin.toPointF());
if (dist.length() < 100)
{
WARNING_LOG(logger) << LOGFUNCTION << ", dist is very small (100m)";
if (reasonOut)
*reasonOut = QObject::tr("DistanceError(%1 > %2)").arg(dist.length()).arg(100);
return false;
}
if (v0 < vFalling)
{
WARNING_LOG(logger) << LOGFUNCTION << ", v0 ist less than vFalling";
if (reasonOut)
*reasonOut = QObject::tr("SpeedError(v0=%1 ; vF %2)").arg(v0).arg(vFalling);
return false;
}
//
// line between origin and idealHit:
//
const QVector2D &distHitAndOrigin = QVector2D(idealHit.toPointF() - origin.toPointF());
const double length = distHitAndOrigin.length();
const double factorX = distHitAndOrigin.x() / length;
const double factorY = distHitAndOrigin.y() / length;
//
// Apo
//
const QPointF &apo2D = XY::calculateApogaeum(v0, betaRad);
// translate from XY to XYZ
QPoint3D apoAbs(apo2D.x() * factorX, // .x() !!
apo2D.x() * factorY, // .x() !!
apo2D.y());
apoAbs += origin;
// check origin -> apo -> idealHit
if (!checkSimple(origin, apoAbs, idealHit, reasonOut))
{
WARNING_LOG(logger) << LOGFUNCTION << ", checkOriginApoHit failed";
return false;
}
//
// TAP
//
// vFalling is maybe slightly smaller than v0;
// tapTime is typically 8 sec
const QPointF &tapRelToImpact = XY::calculateTapRelToIdealHit(vFalling, alphaRad, tapTimeSec);
// translate from XY to XYZ
QPoint3D tapAbs(tapRelToImpact.x() * factorX, // .x() !!
tapRelToImpact.x() * factorY, // .x() !!
tapRelToImpact.y());
tapAbs += idealHit;
// set out value:
idealTapAbsoluteOut = tapAbs;
// check origin -> apo -> TAP - >idealHit
if (!checkSimple(origin, apoAbs, tapAbs, idealHit, reasonOut))
{
WARNING_LOG(logger) << LOGFUNCTION << " checkOriginApoTapHit failed";
return false;
}
//
// raster phase 1 (ballistic curve)
//
// try to generate a value each second:
//
double x_distancePhase1 = apo2D.x(); // maybe move to Apo' in future (intersection between ballisitic curve an line to TAP)
// does not work: const double x_distancePhase1Strich = XY::calculateApogaeumStrichEval(origin.toPointF(), idealHit.toPointF(), v0, betaRad, tapAbs.toPointF());
// so create temoporary originTemp, idealHitTemp: // ignoring the zValue
// {
// const QPointF originTemp;
// const QPointF idealHitTemp(dist.length(), 0);
// const QPointF tapTemp = idealHitTemp + tapRelToImpact;
// double x_distancePhase1Strich = XY::calculateApogaeumStrichEval(originTemp, idealHitTemp, v0, betaRad, tapTemp);
// ERROR_LOG(logger) << " x_distancePhase1a = " << x_distancePhase1;
// ERROR_LOG(logger) << " x_distancePhase1b = " << x_distancePhase1Strich;
// if (!std::isnan(x_distancePhase1Strich) && (x_distancePhase1Strich > x_distancePhase1))
// x_distancePhase1 = x_distancePhase1Strich;
// ERROR_LOG(logger) << " apo2D.x() = " << apo2D.x()
// << ", x_distancePhase1Strich = " << x_distancePhase1Strich
// << ", finally use: x_distancePhase1 = " << x_distancePhase1;
// }
const double secPerIntervall = 1;
double x_delta_per_step_out = -1;
double secPerIntervall_out = -1;
const QVector<QPointF> &phase1_bal = XY::generateBallisticCurveUntil(v0, betaRad, x_distancePhase1, secPerIntervall,
&x_delta_per_step_out, &secPerIntervall_out);
trajectoryOut.clear();
trajectoryOut.reserve(phase1_bal.size()+5);
int cnt = 0;
for (const QPointF &xyVertex : qAsConst(phase1_bal))
{
// translate from XY to XYZ
const QPoint3D p3d_offset(xyVertex.x() * factorX, // xyVertex.x()
xyVertex.x() * factorY, // xyVertex.x()
xyVertex.y());
// is relative to origin (gun)
addItemLocal(trajectoryOut, p3d_offset+origin, (secPerIntervall_out*cnt));
++cnt;
} // for
// ToDo: Check!!!
// const double distPhase1 = apo2D.x();
// const double timeSpanForPhase1Sec = distPhase1 / v0 * cos(betaRad); // / xy-Anteil der Abschussgeschwindigkeit
const double timeSpanForPhase1Sec = trajectoryOut.last().time();
double timeSpanForPhase2Sec = -1;
double timeSpanForPhase3Sec = -1;
INFO_LOG(logger) << "PHASE 1: generateBallisticCurveUntil: "
<< ", phase1_bal.size() = " << phase1_bal.size()
<< ", x_delta_per_step_out = " << x_delta_per_step_out
<< ", secPerIntervall_out = " << secPerIntervall_out
<< ", v0 * cos(betaRad) = " << v0 * cos(betaRad)
<< ", timeSpanForPhase1Sec = " << timeSpanForPhase1Sec;
//
// add gliding phase / tap
//
double timeOffset = 0;
const double distPhase2 = (apoAbs - tapAbs).length();
timeSpanForPhase2Sec = distPhase2 / vGliding;
INFO_LOG(logger) << " continue with PHASE 2"
<< ", distPhase2 = " << distPhase2
<< ", timeSpanForPhase2Sec = " << timeSpanForPhase2Sec;
// continue rastering?
if (options.testFlag(Option::OptRasterApoTap))
{
// which kind of rastering?
if (options.testFlag(Option::OptRasterMetrical))
{
// dist from APO (or APO') to TAP: ( apoAbs , tapAbs )
double realDelta_xy_out = -1;
rasterLineByDistance(apoAbs, tapAbs, x_delta_per_step_out, trajectoryOut, &realDelta_xy_out);
DEBUG_LOG(logger) << " rastering was done for line ApoTap: realDelta_xy_out " << realDelta_xy_out;
}
else
{
// we can use x_delta_per_step_out and or secPerIntervall_out
double realDeltaTime_out = -1;
rasterLineByTime(apoAbs, tapAbs,
timeSpanForPhase1Sec, vGliding, 1,
trajectoryOut, &realDeltaTime_out);
DEBUG_LOG(logger) << " rastering was done for line ApoTap: "
<< ", distPhase2 =" << distPhase2
<< ", timeSpanForPhase2Sec =" << timeSpanForPhase2Sec
<< ", realDeltaTime_out " << realDeltaTime_out;
}
} else {
// TabAbs has to be added in any case!!!
addItemLocal(trajectoryOut, tapAbs, timeSpanForPhase1Sec + timeSpanForPhase2Sec);
}
// prepare timeOffset for next phase:
const QPoint4D &pnt4DRef = trajectoryOut.back();
timeOffset = pnt4DRef.time();
//
// add falling phase / idealHit
//
if (options.testFlag(Option::OptIncludeIdealHit))
{
const double distPhase3 = (idealHit - tapAbs).length();
timeSpanForPhase3Sec = distPhase3 / vFalling;
INFO_LOG(logger) << " continue with PHASE 3"
<< ", distPhase3 = " << distPhase3
<< ", timeSpanForPhase3Sec = " << timeSpanForPhase3Sec;
if (options.testFlag(Option::OptRasterTapHit))
{
// which kind of rastering?
if (options.testFlag(Option::OptRasterMetrical))
{
// we can use x_delta_per_step_out and or secPerIntervall_out
// dist from TAP to hit: ( tapAbs , idealHit )
double realDelta_xy_out = -1;
rasterLineByDistance(tapAbs, idealHit, x_delta_per_step_out, trajectoryOut, &realDelta_xy_out);
DEBUG_LOG(logger) << " rastering was done for line TapHit: realDelta_xy_out " << realDelta_xy_out;
} else {
double realDeltaTime_out = -1;
rasterLineByTime(tapAbs, idealHit,
timeOffset, vFalling, 1,
trajectoryOut, &realDeltaTime_out);
DEBUG_LOG(logger) << " rastering was done for line TapHit: realDeltaTime_out " << realDeltaTime_out
<< ", distPhase3 = " << distPhase3
<< ", vFalling = " << vFalling
<< ", timeSpanForPhase3Sec = " << timeSpanForPhase3Sec;
}
} else {
// simply add single point:
addItemLocal(trajectoryOut, idealHit, timeSpanForPhase1Sec + timeSpanForPhase2Sec + timeSpanForPhase3Sec);
}
}
const double timeSpanComplete = timeSpanForPhase1Sec + timeSpanForPhase2Sec + timeSpanForPhase3Sec;
const double diffTime = trajectoryOut.last().time() - timeSpanComplete;
INFO_LOG(logger) << " rastering was done"
<< ", timeSpanForPhase1Sec = " << timeSpanForPhase1Sec
<< ", timeSpanForPhase2Sec = " << timeSpanForPhase2Sec
<< ", timeSpanForPhase3Sec = " << timeSpanForPhase3Sec
<< ", timeSpanComplete = " << timeSpanComplete
<< " ### diffTime related to .last() = " << diffTime;
Q_ASSERT(diffTime < 1);
return true;
}
bool TrajectoryGeneratorVulcano::checkSimple(const QPoint3D &origin,
const QPoint3D &apoAbs,
const QPoint3D &idealHit,
QString *reasonOut)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << ", origin = " << origin
<< ", apoAbs = " << apoAbs
<< ", idealHit = " << idealHit;
const QVector2D hitDist = QVector2D(idealHit.toPointF() - origin.toPointF());
const QVector2D apoDist = QVector2D(apoAbs.toPointF() - origin.toPointF());
// apo dist must be less than the half of (idealHit-origin)
if (apoDist.length() > (hitDist.length() / 2.0))
{
WARNING_LOG(logger) << LOGFUNCTION << ", apo dist must be less than the half of (idealHit-origin)"
<< ", origin = " << origin
<< ", apoAbs = " << apoAbs
<< ", idealHit = " << idealHit
<< ", apoDist.length() = " << apoDist.length()
<< ", hitDist.length()/2 = " << hitDist.length()/2;
if (reasonOut)
*reasonOut = QObject::tr("DistanceError(apogaeumDistance=%1 = impactDistance/2=%2)").arg(apoDist.length()).arg((hitDist.length() / 2.0));
return false;
}
INFO_LOG(logger) << LOGFUNCTION << ", apo dist OKAY :-) the half of (idealHit-origin)"
<< ", origin = " << origin
<< ", apoAbs = " << apoAbs
<< ", idealHit = " << idealHit
<< ", apoDist.length() = " << apoDist.length()
<< ", hitDist.length()/2 = " << hitDist.length()/2;
if (apoAbs.z() < idealHit.z())
{
ERROR_LOG(logger) << LOGFUNCTION << ", apoAbs.z() is less than idealHit.z()"
<< "( apoAbs=" << apoAbs
<< "; idealHit=" << idealHit << ")";
if (reasonOut)
*reasonOut = QObject::tr("HeigthError(apogaeum.z=%1 >= impact.z=%2)").arg(apoAbs.z()).arg(idealHit.z());
return false;
}
return true;
}
bool TrajectoryGeneratorVulcano::checkSimple(const QPoint3D &origin,
const QPoint3D &apoAbs,
const QPoint3D &tabAbs,
const QPoint3D &idealHit, QString *reasonOut)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << LOGFUNCTION
<< ", origin = " << origin
<< ", apoAbs = " << apoAbs
<< ", tabAbs = " << tabAbs
<< ", idealHit = " << idealHit;
// apo dist must be less than the half of (idealHit-origin)
if (apoAbs.z() < tabAbs.z())
{
WARNING_LOG(logger) << LOGFUNCTION << ", apo.z() dist must higher than tab.z()"
<< "( apoAbs=" << apoAbs
<< "; tabAbs=" << tabAbs << ")";
if (reasonOut)
*reasonOut = QObject::tr("HeigthError(apogaeum.z=%1 >= tap.z=%2)").arg(apoAbs.z()).arg(tabAbs.z());
return false;
}
// tabAbs must 'between' apo and idealHit;
const QVector2D apoDist = QVector2D(apoAbs.toPointF() - origin.toPointF());
const QVector2D tabDist = QVector2D(tabAbs.toPointF() - origin.toPointF());
const QVector2D hitDist = QVector2D(idealHit.toPointF() - origin.toPointF());
if (tabDist.length() < apoDist.length())
{
WARNING_LOG(logger) << LOGFUNCTION << ", tab dist must be less than apo dist"
<< "( apoAbs=" << apoAbs
<< "; tabAbs=" << tabAbs
<< "; idealHit=" << idealHit << ")";
if (reasonOut)
*reasonOut = QObject::tr("DistanceError(tabDist=%1 >= apoDist=%2)").arg(tabDist.length()).arg(apoDist.length());
return false;
}
if (hitDist.length() < tabDist.length())
{
WARNING_LOG(logger) << LOGFUNCTION << ", hitDist dist must be less than tabDist dist"
<< "( apoAbs=" << apoAbs
<< "; tabAbs=" << tabAbs
<< "; idealHit=" << idealHit << ")";
if (reasonOut)
*reasonOut = QObject::tr("DistanceError(hitDist=%1 >= tabDist=%2)").arg(hitDist.length()).arg(tabDist.length());
return false;
}
INFO_LOG(logger) << LOGFUNCTION
<< ", apoAbs=" << apoAbs
<< ", tabAbs=" << tabAbs
<< ", idealHit=" << idealHit;
return true;
}
void TrajectoryGeneratorVulcano::getSpeedPerPhase(double v0_in, double betaRad,
double &vGlidingOut, double &vFallingOut)
{
LOGGER_T logger = getLocalLogger();
Q_UNUSED(betaRad);
vGlidingOut = 700; // Gleitgeschwindigkeit
vFallingOut = 375; // 3000m Höhe bei TapT=8 sec -> 375 m/s // Fallgeschwindigkeit
DEBUG_LOG(logger) << " v0_in " << v0_in
<< ", betaDeg " << betaRad * LtsCoordTrans::rad2deg()
<< ", vGlidingOut " << vGlidingOut << ", vFallingOut " << vFallingOut;
}
void TrajectoryGeneratorVulcano::rasterLineByDistance(const QPoint3D &p0, const QPoint3D &p1,
double desiredDelta_xy,
TRAJECTORY_METRIC &trajectoryOut, double *realDelta_xy_out)
{
LOGGER_T logger = getLocalLogger();
// we can use x_delta_per_step_out and maybe secPerIntervall_out
// dist from APO (or APO') to TAP: ( apoAbs , tapAbs )
// DistXY:
const double distMeter = QVector2D( p1.toPointF() - p0.toPointF() ).length();
const int steps = (distMeter/desiredDelta_xy); // implicit round
const double realDelta_x = distMeter / steps;
DEBUG_LOG(logger) << LOGFUNCTION
<< " desiredDelta_xy " << desiredDelta_xy
<< ", distMeter " << distMeter
<< ", steps " << steps
<< ", realDelta_x " << realDelta_x;
// split 3dim Vector between both points according step:
const QPoint3D &dist3D = p1 - p0; // keep in mind: tapAbs - apoAbs
// step by distance
for (int i=1; i <= steps; ++i) // use 'i <= steps'
{
// try to avoid to much calculation, like:
// const QPoint3D &curPointAbs = p0 + (dist3D * i / steps);
// very small neg values may occur: item (30000,70,2.27374e-13)
// ^^^^^^^^^^^
const QPoint3D curPointAbs = (i==steps) ? p0 + dist3D : p0 + (dist3D * i / steps);
// DEBUG_LOG(logger) << " i " << i
// << ", curPointAbs = " << curPointAbs;
addItemLocal(trajectoryOut, curPointAbs);
} // for
if (realDelta_xy_out)
*realDelta_xy_out = realDelta_x;
}
void TrajectoryGeneratorVulcano::rasterLineByTime(const QPoint3D &p0, const QPoint3D &p1,
double timeOffsetSec, double speedMeterPerSec, double desiredDeltaTime,
TRAJECTORY_METRIC &trajectoryOut, double *realDeltaTime_out)
{
LOGGER_T logger = getLocalLogger();
const QPoint3D &dist3D = p1 - p0; // keep in mind: tapAbs - apoAbs
const double timeSpanForDistance = dist3D.length() / speedMeterPerSec;
Q_ASSERT(timeSpanForDistance > 1);
const int steps = timeSpanForDistance / desiredDeltaTime; // implicit round;
const double realDeltaTime = timeSpanForDistance / steps;
Q_ASSERT(realDeltaTime > 0);
DEBUG_LOG(logger) << LOGFUNCTION
<< " timeOffsetSec " << timeOffsetSec
<< ", speedMeterPerSec " << speedMeterPerSec
<< ", desiredDeltaTime " << desiredDeltaTime
<< ", dist3D.length " << dist3D.length()
<< ", steps " << steps
<< ", realDeltaTime " << realDeltaTime;
// split 3dim Vector between both points according step:
// step by time:
for (int i=1; i <= steps; ++i) // use 'i <= steps'
{
// try to avoid to much calculation, like:
// const QPoint3D &curPointAbs = p0 + (dist3D * i / steps);
// very small neg values may occur: item (30000,70,2.27374e-13)
// ^^^^^^^^^^^
const QPoint3D &curPointAbs = (i==steps) ? p0 + dist3D : p0 + (dist3D * i / steps);
double timeAbsToOffset = timeOffsetSec + (i*realDeltaTime);
// DEBUG_LOG(logger) << " i " << i
// << ", curPointAbs " << curPointAbs
// << " - " << " timeAbsToOffset: " << timeAbsToOffset;
addItemLocal(trajectoryOut, curPointAbs, timeAbsToOffset);
} // for
if (realDeltaTime_out)
*realDeltaTime_out = realDeltaTime;
}
//
// some helper
//
namespace XY
{
double calculateBetaRad(double v0, double peakHeightRelToOrigin, QString *reasonOut)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << LOGFUNCTION << ", v0 = " << v0 << ", peakHeightRelToOrigin = " << peakHeightRelToOrigin;
const double betaRad = asin(sqrt(2.0 * G * peakHeightRelToOrigin / (v0 * v0)));
DEBUG_LOG(logger) << LOGFUNCTION << ", v0 = " << v0 << ", peakHeightRelToOrigin = " << peakHeightRelToOrigin
<< " -> betaRad = " << betaRad << " betaDeg = " << betaRad * LtsCoordTrans::rad2deg();
if (std::isnan(betaRad))
{
ERROR_LOG(logger) << LOGFUNCTION << ", v0 = " << v0 << ", peakHeightRelToOrigin = " << peakHeightRelToOrigin
<< " -> betaRad = " << betaRad << " betaDeg = " << betaRad * LtsCoordTrans::rad2deg();
if (reasonOut)
*reasonOut = QObject::tr("calculate angle beta failed(v0=%1 ; pH %2 ; beta=%3)").arg(v0).arg(peakHeightRelToOrigin).arg(betaRad);
}
return betaRad;
}
QPointF calculateApogaeum(double v0, double betaRad)
{
LOGGER_T logger = getLocalLogger();
TRACE_LOG(logger) << LOGFUNCTION << ", v0 = " << v0 << ", betaRad = " << betaRad << ", betaDeg = " << betaRad * LtsCoordTrans::rad2deg();
QPointF apogaeum;
apogaeum.rx() = v0 * v0 * sin(2.0 * betaRad) / (2.0 * G);
apogaeum.ry() = v0 * v0 * sin(betaRad) * sin(betaRad) / (2.0 * G);
INFO_LOG(logger) << LOGFUNCTION << ", v0 = " << v0 << ", betaRad = " << betaRad << ", betaDeg = " << betaRad * LtsCoordTrans::rad2deg()
<< ", apogaeum = " << apogaeum.x() << " # " << apogaeum.y();
return apogaeum;
}
double calculateApogaeumStrichEval(const QPointF &origin, const QPointF &idealHit,
double v0, double betaRad, const QPointF &tap_relToOrigin)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << LOGFUNCTION
<< ", origin = " << origin
<< ", idealHit = " << idealHit
<< ", v0 = " << v0 << ", betaRad = " << betaRad << ", betaDeg = " << betaRad * LtsCoordTrans::rad2deg()
<< ", tap_relToOrigin = " << tap_relToOrigin;
// Berechnung Flugzeit T_ApoStrich vom Start bis APO'
const double a = - G / 2.0;
const double b = v0 * sin(betaRad);
const double h_TAP = tap_relToOrigin.y(); // y-Koordinate (Höhe) vom TAP
// Unter der nur hier getroffenen Annahme, dass das Geschoss über den
// gesamten Verlauf der Fluggkurve mit einer konstanten Geschwindigkeit bewegt:
const double T_TAP = tap_relToOrigin.x() / v0; // Flugzeit des Geschosses vom Abschuss bis zum TAP
double temp = a * (T_TAP * ( a * T_TAP + b) - h_TAP);
if (temp < 0)
FATAL_LOG(logger) << " temp NEGATIVE! " << temp;
else
DEBUG_LOG(logger) << " temp POSITIVE! " << temp;
// const double T_ApoStrich = T_TAP + sqrt(a * (T_TAP * ( a* T_TAP + b) - h_TAP)) / a;
const double T_ApoStrich = T_TAP + sqrt(a * (T_TAP * ( a* T_TAP + b) - h_TAP)) / a;
// Berechnung Strecke vom Start bis APO' (aka distancePhase1a)
const double apoStrich_x = v0 * cos(betaRad) * T_ApoStrich;
DEBUG_LOG(logger) << LOGFUNCTION << ", T_TAP = " << T_TAP
<< ", temp = " << temp
<< ", T_ApoStrich = " << T_ApoStrich
<< ", apoStrich_x = " << apoStrich_x;
return apoStrich_x;
}
QPointF calculateTapRelToIdealHit(double vFalling, double alphaRad, double timeTapT_sec)
{
LOGGER_T logger = getLocalLogger();
DEBUG_LOG(logger) << LOGFUNCTION << ", v0 = " << vFalling
<< ", alphaRad = " << alphaRad << ", alphaDeg = " << alphaRad * LtsCoordTrans::rad2deg()
<< ", timeTapT_sec = " << timeTapT_sec;
QPointF tap;
tap.rx() = timeTapT_sec * vFalling * cos(alphaRad) * -1; // *-1 : move left to impact
tap.ry() = timeTapT_sec * vFalling * sin(alphaRad);
#ifdef LTS_DEBUG
const double lengthVec = QVector2D(tap).length();
const double lengthProduct = timeTapT_sec * vFalling;
const double quot = fabs(lengthVec / lengthProduct);
Q_ASSERT((quot-1.0) < 0.1);
#endif
return tap;
}
QVector<QPointF> generateBallisticCurveUntil(double v0, double betaRad,
double max_x_distance, double secPerIntervall,
double *deltaXperStep_out, double *secPerIntervall_out)
{
LOGGER_T logger = getLocalLogger();
QVector<QPointF> points;
const double perfectDelta_x = secPerIntervall * v0 * cos(betaRad);
const double distancePhase1 = max_x_distance; // similar to Apo
const int steps = (distancePhase1/perfectDelta_x); // implicit round
const double realDelta_x = distancePhase1 / steps;
INFO_LOG(logger) << LOGFUNCTION
<< ", betaDeg = " << betaRad*LtsCoordTrans::rad2deg()
<< ", max_x_distance = " << max_x_distance
<< ", perfectDelta_x = " << perfectDelta_x
<< ", realDelta_x = " << realDelta_x
<< ", steps = " << steps;
points.reserve(steps+5);
for (int curStep=0; curStep <= steps; ++curStep)
{
double x_val = curStep * realDelta_x;
double time = x_val / (v0 * cos(betaRad));
double h = v0 * time * sin(betaRad) - G * time * time / 2.0;
INFO_LOG(logger) << LOGFUNCTION
<< ", x_valx " << x_val
<< ", distancePhase1 " << distancePhase1
<< ", time " << time
<< ", h " << h;
points.append( QPointF(x_val, h) );
};
// Out-vars:
if (deltaXperStep_out)
*deltaXperStep_out = realDelta_x;
if (secPerIntervall_out)
*secPerIntervall_out = realDelta_x / (v0 * cos(betaRad));
INFO_LOG(logger) << LOGFUNCTION
<< ", betaDeg = " << betaRad*LtsCoordTrans::rad2deg()
<< ", max_x_distance = " << max_x_distance
<< ", perfectDelta_x = " << perfectDelta_x
<< ", realDelta_x = " << realDelta_x
<< ", steps = " << steps
<< ", points.size() = " << points.size()
<< ", expectedDuration = " << points.size()* (realDelta_x / (v0 * cos(betaRad)))
;
return points;
}
QPointF calculateTapRelToOrigin(const QPointF &idealHit, const QPointF &tap_relToIdealHit)
{
return idealHit + tap_relToIdealHit;
}
} // end namespace
} // namespace AreaEffectSimulation
@@ -0,0 +1,222 @@
/**
* @RDE_HEADER@
* TrajectoryGenerator for Vulcano
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2024-04-16
*/
#pragma once
#include "../logger.h"
#include <Trajectory/trajectory_export.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <QVector>
#include <QVector2D>
#include <cmath>
#include <iomanip> // std::setprecision
/*
LTD Laser Target Designator
SAL Semi Active Laser
TAP Target Acquisition Point
OTL Oberserver Target Line
GTL Gun Target Line
PTL Projectile Target Line
PRF Pulse Repitition Frequency
Part 1: Ballistische Flugphase
calcBalPhase
Part 2: Gleitphase
calcTapPhase ??
Part 3: EndAnflugPhase
calcSalPhase ???
unklar:
calcTAP
calcSalPhase
isSalTarget
Wichtige Notizen:
ETB-Bericht Rev. A
In der SAL-Endanflugphase beginnt die Zielerfassung mit dem SAL-Sensor.
Hierzu wird bei Erreichen des TAP in einer Höhe von ca. 3500m
die ballistische Haube des Geschosses abgesprengt, so dass der SAL-Sensor
freie Sicht hat.
*/
namespace AreaEffectSimulation
{
/*
zwei-dimensionale Betrachtung
y
| Apogaeum / Scheitelpunkt
| |
| _
| / \ ----- gliding
| / ----- gliding, continued until TAP (TargetAcquisitionPoint)
| / \
| / \
|/ beta alpha \
---------------------------------------------------------- x
^ originator/gun ^ ideal impact / hit
*/
using QPoint3D = LtsUtils::QPoint3D;
class QPoint4D : public QPoint3D
{
public:
explicit QPoint4D(double xpos=0.0, double ypos=0.0, double zpos=0.0, double time=0.0)
: QPoint3D(xpos, ypos, zpos)
, m_time(time)
{}
void setTime(double time ) { m_time = time; }
double time() const { return m_time; }
private:
double m_time;
};
inline std::ostream& operator<< (std::ostream& stream, const QPoint4D& pos)
{
stream << "(" << QString::number(pos.x(), 'f', 3)
<< ", " << QString::number(pos.y(), 'f', 3)
<< ", " << QString::number(pos.z(), 'f', 3)
<< ", t=" << QString::number(pos.time(), 'f', 3) << ")";
return stream;
}
using TRAJECTORY_METRIC = QVector<QPoint4D>;
/**
* @brief The TrajectoryGeneratorVulcano class provides methods to generate a vulcano flight curve.
* All calculations are using metrical coordinates.
*/
//
// 2-dim operation:
//
namespace XY
{
// speed/v [m/sec]
// distance [m]
TRAJECTORY_EXPORT double calculateBetaRad(double v0, double peakHeightRelToOrigin, QString *reasonOut = nullptr);
TRAJECTORY_EXPORT QPointF calculateApogaeum(double v0, double betaRad);
TRAJECTORY_EXPORT double calculateApogaeumStrichEval(const QPointF &origin, const QPointF &idealHit,
double v0, double betaRad, const QPointF &tap_relToOrigin);
TRAJECTORY_EXPORT QPointF calculateTapRelToIdealHit(double vFalling, double alphaRad, double timeTapT_sec);
TRAJECTORY_EXPORT QPointF calculateTapRelToOrigin(const QPointF &idealHit, const QPointF &tap_relToIdealHit);
TRAJECTORY_EXPORT QVector<QPointF> generateBallisticCurveUntil(double v0, double betaRad,
double max_x_distance, double secPerIntervall,
double *deltaXperStep_out = nullptr, double *secPerIntervall_out = nullptr);
};
class TRAJECTORY_EXPORT TrajectoryGeneratorVulcano
{
public:
// defaults:
static inline double defaultTapTimeSec() { return 8; } // sec
static inline double defaultv0() { return 870; } // m/sec , 730 ; _870_ ; 1020 , 1100
// Options:
enum Option {
OptRasterApoTap = 1, // 0000001
OptRasterApoTapTime = 2, // 0000010
OptIncludeIdealHit = 4, // 0000100
OptRasterTapHit = 8, // 0001000
OptRasterMetrical = 16, // 0010000
// OptFutureUse1 = 32,
// OptFutureUse2 = 64,
};
Q_DECLARE_FLAGS(Options, Option)
//
// main:
//
/**
* @brief trajectoryVulcano generate vulcano flight curve.
* phase 1: ballistic; phase 2: gliding from peak (APO|APO') to TAP; [ phase 3: TAP to ideal hit ]
* All calculations are using metrical coordinates.
* @param origin metrical coordinates
* @param idealHit metrical coordinates
* @param v0 Abschussgeschwindigkeit
* @param betaRad Abschussgeschwindigkeit
* @param vGliding Gleitgeschwindigkeit
* @param vFalling Fallgeschwindigkeit
* @param alphaRad Fallwinkel
* @param tapTime Fallzeit
* @param options
* @param trajectoryOut metric, relativ to origin , time starts be zero
* @return true on success, otherwise false
*/
static bool trajectoryVulcano(const QPoint3D &origin, const QPoint3D &idealHit,
double v0, double betaRad, // Abschussgeschwindigkeit, -winkel
double vGliding,
double vFalling , double alphaRad, double tapTimeSec, // Fallwinkel, -geschwindigkeit, -zeit
TrajectoryGeneratorVulcano::Options options,
TRAJECTORY_METRIC &trajectoryOut,
QPoint3D &idealTapOut,
QString *reasonOut);
//
// checks:
//
static bool checkSimple(const QPoint3D &origin, const QPoint3D &apoAbs, const QPoint3D &idealHit, QString *reasonOut);
static bool checkSimple(const QPoint3D &origin, const QPoint3D &apoAbs, const QPoint3D &tabAbs, const QPoint3D &idealHit, QString *reasonOut);
//
// helper:
//
// get speed for every seperate phase
static void getSpeedPerPhase(double v0_in, double betaRad,
double &vGlidingOut, double &vFallingOut);
static void rasterLineByDistance(const QPoint3D &p0, const QPoint3D &p1,
double desiredDelta_xy,
TRAJECTORY_METRIC &trajectoryOut, double *realDelta_xy_out);
static void rasterLineByTime(const QPoint3D &p0, const QPoint3D &p1,
double timeOffsetSec, double speedMeterPerSec, double desiredDeltaTime,
TRAJECTORY_METRIC &trajectoryOut, double *realDeltaTime_out);
protected:
// empty
};
Q_DECLARE_OPERATORS_FOR_FLAGS(TrajectoryGeneratorVulcano::Options)
} // namespace AreaEffectSimulation
@@ -0,0 +1,220 @@
/**
* @RDE_HEADER@
* Trajectory Helper
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2023-11-08
*/
#include "trajectory_helper.h"
#include "../logger.h"
#include <LtsUtilsCore/qpoint3d.h>
#include <QObject>
#include <QString>
#include <QDebug>
#include <iostream>
#include <cmath>
namespace AreaEffectSimulation
{
double TrajectoryHelper::flightDurationSec(const LtsUtils::QPoint3D &posStart, const LtsUtils::QPoint3D &posEnd, double peakHeightAbs,
double *betaRadiantOut, double *vOut,
QString *reasonOut)
{
if ((posStart.z() > peakHeightAbs) || (posEnd.z() > peakHeightAbs))
{
if (reasonOut)
{
//: "Höhenfehler"
*reasonOut = QObject::tr("HeightElevationError(%1 %2 %3)").arg(posStart.z()).arg(peakHeightAbs).arg(posEnd.z());
}
return -1.0;
}
double z = posEnd.z() - posStart.z();
double h = peakHeightAbs - posStart.z();
double x = posEnd.x() - posStart.x();
double y = posEnd.y() - posStart.y();
double r = sqrt(x * x + y * y);
if (r < 0.01)
{
if (reasonOut)
{
//: "StartEndeAbstandsfehler"
*reasonOut = QObject::tr("StartEndDistanceError(%1)").arg(r);
}
return 0.0; // No distance -> no duration -> 0.0
}
double beta; // Abschusswinkel / elevation_angle in radiant
double v;
if (fabs(z) < 0.01)
{
beta = atan(4.0 * h / r);
v = sqrt(9.81 * r / sin(2.0 * beta));
}
else
{
double b;
double p = - 4.0 * h / r;
double q = 4.0 * h * z / (r * r); // ehemals double q = - 4.0 * h * z / (r * r);
if (p * p / 4.0 > q)
b = - (p / 2.0) + sqrt(p * p / 4.0 - q);
else
return -1.0;
beta = atan(b);
double a = b * b / (4.0 * h);
double rh = b / a;
v = sqrt(9.81 * rh / sin(2.0 * beta));
}
// handle optional values:
if (betaRadiantOut)
*betaRadiantOut = beta;
if (vOut)
*vOut = v;
const double result = r / (v * cos(beta));
if ((result < 0) && reasonOut)
{
//: "FlugzeitFehlerUnbekannt"
*reasonOut = QObject::tr("FlightDurationSecErrorUnknown(%1)").arg(result);
}
return result;
}
double TrajectoryHelper::peakHeightAbs(const LtsUtils::QPoint3D &posStart, const LtsUtils::QPoint3D &posEnd, double timeSec,
double *betaRadiantOut, double *vOut,
QString *reasonOut)
{
double x = posEnd.x() - posStart.x();
double y = posEnd.y() - posStart.y();
double r = sqrt(x * x + y * y);
if (r < 0.01)
{
if (reasonOut)
{
//: "StartEndeAbstandsfehler"
*reasonOut = QObject::tr("StartEndDistanceError(%1)").arg(r);
}
return 0.0;
}
double v, rh;
double t2 = timeSec * timeSec;
double z = posEnd.z() - posStart.z();
if (fabs(z) < 0.01)
{
v = sqrt((9.81 * 9.81 * t2 / 4.0) + (r * r / t2));
rh = r;
}
else
{
double vx = r / timeSec;
double vy = (9.81 * timeSec * timeSec / 2.0 + z) / timeSec;
v = sqrt(vx * vx + vy * vy);
rh = 2.0 * r * sqrt((v * v - r * r / t2)) / (9.81 * timeSec);
}
double arcSin = 9.81 * rh / (v * v);
if (arcSin > 1.0)
{
if (reasonOut)
{
//: "45GradSchuss"
*reasonOut = QObject::tr("45DegreeShot(%1)").arg(arcSin);
}
return -1;
}
// beta: Abschusswinkel / elevationsWinkel in radiant
double beta;
if (fabs(9.81 * rh - v * v) < 0.0001)
{
beta = M_PI_4;
}
else
{
if (9.81 * rh > (v * v))
{
if (reasonOut)
{
//: "GipfelhöhenFehlerUnbekannt"
*reasonOut = QObject::tr("PeakHeightUnknownError(rh=%1,v=%2)").arg(rh).arg(v);
}
return -1;
}
beta = asin( 9.81 * rh / (v * v)) / 2.0;
}
// [GUEZE-2472] Inkonsitente Werte bei der Berechnung der Gipfelhöhe (peakHeight)
// Es gibt nur eine Flugzeit zu einer maximalen Höhe h bei Vorgabe der Zielentfernung.
// if (beta < M_PI_2)
// {
// if (trajMode == TrajectoryMode::TrajectoryModeHigh)
// beta = M_PI_2 - beta;
// }
// else
// {
// if (trajMode == TrajectoryMode::TrajectoryModeLow)
// beta = M_PI_2 - beta;
// }
double beta2;
if (beta < M_PI_2)
beta2 = M_PI_2 - beta;
else
beta2 = M_PI_2 + beta;
if (fabs((timeSec * v) * cos(beta) - r) > fabs((timeSec * v) * cos(beta2) - r))
beta = beta2;
double h = v * v * sin(beta) * sin(beta) / (2.0 * 9.81);
h += posStart.z();
// handle optional values:
if (betaRadiantOut)
*betaRadiantOut = beta;
if (vOut)
*vOut = v;
return h;
}
LtsUtils::QPoint3D TrajectoryHelper::realStartPoint(const LtsUtils::QPoint3D &apogaeum, const LtsUtils::QPoint3D &endPos)
{
LtsUtils::QPoint3D realStart = apogaeum - (endPos-apogaeum);
realStart.setZ(endPos.z());
return realStart;
}
//const QString TrajectoryGenerator::string4poly(const TrajectoryPoint3D &trajectory)
//{
// if (trajectory.isEmpty())
// return (QStringLiteral("Empty Polygon!"));
// const Point3D &p3D = trajectory.first();
// int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(p3D.toPointF()).zone;
// QStringList pointSl;
// pointSl.reserve(trajectory.size());
// for (int i = 0; i < trajectory.size(); ++i)
// pointSl << QString(QStringLiteral("utm=%1 h=%2")).arg(LtsCoordTrans::UtmCoordinate::fromGeographic(p3D.toPointF(), zone).mgrs(), QString::number(p3D.z(), 'f', 1));
// return QStringLiteral("%1 Values (%2)").arg(trajectory.count()).arg(pointSl.join(QStringLiteral(" # ")));
//}
} // namespace AreaEffectSimulation
@@ -0,0 +1,74 @@
/**
* @RDE_HEADER@
* Trajectory Helper
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLTE, 2023-11-08
*/
#pragma once
#include <AreaEffectSimulation/aes_export.h>
#include <Trajectory/trajectory_export.h>
// Forward
namespace LtsUtils {
class QPoint3D;
}
namespace AreaEffectSimulation
{
class TRAJECTORY_EXPORT TrajectoryHelper
{
public:
/**
* @brief TrajectoryHelper::flightDurationSec
* @param posStart start of the curve
* @param posEnd end of the curve
* @param peakHeightAbs absolute peak of the curve value
* @param betaRadiantOut start angle
* @param vOut firing speed
* @param reasonOut error information
* @return Flugzeit in Sekunden (Rueckgabewert -1 bei Eingabe zu kleiner Hoehe / -2 bei Hoehenunterschieden)
*/
static double flightDurationSec(const LtsUtils::QPoint3D &posStart, const LtsUtils::QPoint3D &posEnd, double peakHeightAbs,
double *betaRadiantOut = nullptr, double *vOut = nullptr,
QString *reasonOut = nullptr);
enum class TrajectoryMode
{
TrajectoryModeLow = 0, // flat
TrajectoryModeHigh
};
/**
* @brief TrajectoryHelper::peakHeightAbs
* @param posStart start of the curve
* @param posEnd end of the curve
* @param timeSec flightDuration in seconds
* @param trajMode LOW (0) or HIGH (1) curve
* @param betaRadiantOut start angle
* @param vOut firing speed
* @param reasonOut error information
* @return absolute peak
*/
static double peakHeightAbs(const LtsUtils::QPoint3D &posStart, const LtsUtils::QPoint3D &posEnd, double timeSec,
double *betaRadiantOut = nullptr, double *vOut = nullptr,
QString *reasonOut = nullptr);
/**
* @brief calculate realStartPoint based on apogaeum and endpos (more of less mirroring)
* @param apogaeum of ideal parabel
* @param posEnd impact position
* @return mirrored point (z values of posEnd is taken)
*/
static LtsUtils::QPoint3D realStartPoint(const LtsUtils::QPoint3D &apogaeum, const LtsUtils::QPoint3D &endPos);
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,51 @@
/**
* Commom base types * $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-05-19
*/
#pragma once
#include <QVector>
#include <LtsUtilsCore/qpoint3d.h>
#include <Trajectory/trajectory_export.h>
namespace AreaEffectSimulation
{
/**
* @brief The TrajectoryPoint class was introduced for ADLER / SAA
* initially the positions are metrical and relative.
*/
class TrajectoryPoint
{
public:
TrajectoryPoint() = default;
TrajectoryPoint(double x, double y, double z, double _time = -1)
: position(x,y,z), time (_time)
{}
TrajectoryPoint(const LtsUtils::QPoint3D &_position, double _time)
: position(_position), time (_time)
{}
LtsUtils::QPoint3D position; // x,y are relative, z is above sea level; both metrical
double time {-1.0}; // relative
};
Q_DECL_CONSTEXPR inline bool operator==(const TrajectoryPoint &p1, const TrajectoryPoint &p2)
{
return p1.position == p2.position && p1.time == p2.time;
}
Q_DECL_CONSTEXPR inline bool operator!=(const TrajectoryPoint &p1, const TrajectoryPoint &p2)
{
return p1.position != p2.position || p1.time != p2.time;
}
} // namespace AreaEffectSimulation
// at global namespace:
TRAJECTORY_EXPORT std::ostream &operator<< (std::ostream &stream, const AreaEffectSimulation::TrajectoryPoint &value);
@@ -0,0 +1,86 @@
/**
* @RDE_HEADER@
* TrajectoryTransform
* $Id$
* $HeadURL$
* $Author$
* Created by Gerlinde Rudolphus, ESZS, 2026
*/
#include "trajectory_transform.h"
// Algorithmus: Zeitgewichtete Bézier-Korrektur (empfohlen)
// Kerngedanke
// Die ursprüngliche Trajektorie bleibt die Basis
// Eine glatte Korrekturkurve wird addiert
// Die Korrektur ist:
// 0 am Start
// maximal am Ziel
// zeitlich C¹-stetig
// Formel
// Für jeden Punkt:
// Pneu(t) = Palt(t) + w(t) · ΔZ
// mit
// ΔZ = Zneu − Zalt
// w(t) = 3s² − 2s³ (smoothstep)
// s = (t − t0) / (tEnd − t0)
// Dadurch:
// Start bleibt exakt gleich
// Zielpunkt wird exakt erreicht
// Bewegung bleibt glatt
namespace AreaEffectSimulation
{
namespace TrajectoryTransform
{
double smoothStep(double x)
{
return x * x * (3.0 - 2.0 * x);
}
template <typename T>
T clamp(T v, T lo, T hi) {
return (v < lo) ? lo : (v > hi) ? hi : v; }
QVector<TrajectoryPoint> transformToNewTarget(
const QVector<TrajectoryPoint>& originalTrajMetrical,
const LtsUtils::QPoint3D& newTargetMetrical)
{
if (originalTrajMetrical.size() < 2)
return originalTrajMetrical;
QVector<TrajectoryPoint> result = originalTrajMetrical;
const LtsUtils::QPoint3D &oldTarget = originalTrajMetrical.last().position;
const LtsUtils::QPoint3D &delta = newTargetMetrical - oldTarget;
const double t0 = originalTrajMetrical.first().time;
const double tEnd = originalTrajMetrical.last().time;
const double dt = tEnd - t0;
for (int i = 0; i < result.size(); ++i)
{
double s = (result[i].time - t0) / dt;
s = clamp(s, 0.0, 1.0);
double w = smoothStep(s);
result[i].position += delta * w;
}
return result;
}
} // namespace TrajectoryTransform
} // namespace AreaEffectSimulation
std::ostream &operator<< (std::ostream &stream, const AreaEffectSimulation::TrajectoryPoint &value)
{
stream << "TrajectoryPoint(" << "position=" << value.position << "; time=" << value.time;
return stream;
}
@@ -0,0 +1,23 @@
#pragma once
#include "trajectory_point.h"
#include <Trajectory/trajectory_export.h>
#include <QVector>
#include <LtsUtilsCore/qpoint3d.h>
namespace AreaEffectSimulation
{
// prefer namespace instead of class with static member:
// https://stackoverflow.com/questions/1434937/namespace-functions-versus-static-methods-on-a-class
// https://stackoverflow.com/questions/1434937/namespace-functions-versus-static-methods-on-a-class#:~:text=You%20should%20use%20a%20namespace,can%20using%20a%20namespace%20member
namespace TrajectoryTransform
{
TRAJECTORY_EXPORT QVector<TrajectoryPoint> transformToNewTarget(const QVector<TrajectoryPoint>& originalTrajMetrical,
const LtsUtils::QPoint3D& newTargetMetrical);
}
} // namespace AreaEffectSimulation
@@ -0,0 +1,8 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
ADD_SUBDIRECTORY(UnitTest_TrajectorySplitter)
Loaded 100 of 16245 files, more files were not shown because too many files have changed in this diff. Show more