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

+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)
@@ -0,0 +1,124 @@
{ "xRelativ": 0, "yRelativ": 87, "zRelativ": 0, "zeit": 0 } { "xRelativ":
264.92901384887057, "yRelativ": 174.47292577550235, "zRelativ": 0.02662089743508293, "zeit": 0.42316757561829477 } { "xRelativ": 527.817117836265,
"yRelativ": 259.39749108373843, "zRelativ": 0.10711867810716591, "zeit": 0.8671332156643488 } { "xRelativ": 788.7136308929233, "yRelativ":
341.7537090754077, "zRelativ": 0.24257733886856256, "zeit": 1.317025093437079 } { "xRelativ": 1047.6615285149353, "yRelativ": 421.51802443336, "zRelativ":
0.4341458137452195, "zeit": 1.7729216456145016 } { "xRelativ": 1272.670456388367, "yRelativ": 489.16424954886634, "zRelativ": 0.648743912367082,
"zeit": 2.175420509369112 } { "xRelativ": 1496.1617588664074, "yRelativ": 554.7638718196823, "zRelativ": 0.9079763526756811, "zeit": 2.580929441712794 }
{ "xRelativ": 1718.0422067916973, "yRelativ": 618.2656482647882, "zRelativ": 1.2125072432874477, "zeit": 2.9908190774616394 } { "xRelativ":
1938.330944134444, "yRelativ": 679.6520331535114, "zRelativ": 1.5631256945293488, "zeit": 3.405114349100484 } { "xRelativ": 2157.044359965948, "yRelativ":
738.9042569599119, "zRelativ": 1.9606317053734932, "zeit": 3.8238343302505746 } { "xRelativ": 2374.179454251386, "yRelativ": 795.9981045121799,
"zRelativ": 2.4057001489064205, "zeit": 4.246959426288938 } { "xRelativ": 2589.7075746926794, "yRelativ": 850.9031532411445,
"zRelativ": 2.8988951350823706, "zeit": 4.674416976133513 } { "xRelativ": 2803.625245250845, "yRelativ": 903.5962127427072,
"zRelativ": 3.4408242962627167, "zeit": 5.106179504182334 } { "xRelativ": 3015.753395193171, "yRelativ": 954.0134750176439,
"zRelativ": 4.0315840043246425, "zeit": 5.541857190559005 } { "xRelativ": 3226.04018172054, "yRelativ": 1002.1263109737092, "zRelativ": 4.671491724277319,
"zeit": 5.981306785503242 } { "xRelativ": 3434.503695723219, "yRelativ": 1047.9235062132045, "zRelativ": 5.361047083221246, "zeit": 6.424524728114267 } {
"xRelativ": 3641.159218345649, "yRelativ": 1091.3931626499334, "zRelativ": 6.100752704963098, "zeit": 6.87150067202138 } { "xRelativ": 3846.0195490859824,
"yRelativ": 1132.5229007478931, "zRelativ": 6.891111327028094, "zeit": 7.322217861888602 } { "xRelativ": 4049.0953248019264, "yRelativ":
1171.3000490713105, "zRelativ": 7.73262312147463, "zeit": 7.776653548451646 } { "xRelativ": 4250.395329938128, "yRelativ": 1207.7118204515107,
"zRelativ": 8.62578323430379, "zeit": 8.234779443194908 } { "xRelativ": 4449.9039507029875, "yRelativ": 1241.7416941206964, "zRelativ": 9.570963177466668,
"zeit": 8.696509201569398 } { "xRelativ": 4647.484925820487, "yRelativ": 1273.35579439689, "zRelativ": 10.567796577492881, "zeit": 9.161470612161173 } {
"xRelativ": 4815.295806413785, "yRelativ": 1298.5235324118603, "zRelativ": 11.463385441541586, "zeit": 9.562509460890828 } { "xRelativ":
4981.693048622579, "yRelativ": 1321.9069836272317, "zRelativ": 12.397105821249431, "zeit": 9.965840845672002 } { "xRelativ": 5146.687445822314,
"yRelativ": 1343.5036375846614, "zRelativ": 13.36908702977499, "zeit": 10.371448875731526 } { "xRelativ": 5310.289247221179, "yRelativ":
1363.3110214569886, "zRelativ": 14.379455555652042, "zeit": 10.779316221496169 } { "xRelativ": 5472.508280876805, "yRelativ": 1381.32673988717,
"zRelativ": 15.428334686559369, "zeit": 11.189424416135394 } { "xRelativ": 5633.3540705338055, "yRelativ": 1397.5485073966336,
"zRelativ": 16.515844254274977, "zeit": 11.60175416482782 } { "xRelativ": 5792.918488561989, "yRelativ": 1411.9812057614772,
"zRelativ": 17.642695945593214, "zeit": 12.016500348251098 } { "xRelativ": 5951.602993266749, "yRelativ": 1424.649459083988,
"zRelativ": 18.812027905606268, "zeit": 12.434673896701511 } { "xRelativ": 6109.436505497795, "yRelativ": 1435.5373424624672,
"zRelativ": 20.024545582829482, "zeit": 12.856329450140114 } { "xRelativ": 6266.509511696176, "yRelativ": 1444.6310496431215,
"zRelativ": 21.28146507192771, "zeit": 13.28169203569395 } { "xRelativ": 6423.704767078978, "yRelativ": 1451.9452196648554,
"zRelativ": 22.590782872547205, "zeit": 13.713151591402328 } { "xRelativ": 6581.511294764046, "yRelativ": 1457.4420159446634,
"zRelativ": 23.958181651576584, "zeit": 14.152081584408341 } { "xRelativ": 6740.708204467678, "yRelativ": 1461.063997699184,
"zRelativ": 25.392696730677216, "zeit": 14.60069300198115 } { "xRelativ": 6901.974936945717, "yRelativ": 1462.7127400025756,
"zRelativ": 26.90355408049825, "zeit": 15.060978072354045 } { "xRelativ": 7065.931914784388, "yRelativ": 1462.2520863694708,
"zRelativ": 28.50045932326631, "zeit": 15.534773332435343 } { "xRelativ": 7204.3899296521095, "yRelativ": 1460.1465791292762,
"zRelativ": 29.89774175801777, "zeit": 15.939311665982192 } { "xRelativ": 7344.310327774728, "yRelativ": 1456.3886585442183,
"zRelativ": 31.35592735710163, "zeit": 16.3521166282013 } { "xRelativ": 7485.506385757768, "yRelativ": 1450.9036028742253, "zRelativ": 32.875180047653885,
"zeit": 16.7726404680203 } { "xRelativ": 7627.7846984507705, "yRelativ": 1443.6243527953839, "zRelativ": 34.45522654617, "zeit": 17.200295887629697 } {
"xRelativ": 7770.96783657639, "yRelativ": 1434.490934439522, "zRelativ": 36.095593855551805, "zeit": 17.63452550541688 } { "xRelativ": 7914.891572427645,
"yRelativ": 1423.4503767882618, "zRelativ": 37.79571974304789, "zeit": 18.07479676390447 } { "xRelativ": 8059.403915188324, "yRelativ": 1410.456436242184,
"zRelativ": 39.5549601209268, "zeit": 18.52060181849778 } { "xRelativ": 8204.366506351464, "yRelativ": 1395.4690478428847, "zRelativ": 41.37262014144733,
"zeit": 18.971464479435113 } { "xRelativ": 8349.655456359758, "yRelativ": 1378.453653736664, "zRelativ": 43.24798279390431, "zeit": 19.426945432815643 }
{ "xRelativ": 8495.161679455245, "yRelativ": 1359.3804541368684, "zRelativ": 45.180334266378004, "zeit": 19.886645813789276 } { "xRelativ":
8640.790802339236, "yRelativ": 1338.2236212923929, "zRelativ": 47.16898568033212, "zeit": 20.350209288555977 } { "xRelativ": 8786.462730602496,
"yRelativ": 1314.9605102311587, "zRelativ": 49.2132910994555, "zeit": 20.81732285371174 } { "xRelativ": 8932.110956878605, "yRelativ": 1289.5708924959679,
"zRelativ": 51.312661940457964, "zeit": 21.287716585014074 } { "xRelativ": 9077.682313654632, "yRelativ": 1262.0361113438485,
"zRelativ": 53.46659106527009, "zeit": 21.761164607415193 } { "xRelativ": 9223.15299564122, "yRelativ": 1232.334925946738, "zRelativ": 55.67514660103901,
"zeit": 22.237539310761296 } { "xRelativ": 9368.499044808717, "yRelativ": 1200.4481097398589, "zRelativ": 57.938683482733126, "zeit": 22.71671693082566 }
{ "xRelativ": 9513.696966361245, "yRelativ": 1166.3580456065579, "zRelativ": 60.25749995488794, "zeit": 23.19857964224667 } { "xRelativ":
9658.731690049053, "yRelativ": 1130.0465954717768, "zRelativ": 62.63193322853328, "zeit": 23.683042287890338 } { "xRelativ": 9803.595681312554,
"yRelativ": 1091.4946627331606, "zRelativ": 65.06236075273098, "zeit": 24.17005024564265 } { "xRelativ": 9948.288068488084, "yRelativ":
1050.6818120718194, "zRelativ": 67.54920058086417, "zeit": 24.659577214566927 } { "xRelativ": 10092.813834134578, "yRelativ": 1007.5859338231913,
"zRelativ": 70.09291151720386, "zeit": 25.151623100713145 } { "xRelativ": 10237.183074859822, "yRelativ": 962.1829474248021,
"zRelativ": 72.69399312942286, "zeit": 25.64621203834946 } { "xRelativ": 10381.410330438524, "yRelativ": 914.4465384758553, "zRelativ": 75.35298568895132,
"zeit": 26.143390568795883 } { "xRelativ": 10525.276790425323, "yRelativ": 864.4323748374304, "zRelativ": 78.06594706166167, "zeit": 26.64240073362623 }
{ "xRelativ": 10668.292565257545, "yRelativ": 812.3118170374198, "zRelativ": 80.82322553052114, "zeit": 27.14152290057901 } { "xRelativ":
10810.450109000809, "yRelativ": 758.1014590452174, "zRelativ": 83.6238919660965, "zeit": 27.64070006753179 } { "xRelativ": 10951.756580024134, "yRelativ":
701.8127436432027, "zRelativ": 86.4672527220736, "zeit": 28.13993223448457 } { "xRelativ": 11004.943335671449, "yRelativ": 679.9994989836455,
"zRelativ": 87.55287154361422, "zeit": 28.32863291589727 } { "xRelativ": 11024.335485058064, "yRelativ": 671.9830718180636, "zRelativ": 87.92903026884116,
"zeit": 28.420639771216415 } { "xRelativ": 11100.21984616728, "yRelativ": 639.7392557756232, "zRelativ": 89.40212615944367, "zeit": 28.821882126373755 }
{ "xRelativ": 11152.742165081048, "yRelativ": 616.0529483568182, "zRelativ": 90.42341406537633, "zeit": 29.226286071708532 } { "xRelativ":
11193.271852123562, "yRelativ": 596.3408074418735, "zRelativ": 91.21278397948197, "zeit": 29.631428430972147 } { "xRelativ": 11226.28500991867,
"yRelativ": 578.8210033928916, "zRelativ": 91.85675684807502, "zeit": 30.03540991971389 } { "xRelativ": 11254.227157001867, "yRelativ": 562.5123550319646,
"zRelativ": 92.4026108289216, "zeit": 30.438482771707214 } { "xRelativ": 11278.594301262206, "yRelativ": 546.7919561769685, "zRelativ": 92.87927865140509,
"zeit": 30.8420332938371 } { "xRelativ": 11300.357097851796, "yRelativ": 531.2266409087222, "zRelativ": 93.30553868133093, "zeit": 31.24781331532016 } {
"xRelativ": 11319.794086200334, "yRelativ": 515.8078371851949, "zRelativ": 93.68667708034342, "zeit": 31.64938614114135 } { "xRelativ":
11337.989362305336, "yRelativ": 499.7997097923123, "zRelativ": 94.04382573010889, "zeit": 32.060973130558004 } { "xRelativ": 11354.447034153147,
"yRelativ": 483.7648081358486, "zRelativ": 94.36714405385955, "zeit": 32.46462420892174 } { "xRelativ": 11370.473778499521, "yRelativ": 466.4902966169071,
"zRelativ": 94.68221569173885, "zeit": 32.88797252052937 } { "xRelativ": 11384.89101884696, "yRelativ": 449.33805886194824, "zRelativ": 94.96579170236389,
"zeit": 33.29598001219888 } { "xRelativ": 11395.981469933671, "yRelativ": 434.96545697335347, "zRelativ": 95.18399600505616, "zeit": 33.62863291589727 }
{ "xRelativ": 11409.236947977843, "yRelativ": 416.29617224851137, "zRelativ": 95.44482733206365, "zeit": 34.04082881539747 } { "xRelativ":
11411.285518092966, "yRelativ": 413.26044882799937, "zRelativ": 95.48513715974597, "zeit": 34.10532899875924 } { "xRelativ": 11419.459782669115,
"yRelativ": 400.45101207953184, "zRelativ": 95.64597096552859, "zeit": 34.50706881576209 } { "xRelativ": 11423.734861523346, "yRelativ":
392.5118315876584, "zRelativ": 95.73005916380147, "zeit": 34.910235145415434 } { "xRelativ": 11426.49578154897, "yRelativ": 386.0591251966,
"zRelativ": 95.78432184334547, "zeit": 35.314853248312566 } { "xRelativ": 11428.434132450526, "yRelativ": 380.1405848375414,
"zRelativ": 95.82235658843106, "zeit": 35.72479461543669 } { "xRelativ": 11429.826693932191, "yRelativ": 374.4631097940547, "zRelativ": 95.8496034725259,
"zeit": 36.13686244791632 } { "xRelativ": 11430.858784299904, "yRelativ": 368.76345688039686, "zRelativ": 95.86970170148653, "zeit": 36.55950145263015 }
{ "xRelativ": 11431.58968493453, "yRelativ": 363.2663541915362, "zRelativ": 95.88383019968666, "zeit": 36.97094102626642 } { "xRelativ":
11432.162550220839, "yRelativ": 357.3343031965715, "zRelativ": 95.89477793773139, "zeit": 37.416671227566354 } { "xRelativ": 11432.57453933342,
"yRelativ": 351.35486425071315, "zRelativ": 95.9025105233024, "zeit": 37.86665234292258 } { "xRelativ": 11432.877837647795, "yRelativ":
345.10909634518674, "zRelativ": 95.90804515577143, "zeit": 38.33692802395387 } { "xRelativ": 11433.09846822694, "yRelativ": 338.517286188265,
"zRelativ": 95.91189045274999, "zeit": 38.833363879071 } { "xRelativ": 11433.248677646154, "yRelativ": 331.87916060955405, "zRelativ": 95.91431459190422,
"zeit": 39.333363879071 } { "xRelativ": 11433.348882570777, "yRelativ": 325.2422397338318, "zRelativ": 95.91572983853065, "zeit": 39.833363879071 } {
"xRelativ": 11433.414518760721, "yRelativ": 318.60681330888696, "zRelativ": 95.91644574367233, "zeit": 40.333363879070994 } { "xRelativ":
11433.456260054918, "yRelativ": 311.973114406024, "zRelativ": 95.91667699041493, "zeit": 40.833363879070994 } { "xRelativ": 11433.481486984167,
"yRelativ": 305.3412990458193, "zRelativ": 95.9165724557235, "zeit": 41.333363879070994 } { "xRelativ": 11433.495302253918, "yRelativ":
298.71146245908585, "zRelativ": 95.9162353668241, "zeit": 41.83336387907099 } { "xRelativ": 11433.500548152258, "yRelativ": 293.4090555920253,
"zRelativ": 95.91584714678909, "zeit": 42.23336387907099 } { "xRelativ": 11433.501967204562, "yRelativ": 286.7829015145368, "zRelativ": 95.91525679993751,
"zeit": 42.733363879070986 } { "xRelativ": 11433.499335919818, "yRelativ": 280.15882375525115, "zRelativ": 95.91458354139951, "zeit": 43.233363879070986
} { "xRelativ": 11433.493907953312, "yRelativ": 273.536830677164, "zRelativ": 95.91385300842985, "zeit": 43.733363879070986 } { "xRelativ":
11433.486549507788, "yRelativ": 266.91692553984, "zRelativ": 95.9130829647681, "zeit": 44.23336387907098 } { "xRelativ": 11433.479681686724, "yRelativ":
261.6225051311806, "zRelativ": 95.91244688984187, "zeit": 44.633363879070984 } { "xRelativ": 11433.47022997119, "yRelativ": 255.00635849002902,
"zRelativ": 95.91163415590303, "zeit": 45.13336387907098 } { "xRelativ": 11433.460096115776, "yRelativ": 248.39229789577348,
"zRelativ": 95.91080763291724, "zeit": 45.63336387907098 }
{ "xRelativ": 11004.943335671449, "yRelativ": 679.9994989836455, "zRelativ": 87.55287154361422,
"zeit": 28.32863291589727 } { "xRelativ": 11043.727634444678, "yRelativ": 663.9667038330389, "zRelativ": 88.3051889940681, "zeit": 28.506460354460955 } {
"xRelativ": 11119.006328411935, "yRelativ": 631.7585823372341, "zRelativ": 89.76652640827223, "zeit": 28.906818574224253 } { "xRelativ":
11171.917972657753, "yRelativ": 607.7540924048816, "zRelativ": 90.79537083018761, "zeit": 29.308956641395085 } { "xRelativ": 11213.546941861956,
"yRelativ": 587.4288634353316, "zRelativ": 91.60613181351381, "zeit": 29.71415104903123 } { "xRelativ": 11247.924664954591, "yRelativ": 569.1686105331734,
"zRelativ": 92.27670528358261, "zeit": 30.11865680553989 } { "xRelativ": 11277.473529493598, "yRelativ": 551.9745945754645, "zRelativ": 92.85393305216392,
"zeit": 30.523192972002366 } { "xRelativ": 11303.510356440383, "yRelativ": 535.3123986302426, "zRelativ": 93.36325360985394, "zeit": 30.92682745026673 }
{ "xRelativ": 11327.577437716074, "yRelativ": 518.3252752102593, "zRelativ": 93.83464803213366, "zeit": 31.341524665217197 } { "xRelativ":
11349.641672687, "yRelativ": 501.12144249545435, "zRelativ": 94.26732562403264, "zeit": 31.75855034515161 } { "xRelativ": 11369.765284326642, "yRelativ":
483.8130803812575, "zRelativ": 94.66236165678039, "zeit": 32.17127410388573 } { "xRelativ": 11388.327099857737, "yRelativ": 466.24971891921405,
"zRelativ": 95.02706541088766, "zeit": 32.58116552646909 } { "xRelativ": 11406.017762440957, "yRelativ": 447.8622790882401, "zRelativ": 95.37491218944194,
"zeit": 33.00003479838909 } { "xRelativ": 11422.674212587985, "yRelativ": 428.8630725613948, "zRelativ": 95.70261568950436, "zeit": 33.42202695039745 } {
"xRelativ": 11438.234029710628, "yRelativ": 409.42577037021096, "zRelativ": 96.00886727478525, "zeit": 33.84307821894881 } { "xRelativ":
11452.689275337752, "yRelativ": 389.7031408150357, "zRelativ": 96.29343689359482, "zeit": 34.26021424776057 } { "xRelativ": 11464.71710783156, "yRelativ":
371.909984788195, "zRelativ": 96.5302211066025, "zeit": 34.62863291589727 } { "xRelativ": 11477.755322713407, "yRelativ": 351.04268351808685,
"zRelativ": 96.78684636252149, "zeit": 35.044035435387514 } { "xRelativ": 11478.71814942543, "yRelativ": 349.43379687939347,
"zRelativ": 96.80579361537536, "zeit": 35.07509661563648 } { "xRelativ": 11486.513069239078, "yRelativ": 335.7661291318016, "zRelativ": 96.95915516430225,
"zeit": 35.47517459504593 } { "xRelativ": 11490.518074724874, "yRelativ": 327.5136024325229, "zRelativ": 97.03790090782059, "zeit": 35.87820118223462 } {
"xRelativ": 11493.064658354193, "yRelativ": 320.96043709953466, "zRelativ": 97.08791443049023, "zeit": 36.27933906477598 } { "xRelativ":
11494.837446546813, "yRelativ": 315.0483179389918, "zRelativ": 97.12266178839167, "zeit": 36.68279449934332 } { "xRelativ": 11496.119408344146,
"yRelativ": 309.3783634813608, "zRelativ": 97.14770566200089, "zeit": 37.09088182111604 } { "xRelativ": 11497.070857247425, "yRelativ": 303.7024052820246,
"zRelativ": 97.1661941532448, "zeit": 37.51012220397199 } { "xRelativ": 11497.736335528018, "yRelativ": 298.322652240787, "zRelativ": 97.17902200050501,
"zeit": 37.91231871352893 } { "xRelativ": 11498.249863869263, "yRelativ": 292.667249805557, "zRelativ": 97.18880249154032, "zeit": 38.33749091881605 } {
"xRelativ": 11498.606515498383, "yRelativ": 287.28931803609777, "zRelativ": 97.19547534066456, "zeit": 38.74284195244496 } { "xRelativ":
11498.907073677967, "yRelativ": 280.9474775583367, "zRelativ": 97.2009434860124, "zeit": 39.221425986209404 } { "xRelativ": 11499.121563240604,
"yRelativ": 274.34404243874127, "zRelativ": 97.20466256494127, "zeit": 39.720062770644574 } { "xRelativ": 11499.266493362005, "yRelativ":
267.7249346365973, "zRelativ": 97.2069814590229, "zeit": 40.22006277064457 } { "xRelativ": 11499.362911009477, "yRelativ": 261.1076959097998,
"zRelativ": 97.20832226297901, "zeit": 40.72006277064457 } { "xRelativ": 11499.42583300566, "yRelativ": 254.49226735111273, "zRelativ": 97.20898592381488,
"zeit": 41.22006277064457 } { "xRelativ": 11499.465060791077, "yRelativ": 248.00000032681314, "zRelativ": 97.20918057242068, "zeit": 41.710891614207426 }
@@ -0,0 +1,30 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
../trajectory_splitter.cpp
../trajectory_splitter.h
trajectory_splitter_unit_test.cpp
trajectory_splitter_unit_test.h
)
SOURCE_GROUP( UnitTestSources FILES ${UnitTestSources} )
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestTrajectorySplitter ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestTrajectorySplitter
NGLLogger
nglcoreapplication
Qt5::Core
Qt5::Test
svcAesCommon
)
ADD_TEST(NAME aesUnitTestTrajectorySplitter COMMAND $<TARGET_FILE:aesUnitTestTrajectorySplitter>)
ENDIF()
@@ -0,0 +1,285 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#include "trajectory_splitter_unit_test.h"
#include "../trajectory_splitter.h"
#include <nglapplication/nglcoreapplication.h>
#include <QtTest/QTest>
#include <iostream>
using namespace AreaEffectSimulation;
//
//
// This class is an UnitTest with values taken from the TNE.
// That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
//
//
static TrajectoryType getExampleData()
{
TrajectoryType vector;
// trunk (size = 5)
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 1));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 2));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 3));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 4));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 5));
// branch 1 (size = 3)
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 8));
// branch 2 (size = 4)
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 8));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 9));
return vector;
}
static TrajectoryType getRealTrunk()
{
TrajectoryType vector;
// trunk (size = 5)
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 1));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 2));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 3));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 4));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 5));
return vector;
}
static TrajectoryType getRealBranch1()
{
TrajectoryType vector;
// branch 1 (size = 3)
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 8));
return vector;
}
static TrajectoryType getRealBranch2()
{
TrajectoryType vector;
// branch 2 (size = 4)
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 8));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 9));
return vector;
}
static TrajectoryType getRealTrajectory1()
{
TrajectoryType vector;
// trunk (size = 5)
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 1));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 2));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 3));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 4));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 5));
// branch 1 (size = 3)
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(1.1, 0.2, 0.3, 8));
return vector;
}
static TrajectoryType getRealTrajectory2()
{
TrajectoryType vector;
// trunk (size = 5)
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 1));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 2));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 3));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 4));
vector.emplace_back(Core::GeoPositionRelativeTime(0.1, 0.2, 0.3, 5));
// branch 2 (size = 4)
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 6));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 7));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 8));
vector.emplace_back(Core::GeoPositionRelativeTime(2.1, 0.2, 0.3, 9));
return vector;
}
// TrajectorySplitter::init is protected.
// To gain access, create a temporary class and enable access:
class MyLocalTrajectorySplitter : public TrajectorySplitter<std::vector<Core::GeoPositionRelativeTime>>
{
public:
using BaseClass = TrajectorySplitter;
explicit MyLocalTrajectorySplitter(const TrajectoryType &trajectory)
: BaseClass(trajectory)
{}
bool init() const
{
return BaseClass::init();
}
size_t rawTrajectorySize() const
{
return BaseClass::rawTrajectorySize();
}
static qint64 findDuplicateTimeInternal(const TrajectoryType &trajectory) { return BaseClass::findDuplicateTimeInternal(trajectory);}
static int findDuplicateBranchIndexInternal(const TrajectoryType &trajectory) { return BaseClass::findDuplicateBranchIndexInternal(trajectory);}
static int findIndexOfEndOfFirstBranchInternal(const TrajectoryType &trajectory) { return BaseClass::findIndexOfEndOfFirstBranchInternal(trajectory);}
};
void UnitTestTrajectorySplitter::init()
{
m_trajectory = getExampleData();
}
void UnitTestTrajectorySplitter::test_rawTrajectorySize()
{
QCOMPARE(m_trajectory.size(), 12);
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
}
void UnitTestTrajectorySplitter::test_findDuplicateTimeInternal()
{
const int value = MyLocalTrajectorySplitter::findDuplicateTimeInternal(m_trajectory);
QCOMPARE(value, 6);
}
void UnitTestTrajectorySplitter::test_findDuplicateTimeInternalFailure()
{
const int value = MyLocalTrajectorySplitter::findDuplicateTimeInternal(getRealTrunk());
QCOMPARE(value, -1);
}
void UnitTestTrajectorySplitter::test_findDuplicateBranchIndexInternal()
{
const int idx = MyLocalTrajectorySplitter::findDuplicateBranchIndexInternal(m_trajectory);
QCOMPARE(idx, 5);
}
void UnitTestTrajectorySplitter::test_findDuplicateBranchIndexInternalFailure()
{
const int idx = MyLocalTrajectorySplitter::findDuplicateBranchIndexInternal(getRealTrunk());
QCOMPARE(idx, -1);
}
void UnitTestTrajectorySplitter::test_init()
{
{
MyLocalTrajectorySplitter splitter({});
QCOMPARE(splitter.init(), false);
}
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
QCOMPARE(splitter.init(), true);
QCOMPARE(splitter.init(), true); // execute a second time
}
}
void UnitTestTrajectorySplitter::test_findIndexOfEndOfFirstBranchInternal()
{
const int idx = MyLocalTrajectorySplitter::findIndexOfEndOfFirstBranchInternal(m_trajectory);
QCOMPARE(idx, 7);
}
void UnitTestTrajectorySplitter::test_trunk()
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
const auto trunk = splitter.getTrunk();
QCOMPARE(trunk.size(), 5);
const auto realTrunk = getRealTrunk();
QCOMPARE(trunk, realTrunk);
}
void UnitTestTrajectorySplitter::test_branch1()
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
const auto br1 = splitter.getBranch1();
QCOMPARE(br1.size(), 3);
const auto &realBranch1 = getRealBranch1();
QCOMPARE(br1, realBranch1);
}
void UnitTestTrajectorySplitter::test_branch2()
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
const auto br2 = splitter.getBranch2();
QCOMPARE(br2.size(), 4);
const auto &realBranch2 = getRealBranch2();
QCOMPARE(br2, realBranch2);
}
void UnitTestTrajectorySplitter::test_trajectory1()
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
const auto traj1 = splitter.getTrajectory1();
QCOMPARE(traj1.size(), 8);
const auto &getRealTraj1 = getRealTrajectory1();
QCOMPARE(traj1, getRealTraj1);
}
void UnitTestTrajectorySplitter::test_trajectory2()
{
MyLocalTrajectorySplitter splitter(m_trajectory);
QCOMPARE(splitter.rawTrajectorySize(), 12);
const auto tra2 = splitter.getTrajectory2();
QCOMPARE(tra2.size(), 9);
const auto &getRealTraj2 = getRealTrajectory2();
QCOMPARE(tra2, getRealTraj2);
}
void UnitTestTrajectorySplitter::test_failure()
{
MyLocalTrajectorySplitter splitter(getRealTrunk());
QCOMPARE(splitter.rawTrajectorySize(), 5);
QCOMPARE(splitter.init(), false);
QCOMPARE(splitter.getTrunk().size(), 0);
QCOMPARE(splitter.getBranch1().size(), 0);
QCOMPARE(splitter.getBranch2().size(), 0);
QCOMPARE(splitter.getTrajectory1().size(), 0);
QCOMPARE(splitter.getTrajectory2().size(), 0);
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTest_TrajectorySplitter", "log4cxxUnitTest.props");
UnitTestTrajectorySplitter tc;
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QCoreApplication>
#include <QtTest/QTest>
#include <Core/core.hpp>
using TrajectoryType = std::vector<Core::GeoPositionRelativeTime>;
/**
* @brief This class is an UnitTest with values taken from the TNE.
* That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
*/
class UnitTestTrajectorySplitter : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void test_rawTrajectorySize();
void test_findDuplicateTimeInternal();
void test_findDuplicateTimeInternalFailure();
void test_findDuplicateBranchIndexInternal();
void test_findDuplicateBranchIndexInternalFailure();
void test_init();
void test_findIndexOfEndOfFirstBranchInternal();
void test_trunk();
void test_branch1();
void test_branch2();
void test_trajectory1();
void test_trajectory2();
void test_failure();
// void cleanup();
private:
TrajectoryType m_trajectory;
};
@@ -0,0 +1 @@
cat RealExampleData.json | jq
@@ -0,0 +1,100 @@
/**
* @RDE_HEADER@
* TrajectorySplitter
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-04-10
*/
#include "trajectory_splitter.h"
#include <QVector>
namespace AreaEffectSimulation
{
#ifdef NEVER
{ "xRelativ": 0, "yRelativ": 87, "zRelativ": 0, "zeit": 0 }
{ "xRelativ": 264.92901384887057, "yRelativ": 174.47292577550235, "zRelativ": 0.02662089743508293, "zeit": 0.42316757561829477 }
{ "xRelativ": 527.817117836265, "yRelativ": 259.39749108373843, "zRelativ": 0.10711867810716591, "zeit": 0.8671332156643488 }
{ "xRelativ": 788.7136308929233, "yRelativ": 341.7537090754077, "zRelativ": 0.24257733886856256, "zeit": 1.317025093437079 }
{ "xRelativ": 1047.6615285149353, "yRelativ": 421.51802443336, "zRelativ": 0.4341458137452195, "zeit": 1.7729216456145016 }
...
{ "xRelativ": 10668.292565257545, "yRelativ": 812.3118170374198, "zRelativ": 80.82322553052114, "zeit": 27.14152290057901 }
{ "xRelativ": 10810.450109000809, "yRelativ": 758.1014590452174, "zRelativ": 83.6238919660965, "zeit": 27.64070006753179 }
{ "xRelativ": 10951.756580024134, "yRelativ": 701.8127436432027, "zRelativ": 86.4672527220736, "zeit": 28.13993223448457 }
{ "xRelativ": 11004.943335671449, "yRelativ": 679.9994989836455, "zRelativ": 87.55287154361422, "zeit": 28.32863291589727 } <- "zeit": 28.32863291589727
{ "xRelativ": 11024.335485058064, "yRelativ": 671.9830718180636, "zRelativ": 87.92903026884116, "zeit": 28.420639771216415 }
{ "xRelativ": 11100.21984616728, "yRelativ": 639.7392557756232, "zRelativ": 89.40212615944367, "zeit": 28.821882126373755 }
...
{ "xRelativ": 11433.493907953312, "yRelativ": 273.536830677164, "zRelativ": 95.91385300842985, "zeit": 43.733363879070986 }
{ "xRelativ": 11433.486549507788, "yRelativ": 266.91692553984, "zRelativ": 95.9130829647681, "zeit": 44.23336387907098 }
{ "xRelativ": 11433.479681686724, "yRelativ": 261.6225051311806, "zRelativ": 95.91244688984187, "zeit": 44.633363879070984 }
{ "xRelativ": 11433.47022997119, "yRelativ": 255.00635849002902, "zRelativ": 95.91163415590303, "zeit": 45.13336387907098 }
{ "xRelativ": 11433.460096115776, "yRelativ": 248.39229789577348, "zRelativ": 95.91080763291724, "zeit": 45.63336387907098 }
// { "xRelativ": 11004.943335671449, "yRelativ": 679.9994989836455, "zRelativ": 87.55287154361422,"zeit": 28.32863291589727 } <- "zeit": 28.32863291589727
// { "xRelativ": 11043.727634444678, "yRelativ": 663.9667038330389, "zRelativ": 88.3051889940681, "zeit": 28.506460354460955 }
// { "xRelativ": 11119.006328411935, "yRelativ": 631.7585823372341, "zRelativ": 89.76652640827223, "zeit": 28.906818574224253 }
...
{ "xRelativ": 11499.121563240604, "yRelativ": 274.34404243874127, "zRelativ": 97.20466256494127, "zeit": 39.720062770644574 }
{ "xRelativ": 11499.266493362005, "yRelativ": 267.7249346365973, "zRelativ": 97.2069814590229, "zeit": 40.22006277064457 }
{ "xRelativ": 11499.362911009477, "yRelativ": 261.1076959097998, "zRelativ": 97.20832226297901, "zeit": 40.72006277064457 }
{ "xRelativ": 11499.42583300566, "yRelativ": 254.49226735111273, "zRelativ": 97.20898592381488, "zeit": 41.22006277064457 }
{ "xRelativ": 11499.465060791077, "yRelativ": 248.00000032681314, "zRelativ": 97.20918057242068, "zeit": 41.710891614207426 }
#endif // NEVER
/*
Approach Malte Diets, 2026-05-19
template <typename T> std::vector<std::vector<T>> getIndividualPaths(const std::vector<T> &flatTree)
{
if (flatTree.empty())
return {};
std::vector<std::vector<T>> allPaths;
std::vector<T> currentPath;
std::map<T, size_t> elementIndices;
for (const auto &element : flatTree)
{
auto it = elementIndices.find(element);
if (it != elementIndices.end())
{
allPaths.push_back(currentPath);
size_t splitIndex = it->second;
for (size_t i = splitIndex + 1; i < currentPath.size(); ++i)
elementIndices.erase(currentPath[i]);
currentPath.erase(currentPath.begin() + (splitIndex + 1), currentPath.end());
}
else
{
elementIndices[element] = currentPath.size();
currentPath.push_back(element);
}
}
if (!currentPath.empty())
allPaths.push_back(std::move(currentPath));
return allPaths;
}
*/
} // namespace AreaEffectSimulation
@@ -0,0 +1,211 @@
/**
* @RDE_HEADER@
* TrajectorySplitter
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, ESVG, 2026-04-10
*/
#pragma once
//#include <LtsCoordinate/ltscoordinate_export.h>
#include <Core/core.hpp>
#include <QVector>
namespace AreaEffectSimulation
{
// E01562-294: Falsche Darstellung Flugkurve SMArt
// At SMArt ADLER (or SAA?) generates a polyline which is "dividing":
// x x
// x x
// x x x
// x x x
// | Start | T1 | T2 (Target 1 & Target 2)
// Same diagram, but with order
// x4 x5
// x3 x6 (duplicate)
// x2 x7 x10
// x1 x8 x11
// x12
// The relativeTime step backward between x8 and x10.
//using TRAJECTORY_POINT_TYPE = Core::RelativePositionRelativeTime;
//using TRAJECTORY_TYPE = std::vector<Core::RelativePositionRelativeTime>;
//!
//! \brief The TrajectorySplitter class splits a combined/special vector into single trajectories.
//! See UnitTestTrajectorySplitter
//!
template<typename TRAJECTORY_TYPE>
class TrajectorySplitter
{
public:
// friend class UnitTestTrajectorySplitter;
explicit TrajectorySplitter(const TRAJECTORY_TYPE &trajectory)
: m_trajectory(trajectory)
{}
TRAJECTORY_TYPE getTrunk() const
{
if (!init())
{
return {};
}
TRAJECTORY_TYPE trunk(m_trajectory.begin(), m_trajectory.begin()+m_branchIndex);
return trunk;
}
TRAJECTORY_TYPE getBranch1() const
{
if (!init())
{
return {};
}
const int idx = findIndexOfEndOfFirstBranchInternal(m_trajectory);
const TRAJECTORY_TYPE branch1(m_trajectory.begin()+m_branchIndex, m_trajectory.begin()+idx+1);
return branch1;
}
TRAJECTORY_TYPE getBranch2() const
{
if (!init())
{
return {};
}
const int idx = findIndexOfEndOfFirstBranchInternal(m_trajectory);
const TRAJECTORY_TYPE branch2(m_trajectory.begin()+idx+1, m_trajectory.end());
return branch2;
}
TRAJECTORY_TYPE getTrajectory1() const
{
if (!init())
{
return {};
}
auto complete = getTrunk();
const auto &branch = getBranch1();
complete.insert(complete.end(), branch.begin(), branch.end());
return complete;
}
TRAJECTORY_TYPE getTrajectory2() const
{
if (!init())
{
return {};
}
auto complete = getTrunk();
const auto &branch = getBranch2();
complete.insert(complete.end(), branch.begin(), branch.end());
return complete;
}
size_t rawTrajectorySize() const { return m_trajectory.size(); }
protected:
static qint64 findDuplicateTimeInternal(const TRAJECTORY_TYPE &trajectory)
{
// extract relativeTimes, sort, find adjacent duplicates
QVector<qint64> relativeTimes;
relativeTimes.reserve(static_cast<int>(trajectory.size()));
for (const auto &posTimeAware : trajectory)
{
relativeTimes.append(posTimeAware.relativeTime());
}
std::sort(relativeTimes.begin(), relativeTimes.end());
auto it = std::adjacent_find(relativeTimes.begin(), relativeTimes.end());
if (it == relativeTimes.end())
{
return -1; // not found
}
const qint64 duplicateRelativeTime = *it;
return duplicateRelativeTime;
}
static int findDuplicateBranchIndexInternal(const TRAJECTORY_TYPE &trajectory)
{
const qint64 duplicateRelativeTime = findDuplicateTimeInternal(trajectory);
if (duplicateRelativeTime < 0)
{
return -1; // not found
}
// find first occurance:
int idx = 0;
{
for (const auto &posTimeAware : trajectory)
{
const qint64 currentTime = posTimeAware.relativeTime();
if (duplicateRelativeTime == currentTime)
{
break;
}
++idx;
} // for
}
return idx;
}
static int findIndexOfEndOfFirstBranchInternal(const TRAJECTORY_TYPE &trajectory)
{
int idx = -1;
{
qint64 currentTime = std::numeric_limits<qint64>::min();
for (const auto &posTimeAware : trajectory)
{
if (posTimeAware.relativeTime() < currentTime)
{
break;
}
currentTime = posTimeAware.relativeTime();
++idx;
} // for
}
return idx;
}
bool init() const
{
// already calculated?
if (m_branchIndex > 0)
{
return true;
}
// sanity check:
if (m_trajectory.size() < 3)
{
return false;
}
m_branchIndex = findDuplicateBranchIndexInternal(m_trajectory);
return (m_branchIndex >= 0);
}
private:
TRAJECTORY_TYPE m_trajectory;
mutable int m_branchIndex{0};
};
} // namespace AreaEffectSimulation
@@ -0,0 +1,34 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
aes_test_fire_automaton.cpp
aes_test_fire_automaton.h
)
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestFireAutomaton ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestFireAutomaton
NGLLogger
nglcoreapplication
LtsCommandLineParser
LtsCoordinate
Qt5::Core
Qt5::Test
ddsconnector
svcAesCommon
svcAesClient
svcBldClient
svcExmClient
svcIsmClient
svcPdmClient
svcPpmClient
)
ADD_TEST(NAME aesUnitTestFireAutomaton COMMAND $<TARGET_FILE:aesUnitTestFireAutomaton>)
ENDIF()
@@ -0,0 +1,413 @@
/**
* AES module test fire automaton
* $Id$
* $HeadURL$
* $Author$
*/
#include <QCoreApplication>
#include <QTimer>
#include <QDateTime>
#include <QTest>
#include <QVector>
#include <QSignalSpy>
#include <nglapplication/nglcoreapplication.h>
#include <AreaEffectSimulation/Utils/fire_automaton.h>
#include <AreaEffectSimulation/logger.h>
#include "aes_test_fire_automaton.h"
using namespace AreaEffectSimulation;
void TestFireAutomaton::initTestCase()
{ }
void TestFireAutomaton::init()
{
m_receivedFire.clear();
m_finishedTime = 0;
}
void TestFireAutomaton::fireReceived(int32_t id)
{
m_receivedFire.append({QDateTime::currentMSecsSinceEpoch(), id});
}
/*
Test basic pattern with fire slots = rounds
*/
void TestFireAutomaton::testSchedule_0()
{
int32_t effectDurationMSecs = 500;
int32_t cadence = 100;
int32_t numberOfRounds = 6;
QVector<int32_t> effectorIds = {1};
QString pattern = QStringLiteral("all, next, all, next, all, next");
QVector<qint64> expectedDelays = {0, 100, 200, 300, 400, 500};
QVector<QVector<int32_t>> expectedSchedule = {{1}, {1}, {1}, {1}, {1}, {1}};
testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule);
}
/*
Test pattern with fill element
*/
void TestFireAutomaton::testSchedule_1()
{
int32_t effectDurationMSecs = 800;
int32_t cadence = 100;
int32_t numberOfRounds = 13;
QVector<int32_t> effectorIds = {1, 2, 3};
QString pattern = QStringLiteral("all,next*,all");
QVector<qint64> expectedDelays = {0, 0, 0, 100, 200, 300, 400, 500, 600, 700, 800, 800, 800};
QVector<QVector<int32_t>> expectedSchedule = {{1, 2, 3}, {1}, {2}, {3}, {1}, {2}, {3}, {1}, {1, 2, 3}};
testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule);
}
/*
Test fill pattern element with 0 fill
*/
void TestFireAutomaton::testSchedule_2()
{
int32_t effectDurationMSecs = 200;
int32_t cadence = 200;
int32_t numberOfRounds = 6;
QVector<int32_t> effectorIds = {1, 2, 3};
QString pattern = QStringLiteral("all,all*,all");
QVector<qint64> expectedDelays = {0, 0, 0, 200, 200, 200};
QVector<QVector<int32_t>> expectedSchedule = {{1,2,3}, {1,2,3}};
testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule);
}
/*
Test pattern with fire slots > rounds
*/
void TestFireAutomaton::testSchedule_3()
{
int32_t effectDurationMSecs = 200;
int32_t cadence = 200;
int32_t numberOfRounds = 5;
QVector<int32_t> effectorIds = {1, 2, 3, 4};
QString pattern = QStringLiteral("all,all");
QVector<qint64> expectedDelays = {0, 0, 0, 0, 200};
QVector<QVector<int32_t>> expectedSchedule = {{1, 2, 3, 4}, {1}};
testScheduling(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testScheduling(cadence, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern, expectedDelays, expectedSchedule);
testStepping(numberOfRounds, effectorIds, pattern, expectedSchedule);
}
/*
Test pattern with rounds > fire slots
*/
void TestFireAutomaton::testSchedule_4()
{
int32_t effectDurationMSecs = 100;
int32_t numberOfRounds = 5;
QVector<int32_t> effectorIds = {1};
QString pattern = QStringLiteral("next, next");
QVector<qint64> expectedDelays = {0, 100};
QVector<QVector<int32_t>> expectedSchedule = {{1}, {1}};
FireAutomaton automaton;
QVERIFY(!automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, pattern));
}
void TestFireAutomaton::testScheduling(int32_t time, FireAutomaton::TimeType type, int32_t numberOfRounds, const QVector<int32_t>& effectorIds,
const QString& pattern, QVector<qint64> expectedDelays, QVector<QVector<int32_t>> expectedSchedule)
{
FireAutomaton automaton;
QVERIFY(automaton.init(time, type, numberOfRounds, effectorIds, pattern));
QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t)));
QVERIFY(spyFire.isValid());
QSignalSpy spyFinished(&automaton, SIGNAL(finished()));
QVERIFY(spyFinished.isValid());
connect(&automaton, &FireAutomaton::fire, this, &TestFireAutomaton::fireReceived);
qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch();
automaton.start();
QVERIFY(spyFinished.wait(1000));
QVERIFY(withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, expectedDelays.last(), 50));
int32_t expectedShots = 0;
for(const auto& tick : expectedSchedule)
expectedShots += tick.size();
QCOMPARE(spyFire.count(), expectedShots);
for(int32_t i=0; i < numberOfRounds; i++) {
withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, expectedDelays.last(), 50);
}
QVector<QVector<int32_t>> schedule;
QVector<int32_t> tickIds;
qint64 firstFireTime = m_receivedFire[0].time;
if(expectedDelays.size() != m_receivedFire.size())
{
QVERIFY(false);
return;
}
// first fire time is basis for delays
for(int i=0; i < m_receivedFire.size(); i++)
{
m_receivedFire[i].time = m_receivedFire[i].time - firstFireTime;
QVERIFY(withinTolerance(m_receivedFire[i].time, expectedDelays[i], 50));
}
tickIds.clear();
tickIds.append(m_receivedFire[0].id);
for(int i=1; i < m_receivedFire.size(); i++)
{
if(withinTolerance(m_receivedFire[i - 1].time, m_receivedFire[i].time, 50))
{
tickIds.append(m_receivedFire[i].id);
} else {
schedule.append(tickIds);
tickIds.clear();
tickIds.append(m_receivedFire[i].id);
}
}
schedule.append(tickIds);
QVERIFY(schedule.size() == expectedSchedule.size());
for(int i=0; i < schedule.size(); i++)
{
QVERIFY(schedule[i] == expectedSchedule[i]);
}
m_receivedFire.clear();
}
void TestFireAutomaton::testStepping(int32_t numberOfRounds, const QVector<int32_t>& effectorIds,
const QString& pattern, QVector<QVector<int32_t>> expectedSchedule)
{
FireAutomaton automaton;
QVERIFY(automaton.init(0, FireAutomaton::TimeType::Cadence, numberOfRounds, effectorIds, pattern));
QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t)));
QVERIFY(spyFire.isValid());
QSignalSpy spyFinished(&automaton, SIGNAL(finished()));
QVERIFY(spyFinished.isValid());
connect(&automaton, &FireAutomaton::fire, this, &TestFireAutomaton::fireReceived);
for(int i=0; i < expectedSchedule.size(); i++)
{
QVERIFY(automaton.performStep());
QTest::qWait(100);
}
QVERIFY(spyFinished.count() == 1);
QVector<QVector<int32_t>> schedule;
QVector<int32_t> tickIds;
qint64 firstFireTime = m_receivedFire[0].time;
// first fire time is basis for delays
for(int i=0; i < m_receivedFire.size(); i++)
{
m_receivedFire[i].time = m_receivedFire[i].time - firstFireTime;
}
tickIds.clear();
tickIds.append(m_receivedFire[0].id);
for(int i=1; i < m_receivedFire.size(); i++)
{
if(withinTolerance(m_receivedFire[i - 1].time, m_receivedFire[i].time, 50))
{
tickIds.append(m_receivedFire[i].id);
} else {
schedule.append(tickIds);
tickIds.clear();
tickIds.append(m_receivedFire[i].id);
}
}
schedule.append(tickIds);
QVERIFY(schedule.size() == expectedSchedule.size());
for(int i=0; i < schedule.size(); i++)
{
QVERIFY(schedule[i] == expectedSchedule[i]);
}
m_receivedFire.clear();
}
void TestFireAutomaton::testResumePaused()
{
QVector<int32_t> effectorIds = {1};
int32_t effectDurationMSecs = 1000;
int32_t numberOfRounds = 2;
FireAutomaton automaton;
QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("all*")));
QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t)));
QVERIFY(spyFire.isValid());
QSignalSpy spyFinished(&automaton, SIGNAL(finished()));
QVERIFY(spyFinished.isValid());
qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch();
QVERIFY(automaton.start());
QTest::qWait(100);
automaton.pause();
QCOMPARE(automaton.getState(), FireAutomaton::State::Paused);
qint64 pauseTimeMSecs = QDateTime::currentMSecsSinceEpoch();
QVERIFY(spyFire.count() == 1);
QTest::qWait(100);
qint64 resumeTimeMSecs = QDateTime::currentMSecsSinceEpoch();
automaton.resume();
QCOMPARE(automaton.getState(), FireAutomaton::State::Running);
QVERIFY(spyFinished.wait(2000));
qint64 measuredDuration = (QDateTime::currentMSecsSinceEpoch() - startTimeMSecs) - resumeTimeMSecs + pauseTimeMSecs;
QVERIFY(spyFinished.count() == 1);
QVERIFY(withinTolerance(measuredDuration, effectDurationMSecs, 100));
QVERIFY(spyFire.count() == numberOfRounds);
}
void TestFireAutomaton::testStop()
{
QVector<int32_t> effectorIds = {1, 2, 3, 4, 5, 6};
int32_t effectDurationMSecs = 500;
int32_t numberOfRounds = 10;
FireAutomaton automaton;
QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("next*")));
QTest::qWait(10);
QSignalSpy spyFire(&automaton, SIGNAL(fire(int32_t)));
QVERIFY(spyFire.isValid());
QSignalSpy spyFinished(&automaton, SIGNAL(finished()));
QVERIFY(spyFinished.isValid());
QVERIFY(automaton.start());
QTest::qWait(100);
automaton.stop();
int32_t fireCount = spyFire.count();
QVERIFY(!spyFinished.wait(1000)); // expected to timeout
QCOMPARE(spyFire.count(), fireCount); // expected to not have changed
QVERIFY(spyFire.count() < numberOfRounds);
// start to finish
qint64 startTimeMSecs = QDateTime::currentMSecsSinceEpoch();
QVERIFY(automaton.start());
QVERIFY(spyFinished.wait(1000));
QVERIFY(spyFire.count() - fireCount == numberOfRounds);
QVERIFY(withinTolerance(QDateTime::currentMSecsSinceEpoch() - startTimeMSecs, effectDurationMSecs, 50));
}
void TestFireAutomaton::testStates()
{
QVector<int32_t> effectorIds = {11, 10};
int32_t effectDurationMSecs = 500;
int32_t numberOfRounds = 10;
FireAutomaton automaton;
QSignalSpy spyFinished(&automaton, SIGNAL(finished()));
QVERIFY(spyFinished.isValid());
// init
QCOMPARE(automaton.getState(), FireAutomaton::State::Uninitialized);
QVERIFY(!automaton.start());
QVERIFY(!automaton.pause());
QVERIFY(!automaton.resume());
QVERIFY(automaton.init(effectDurationMSecs, FireAutomaton::TimeType::Duration, numberOfRounds, effectorIds, QStringLiteral("all*")));
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
QVERIFY(!automaton.pause());
QVERIFY(!automaton.resume());
// start -> pause -> resume -> stop
QVERIFY(automaton.start());
QCOMPARE(automaton.getState(), FireAutomaton::State::Running);
QVERIFY(!automaton.start());
QVERIFY(!automaton.resume());
QVERIFY(automaton.pause());
QCOMPARE(automaton.getState(), FireAutomaton::State::Paused);
QVERIFY(!automaton.start());
QVERIFY(!automaton.pause());
QVERIFY(automaton.resume());
QCOMPARE(automaton.getState(), FireAutomaton::State::Running);
QVERIFY(spyFinished.wait(1000));
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
QVERIFY(!automaton.pause());
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
QVERIFY(!automaton.resume());
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
// start -> stop
QVERIFY(automaton.start());
QCOMPARE(automaton.getState(), FireAutomaton::State::Running);
QVERIFY(!automaton.start());
QVERIFY(!automaton.resume());
QVERIFY(spyFinished.wait(1000));
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
// start -> finish
QVERIFY(automaton.start());
QCOMPARE(automaton.getState(), FireAutomaton::State::Running);
QVERIFY(!automaton.start());
QVERIFY(!automaton.resume());
QVERIFY(spyFinished.wait(1000));
QCOMPARE(automaton.getState(), FireAutomaton::State::Stopped);
}
int main(int argc, char *argv[])
{
qRegisterMetaType<int32_t>("int32_t");
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTest_FireAutomaton", "log4cxx_aes.props");
TestFireAutomaton testCase;
return QTest::qExec(&testCase, argc, argv);
}
@@ -0,0 +1,57 @@
/**
* AES unit test fire automaton
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <cstdint>
#include <QtCore>
#include <QMetaType>
#include <AreaEffectSimulation/Utils/fire_automaton.h>
Q_DECLARE_METATYPE(int32_t)
namespace AreaEffectSimulation
{
class TestFireAutomaton: public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void init();
void testResumePaused();
void testStop();
void testStates();
void testSchedule_0();
void testSchedule_1();
void testSchedule_2();
void testSchedule_3();
void testSchedule_4();
private:
struct FireEvent
{
qint64 time;
int32_t id;
};
void testScheduling(int32_t time, FireAutomaton::TimeType type, int32_t numberOfRounds, const QVector<int32_t>& effectorIds,
const QString& pattern, QVector<qint64> expectedDelays, QVector<QVector<int32_t>> expectedSchedule);
void testStepping(int32_t numberOfRounds, const QVector<int32_t>& effectorIds, const QString& pattern, QVector<QVector<int32_t>> expectedSchedule);
void fireReceived(int32_t id);
QVector<FireEvent> m_receivedFire;
qint64 m_finishedTime;
};
static bool withinTolerance(qint64 measured, qint64 expected, qint64 tolerance) {
return qAbs(measured-expected) <= tolerance;
};
} // namespace AreaEffectSimulation
+48
View File
@@ -0,0 +1,48 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
NGL_ADD_EXECUTABLE(AAR SV NO_TRANSLATION aes_statemachine_test
aes_statemachine_test.cpp
aes_statemachine_test.h
../object_base.cpp
../object_base.h
../dds_handler.cpp
../dds_handler.h
../special_area_provider.cpp
../special_area_provider.h
)
NGL_TARGET_INCLUDE_DIRECTORIES( aes_statemachine_test
PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/..
)
NGL_TARGET_LINK_LIBRARIES(aes_statemachine_test
ddsconnector
nglcoreapplication
svcAesClient
svcAesServer
svcCesClient
svcCoreClient
svcPdmClient
svcPpmClient
svcTabClient
svcIsmClient
svcAsmClient
svcExmClient
Qt5::Core
Qt5::Test
)
NGL_TARGET_COMPILE_DEFINITIONS(aes_statemachine_test PRIVATE AES_STATEMACHINE_TEST)
IF(TARGET aes_statemachine_test)
ADD_TEST(NAME aes_statemachine_test COMMAND $<TARGET_FILE:aes_statemachine_test>)
ENDIF()
ADD_SUBDIRECTORY(UnitTest_Basics)
ADD_SUBDIRECTORY(UnitTest_TneAlgo)
ADD_SUBDIRECTORY(UnitTest_Polygons)
@@ -0,0 +1,29 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
basics_unit_test.cpp
basics_unit_test.h
)
SOURCE_GROUP( UnitTestSources FILES ${UnitTestSources} )
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestBasics ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestBasics
NGLLogger
nglcoreapplication
LtsCoordTrans
Qt5::Core
Qt5::Test
svcAesCommon
)
ADD_TEST(NAME aesUnitTestBasics COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
@@ -0,0 +1,427 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#include "basics_unit_test.h"
#include <nglapplication/nglcoreapplication.h>
#include <AreaEffectSimulation/aes.hpp>
#include <QtTest/QTest>
#include <QTimer>
#include <QDebug>
#include <QtTest/QSignalSpy>
#include <iostream>
using namespace AreaEffectSimulation;
UnitTestBasics::UnitTestBasics()
: m_logger(getLogger("UnitTest"))
{}
void UnitTestBasics::test_isNull_isNaN()
{
QPointF point1(1.1, 1.2);
QVERIFY2(point1.isNull() == false, "QPointF isNull failed");
QPointF point2;
QVERIFY2(point2.isNull() == true, "QPointF isNull failed");
QPointF point3(0, 0);
QVERIFY2(point3.isNull() == true, "QPointF isNull failed");
QPointF point4(1, 2);
point4.ry() = 0;
QVERIFY2(point4.isNull() == false, "QPointF isNull failed");
QPointF point5(1, 2);
point5.rx() = std::nan("");
point5.ry() = std::nan("");
// !!! nan is not detected be isNull !!!
QVERIFY2(point5.isNull() == false, "QPointF isNull failed");
// Qt::5.14.1: mit isNull() wird NaN nicht erkannt.
// Eine separate Prüfung ergibt Sinn!
}
void UnitTestBasics::test_operator_equal_1()
{
// Some cases the operator == recognizes a difference, but i don´t know why.
// crate this testCase:
AesItemView aesItem1, aesItem2;
QVERIFY2(aesItem1 == aesItem2, "AesItemView::operator== failed");
aesItem1.id(1);
aesItem2.id(1);
QVERIFY2(aesItem1 == aesItem2, "AesItemView::operator== failed");
std::vector<Core::GeoPosition> formVec;
formVec.reserve(100);
for (int i=1; i< 100; ++i)
formVec.emplace_back(Core::GeoPosition(i*0.001, 100/i, 100/i));
aesItem1.aesItemId(12);
aesItem1.sampleTime(12345567);
aesItem1.chargeStruct().chargeTypeId(123);
aesItem1.effectStart(123456789);
aesItem1.form(formVec);
aesItem1.boundingBoxBottomLeft(Core::GeoPosition2D(0.1, 0.2));
aesItem1.boundingBoxTopRight(Core::GeoPosition2D(1.1, 1.2));
aesItem1.aesTypeId(AreaEffectSimulation::AesType_def::AtBoobyTrap);
aesItem1.effectStateId(AreaEffectSimulation::AesEffectState_def::EsActivated);
aesItem1.partyId(2);
aesItem1.setId(2334);
aesItem2 = aesItem1;
QVERIFY2(aesItem1 == aesItem2, "AesItemView::operator== failed");
}
void foo(const std::vector<QPoint> &vec)
{
qCritical() << " foo: number of elements = " << vec.size();
// copy data to tele
for (unsigned int i = 0; i < 4; ++i)
{
int offsetX = -1;
int offsetY = -1;
// qCritical() << " foo: vec.size()-1 = " << vec.size()-1;
if (i <= vec.size()-1)
{
offsetX = vec[i].x();
offsetY = vec[i].y();
}
// instead of writing data to telegram, use qCritical() to print:
qCritical() << " i=" << i
<< " # offset [x-y]=" << offsetX << " # " << offsetY;
}
}
void UnitTestBasics::test_container()
{
// Define source data
std::vector<QPoint> sourcePointVec;
sourcePointVec.push_back(QPoint(1, 1));
sourcePointVec.push_back(QPoint(2, 2));
// call foo
foo(sourcePointVec);
}
// https://medium.com/@sidbhasin82/c-templates-what-is-std-enable-if-and-how-to-use-it-fd76d3abbabe
class ObjectBASE
{
public:
int32_t memberBaseINT{0};
int32_t getMemberInt() { return 11; }
};
class ObjectINT : public ObjectBASE
{
public:
int32_t memberINT{0};
int32_t getMemberInt() { return memberINT; }
};
class ObjectDOUBLE : public ObjectBASE
{
public:
double memberDOUBLE{0.0};
};
template <class C>
class HasRequestId
{
template <class T>
static std::true_type testSignature(const int32_t &(T::*)() const);
template <class T>
static std::true_type testSignature(int32_t (T::*)() const);
template <class T>
static decltype(testSignature(&T::getMemberInt)) test(std::nullptr_t);
template <class T>
static std::false_type test(...);
public:
using type = decltype(test<C>(nullptr));
static const bool value = type::value;
};
template<typename T>
struct has_memberDouble {
static const bool value = false; //Compile time Constant
};
template<>
struct has_memberDouble<ObjectINT> {
static const bool value = true;
};
template < typename T, typename std::enable_if < has_memberDouble<T>::value, int >::type = 0 >
void getFilterType(const T &obj)
{
qCritical() << " HasRequestId";
// sendErrorInternal(request.errorSlot, reason, request.uniqueId, request.sample.data().requestId());
}
// -------------------------------------------------------------------------------------------------------------
template < typename T, typename std::enable_if < !has_memberDouble<T>::value, int >::type = 0 >
void getFilterType(const T &obj)
{
qCritical() << " ! HasRequestId";
// sendErrorInternal(request.errorSlot, reason, request.uniqueId, -1);
}
template <typename T>
class worker
{
public:
virtual void do_stuff()
{
qCritical() << "**** do_stuff: this" << typeid(*this).name();
}
};
void UnitTestBasics::test_SFINAE()
{
worker<ObjectINT> inst1;
inst1.do_stuff();
worker<ObjectDOUBLE> inst2;
inst2.do_stuff();
ObjectINT oInt;
getFilterType(oInt);
ObjectDOUBLE oDouble;
getFilterType(oDouble);
}
void UnitTestBasics::test_fireTimer10()
{
FiringTimer ft(QStringLiteral("TestEnv10"));
QStringList logSl = ft.getLogHistorie();
QVERIFY2(logSl.size() == 0, "getCommandHistorie failed");
{
bool success = ft.startFiring();
QVERIFY2(!success, "startFiring not failed");
}
{
ft.init(10*1000, 5, 2.567*1000); // effectDuration, ammoCount, ammoFlightDuration
bool success = ft.startFiring();
QVERIFY2(success, "startFiring failed");
success = ft.startFiring();
QVERIFY2(!success, "startFiring failed");
QStringList logSl1 = ft.getLogHistorie();
QVERIFY2(logSl1.size() == 1, "getCommandHistorie failed");
for (const auto &log : qAsConst(logSl1))
NOTICE_LOG(m_logger) << "log ='" << log << "'";
}
{
ft.reset();
QStringList logSl2 = ft.getLogHistorie();
QVERIFY2(logSl2.size() == 0, "getCommandHistorie failed");
}
}
void UnitTestBasics::test_fireTimer15()
{
FiringTimer ft(QStringLiteral("TestEnv15"));
ft.init(10*1000, 5, 0.0); // effectDurationMsec, ammoFlightDurationMsec
// count firing
QSignalSpy spyFiring(&ft, &FiringTimer::firing);
ft.startFiring(); // with this, the very first signal 'firing' will be emitted
// count firingFinished
QSignalSpy spyFinished(&ft, &FiringTimer::firingFinished);
QSignalSpy spyFinishedPlus(&ft, &FiringTimer::firingFinishedPlus);
QSignalSpy spyStateChanged(&ft, &FiringTimer::stateChangeDelayedAsInt);
spyFinishedPlus.wait(20000);
// call wait twice (wait for CMD_EXPIRED, CMD_EXPIRED_PLUS)
spyStateChanged.wait(5000);
spyStateChanged.wait(5000);
// Verify at end:
QVERIFY2(spyFiring.count() == 5, "FiringTimer: signalCount failed, spyFiring");
QVERIFY2(spyFinished.count() == 1, "FiringTimer: signalCount failed, spyFinished");
QVERIFY2(spyFinishedPlus.count() == 1, "FiringTimer: signalCount failed, spyFinishedPlus");
QVERIFY2(spyStateChanged.count() == 3, "FiringTimer: signalCount failed, spyStateChanged");
NOTICE_LOG(m_logger) << " spyStateChanged.count() " << spyStateChanged.count();
// check order of signal paramter:
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[0][0].toInt()) == FiringTimer::CMD::CMD_START, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[1][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[2][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED_PLUS, "FiringTimer: StateChanged.argument failed, spyStateChanged");
}
void UnitTestBasics::test_fireTimer20()
{
FiringTimer ft(QStringLiteral("TestEnv20"));
ft.init(10*1000, 5, 2.567*1000); // effectDurationMsec, ammoFlightDurationMsec
// count firing
QSignalSpy spyFiring(&ft, &FiringTimer::firing);
ft.startFiring(); // with this, the very first signal 'firing' will be emitted
// count firingFinished
QSignalSpy spyFinished(&ft, &FiringTimer::firingFinished);
QSignalSpy spyFinishedPlus(&ft, &FiringTimer::firingFinishedPlus);
QSignalSpy spyStateChanged(&ft, &FiringTimer::stateChangeDelayedAsInt);
spyFinishedPlus.wait(20000);
// call wait twice (wait for CMD_EXPIRED, CMD_EXPIRED_PLUS)
spyStateChanged.wait(5000);
spyStateChanged.wait(5000);
// Verify at end:
QVERIFY2(spyFiring.count() == 5, "FiringTimer: signalCount failed, spyFiring");
QVERIFY2(spyFinished.count() == 1, "FiringTimer: signalCount failed, spyFinished");
QVERIFY2(spyFinishedPlus.count() == 1, "FiringTimer: signalCount failed, spyFinishedPlus");
QVERIFY2(spyStateChanged.count() == 3, "FiringTimer: signalCount failed, spyStateChanged");
// QList<QVariant> arguments = spyStateChanged.takeFirst(); // take the first signal
// NOTICE_LOG(m_logger) << " spyStateChanged.count() " << spyStateChanged.count();
// for (const QList<QVariant> &argument : qAsConst(spyStateChanged))
// NOTICE_LOG(m_logger) << " spyStateChanged . argument " << argument.first().toInt();
// check order of signal paramter:
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[0][0].toInt()) == FiringTimer::CMD::CMD_START, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[1][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[2][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED_PLUS, "FiringTimer: StateChanged.argument failed, spyStateChanged");
}
void UnitTestBasics::test_fireTimer21()
{
FiringTimer ft(QStringLiteral("TestEnv21"));
ft.init(10*1000, 5, 2.567*1000); // effectDuration, ammoCount, ammoFlightDuration
ft.startFiring();
QTimer::singleShot(3000, this, [this, &ft]() {
NOTICE_LOG(m_logger) << " trigger pauseFiring...";
ft.pauseFiring();
});
QSignalSpy spyFinished(&ft, &FiringTimer::firingFinished);
QSignalSpy spyFinishedPlus(&ft, &FiringTimer::firingFinishedPlus);
QSignalSpy spyStateChanged(&ft, &FiringTimer::stateChangeDelayedAsInt);
spyFinishedPlus.wait(10000);
// call wait twice (wait for CMD_EXPIRED, CMD_EXPIRED_PLUS)
spyStateChanged.wait(5000);
spyStateChanged.wait(5000);
// Verify at end:
QVERIFY2(spyFinished.count() == 0, "FiringTimer: signalCount failed, spyFinished");
QVERIFY2(spyFinishedPlus.count() == 0, "FiringTimer: signalCount failed, spyFinishedPlus");
QVERIFY2(spyStateChanged.count() == 2, "FiringTimer: signalCount failed, spyStateChanged");
// check order of signal paramter:
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[0][0].toInt()) == FiringTimer::CMD::CMD_START, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[1][0].toInt()) == FiringTimer::CMD::CMD_PAUSE, "FiringTimer: StateChanged.argument failed, spyStateChanged");
// !! no finished !!
}
void UnitTestBasics::test_fireTimer22()
{
FiringTimer ft(QStringLiteral("TestEnv22"));
ft.init(10*1000, 5, 3.000*1000); // effectDuration, ammoCount, ammoFlightDuration
// count firing
QSignalSpy spyFiring(&ft, &FiringTimer::firing);
ft.startFiring();
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
QTimer::singleShot(3000, this, [this, &ft]() {
NOTICE_LOG(m_logger) << " trigger pauseFiring...";
ft.pauseFiring();
});
QTimer::singleShot(5123, this, [this, &ft]() {
NOTICE_LOG(m_logger) << " trigger (re)StartFiring / Resume ...";
ft.startFiring();
});
QSignalSpy spyFinished(&ft, &FiringTimer::firingFinished);
QSignalSpy spyFinishedPlus(&ft, &FiringTimer::firingFinishedPlus);
QSignalSpy spyStateChanged(&ft, &FiringTimer::stateChangeDelayedAsInt);
spyFinishedPlus.wait(20000);
// call wait twice (wait for CMD_EXPIRED, CMD_EXPIRED_PLUS)
spyStateChanged.wait(5000);
spyStateChanged.wait(5000);
// Verify at end:
QVERIFY2(spyFiring.count() == 5, "FiringTimer: signalCount failed, spyFiring");
QVERIFY2(spyFinished.count() == 1, "FiringTimer: signalCount failed, spyFinished");
QVERIFY2(spyFinishedPlus.count() == 1, "FiringTimer: signalCount failed, spyFinishedPlus");
QVERIFY2(spyStateChanged.count() == 5, "FiringTimer: signalCount failed, spyStateChanged");
// check order of signal paramter:
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[0][0].toInt()) == FiringTimer::CMD::CMD_START, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[1][0].toInt()) == FiringTimer::CMD::CMD_PAUSE, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[2][0].toInt()) == FiringTimer::CMD::CMD_RESUME, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[3][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED, "FiringTimer: StateChanged.argument failed, spyStateChanged");
QVERIFY2(static_cast<FiringTimer::CMD>(spyStateChanged[4][0].toInt()) == FiringTimer::CMD::CMD_EXPIRED_PLUS, "FiringTimer: StateChanged.argument failed, spyStateChanged");
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
// stop firing2 after [ms]: 5999, m_activeMsec in total = 8902, m_pausedMsec in total = 2000
// ^^^^ maybe you expect 10000
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTest_TNE-ALOG", "log4cxxUnitTest.props");
UnitTestBasics tc;
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,50 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <../../logger.h>
#include <QCoreApplication>
#include <QtTest/QTest>
#include <AreaEffectSimulation/Utils/firing_timer.h>
class UnitTestBasics : public QObject
{
Q_OBJECT
public:
UnitTestBasics();
private Q_SLOTS:
// void init();
void test_isNull_isNaN();
void test_operator_equal_1();
void test_container();
void test_SFINAE();
void test_fireTimer10();
void test_fireTimer15();
void test_fireTimer20();
void test_fireTimer21();
void test_fireTimer22();
// void cleanup();
private:
AreaEffectSimulation::LOGGER_T &m_logger;
};
@@ -0,0 +1,29 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
polygons_unit_test.cpp
polygons_unit_test.h
)
SOURCE_GROUP( UnitTestSources FILES ${UnitTestSources} )
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestPolygons ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestPolygons
NGLLogger
nglcoreapplication
LtsCoordTrans
Qt5::Core
Qt5::Test
svcAesCommon
)
ADD_TEST(NAME aesUnitTestPolygons COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
@@ -0,0 +1,308 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#include "polygons_unit_test.h"
#include <Services/AreaEffectSimulation/Utils/polygons.h>
#include <nglapplication/nglcoreapplication.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QTransform>
#include <QTextStream> //
#include <QtTest/QTest>
#include <QDebug>
#include <iostream>
using namespace AreaEffectSimulation;
double distanceMeter(const LtsCoordTrans::UtmCoordinate utmCoord1, const LtsCoordTrans::UtmCoordinate &utmCoord2)
{
double dist = LtsCoordTrans::distance(utmCoord1, utmCoord2);
qCritical() << " dist" << dist;
return dist;
}
double distanceMeter(const QPointF &geoCoord1, const LtsCoordTrans::UtmCoordinate &utmCoord2)
{
const LtsCoordTrans::UtmCoordinate &utmCoord1 = LtsCoordTrans::UtmCoordinate::fromGeographic(geoCoord1);
double dist = LtsCoordTrans::distance(utmCoord1, utmCoord2);
qCritical() << " dist" << dist;
return dist;
}
void UnitTestPolygons::test_QTrans()
{
QPolygonF poly;
poly.reserve(3);
poly.append(QPointF(0, 0));
poly.append(QPointF(1, 0));
poly.append(QPointF(2, 0));
//rotate
{
QTransform trans;
trans.rotate(90);
QPolygonF poly2a = trans.map(poly);
qCritical() << "poly2a = " << poly2a;
QVERIFY2(poly2a.first() == QPointF(0, 0), "QTransform failed");
QVERIFY2(poly2a.last() == QPointF(0, 2), "QTransform failed; vec.last()");
}
{
QPolygonF poly2b = QTransform().rotate(90).map(poly); // squence in a single line
qCritical() << "poly2b = " << poly2b;
QVERIFY2(poly2b.first() == QPointF(0, 0), "QTransform failed");
QVERIFY2(poly2b.last() == QPointF(0, 2), "QTransform failed; vec.last()");
}
}
void printAbl(const Polygons::AreaBottomLeft &obj)
{
qCritical() << " AreaBottomLeft = "
<< ", cornerBL=" << obj.m_cornerBottomLeft
<< ", m_width=" << obj.m_width
<< ", m_height=" << obj.m_height
<< ", m_height=" << obj.m_height;
}
void UnitTestPolygons::test_explosionClusterAddTla()
{
double width = 1000; // x
double height = 100; // y
double rotation_deg = 0;
const Polygons::AreaBottomLeft boundaryOrg(QPointF(123, 123), width, height, rotation_deg);
printAbl(boundaryOrg);
{
int zog = 0;
const Polygons::AreaBottomLeft areaBlWithZog = Polygons::explosionClusterAddTla(boundaryOrg, zog);
printAbl(areaBlWithZog);
QVERIFY2(areaBlWithZog.m_cornerBottomLeft == boundaryOrg.m_cornerBottomLeft, "m_cornerBottomLeft failed");
QVERIFY2(areaBlWithZog.m_width == boundaryOrg.m_width, "m_width failed");
QVERIFY2(areaBlWithZog.m_height == boundaryOrg.m_height, "m_height failed");
QVERIFY2(areaBlWithZog.m_rotation_deg == boundaryOrg.m_rotation_deg, "m_rotation_deg failed");
}
{
int zog = 10;
const Polygons::AreaBottomLeft areaBlWithZog = Polygons::explosionClusterAddTla(boundaryOrg, zog);
printAbl(areaBlWithZog);
QVERIFY2(areaBlWithZog.m_cornerBottomLeft == QPointF(113, 113), "m_cornerBottomLeft failed");
QVERIFY2(areaBlWithZog.m_width == boundaryOrg.m_width+(2*zog), "m_width failed");
QVERIFY2(areaBlWithZog.m_height == boundaryOrg.m_height+(2*zog), "m_height failed");
QVERIFY2(areaBlWithZog.m_rotation_deg == boundaryOrg.m_rotation_deg, "m_rotation_deg failed");
}
{
double rotation_deg = 30;
const Polygons::AreaBottomLeft boundaryOrgRot(QPointF(123, 123), width, height, rotation_deg);
int zog = 10;
const Polygons::AreaBottomLeft areaBlWithZog = Polygons::explosionClusterAddTla(boundaryOrgRot, zog);
printAbl(areaBlWithZog);
QVERIFY2((areaBlWithZog.m_cornerBottomLeft - QPointF(109.34, 119.34)).manhattanLength() < 1, "m_cornerBottomLeft failed");
QVERIFY2(areaBlWithZog.m_width == boundaryOrgRot.m_width+(2*zog), "m_width failed");
QVERIFY2(areaBlWithZog.m_height == boundaryOrgRot.m_height+(2*zog), "m_height failed");
QVERIFY2(areaBlWithZog.m_rotation_deg == boundaryOrgRot.m_rotation_deg, "m_rotation_deg failed");
}
}
void UnitTestPolygons::test_explosionClusterAreaToMtpsRelativeToBottomLeft_noRotation()
{
double width = 1000; // x
double height = 100; // y
double rotation_deg = 0;
const Polygons::AreaBottomLeft boundary(QPointF(123, 123), width, height, rotation_deg);
const int numberOfMTPs = 4;
double singleWidthOut = 0;
double singleHeightOut = 0;
QVector<QPointF> vec = Polygons::explosionClusterAreaToMtpsRelativeToBottomLeft(boundary, numberOfMTPs, &singleWidthOut, &singleHeightOut);
qCritical() << "vec = " << vec;
qCritical() << "singleWidthOut = " << singleWidthOut;
qCritical() << "singleHeightOut = " << singleHeightOut;
// independent on rotation
QVERIFY2(vec.size() == numberOfMTPs, "noRotation failed; numberOfMTPs");
QVERIFY2(singleWidthOut == width/4, "noRotation failed; singleWidthOut");
QVERIFY2(singleHeightOut == height, "noRotation failed; singleHeightOut");
// depending on rotation
QVERIFY2(vec.first() == QPointF(125.0, 50), "noRotation failed");
QVERIFY2(vec[1] == QPointF(375.0, 50), "noRotation failed");
QVERIFY2(vec[2] == QPointF(625.0, 50), "noRotation failed");
QVERIFY2(vec.last() == QPointF(875.0, 50), "noRotation failed");
}
void UnitTestPolygons::test_explosionClusterAreaToMtpsRelativeToBottomLeft_withRotation()
{
double width = 1000; // x
double height = 100; // y
double rotation_deg = -90;
const Polygons::AreaBottomLeft boundary(QPointF(123, 123), width, height, rotation_deg);
const int numberOfMTPs = 4;
double singleWidthOut = 0;
double singleHeightOut = 0;
QVector<QPointF> vec = Polygons::explosionClusterAreaToMtpsRelativeToBottomLeft(boundary, numberOfMTPs, &singleWidthOut, &singleHeightOut);
qCritical() << "vec = " << vec;
qCritical() << "singleWidthOut = " << singleWidthOut;
qCritical() << "singleHeightOut = " << singleHeightOut;
// independent on rotation
QVERIFY2(vec.size() == numberOfMTPs, "withRotation failed; numberOfMTPs");
QVERIFY2(singleWidthOut == width/4, "withRotation failed; singleWidthOut");
QVERIFY2(singleHeightOut == height, "withRotation failed; singleHeightOut");
// depending on rotation
QVERIFY2(vec.first() == QPointF(-50, 125), "withRotation failed");
QVERIFY2(vec[1] == QPointF(-50, 375), "withRotation failed");
QVERIFY2(vec[2] == QPointF(-50, 625), "withRotation failed");
QVERIFY2(vec.last() == QPointF(-50, 875), "withRotation failed");
}
void UnitTestPolygons::test_explosionClusterSingleExplosionsGAUSS_Mtp()
{
Database::AesExplosionCluster explosionClusterDb;
double width = 1000; // x
double height = 100; // y
double rotation_deg = 0;
LtsCoordTrans::UtmCoordinate UTM = LtsCoordTrans::UtmCoordinate::fromMgrs(QLatin1String("32UMD100000100000")); // 32U MD 100000 100000
const Polygons::AreaBottomLeft boundary(UTM.geographic(), width, height, rotation_deg);
explosionClusterDb.id = 1;
explosionClusterDb.impactPosition = boundary.m_cornerBottomLeft;
explosionClusterDb.impactLength = boundary.m_height;
explosionClusterDb.impactWidth = boundary.m_width;
explosionClusterDb.rotationAngle = boundary.m_rotation_deg;
explosionClusterDb.zog = 0;
explosionClusterDb.impactCount = 8;
explosionClusterDb.mhpCountRequest = 4;
// rotation = 0
{
double mtpRotationNorthDegree = explosionClusterDb.rotationAngle;
const QVector<QPointF> &singleExplosions = Polygons::explosionClusterSingleExplosionsGAUSS_Mtp(explosionClusterDb, mtpRotationNorthDegree);
qCritical() << "rotation_deg = " << rotation_deg
<< ", mtpRotationNorthDegree = " << mtpRotationNorthDegree
<< ", explosionClusterDb = " << singleExplosions;
for (const auto &singleExplosion : qAsConst(singleExplosions))
qDebug() << "singleExUtm: " << LtsCoordTrans::UtmCoordinate::fromGeographic(singleExplosion).mgrs();
size_t i = 0;
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10029 10048"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10298 10042"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10682 10046"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10908 10055"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10101 10076"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10386 10022"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10581 10032"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10828 10065"))) < 1, "ERROR, distanceMeter failed!!");
}
// rotation = -90
rotation_deg = -90;
const Polygons::AreaBottomLeft boundary2(UTM.geographic(), width, height, rotation_deg);
explosionClusterDb.id = 1;
explosionClusterDb.impactPosition = boundary2.m_cornerBottomLeft;
explosionClusterDb.impactLength = boundary2.m_height;
explosionClusterDb.impactWidth = boundary2.m_width;
explosionClusterDb.rotationAngle = boundary2.m_rotation_deg;
{
double mtpRotationNorthDegree = explosionClusterDb.rotationAngle;
const QVector<QPointF> &singleExplosions = Polygons::explosionClusterSingleExplosionsGAUSS_Mtp(explosionClusterDb, mtpRotationNorthDegree);
qCritical() << "rotation_deg = " << rotation_deg
<< ", mtpRotationNorthDegree = " << mtpRotationNorthDegree
<< " explosionClusterDb = " << singleExplosions;
for (const auto &singleExplosion : qAsConst(singleExplosions))
qCritical() << "singleExUtm: " << LtsCoordTrans::UtmCoordinate::fromGeographic(singleExplosion).mgrs();
size_t i = 0;
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09952 10029"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09958 10298"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09954 10682"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09945 10908"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09924 10101"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09978 10386"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09968 10581"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09935 10828"))) < 1, "ERROR, distanceMeter failed!!");
}
// rotation = -45
rotation_deg = -45;
int zog = 100;
const Polygons::AreaBottomLeft boundary3(UTM.geographic(), width, height, rotation_deg);
explosionClusterDb.id = 1;
explosionClusterDb.impactPosition = boundary3.m_cornerBottomLeft;
explosionClusterDb.impactLength = boundary3.m_height;
explosionClusterDb.impactWidth = boundary3.m_width;
explosionClusterDb.rotationAngle = boundary3.m_rotation_deg;
explosionClusterDb.zog = zog;
{
double mtpRotationNorthDegree = explosionClusterDb.rotationAngle;
const QVector<QPointF> &singleExplosions = Polygons::explosionClusterSingleExplosionsGAUSS_Mtp(explosionClusterDb, mtpRotationNorthDegree);
qCritical() << "rotation_deg = " << rotation_deg
<< ", zog = " << zog
<< ", mtpRotationNorthDegree = " << mtpRotationNorthDegree
<< " explosionClusterDb = " << singleExplosions;
for (const auto &singleExplosion : qAsConst(singleExplosions))
qCritical() << "singleExUtm: " << LtsCoordTrans::UtmCoordinate::fromGeographic(singleExplosion).mgrs();
size_t i = 0;
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09923 09984"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10164 10200"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10480 10536"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10654 10746"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 09925 10104"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10281 10233"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10426 10419"))) < 1, "ERROR, distanceMeter failed!!");
QVERIFY2(distanceMeter(singleExplosions[i++], LtsCoordTrans::UtmCoordinate::fromMgrs(QStringLiteral("32U MD 10565 10699"))) < 1, "ERROR, distanceMeter failed!!");
}
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTest_TNE-ALOG", "log4cxxUnitTest.props");
UnitTestPolygons tc;
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,34 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QCoreApplication>
#include <QtTest/QTest>
class UnitTestPolygons : public QObject
{
Q_OBJECT
private Q_SLOTS:
// void init();
void test_QTrans();
void test_explosionClusterAddTla();
void test_explosionClusterAreaToMtpsRelativeToBottomLeft_noRotation();
void test_explosionClusterAreaToMtpsRelativeToBottomLeft_withRotation();
void test_explosionClusterSingleExplosionsGAUSS_Mtp();
// void cleanup();
private:
};
@@ -0,0 +1,28 @@
#
# @RDE_HEADER@
# $Id$
# $HeadURL$
# $Author$
#
SET(UnitTestSources
tne_algo_unit_test.cpp
tne_algo_unit_test.h
)
SOURCE_GROUP( UnitTestSources FILES ${UnitTestSources} )
IF(BUILD_TESTING)
ADD_EXECUTABLE(aesUnitTestTneAlgorithmen ${LinkedSources} ${UnitTestSources} )
TARGET_LINK_LIBRARIES(aesUnitTestTneAlgorithmen
NGLLogger
nglcoreapplication
Qt5::Core
Qt5::Test
svcAesCommon
)
ADD_TEST(NAME aesUnitTestTneAlgorithmen COMMAND $<TARGET_FILE:aesUnitTestTneAlgorithmen>)
ENDIF()
@@ -0,0 +1,142 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#include "tne_algo_unit_test.h"
#include <Services/AreaEffectSimulation/Utils/tne_algorithmen.h>
#include <nglapplication/nglcoreapplication.h>
#include <QtTest/QTest>
#include <QFile>
#include <QDir>
#include <iostream>
using namespace AreaEffectSimulation;
//
//
// This class is an UnitTest with values taken from the TNE.
// That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
//
//
void UnitTestVerteilung::test_long()
{
int size = sizeof(int);
std::cout << " sizeof 'int' " << size << std::endl;
size = sizeof(long);
std::cout << " sizeof 'long' " << size << std::endl;
size = sizeof(long long);
std::cout << " sizeof 'long long' " << size << std::endl;
size = sizeof(qint32);
std::cout << " sizeof 'qint32g' " << size << std::endl;
}
void UnitTestVerteilung::test_xorshift32()
{
// 1
ULONG pxInOut = 123;
ushort ret = TNE_ALGORITHMEN::xorshift32(&pxInOut);
std::cout << " test_xorshift32 ret: " << ret << std::endl;
QVERIFY2(ret == 28668, "xorshift32 failed");
}
void UnitTestVerteilung::test_xorshift32_nv()
{
ULONG _generator = 123;
short _bereich = 500;
// _generator = 123, _bereich = 500 => laut Moetje: -73, 93, -79, -321, -25
int ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " xorshift32_nv ret: " << ret << std::endl;
QVERIFY2(ret == -73, "xorshift32_nv failed");
ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " xorshift32_nv ret: " << ret << std::endl;
QVERIFY2(ret == 93, "xorshift32_nv failed");
ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " xorshift32_nv ret: " << ret << std::endl;
QVERIFY2(ret == -79, "xorshift32_nv failed");
ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " xorshift32_nv ret: " << ret << std::endl;
QVERIFY2(ret == -321, "xorshift32_nv failed");
ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " xorshift32_nv ret: " << ret << std::endl;
QVERIFY2(ret == -25, "xorshift32_nv failed");
}
void UnitTestVerteilung::test_xorshift32_nv_saveToFile()
{
ULONG _generator = 123;
SHORT _bereich = 500;
QString tempDir = QDir::tempPath();
QFile file(tempDir + QDir::separator() + QStringLiteral("tne_algorithmen_xorshift32_nv_data.txt"));
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
return;
QTextStream out(&file);
out << "Test vom Algorithmus 'xorshift32_nv'" << "\n";
out << "_generator = " << _generator << "\n";
out << "_bereich = " << _bereich << "\n";
out << " Start " << "\n";
for (int i = 0; i < 1000; ++i)
{
out << "_generator = " << _generator;
int ret = TNE_ALGORITHMEN::xorshift32_nv(&_generator, _bereich, 3);
std::cout << " test_xorshift32_nv ret: " << ret << std::endl;
out << " -> erzeugter Wert = " << ret << "\n";
}
out << "Ende :-)" << "\n";
}
void UnitTestVerteilung::test_areaExlosionGAUSS()
{
const int wirkfeldKennung = 4711;
const int breite_m = 500; // Vielfaches von 5m
const int tiefe_m = 50; // Vielfaches von 5m
const int anzahl_einschlaege = 10;
QVector<QPointF> impacts = TNE_ALGORITHMEN::areaExlosionsGAUSS(wirkfeldKennung,
breite_m, tiefe_m,
anzahl_einschlaege);
QVERIFY2(impacts.size() == anzahl_einschlaege, "test_areaExlosionGAUSS failed");
QVERIFY2(impacts[0] == QPointF(206, 29), "test_areaExlosionGAUSS failed");
QVERIFY2(impacts[1] == QPointF(252, 42), "test_areaExlosionGAUSS failed");
QVERIFY2(impacts[2] == QPointF(110, 21), "test_areaExlosionGAUSS failed");
QVERIFY2(impacts[3] == QPointF(209, 32), "test_areaExlosionGAUSS failed");
}
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTest_TNE-ALOG", "log4cxxUnitTest.props");
UnitTestVerteilung tc;
return QTest::qExec(&tc, argc, argv);
}
@@ -0,0 +1,45 @@
/**
* @RDE_HEADER@
* Unittest
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QCoreApplication>
#include <QtTest/QTest>
using ULONG = quint32;
using LONG = qint32;
using SHORT = qint32;
/**
* @brief This class is an UnitTest with values taken from the TNE.
* That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
*/
class UnitTestVerteilung : public QObject
{
Q_OBJECT
private Q_SLOTS:
// void init();
void test_long();
void test_xorshift32();
void test_xorshift32_nv();
void test_xorshift32_nv_saveToFile();
void test_areaExlosionGAUSS();
// void cleanup();
private:
};
@@ -0,0 +1,150 @@
/**
* Unittest for testing the statemachine for aes objects
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_statemachine_test.h"
#include <QDebug>
#include <QSignalSpy>
#include <QTest>
#include <nglapplication/nglcoreapplication.h>
using namespace AreaEffectSimulation;
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("StateMachineTest", "log4cxx_AreaEffectSimulation.props");
StateMachineTest *test = new StateMachineTest;
int retCode = QTest::qExec(test, argc, argv);
delete test;
return retCode;
}
namespace QTest
{
template<>
char *toString(const AesEffectState &s) { std::ostringstream str; str << s; return strdup(str.str().c_str()); }
}
DummyAesObject::DummyAesObject(QObject *parent)
: ObjectBaseTmpl(nullptr, QStringLiteral("DummyAesObject"), parent)
{}
QPolygonF DummyAesObject::generateForm()
{
return QPolygonF();
}
StateMachineTest::StateMachineTest()
: m_dummyAesObject(new DummyAesObject(this))
{}
StateMachineTest::~StateMachineTest()
{
delete m_dummyAesObject;
}
void StateMachineTest::staticTests()
{
const time_t tNow = time(nullptr);
// newly created, nothing set -> checkEnterNewState() should switch to created
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsCreated);
// now we set a activation time -> Prepared
m_dummyAesObject->setActivationTime(tNow + 5);
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsPrepared);
// now back to created
m_dummyAesObject->setActivationTime(0);
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsCreated);
// directly to activated
m_dummyAesObject->setActivationTime(tNow);
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsActivated);
// pause not possible
m_dummyAesObject->setSupportsPausing(false);
QCOMPARE(m_dummyAesObject->trySwitchToPaused(), false);
// now possible
m_dummyAesObject->setSupportsPausing(true);
QCOMPARE(m_dummyAesObject->trySwitchToPaused(), true);
// and back to activated
QCOMPARE(m_dummyAesObject->isActivationTimeReached(), true); // sanity
QCOMPARE(m_dummyAesObject->trySwitchToActivated(), true);
// set end time
m_dummyAesObject->setActivationTime(tNow);
m_dummyAesObject->setDurationSec(1);
// wait 1.5sec (100 * QTest::qWait(15))
for (int i = 0; i < 100; ++i)
{
QTest::qWait(15);
}
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsExpired);
// try to delete it
QCOMPARE(m_dummyAesObject->trySwitchToRemoved(), false);
m_dummyAesObject->setActivationTime(0);
m_dummyAesObject->setDurationSec(0);
m_dummyAesObject->setAesEffectState(AesEffectState::EsUnknown);
QCOMPARE(m_dummyAesObject->trySwitchToRemoved(), false);
m_dummyAesObject->setAesEffectState(AesEffectState::EsCreated);
QCOMPARE(m_dummyAesObject->trySwitchToRemoved(), true);
m_dummyAesObject->setAesEffectState(AesEffectState::EsPaused);
QCOMPARE(m_dummyAesObject->trySwitchToRemoved(), true);
// an active/paused object must not set to deleted but to expired
QCOMPARE(m_dummyAesObject->effectState().underlying(), AesEffectState::EsExpired);
QCOMPARE(m_dummyAesObject->trySwitchToCreated(), false);
}
void StateMachineTest::dynamicTests()
{
const time_t tNow = time(nullptr);
QSignalSpy spy(m_dummyAesObject, &DummyAesObject::effectStateChanged);
m_dummyAesObject->setAesEffectState(AesEffectState::EsUnknown);
// set activation time
m_dummyAesObject->setActivationTime(tNow + 1);
QCOMPARE(m_dummyAesObject->isActivationTimeSet(), true);
// set end time
m_dummyAesObject->setDurationSec(2);
QCOMPARE(m_dummyAesObject->isEndTimeSet(), true);
QCOMPARE(m_dummyAesObject->checkEnterNewState().underlying(), AesEffectState::EsPrepared);
QCOMPARE(spy.count(), 2); // Created, Prepared
spy.clear();
qDebug() << "Waiting for activation time";
for (int i = 0; i < 100; ++i)
{
QTest::qWait(15); // wait until the object is Activated
if (m_dummyAesObject->effectState() == AesEffectState::EsActivated)
break;
}
QCOMPARE(m_dummyAesObject->effectState().underlying(), AesEffectState::EsActivated);
qDebug() << "Waiting for end time";
for (int i = 0; i < 200; ++i)
{
QTest::qWait(15); // wait until the object is Expired
if (m_dummyAesObject->effectState() == AesEffectState::EsExpired)
break;
}
AreaEffectSimulation::AesEffectState state = m_dummyAesObject->effectState();
QCOMPARE(state.underlying(), AesEffectState::EsExpired);
QCOMPARE(spy.count(), 2); // Activated, Done
spy.clear();
}
@@ -0,0 +1,60 @@
/**
* Unittest for testing the statemachine for aes objects
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <AreaEffectSimulation/object_base.h>
namespace AreaEffectSimulation
{
class DummyAesObject final: public ObjectBaseTmpl<Database::AesNContamination>
{
Q_OBJECT
public:
explicit DummyAesObject(QObject *parent = nullptr);
void setAesEffectState(AesEffectState newState) { m_data.effectStateId = newState; }
void setSupportsPausing(bool bSupportsPausing) { m_bSupportsPausing = bSupportsPausing; }
void setActivationTime(time_t actTime)
{
if (actTime)
m_data.effectStart = QDateTime::fromMSecsSinceEpoch(actTime * 1000LL);
else
m_data.effectStart = QDateTime();
}
void setDurationSec(int durationSec)
{
m_data.effectDuration = durationSec * 1000;
}
qint64 partitionTimeMSec() const override
{
return partitionTime().toMSecsSinceEpoch();
}
QDateTime partitionTime() const override
{
return QDateTime::currentDateTimeUtc();
}
std::vector<Core::GeoPosition> form() const override { return {}; }
QPolygonF generateForm() override;
AesType aesType() const override { return AesType::AtUnknown; }
bool isCentral() const override { return m_data.isCentral; }
};
class StateMachineTest : public QObject
{
Q_OBJECT
public:
StateMachineTest();
~StateMachineTest();
private Q_SLOTS:
void staticTests();
void dynamicTests();
private:
DummyAesObject *m_dummyAesObject;
};
} // namespace AreaEffectSimulation
+62
View File
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* Some helper classes / functions for AES
* $Id$
* $HeadURL$
* $Author$
*/
#include "aes_helper.h"
#include <QHash>
#include <QSet>
namespace AreaEffectSimulation
{
//
// class HandledImpactsStorage::Private
//
class HandledImpactsStorage::Private
{
public:
Private() = default;
~Private() = default;
// data:
QHash<int, QSet<int>> m_aesItemId2Index; // Key = aesItemId ; processed/calculated index
};
//
// class HandledImpactsStorage
//
HandledImpactsStorage::HandledImpactsStorage()
: d(new Private)
{}
HandledImpactsStorage::~HandledImpactsStorage()
{
delete d;
}
void HandledImpactsStorage::setIndexProcessed(int aesItemId, int index)
{
d->m_aesItemId2Index[aesItemId].insert(index);
}
bool HandledImpactsStorage::isIndexProcessed(int aesItemId, int index)
{
return d->m_aesItemId2Index[aesItemId].contains(index);
}
void HandledImpactsStorage::removeAesItem(int aesItemId)
{
d->m_aesItemId2Index.remove(aesItemId);
}
//------------------------------------------------------------------------------
} // namespace AreaEffectSimulation
Loaded 100 of 16245 files, more files were not shown because too many files have changed in this diff. Show more