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

+196
View File
@@ -0,0 +1,196 @@
#
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE oss_service.xml)
#
# --- Library svcOssCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
oss_export.h
logger.cpp
logger.h
)
# copy oss_predefined_data.sql to share
SET(in_file_sql "oss_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 oss_enums.idl / oss.idl
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate oss_db_impl.cpp/.h, oss_db.h
CODEGEN_DATABASE(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from idl
RTIDDSGEN(SRCS_common oss_enums.idl)
RTIDDSGEN(SRCS_common oss.idl)
# Generate oss_dt[s,c]_types.h, oss_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# Generate oss_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS svcOssCommon SHARED ${SRCS_common})
NGL_TARGET_INCLUDE_DIRECTORIES(svcOssCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcOssCommon
PRIVATE
RTI::nddscpp2
PUBLIC
Qt5::Core
svcMsgCommon
ngldb
ddscore
NGLLogger
)
NGL_INSTALL_TARGET(svcOssCommon)
#
# --- Library svcOssClient --------------------------------------------------------------------------------
#
SET(SRCS_client
oss_export.h
)
# generate oss_[d|f]tc.h, oss_[d|f]tc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS svcOssClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcOssClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcOssClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcOssCommon
ddsftc
)
NGL_INSTALL_TARGET(svcOssClient)
#
# --- Library svcOssServer --------------------------------------------------------------------------------
#
SET(SRCS_server
oss_export.h
)
# generate oss_[d|f]ts.h, oss_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV svcOssServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcOssServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcOssServer
PRIVATE
RTI::nddscpp2
ddsconnector
ddsfts
PUBLIC
svcOssCommon
ngldb
)
NGL_INSTALL_TARGET(svcOssServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- RecordReplayPlugin --------------------------------------------------------------
#
CREATE_RECORD_REPLAY_PLUGIN(${xml_FILE} _srcs_not_needed)
#
# --- Executable svcOcSituationServer (sOSS) -------------------------------------------------
#
SET(SRCS_exe
main.cpp
main_handler.cpp
main_handler.h
central_aes_data_builder.cpp
central_aes_data_builder.h
central_aes_data_queue.cpp
central_aes_data_queue.h
dds_handler.cpp
dds_handler.h
handler_oc_aes_data.cpp
handler_oc_aes_data.h
handler_oc_central_aes_data.cpp
handler_oc_central_aes_data.h
handler_oc_data.cpp
handler_oc_data.h
handler_oc_situation_data.cpp
handler_oc_situation_data.h
position_converter.cpp
position_converter.h
settings.cpp
settings.h
oc_situation.h
${xml_FILE}
)
# Generate oss.sql, oss.xml
CODEGEN_SQL(SRCS_exe ${xml_FILE})
NGL_ADD_SVC_ICON(SRCS_exe)
NGL_ADD_EXECUTABLE(AAR SV svcOcSituationServer ${SRCS_exe})
NGL_TARGET_LINK_LIBRARIES(svcOcSituationServer
Qt5::Core
svcOssCommon
svcOssServer
svcCoreClient
svcAesClient
svcAsmClient
svcEvlClient
svcExmClient
svcGimClient
svcMsgClient
svcPpmClient
svcTabClient
ddsconnector
nglsettings
ProjectConfig
LtsCommandLineParser
LtsCoordTrans
LtsUtilsCoreHeaderOnly
NGLLogger
nglcoreapplication
ngltools
)
NGL_INSTALL_TARGET(svcOcSituationServer)
NGL_INSTALL(AAR SV FILES log4cxx_oss.props DESTINATION bin)
NGL_ADD_TARGET_LINK(svcOcSituationServer sOSS)
# Uncomment if necessary
#ADD_SUBDIRECTORY(UnitTests)
#ADD_SUBDIRECTORY(ModuleTests)
+500
View File
@@ -0,0 +1,500 @@
/**
* Manages central aes data to be sent
* $Id: handler_oc_situation_data.h 57655 2022-07-20 11:38:28Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.h $
* $Author: karwat $
* Created by Hannes Fritsche, SLTE, 16-08-2022
*/
#include "central_aes_data_builder.h"
#include "LtsUtils/LtsUtilsCore/qbytearray_byteaccess.h"
#include "QLineF"
namespace OcSituationServer
{
namespace DataBuilder
{
QByteArray getByteStreamAesItem(const AreaEffectSimulation::EffectCluster &effectCluster, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtExplosionCluster); // we send EffectCluster as ExplosionCluster
addAesItemId(byteStream, effectCluster.id());
addName(byteStream, effectCluster.name());
addEffectStart(byteStream, effectCluster.effectStart().is_set() ? effectCluster.effectStart().get() : -1);
addEffectDuration(byteStream, effectCluster.effectDuration());
addArea(byteStream, effectCluster.area());
addMhpCount(byteStream, static_cast<int32_t>(effectCluster.effectors().size()));
addMhpRotation(byteStream, effectCluster.effectors().size() ? effectCluster.effectors()[0].mhpRotation() : 1);
addImpactCount(byteStream, static_cast<int32_t>(effectCluster.effectors().size()));
addChargeTypeId(byteStream, effectCluster.chargeStruct().chargeTypeId());
addPartyId(byteStream, effectCluster.partyId().is_set() ? effectCluster.partyId().get() : -1);
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::NContamination &nContamination, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtNContamination);
addAesItemId(byteStream, nContamination.id());
addName(byteStream, nContamination.name());
addEffectStart(byteStream, nContamination.effectStart().is_set() ? nContamination.effectStart().get() : -1);
addEffectDuration(byteStream, nContamination.effectDuration());
addChargeTypeId(byteStream, nContamination.chargeStruct().chargeTypeId());
addImpactPosition(byteStream, nContamination.impactPosition());
addBorderLengthInner(byteStream, nContamination.borderLengthInner());
addBorderLengthOuter(byteStream, nContamination.borderLengthOuter());
addImpactRadius(byteStream, nContamination.impactRadius());
addSpanAngle(byteStream, nContamination.spanAngle());
addRotationAngle(byteStream, nContamination.rotationAngle());
addDetonationValue(byteStream, nContamination.detonationValue());
addPartyId(byteStream, nContamination.partyId().is_set() ? nContamination.partyId().get() : -1);
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::BCContamination &bCContamination, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtBCContamination);
addAesItemId(byteStream, bCContamination.id());
addName(byteStream, bCContamination.name());
addEffectStart(byteStream, bCContamination.effectStart().is_set() ? bCContamination.effectStart().get() : -1);
addEffectDuration(byteStream, bCContamination.effectDuration());
addChargeTypeId(byteStream, bCContamination.chargeStruct().chargeTypeId());
addImpactPosition(byteStream, bCContamination.impactPosition());
addImpactRadius(byteStream, bCContamination.impactRadius());
addPartyId(byteStream, bCContamination.partyId().is_set() ? bCContamination.partyId().get() : -1);
addPropagationDirection(byteStream, bCContamination.propagationDirection());
addWindSpeed(byteStream, bCContamination.windSpeed());
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionArea &explosionArea, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtExplosionArea);
addAesItemId(byteStream, explosionArea.id());
addName(byteStream, explosionArea.name());
addEffectStart(byteStream, explosionArea.effectStart().is_set() ? explosionArea.effectStart().get() : -1);
addEffectDuration(byteStream, explosionArea.effectDuration());
addChargeTypeId(byteStream, explosionArea.chargeStruct().chargeTypeId());
addArea(byteStream, explosionArea.area());
addPartyId(byteStream, explosionArea.partyId().is_set() ? explosionArea.partyId().get() : -1);
addCannonCount(byteStream, explosionArea.cannonCount());
addCannonRounds(byteStream, explosionArea.cannonRounds());
addFireRequestTime(byteStream, explosionArea.fireRequestTime());
addAltitude(byteStream, explosionArea.altitude());
addPurpose(byteStream, explosionArea.purpose());
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionSingle &explosionSingle, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtExplosionSingle);
addAesItemId(byteStream, explosionSingle.id());
addName(byteStream, explosionSingle.name());
addEffectStart(byteStream, explosionSingle.effectStart().is_set() ? explosionSingle.effectStart().get() : -1);
addChargeTypeId(byteStream, explosionSingle.chargeStruct().chargeTypeId());
addImpactPosition(byteStream, explosionSingle.impactPosition());
addPartyId(byteStream, explosionSingle.partyId().is_set() ? explosionSingle.partyId().get() : -1);
addFireRequestTime(byteStream, explosionSingle.fireRequestTime());
addAltitude(byteStream, explosionSingle.altitude());
addPurpose(byteStream, explosionSingle.purpose());
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::MineArea &mineArea, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtMineArea);
addAesItemId(byteStream, mineArea.id());
addName(byteStream, mineArea.name());
addEffectStart(byteStream, mineArea.effectStart().is_set() ? mineArea.effectStart().get() : -1);
addEffectDuration(byteStream, mineArea.effectDuration());
addDensity(byteStream, mineArea.density());
addWidth(byteStream, mineArea.width());
addChargeTypeId(byteStream, mineArea.chargeStruct().chargeTypeId());
addPartyId(byteStream, mineArea.partyId().is_set() ? mineArea.partyId().get() : -1);
addAesMaLayingProcedureType(byteStream, mineArea.layingProcedureType());
addIsLayedHidden(byteStream, mineArea.isLaidHidden());
addIsCenterLine(byteStream, mineArea.isCenterLine());
addSegments(byteStream, mineArea.segments());
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::FunctionalArea &functionalArea, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtFunctionalArea);
addAesItemId(byteStream, functionalArea.id());
addName(byteStream, functionalArea.name());
addEffectStart(byteStream, functionalArea.effectStart().is_set() ? functionalArea.effectStart().get() : -1);
addEffectDuration(byteStream, functionalArea.effectDuration());
addChargeTypeId(byteStream, functionalArea.chargeStruct().chargeTypeId());
addArea(byteStream, functionalArea.area());
addPartyId(byteStream, functionalArea.partyId().is_set() ? functionalArea.partyId().get() : -1);
addPointSymbol(byteStream, functionalArea.pointSymbol().is_set() ? functionalArea.pointSymbol().get() : std::string());
addFunctionalAreaTypeId(byteStream, functionalArea.functionalAreaTypeId());
addDeconMode(byteStream, functionalArea.deconMode());
addDeconTime(byteStream, functionalArea.deconTime());
addMaintMode(byteStream, functionalArea.maintMode());
addMaintTime(byteStream, functionalArea.maintTime());
addMedicMode(byteStream, functionalArea.medicMode());
addMedicTime(byteStream, functionalArea.medicTime());
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::BoobyTrap &boobyTrap, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtBoobyTrap);
addAesItemId(byteStream, boobyTrap.id());
addName(byteStream, boobyTrap.name());
addEffectDuration(byteStream, boobyTrap.effectDuration());
addChargeTypeId(byteStream, boobyTrap.chargeStruct().chargeTypeId());
addImpactPosition(byteStream, boobyTrap.impactPosition());
addImpactRadius(byteStream, boobyTrap.impactRadius());
addTriggerRadius(byteStream, boobyTrap.triggerRadius());
addRotationAngle(byteStream, boobyTrap.rotationAngle().is_set() ? boobyTrap.rotationAngle().get() : -1);
addCentralAngle(byteStream, boobyTrap.centralAngle().is_set() ? boobyTrap.centralAngle().get() : -1);
addEffectStart(byteStream, boobyTrap.effectStart().is_set() ? boobyTrap.effectStart().get() : -1);
addPartyId(byteStream, boobyTrap.partyId().is_set() ? boobyTrap.partyId().get() : -1);
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::OffRouteMine &offRouteMine, bool &isPrioritized)
{
QByteArray byteStream;
isPrioritized = false;
addAesType(byteStream, AreaEffectSimulation::AesType::AtOffRouteMine);
addAesItemId(byteStream, offRouteMine.id());
addName(byteStream, offRouteMine.name());
addEffectDuration(byteStream, offRouteMine.effectDuration());
addChargeTypeId(byteStream, offRouteMine.chargeStruct().chargeTypeId());
addArea(byteStream, offRouteMine.area());
addImpactRadius(byteStream, offRouteMine.impactRadius().is_set() ? offRouteMine.impactRadius().get() : -1);
addEffectStart(byteStream, offRouteMine.effectStart().is_set() ? offRouteMine.effectStart().get() : -1);
addPartyId(byteStream, offRouteMine.partyId().is_set() ? offRouteMine.partyId().get() : -1);
return byteStream;
}
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionCluster &explosionCluster, bool &isPrioritized)
{
QByteArray byteStream;
addAesType(byteStream, AreaEffectSimulation::AesType::AtExplosionCluster);
addAesItemId(byteStream, explosionCluster.id());
addName(byteStream, explosionCluster.name());
addEffectStart(byteStream, explosionCluster.effectStart().is_set() ? explosionCluster.effectStart().get() : -1);
addEffectDuration(byteStream, explosionCluster.effectDuration());
addArea(byteStream, explosionCluster.area());
addMhpCount(byteStream, static_cast<int32_t>(explosionCluster.mhPoints().size()));
addMhpRotation(byteStream, explosionCluster.mhpRotation());
addImpactCount(byteStream, explosionCluster.impactCount());
addChargeTypeId(byteStream, explosionCluster.chargeStruct().chargeTypeId());
addPartyId(byteStream, explosionCluster.partyId().is_set() ? explosionCluster.partyId().get() : -1);
isPrioritized = explosionCluster.impactCount() == 1; // we prioritize every single fire
return byteStream;
}
void addAesItemId(QByteArray &buf, int32_t aesItemId)
{
LtsUtils::set4Byte(aesItemId, buf);
}
void addAesMaLayingProcedureType(QByteArray &buf, const AreaEffectSimulation::AesMaLayingProcedureType &aesMaLayingProcedureType)
{
addDdsEnumVal(buf, aesMaLayingProcedureType);
}
void addAesType(QByteArray &buf, const AreaEffectSimulation::AesType &aesType)
{
using namespace AreaEffectSimulation;
// we treat EffectClusters as ExplosionClusters
addDdsEnumVal(buf, aesType == AesType::inner_enum::AtEffectCluster ? AesType::inner_enum::AtExplosionCluster : aesType);
}
void addAltitude(QByteArray &buf, int32_t altitude)
{
LtsUtils::set4Byte(altitude, buf);
}
void addArea(QByteArray &buf, const AreaEffectSimulation::Area &area)
{
addPosition2D(buf, area.cornerBottomLeft());
LtsUtils::set8Byte(area.width(), buf);
LtsUtils::set8Byte(area.height(), buf);
LtsUtils::set8Byte(area.rotation(), buf);
}
void addBorderLengthInner(QByteArray &buf, double borderLengthInner)
{
LtsUtils::set8Byte(borderLengthInner, buf);
}
void addBorderLengthOuter(QByteArray &buf, double borderLengthOuter)
{
LtsUtils::set8Byte(borderLengthOuter, buf);
}
void addCannonCount(QByteArray &buf, int32_t cannonCount)
{
LtsUtils::set4Byte(cannonCount, buf);
}
void addCannonRounds(QByteArray &buf, int32_t cannonRounds)
{
LtsUtils::set4Byte(cannonRounds, buf);
}
void addCentralAesChangeType(QByteArray &buf, OcSituation::CentralAesChangeType changeType)
{
LtsUtils::set1Byte(changeType, buf);
}
void addCentralAngle(QByteArray &buf, double centralAngle)
{
LtsUtils::set8Byte(centralAngle, buf);
}
void addChargeTypeId(QByteArray &buf, int32_t chargeType)
{
LtsUtils::set4Byte(chargeType, buf);
}
template<typename T>
void addDdsEnumVal(QByteArray &buf, const T &ddsEnumVal)
{
LtsUtils::set4Byte(static_cast<int32_t>(ddsEnumVal.underlying()), buf);
}
void addDeconMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &deconMode)
{
addDdsEnumVal(buf, deconMode);
}
void addDeconTime(QByteArray &buf, int32_t deconTime)
{
LtsUtils::set4Byte(deconTime, buf);
}
void addDensity(QByteArray &buf, double density)
{
LtsUtils::set8Byte(density, buf);
}
void addDetonationValue(QByteArray &buf, double detonationValue)
{
LtsUtils::set8Byte(detonationValue, buf);
}
void addEffectDuration(QByteArray &buf, int32_t effectDuration)
{
LtsUtils::set4Byte(effectDuration, buf);
}
void addEffectStart(QByteArray &buf, rti::core::int64 effectStart)
{
LtsUtils::set8Byte(effectStart, buf);
}
void addExplosionGeometry(QByteArray &buf, const AreaEffectSimulation::AesExplosionGeometry &explosionGeometry)
{
addDdsEnumVal(buf, explosionGeometry);
}
void addFireRequestTime(QByteArray &buf, rti::core::int64 fireRequestTime)
{
LtsUtils::set8Byte(fireRequestTime, buf);
}
void addIgniterType(QByteArray &buf, int32_t igniterTypeId)
{
LtsUtils::set4Byte(igniterTypeId, buf);
}
void addFunctionalAreaTypeId(QByteArray &buf, int32_t functionalAreaTypeId)
{
LtsUtils::set4Byte(functionalAreaTypeId, buf);
}
void addImpactCount(QByteArray &buf, int32_t impactCount)
{
LtsUtils::set4Byte(impactCount, buf);
}
void addImpactPosition(QByteArray &buf, const Core::GeoPosition2D &position)
{
addPosition2D(buf, position);
}
void addImpactRadius(QByteArray &buf, double impactRadius)
{
LtsUtils::set8Byte(impactRadius, buf);
}
void addIsCenterLine(QByteArray &buf, bool isCenterLine)
{
LtsUtils::set1Byte(isCenterLine, buf);
}
void addIsLayedHidden(QByteArray &buf, bool isLayedHidden)
{
LtsUtils::set1Byte(isLayedHidden, buf);
}
void addMaintMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &maintMode)
{
addDdsEnumVal(buf, maintMode);
}
void addMaintTime(QByteArray &buf, int32_t maintTime)
{
LtsUtils::set4Byte(maintTime, buf);
}
void addMedicMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &medicMode)
{
addDdsEnumVal(buf, medicMode);
}
void addMedicTime(QByteArray &buf, int32_t medicTime)
{
LtsUtils::set4Byte(medicTime, buf);
}
void addMhpCount(QByteArray &buf, int32_t mhpCount)
{
LtsUtils::set4Byte(mhpCount, buf);
}
void addMhpRotation(QByteArray &buf, double mhpRotation)
{
LtsUtils::set8Byte(mhpRotation, buf);
}
void addName(QByteArray &buf, const std::string &name)
{
addString(buf, name, 8);
}
void addPartyId(QByteArray &buf, int32_t partyId)
{
LtsUtils::set4Byte(partyId, buf);
}
void addPointSymbol(QByteArray &buf, const std::string &pointSymbol)
{
addString(buf, pointSymbol, pointSymbol.length());
}
void addPosition2D(QByteArray &buf, const Core::GeoPosition2D &position)
{
LtsUtils::set8Byte(position.lon(), buf);
LtsUtils::set8Byte(position.lat(), buf);
// no timestamp, as it is set in the client anyway
}
void addPurpose(QByteArray &buf, const std::string &purpose)
{
addString(buf, purpose);
}
void addRotationAngle(QByteArray &buf, double rotationAngle)
{
LtsUtils::set8Byte(rotationAngle, buf);
}
void addSegments(QByteArray &buf, const std::vector<AreaEffectSimulation::MineSegment> &segments)
{
unsigned char len = segments.size() + 1;
LtsUtils::set1Byte(len, buf);
if (len == 1)
return;
AreaEffectSimulation::MineSegment lastSeg;
for (const AreaEffectSimulation::MineSegment &currentMineSegment : qAsConst(segments))
{
addPosition2D(buf, currentMineSegment.positionStart());
lastSeg = currentMineSegment;
}
addPosition2D(buf, lastSeg.positionEnd());
}
void addSpanAngle(QByteArray &buf, double spanAngle)
{
LtsUtils::set8Byte(spanAngle, buf);
}
void addString(QByteArray &buf, const std::string &str, size_t maxLen)
{
unsigned char len = std::min(str.size(), size_t(maxLen));
LtsUtils::set1Byte(len, buf);
buf.append(str.c_str(), len);
}
void addTriggerRadius(QByteArray &buf, double triggerRadius)
{
LtsUtils::set8Byte(triggerRadius, buf);
}
void addWidth(QByteArray &buf, double width)
{
LtsUtils::set8Byte(width, buf);
}
void addPropagationDirection(QByteArray &buf, double propagationDirection)
{
LtsUtils::set8Byte(propagationDirection, buf);
}
void addWindSpeed(QByteArray &buf, double windSpeed)
{
LtsUtils::set8Byte(windSpeed, buf);
}
} // namespace DataBuilder
} // namespace OcSituationServer
+81
View File
@@ -0,0 +1,81 @@
/**
* Manages central aes data to be sent
* $Id: handler_oc_situation_data.h 57655 2022-07-20 11:38:28Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.h $
* $Author: karwat $
* Created by Hannes Fritsche, SLTE, 16-08-2022
*/
#pragma once
#include "AreaEffectSimulation/aes_dtc.h"
#include "oc_situation.h"
namespace OcSituationServer
{
namespace DataBuilder
{
QByteArray getByteStreamAesItem(const AreaEffectSimulation::EffectCluster &effectCluster, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::NContamination &nContamination, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::BCContamination &bCContamination, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionArea &explosionArea, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionSingle &explosionSingle, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::MineArea &mineArea, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::FunctionalArea &functionalArea, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::BoobyTrap &boobyTrap, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::OffRouteMine &offRouteMine, bool &isPrioritized);
QByteArray getByteStreamAesItem(const AreaEffectSimulation::ExplosionCluster &explosionCluster, bool &isPrioritized);
// Building Byte Stream
void addAesItemId(QByteArray &buf, int32_t aesItemId);
void addAesMaLayingProcedureType(QByteArray &buf, const AreaEffectSimulation::AesMaLayingProcedureType &aesMaLayingProcedureType);
void addAesType(QByteArray &buf, const AreaEffectSimulation::AesType &aesType);
void addAltitude(QByteArray &buf, int32_t altitude);
void addArea(QByteArray &buf, const AreaEffectSimulation::Area &area);
void addBorderLengthInner(QByteArray &buf, double borderLengthInner);
void addBorderLengthOuter(QByteArray &buf, double borderLengthOuter);
void addCannonCount(QByteArray &buf, int32_t cannonCount);
void addCannonRounds(QByteArray &buf, int32_t cannonRounds);
void addCentralAesChangeType(QByteArray &buf, OcSituation::CentralAesChangeType changeType);
void addCentralAngle(QByteArray &buf, double centralAngle);
void addChargeTypeId(QByteArray &buf, int32_t chargeType);
template <typename T>
void addDdsEnumVal(QByteArray &buf, const T &ddsEnumVal);
void addDeconMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &deconMode);
void addDeconTime(QByteArray &buf, int32_t deconTime);
void addDensity(QByteArray &buf, double density);
void addDetonationValue(QByteArray &buf, double detonationValue);
void addEffectDuration(QByteArray &buf, int32_t effectDuration);
void addEffectStart(QByteArray &buf, rti::core::int64 effectStart);
void addExplosionGeometry(QByteArray &buf, const AreaEffectSimulation::AesExplosionGeometry &explosionGeometry);
void addFireRequestTime(QByteArray &buf, rti::core::int64 fireRequestTime);
void addIgniterType(QByteArray &buf, int32_t igniterTypeId);
void addFunctionalAreaTypeId(QByteArray &buf, int32_t functionalAreaTypeId);
void addImpactCount(QByteArray &buf, int32_t impactCount);
void addImpactPosition(QByteArray &buf, const Core::GeoPosition2D &position);
void addImpactRadius(QByteArray &buf, double impactRadius);
void addIsCenterLine(QByteArray &buf, bool isCenterLine);
void addIsLayedHidden(QByteArray &buf, bool isLayedHidden);
void addMaintMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &maintMode);
void addMaintTime(QByteArray &buf, int32_t maintTime);
void addMedicMode(QByteArray &buf, const AreaEffectSimulation::AesOperatingMode &medicMode);
void addMedicTime(QByteArray &buf, int32_t medicTime);
void addMhpCount(QByteArray &buf, int32_t mhpCount);
void addMhpRotation(QByteArray &buf, double mhpRotation);
void addName(QByteArray &buf, const std::string &name);
void addPartyId(QByteArray &buf, int32_t partyId);
void addPointSymbol(QByteArray &buf, const std::string &pointSymbol);
void addPosition2D(QByteArray &buf, const Core::GeoPosition2D &position);
void addPropagationDirection(QByteArray &buf, double propagationDirection);
void addPurpose(QByteArray &buf, const std::string &purpose);
void addRotationAngle(QByteArray &buf, double rotationAngle);
void addSegments(QByteArray &buf, const std::vector<AreaEffectSimulation::MineSegment> &segments);
void addSpanAngle(QByteArray &buf, double spanAngle);
void addString(QByteArray &buf, const std::string &str, size_t maxLen = 15);
void addTriggerRadius(QByteArray &buf, double triggerRadius);
void addWidth(QByteArray &buf, double width);
void addWindSpeed(QByteArray &buf, double windSpeed);
} // namespace DataBuilder
} // namespace OcSituationServer
+185
View File
@@ -0,0 +1,185 @@
/**
* Manages central aes data to be sent
* $Id: handler_oc_situation_data.h 57655 2022-07-20 11:38:28Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.h $
* $Author: karwat $
* Created by Hannes Fritsche, SLTE, 16-08-2022
*/
#include "central_aes_data_queue.h"
namespace OcSituationServer
{
CentralAesDataQueue::CentralAesDataQueue(QObject *parent)
: QObject(parent)
, m_logger(getLogger("CentralAesDataQueue"))
{
}
void CentralAesDataQueue::addRemoveMessage(int32_t aesItemId, AreaEffectSimulation::AesType aesType, bool isPrioritized)
{
QMutexLocker locker(&m_accessMutex);
QByteArray data;
clearSpecificMessage(aesItemId, OcSituation::Remove);
using namespace DataBuilder;
addCentralAesChangeType(data, OcSituation::Remove);
addAesItemId(data, aesItemId);
addAesType(data, aesType);
if (isPrioritized)
m_internalPrioritizedQueue.prepend({aesItemId, OcSituation::Remove, data});
else
m_internalQueue.prepend({aesItemId, OcSituation::Remove, data});
DEBUG_LOG(m_logger) << "New remove-message added: Id = " << aesItemId
<< ", aesType = " << aesType << ". "
<< m_internalQueue.size() << " normal, "
<< m_internalPrioritizedQueue.size() << " prioritized and "
<< m_internalQueue.size() + m_internalPrioritizedQueue.size() << " remaining messages in total.";
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::EffectCluster &effectCluster)
{
// we treat EffectClusters as ExplosionClusters for the ABT
addRemoveMessage(effectCluster.id(), AreaEffectSimulation::AesType::inner_enum::AtExplosionCluster);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::NContamination &nContamination)
{
addRemoveMessage(nContamination.id(), AreaEffectSimulation::AesType::inner_enum::AtNContamination);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::BCContamination &bCContamination)
{
addRemoveMessage(bCContamination.id(), AreaEffectSimulation::AesType::inner_enum::AtBCContamination);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::ExplosionArea &explosionArea)
{
addRemoveMessage(explosionArea.id(), AreaEffectSimulation::AesType::inner_enum::AtExplosionArea);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::ExplosionSingle &explosionSingle)
{
addRemoveMessage(explosionSingle.id(), AreaEffectSimulation::AesType::inner_enum::AtExplosionSingle);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::MineArea &mineArea)
{
addRemoveMessage(mineArea.id(), AreaEffectSimulation::AesType::inner_enum::AtMineArea);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::FunctionalArea &functionalArea)
{
addRemoveMessage(functionalArea.id(), AreaEffectSimulation::AesType::inner_enum::AtFunctionalArea);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::BoobyTrap &boobyTrap)
{
addRemoveMessage(boobyTrap.id(), AreaEffectSimulation::AesType::inner_enum::AtBoobyTrap);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::OffRouteMine &offRouteMine)
{
addRemoveMessage(offRouteMine.id(), AreaEffectSimulation::AesType::inner_enum::AtOffRouteMine);
}
void CentralAesDataQueue::addRemoveMessage(const AreaEffectSimulation::ExplosionCluster &explosionCluster)
{
addRemoveMessage(explosionCluster.id(), AreaEffectSimulation::AesType::inner_enum::AtExplosionCluster, explosionCluster.impactCount() == 1); // we prioritize every single fire
}
QByteArray CentralAesDataQueue::getSendingData(qint32 size)
{
QMutexLocker locker(&m_accessMutex);
QByteArray result;
qint32 messageCounter = 0;
while (!m_internalPrioritizedQueue.isEmpty() && ((result.size() + m_internalPrioritizedQueue.head().data.size()) <= size))
{
result.append(m_internalPrioritizedQueue.dequeue().data);
++messageCounter;
}
// if the result is empty and we have messages left, there is no message small enough, to fit size
// so we send the next best message
if (result.isEmpty() && !m_internalPrioritizedQueue.isEmpty())
{
result.append(m_internalPrioritizedQueue.dequeue().data);
++messageCounter;
WARNING_LOG(m_logger) << "Could not build data with max size of " << size << " bytes. Delivering data with " << result.size() << " bytes.";
}
while (!m_internalQueue.isEmpty() && ((result.size() + m_internalQueue.head().data.size()) <= size))
{
result.append(m_internalQueue.dequeue().data);
++messageCounter;
}
// if the result is empty and we have messages left, there is no message small enough, to fit size
// so we send the next best message
if (result.isEmpty() && !m_internalQueue.isEmpty())
{
result.append(m_internalQueue.dequeue().data);
++messageCounter;
WARNING_LOG(m_logger) << "Could not build data with max size of " << size << " bytes. Delivering data with " << result.size() << " bytes.";
}
DEBUG_LOG(m_logger) << "Data with max size = " << size << "requestet. " << result.size() << " bytes delivered.";
DEBUG_LOG(m_logger) << messageCounter << " messages are sent. "
<< m_internalQueue.size() << " normal, "
<< m_internalPrioritizedQueue.size() << " prioritized and "
<< m_internalQueue.size() + m_internalPrioritizedQueue.size() << " remaining messages in total.";
return result;
}
bool CentralAesDataQueue::hasMessages() const
{
return !m_internalQueue.isEmpty() || !m_internalPrioritizedQueue.isEmpty();
}
void CentralAesDataQueue::clear()
{
m_internalQueue.clear();
m_internalPrioritizedQueue.clear();
}
void CentralAesDataQueue::clearSpecificMessage(int32_t aesItemId, OcSituation::CentralAesChangeType changeType)
{
for (qint32 counter = 0; counter < m_internalQueue.size(); ++counter)
{
if (m_internalQueue.at(counter).aesItemId == aesItemId && m_internalQueue.at(counter).changeType == changeType)
{
m_internalQueue.removeAt(counter);
DEBUG_LOG(m_logger) << "Removed already existing message. aesItemId = " << aesItemId << ", changeType = " << changeType;
return;
}
}
for (qint32 counter = 0; counter < m_internalPrioritizedQueue.size(); ++counter)
{
if (m_internalPrioritizedQueue.at(counter).aesItemId == aesItemId && m_internalPrioritizedQueue.at(counter).changeType == changeType)
{
m_internalPrioritizedQueue.removeAt(counter);
DEBUG_LOG(m_logger) << "Removed already existing message. aesItemId = " << aesItemId << ", changeType = " << changeType;
return;
}
}
}
} // namespace OcSituationServer
+95
View File
@@ -0,0 +1,95 @@
/**
* Manages central aes data to be sent
* $Id: handler_oc_situation_data.h 57655 2022-07-20 11:38:28Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.h $
* $Author: karwat $
* Created by Hannes Fritsche, SLTE, 16-08-2022
*/
#pragma once
#include <QQueue>
#include "oc_situation.h"
#include <QObject>
#include "logger.h"
#include <QMutex>
#include <QMutexLocker>
#include "central_aes_data_builder.h"
namespace OcSituationServer
{
class CentralAesDataQueue : public QObject
{
Q_OBJECT
public:
explicit CentralAesDataQueue(QObject *parent = nullptr);
template <typename T>
void addAesItem(const T &aesItem)
{
QMutexLocker locker(&m_accessMutex);
QByteArray data;
bool isPrioritized = false; // we prioritize every single fire
clearSpecificMessage(aesItem.id(), OcSituation::AddMod);
DataBuilder::addCentralAesChangeType(data, OcSituation::AddMod);
data.append(DataBuilder::getByteStreamAesItem(aesItem, isPrioritized));
if (isPrioritized)
m_internalPrioritizedQueue.enqueue({aesItem.id(), OcSituation::AddMod, data});
else
m_internalQueue.enqueue({aesItem.id(), OcSituation::AddMod, data});
DEBUG_LOG(m_logger) << "New AesItem added. " << m_internalQueue.size() << " normal, "
<< m_internalPrioritizedQueue.size() << " prioritized and "
<< m_internalQueue.size() + m_internalPrioritizedQueue.size() << " messages in total.";
}
void addRemoveMessage(int32_t aesItemId, AreaEffectSimulation::AesType aesType, bool isPrioritized = false);
void addRemoveMessage(const AreaEffectSimulation::EffectCluster &effectCluster);
void addRemoveMessage(const AreaEffectSimulation::NContamination &nContamination);
void addRemoveMessage(const AreaEffectSimulation::BCContamination &bCContamination);
void addRemoveMessage(const AreaEffectSimulation::ExplosionArea &explosionArea);
void addRemoveMessage(const AreaEffectSimulation::ExplosionSingle &explosionSingle);
void addRemoveMessage(const AreaEffectSimulation::MineArea &mineArea);
void addRemoveMessage(const AreaEffectSimulation::FunctionalArea &functionalArea);
void addRemoveMessage(const AreaEffectSimulation::BoobyTrap &boobyTrap);
void addRemoveMessage(const AreaEffectSimulation::OffRouteMine &offRouteMine);
void addRemoveMessage(const AreaEffectSimulation::ExplosionCluster &explosionCluster);
QByteArray getSendingData(qint32 size);
bool hasMessages() const;
void clear();
private:
void clearSpecificMessage(int32_t aesItemId, OcSituation::CentralAesChangeType changeType);
struct SendingMessage
{
int32_t aesItemId;
OcSituation::CentralAesChangeType changeType;
QByteArray data;
SendingMessage()
: aesItemId(0)
, changeType(OcSituation::AddMod)
, data(QByteArray())
{}
SendingMessage(int32_t aesItemId, OcSituation::CentralAesChangeType changeType, const QByteArray &data)
: aesItemId(aesItemId)
, changeType(changeType)
, data(data)
{}
};
// attributes
QMutex m_accessMutex;
QQueue<SendingMessage> m_internalQueue;
QQueue<SendingMessage> m_internalPrioritizedQueue;
LOGGER_T &m_logger;
};
} // namespace OcSituationServer
+54
View File
@@ -0,0 +1,54 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#include "dds_handler.h"
#include <DDS/Core/pacemaker.h>
namespace OcSituationServer
{
DdsHandler::DdsHandler(int domainId, int exerciseId)
: m_ddsConnector(std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Exercise, exerciseId))
, m_logger(getLogger("DdsHandler"))
{
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
}
bool DdsHandler::init(const QStringList &qosFileList)
{
if (!m_ddsConnector->init(qosFileList))
{
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
return false;
}
// default initialize Publisher and Subscriber
m_ddsConnector->initPublisherSubscriber();
m_dtcCore = Core::Interface::Dtc::create(m_ddsConnector);
bool bRet = m_dtcCore->enablePartitionTime();
connect(m_dtcCore.get(), &Core::Interface::Dtc::partitionTimeChanged,
this, [this](const Core::PartitionTime & t) { emit partitionTimeChanged(t.timeStamp());});
// Pacemaker
m_pacemaker = std::make_shared<Core::Pacemaker>(m_ddsConnector);
m_ddsDts = OcSituationServer::Interface::Dts::create(m_ddsConnector);
m_ddsFts = OcSituationServer::Interface::Fts::create(m_ddsConnector);
bRet &= m_ddsDts->enableAll();
INFO_LOG(m_logger) << "Dts initialised";
bRet &= m_ddsFts->enableAll();
INFO_LOG(m_logger) << "Fts initialised";
return bRet;
}
} // namespace OcSituationServer
+51
View File
@@ -0,0 +1,51 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/core_dtc.h>
#include <OcSituationServer/oss_dts.h>
#include <OcSituationServer/oss_fts.h>
#include "logger.h"
namespace Core
{
class Pacemaker;
}
namespace OcSituationServer
{
class DdsHandler final : public QObject
{
Q_OBJECT
public:
DdsHandler(int domainId, int exerciseId);
bool init(const QStringList &qosFileList = QStringList());
std::shared_ptr<Core::Pacemaker> pacemaker() const { return m_pacemaker; }
std::shared_ptr<DDSConnector::DdsConnector> dds() const { return m_ddsConnector; }
std::shared_ptr<OcSituationServer::Interface::Dts> dts() const { return m_ddsDts; }
std::shared_ptr<OcSituationServer::Interface::Fts> fts() const { return m_ddsFts; }
Q_SIGNALS:
void partitionTimeChanged(qint64 partitionTime);
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<Core::Pacemaker> m_pacemaker;
std::shared_ptr<Core::Interface::Dtc> m_dtcCore;
std::shared_ptr<OcSituationServer::Interface::Dts> m_ddsDts;
std::shared_ptr<OcSituationServer::Interface::Fts> m_ddsFts;
NGL::INGLLogger &m_logger;
};
} // namespace OcSituationServer
+453
View File
@@ -0,0 +1,453 @@
/**
* Handler for aes data to be send to the oc-laptop transparent
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 18-04-2018
*/
#include "handler_oc_aes_data.h"
#include <functional>
#include <QByteArray>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include "settings.h"
namespace OcSituationServer
{
HandlerOcAesData::HandlerOcAesData(MainHandler *mainHandler,
QObject *parent)
: QObject(parent)
, m_aesDtc(mainHandler->aesDtc())
, m_gimDtc(mainHandler->gimDtc())
, m_tabFtc(mainHandler->tabFtc())
, m_dbHandler(mainHandler->db())
, m_positionConverter(mainHandler->positionConverter())
, m_dangerAreaResetCnt(0)
, m_iedResetCnt(0)
, m_collectionTimer(QMap<OcSituation::AesType, QTimer*>())
, m_demandingEquipments(QMap<OcSituation::AesType, QSet<int>>())
, m_iedPositionsToBeSend(QMap<int, GuezIedManagement::IedPosition>())
, m_iedPositionCycleTimer(new QTimer(this))
, m_logger(getLogger("HandlerOcAesData"))
{}
HandlerOcAesData::~HandlerOcAesData()
{}
bool HandlerOcAesData::init()
{
// create collectionTimer
m_collectionTimer[OcSituation::AesType::DangerArea] = new QTimer(this);
m_collectionTimer[OcSituation::AesType::DangerArea]->setSingleShot(true);
m_collectionTimer[OcSituation::AesType::DangerArea]->setInterval(Settings::aesDemandCollectionTime());
connect(m_collectionTimer[OcSituation::AesType::DangerArea], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandDangerAreas);
m_collectionTimer[OcSituation::AesType::Ied] = new QTimer(this);
m_collectionTimer[OcSituation::AesType::Ied]->setSingleShot(true);
m_collectionTimer[OcSituation::AesType::Ied]->setInterval(Settings::aesDemandCollectionTime());
connect(m_collectionTimer[OcSituation::AesType::Ied], &QTimer::timeout, this, &HandlerOcAesData::onCollectionTimeOutDemandIeds);
// set reset counter
QVector<Database::OssResetCounter> dataVec;
// read all Ieds from database
if (!m_dbHandler->select(dataVec))
{
ERROR_LOG(m_logger) << "Error reading reset counter from database!";
return false;
}
for (const auto &data : qAsConst(dataVec))
{
switch (data.aesTypeId)
{
case OcSituation::AesType::DangerArea:
{
m_dangerAreaResetCnt = data.resetCounter;
break;
}
case OcSituation::AesType::Ied:
{
m_iedResetCnt = data.resetCounter;
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << data.aesTypeId << " read from db is not handled.";
break;
}
}
}
// AES DTC enable
if (!m_aesDtc->enableDangerArea())
{
ERROR_LOG(m_logger) << "Enabling AES Data-Topics failed.";
return false;
}
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::dangerAreaChanged, this, &HandlerOcAesData::onDangerAreaChanged);
// GIM DTC enable
if (!m_gimDtc->enableIed() ||
!m_gimDtc->enableIedPosition())
{
ERROR_LOG(m_logger) << "Enabling GIM Data-Topics failed.";
return false;
}
connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedChanged, this, &HandlerOcAesData::onIedChanged);
connect(m_gimDtc.get(), &GuezIedManagement::Interface::Dtc::iedPositionChanged, this, &HandlerOcAesData::onIedPositionChanged);
connect(m_iedPositionCycleTimer, &QTimer::timeout, this, &HandlerOcAesData::sendIedPositions);
m_iedPositionCycleTimer->setInterval(Settings::iedPositionCycleTime());
DEBUG_LOG(m_logger) << "Starting ied position timer with interval: " << m_iedPositionCycleTimer->interval();
m_iedPositionCycleTimer->start();
return true;
}
void HandlerOcAesData::demandAesItems(OcSituation::AesType aesType, int eqNumber)
{
switch (aesType)
{
case OcSituation::AesType::DangerArea:
case OcSituation::AesType::Ied:
{
DEBUG_LOG(m_logger) << "Got demand for aes type: " << aesType << " from equipment: " << eqNumber;
if (!m_collectionTimer[aesType]->isActive())
m_collectionTimer[aesType]->start();
m_demandingEquipments[aesType].insert(eqNumber);
break;
}
default:
{
WARNING_LOG(m_logger) << "Aes type: " << aesType << " is currently not handled.";
}
}
}
void HandlerOcAesData::onSendOcDataReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Oc data could not be send: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Oc data send successfully to TAB!";
}
void HandlerOcAesData::onDangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification mod)
{
Q_UNUSED(data);
Q_UNUSED(mod);
incrementResetCnt(OcSituation::AesType::DangerArea);
}
void HandlerOcAesData::onIedChanged(const GuezIedManagement::Ied &data, DataModification mod)
{
Q_UNUSED(data);
Q_UNUSED(mod);
incrementResetCnt(OcSituation::AesType::Ied);
}
void HandlerOcAesData::onIedPositionChanged(const GuezIedManagement::IedPosition &data, DataModification mod)
{
if (mod == DataModification::eRemoved)
return;
GuezIedManagement::Ied ied = m_gimDtc->ied(data.id());
if (ied.id() > 0)
{
if (ied.pairingState().underlying() != GuezIedManagement::IedPairingState::NotPaired)
m_iedPositionsToBeSend.insert(data.id(), data);
}
else
ERROR_LOG(m_logger) << "Got ied position for not existing ID: " << data.id();
}
void HandlerOcAesData::onCollectionTimeOutDemandDangerAreas()
{
std::function<void (int)> f = std::bind(&HandlerOcAesData::sendDangerAreasToTab, this, std::placeholders::_1);
collectionTimeout(OcSituation::AesType::DangerArea, f);
}
void HandlerOcAesData::onCollectionTimeOutDemandIeds()
{
std::function<void (int)> f = std::bind(&HandlerOcAesData::sendIedsToTab, this, std::placeholders::_1);
collectionTimeout(OcSituation::AesType::Ied, f);
}
void HandlerOcAesData::sendIedPositions()
{
if (m_iedPositionsToBeSend.size() == 0)
{
DEBUG_LOG(m_logger) << "No new ied positions to be send.";
return;
}
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(0); // ied positions are always send broadcast
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &pos : qAsConst(m_iedPositionsToBeSend))
{
LtsUtils::set2Byte(m_gimDtc->ied(pos.id()).equipmentNumber(), data);
const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position()));
LtsUtils::set3Byte(qRound(relCoord.x()), data);
LtsUtils::set3Byte(qRound(relCoord.y()), data);
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added ied position " << pos
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::IedPositions, data);
LtsUtils::set1Byte(cnt, data);
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
m_iedPositionsToBeSend.clear();
}
void HandlerOcAesData::collectionTimeout(OcSituation::AesType type, const std::function<void (int)> &sendFunction)
{
DEBUG_LOG(m_logger) << "There are " << m_demandingEquipments[type].size() << " requests for aes type: " << type
<< " sending Items as " << ((m_demandingEquipments[type].size() > 5) ? QStringLiteral("broadcast") : QStringLiteral("direct")) << " telegram(s)";
if (m_demandingEquipments[type].size() > 5)
sendFunction(0); // broadcast
else
for (int eqNumber : qAsConst(m_demandingEquipments[type]))
sendFunction(eqNumber);
//cleanup
m_demandingEquipments[type].clear();
}
void HandlerOcAesData::sendDangerAreasToTab(int eqNumber)
{
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(eqNumber);
const QMap<int, AreaEffectSimulation::DangerArea> dangerAreas = m_aesDtc->dangerAreas();
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &dangerArea : dangerAreas)
{
if (dangerArea.effectStateId() >= AreaEffectSimulation::AesEffectState::EsCreated) // do not transfer ended or deleted areas
{
LtsUtils::set2Byte(dangerArea.id(), data);
QByteArray name(QByteArray::fromStdString(dangerArea.name()));
name.resize(8);
data.append(name);
if (dangerArea.effectStart().is_set())
LtsUtils::set4Byte(dangerArea.effectStart().get() / 1000, data);
else
LtsUtils::set4Byte(0, data);
LtsUtils::set1Byte(dangerArea.segments().size(), data);
for (const auto &segment : dangerArea.segments())
{
const QPointF relCoord(m_positionConverter->relativeFromGeographic(segment));
LtsUtils::set3Byte(qRound(relCoord.y()), data);
LtsUtils::set3Byte(qRound(relCoord.x()), data);
}
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added danger area " << dangerArea
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::DangerArea, data);
LtsUtils::set1Byte(cnt, data);
}
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
}
void HandlerOcAesData::sendIedsToTab(int eqNumber)
{
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(eqNumber);
const QMap<int, GuezIedManagement::Ied> ieds = m_gimDtc->ieds();
QByteArray data, payload;
int cnt = 0;
// set "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::Ied, data);
LtsUtils::set1Byte(cnt, data);
for (const auto &ied : ieds)
{
LtsUtils::set2Byte(ied.equipmentNumber(), data);
LtsUtils::set1Byte(ied.type().underlying(), data);
uint8_t trigger = (uint8_t(ied.tautWireTrigger()) << 3) |
(uint8_t(ied.lightBarrierTrigger()) << 2) |
(uint8_t(ied.switchTrigger()) << 1) |
(uint8_t(ied.remoteTrigger()));
LtsUtils::set1Byte(trigger, data);
LtsUtils::set1Byte(ied.explosiveForce(), data);
LtsUtils::set1Byte(ied.partyId(), data);
LtsUtils::set1Byte(ied.tacticalState().underlying(), data);
// set an initial position, as only paired ieds are updated concurrently
const auto pos = m_gimDtc->iedPosition(ied.id());
const QPointF relCoord(m_positionConverter->relativeFromGeographic(pos.position()));
LtsUtils::set3Byte(qRound(relCoord.x()), data);
LtsUtils::set3Byte(qRound(relCoord.y()), data);
data[1] = ++cnt;
QByteArray ba = qCompress(data);
DEBUG_LOG(m_logger) << "Added ied " << ied
<< " data size: " << data.size()
<< " compressed size: " << ba.size();
if (ba.size() > Settings::maxNumOfBytePerAesTxCycle()) // one cycle is full -> send
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << ba.toHex(' ');
payload.append(ba);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
cnt = 0;
data.clear();
payload.clear();
// set new "header" data
LtsUtils::set1Byte(OcSituation::TelegramId::AesItems, payload);
LtsUtils::set1Byte(OcSituation::AesType::Ied, data);
LtsUtils::set1Byte(cnt, data);
}
}
if (cnt > 0) // is still data to be send?
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << data.toHex(' ');
DEBUG_LOG(m_logger) << "Compressed Data: " << qCompress(data).toHex(' ');
payload.append(qCompress(data));
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
m_tabFtc->sendOcDataRequest(req, this);
}
}
void HandlerOcAesData::incrementResetCnt(OcSituation::AesType aesType)
{
switch (aesType)
{
case OcSituation::AesType::DangerArea:
{
setResetCnt(aesType, m_dangerAreaResetCnt + 1);
break;
}
case OcSituation::AesType::Ied:
{
setResetCnt(aesType, m_iedResetCnt + 1);
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in incrementResetCnt.";
break;
}
}
}
void HandlerOcAesData::setResetCnt(OcSituation::AesType aesType, int resetCnt)
{
Database::OssResetCounter data;
data.aesTypeId = aesType;
data.resetCounter = resetCnt;
switch (aesType)
{
case OcSituation::AesType::DangerArea:
{
m_dangerAreaResetCnt = resetCnt;
break;
}
case OcSituation::AesType::Ied:
{
m_iedResetCnt = resetCnt;
break;
}
default:
{
WARNING_LOG(m_logger) << "Reset counter for aes type: " << aesType << " is not handled in setResetCnt.";
break;
}
}
// update db
if (!m_dbHandler->update(data))
ERROR_LOG(m_logger) << "Error updating reset counter in db.";
}
} // namespace OcSituationServer
+75
View File
@@ -0,0 +1,75 @@
/**
* Handler for aes data to be send to the oc-laptop transparent
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 18-04-2018
*/
#pragma once
#include <QObject>
#include <QTimer>
#include <QMap>
#include <QSet>
#include <functional>
#include "main_handler.h"
#include "position_converter.h"
#include "oc_situation.h"
#include "logger.h"
namespace OcSituationServer
{
class HandlerOcAesData : public QObject
, protected TrainingAreaBase::Interface::Ftc::SendOcDataReplyHandler
{
Q_OBJECT
public:
explicit HandlerOcAesData(MainHandler *mainHandler,
QObject *parent = nullptr);
~HandlerOcAesData();
bool init();
int dangerAreaResetCnt() { return m_dangerAreaResetCnt; }
int iedResetCnt() { return m_iedResetCnt; }
void demandAesItems(OcSituation::AesType aesType, int eqNumber);
void onSendOcDataReply(const Core::FunctionReplyObject &reply);
private slots:
void onDangerAreaChanged(const AreaEffectSimulation::DangerArea &data, DataModification mod);
void onIedChanged(const GuezIedManagement::Ied &data, DataModification mod);
void onIedPositionChanged(const GuezIedManagement::IedPosition &data, DataModification mod);
void onCollectionTimeOutDemandDangerAreas();
void onCollectionTimeOutDemandIeds();
void sendIedPositions();
private:
void collectionTimeout(OcSituation::AesType type, const std::function<void (int)> &sendFunction);
void sendDangerAreasToTab(int eqNumber);
void sendIedsToTab(int eqNumber);
void incrementResetCnt(OcSituation::AesType aesType);
void setResetCnt(OcSituation::AesType aesType, int resetCnt);
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<GuezIedManagement::Interface::Dtc> m_gimDtc;
std::shared_ptr<TrainingAreaBase::Interface::Ftc> m_tabFtc;
std::shared_ptr<DatabaseInterface> m_dbHandler;
std::shared_ptr<PositionConverter> m_positionConverter;
// add new reset counter also in oss_predefined_data.sql !!
int m_dangerAreaResetCnt;
int m_iedResetCnt;
QMap<OcSituation::AesType, QTimer *> m_collectionTimer;
QMap<OcSituation::AesType, QSet<int>> m_demandingEquipments;
QMap<int, GuezIedManagement::IedPosition> m_iedPositionsToBeSend;
QTimer *m_iedPositionCycleTimer;
LOGGER_T &m_logger;
};
} // namespace OcSituationServer
@@ -0,0 +1,830 @@
/**
* Handler for all oc situation data at the server per client
* $Id: handler_oc_situation_data.cpp 57938 2022-08-26 09:56:44Z diers $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.cpp $
* $Author: diers $
* Created by Philipp Dassow, NLST, 27-02-2018
*/
#include "handler_oc_central_aes_data.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/outdated_position.h>
#include <ParticipantManagement/ppm_common.h>
#include "settings.h"
#include <QLineF>
#include "central_aes_data_queue.h"
#include <QMultiMap>
namespace OcSituationServer
{
////
//// ThreadOcSituationData
////
ThreadOcCentralAesData::ThreadOcCentralAesData(MainHandler *mainHandler,
int equipmentId,
int centerEquipmentId,
int radius)
: m_baseCoordinate(mainHandler->positionConverter()->baseCoordinate())
, m_equipmentId(equipmentId)
, m_centerEquipmentId(centerEquipmentId)
, m_radius(radius)
, m_mainHandler(mainHandler)
, m_logger(getLogger(QStringLiteral("ThreadOcCentralAesData.") + QString::number(equipmentId)))
{}
void ThreadOcCentralAesData::requestOcCentralAesData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &position,
int radius,
bool initial)
{
INFO_LOG(m_logger) << "OC-Situation Request for centerEquipmentId: " << centerEquipmentId
<< " from O/C-laptop-Id: " << m_equipmentId
<< " with radius of " << radius
<< "m, initial: " << initial;
QMutexLocker l(&m_accessMutex);
m_centerType = centerType;
m_centerEquipmentId = centerEquipmentId;
m_radius = radius;
m_position = position;
if (m_handler)
m_handler->setRequestCentralAesData(centerType, centerEquipmentId, position, radius, initial);
else
DEBUG_LOG(m_logger) << "Thread doesn't exist up to now! Do nothing.";
}
void ThreadOcCentralAesData::run()
{
static QMutex s_ddsAccesMutex;
{
QMutexLocker l(&m_accessMutex);
QMutexLocker m(&s_ddsAccesMutex);
DEBUG_LOG(m_logger) << "Instance new OC-Thread for O/C-Laptop-Id: " << m_equipmentId;
m_handler = new HandlerOcCentralAesData(m_mainHandler->dds(),
m_baseCoordinate,
m_equipmentId);
connect(m_handler, &HandlerOcCentralAesData::sendOcData, this, &ThreadOcCentralAesData::sendOcData, Qt::QueuedConnection);
m_handler->setRequestCentralAesData(m_centerType, m_centerEquipmentId, m_position, m_radius, true);
}
INFO_LOG(m_logger) << "OC-Thread started for O/C-Laptop-Id: " << m_equipmentId;
exec();
{
QMutexLocker l(&m_accessMutex);
QMutexLocker m(&s_ddsAccesMutex);
delete m_handler;
m_handler = nullptr;
}
}
//
// HandlerOcSituationData
//
HandlerOcCentralAesData::HandlerOcCentralAesData(const std::shared_ptr<DdsHandler> &dds,
const LtsCoordTrans::UtmCoordinate &baseCoordinate,
int equipmentId)
: m_baseCoordinate(baseCoordinate)
, m_aesDtc(AreaEffectSimulation::Interface::Dtc::create(dds->dds()))
, m_ppmDtc(ParticipantManagement::Interface::Dtc::create(dds->dds()))
, m_equipmentId(equipmentId)
, m_partitionTime(QDateTime::currentMSecsSinceEpoch())
, m_aliveTimer(new QTimer(this))
, m_txTimer(new QTimer(this))
, m_dataQueue(new CentralAesDataQueue(this))
, m_logger(getLogger(QStringLiteral("HandlerOcCentralAesData.") + QString::number(equipmentId)))
{
INFO_LOG(m_logger) << "Create O/C-CentralAesHandler for O/C-Laptop-Id: " << equipmentId
<< " aliveTimeout: " << Settings::aliveTimeout() << " sec"
<< " txCyleTime: " << Settings::txCycleTime() << " sec"
<< " maxTransferBytes: " << Settings::maxNumOfBytePerTxCycle();
connect(dds.get(), &DdsHandler::partitionTimeChanged, this, &HandlerOcCentralAesData::onPartitionTimeChanged);
connect(m_aliveTimer, &QTimer::timeout, this, &HandlerOcCentralAesData::aliveTimeout);
m_aliveTimer->setSingleShot(true);
m_aliveTimer->start(Settings::aliveTimeout());
connect(m_txTimer, &QTimer::timeout, this, &HandlerOcCentralAesData::handleSituation);
m_txTimer->start(Settings::txCycleTime());
connect(this, &HandlerOcCentralAesData::resetInternalData,
this, &HandlerOcCentralAesData::onResetInternalData, Qt::QueuedConnection);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged,
this, &HandlerOcCentralAesData::onParticipantPositionChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::effectClusterChanged,
this, &HandlerOcCentralAesData::onEffectClusterChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::nContaminationChanged,
this, &HandlerOcCentralAesData::onNContaminationChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::bCContaminationChanged,
this, &HandlerOcCentralAesData::onBCContaminationChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionAreaChanged,
this, &HandlerOcCentralAesData::onExplosionAreaChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionSingleChanged,
this, &HandlerOcCentralAesData::onExplosionSingleChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::mineAreaChanged,
this, &HandlerOcCentralAesData::onMineAreaChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::functionalAreaChanged,
this, &HandlerOcCentralAesData::onFunctionalAreaChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::boobyTrapChanged,
this, &HandlerOcCentralAesData::onBoobyTrapChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::offRouteMineChanged,
this, &HandlerOcCentralAesData::onOffRouteMineChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::explosionClusterChanged,
this, &HandlerOcCentralAesData::onExplosionClusterChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerOcCentralAesData::onAesItemViewChanged);
// fetch aes items
m_aesDtc->effectClusters([this](const AreaEffectSimulation::EffectCluster & data)
{
if (data.isCentral())
m_allCentralEffectClusters.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralEffectClusters.size() << " central EffectClusters on startup.";
m_aesDtc->nContaminations([this](const AreaEffectSimulation::NContamination & data)
{
if (data.isCentral())
m_allCentralNContaminations.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralNContaminations.size() << " central NContaminations on startup.";
m_aesDtc->bCContaminations([this](const AreaEffectSimulation::BCContamination & data)
{
if (data.isCentral())
m_allCentralBCContaminations.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralBCContaminations.size() << " central BCContaminations on startup.";
m_aesDtc->explosionAreas([this](const AreaEffectSimulation::ExplosionArea & data)
{
if (data.isCentral())
m_allCentralExplosionsArea.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralExplosionsArea.size() << " central ExplosionsAreas on startup.";
m_aesDtc->explosionSingles([this](const AreaEffectSimulation::ExplosionSingle & data)
{
if (data.isCentral())
m_allCentralExplosionsSingle.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralExplosionsSingle.size() << " central ExplosionsSingles on startup.";
m_aesDtc->mineAreas([this](const AreaEffectSimulation::MineArea & data)
{
if (data.isCentral())
m_allCentralMineAreas.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralMineAreas.size() << " central MineAreas on startup.";
m_aesDtc->functionalAreas([this](const AreaEffectSimulation::FunctionalArea & data)
{
if (data.isCentral())
m_allCentralFunctionalAreas.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralFunctionalAreas.size() << " central FunctionalAreas on startup.";
m_aesDtc->boobyTraps([this](const AreaEffectSimulation::BoobyTrap & data)
{
if (data.isCentral())
m_allCentralBoobyTraps.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralBoobyTraps.size() << " central BoobyTraps on startup.";
m_aesDtc->offRouteMines([this](const AreaEffectSimulation::OffRouteMine & data)
{
if (data.isCentral())
m_allCentralOffRouteMines.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralOffRouteMines.size() << " central OffRouteMines on startup.";
m_aesDtc->explosionClusters([this](const AreaEffectSimulation::ExplosionCluster &data)
{
if (data.isCentral())
m_allCentralExplosionClusters.insert(data.id(), data);
});
TRACE_LOG(m_logger) << "Got " << m_allCentralExplosionClusters.size() << " central ExplosionClusters on startup.";
//fetch aes itemviews
QList<qint32> aesItemIds = allInternalAesItemIds();
m_aesDtc->aesItemViews([this, aesItemIds](const AreaEffectSimulation::AesItemView & data)
{
if (aesItemIds.contains(data.aesItemId()))
m_allCentralAesItemViews.insert(data.aesItemId(), data);
});
TRACE_LOG(m_logger) << "Got " << allInternalAesItemIds().size() << " central AesItems in total on startup. And " << m_allCentralAesItemViews.size() << " AesItemViews.";
m_aesDtc->enableEffectCluster();
m_aesDtc->enableNContamination();
m_aesDtc->enableBCContamination();
m_aesDtc->enableExplosionArea();
m_aesDtc->enableExplosionSingle();
m_aesDtc->enableMineArea();
m_aesDtc->enableFunctionalArea();
m_aesDtc->enableBoobyTrap();
m_aesDtc->enableOffRouteMine();
m_aesDtc->enableExplosionCluster();
m_aesDtc->enableAesItemView();
m_ppmDtc->enableParticipant();
}
void HandlerOcCentralAesData::setRequestCentralAesData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &centerPosition,
int radius,
bool initial)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QMutexLocker l(&m_accessMutex);
DEBUG_LOG(m_logger) << "Requested CenterEquipmentId: " << centerEquipmentId << " radius: " << radius;
bool endThread = false;
if (initial)
onResetInternalData();
m_centerType = centerType;
m_centerEquipmentId = centerEquipmentId;
m_centerPosition = centerPosition;
m_centerParticipantId = 0;
m_radius = radius;
if (centerType == OcSituation::SituationCenterType::Equipment)
{
if (m_centerEquipmentId > 0)
{
ParticipantManagement::Participant centerParticipant = m_ppmDtc->participant(m_ppmDtc->participantIdByEquipmentNumber(m_centerEquipmentId));
if (centerParticipant.id() <= 0)
{
ERROR_LOG(m_logger) << "Participant with EqId " << m_centerEquipmentId
<< " as center of the oc situation request does not exist!";
endThread = true;
}
else
{
const ParticipantManagement::ParticipantPosition &partPosition = m_ppmDtc->participantPosition(m_ppmDtc->participantIdByEquipmentNumber(m_centerEquipmentId));
if (partPosition.participantId() == 0)
{
ERROR_LOG(m_logger) << "Participant with EqId " << m_centerEquipmentId
<< " as center of the oc situation request has no position!";
endThread = true;
}
else
{
m_centerPosition = partPosition.pos();
m_centerParticipantId = centerParticipant.id();
}
}
if (endThread && ++m_retryCount < 5)
{
INFO_LOG(m_logger) << "Retry setting data - maybe participant is not yet available... " << m_retryCount;
QTimer::singleShot(1000 * m_retryCount, this, [this]()
{
// must be initial here
setRequestCentralAesData(m_centerType, m_centerEquipmentId, m_centerPosition, m_radius, true);
});
return;
}
}
else
{
INFO_LOG(m_logger) << "Situation is not requested anymore -> end thread";
endThread = true;
}
}
if (!endThread)
{
if (centerType == OcSituation::SituationCenterType::Equipment)
{
if (m_centerParticipantId == 0)
{
DEBUG_LOG(m_logger) << "m_centerParticipantId == 0";
endThread = true;
}
}
else if ((centerPosition.lon() == std::nan("")) || (centerPosition.lat() == std::nan("")))
{
DEBUG_LOG(m_logger) << "m_centerParticipantId == 0";
endThread = true;
}
}
m_retryCount = 0;
if (endThread)
m_radius = 0; // ==> no data transfer on handleSituation() and quit
buildSituationCircle();
QTimer::singleShot(0, this, &HandlerOcCentralAesData::resetAliveTimer);
if (endThread && !initial)
{
DEBUG_LOG(m_logger) << "Ending Thread --> stopping txTimer and resetting internal data.";
QTimer::singleShot(0, this, &HandlerOcCentralAesData::handleSituation);
emit resetInternalData();
}
}
void HandlerOcCentralAesData::onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType)
{
if (modType != DataModification::eModified
|| m_centerType != OcSituation::SituationCenterType::Equipment
|| data.participantId() != m_centerParticipantId)
return;
m_centerPosition = data.pos();
buildSituationCircle();
}
void HandlerOcCentralAesData::onEffectClusterChanged(const AreaEffectSimulation::EffectCluster &data, DataModification modType)
{
onAesItemChanged(m_allCentralEffectClusters, data, modType);
}
void HandlerOcCentralAesData::onNContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType)
{
onAesItemChanged(m_allCentralNContaminations, data, modType);
}
void HandlerOcCentralAesData::onBCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType)
{
onAesItemChanged(m_allCentralBCContaminations, data, modType);
}
void HandlerOcCentralAesData::onExplosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType)
{
onAesItemChanged(m_allCentralExplosionsArea, data, modType);
}
void HandlerOcCentralAesData::onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType)
{
onAesItemChanged(m_allCentralExplosionsSingle, data, modType);
}
void HandlerOcCentralAesData::onMineAreaChanged(const AreaEffectSimulation::MineArea &data, DataModification modType)
{
onAesItemChanged(m_allCentralMineAreas, data, modType);
}
void HandlerOcCentralAesData::onFunctionalAreaChanged(const AreaEffectSimulation::FunctionalArea &data, DataModification modType)
{
onAesItemChanged(m_allCentralFunctionalAreas, data, modType);
}
void HandlerOcCentralAesData::onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType)
{
onAesItemChanged(m_allCentralBoobyTraps, data, modType);
}
void HandlerOcCentralAesData::onOffRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType)
{
onAesItemChanged(m_allCentralOffRouteMines, data, modType);
}
void HandlerOcCentralAesData::onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType)
{
onAesItemChanged(m_allCentralExplosionClusters, data, modType);
}
void HandlerOcCentralAesData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
const QVector<AreaEffectSimulation::AesType> validTypes = {AreaEffectSimulation::AesType::inner_enum::AtEffectCluster,
AreaEffectSimulation::AesType::inner_enum::AtNContamination,
AreaEffectSimulation::AesType::inner_enum::AtBCContamination ,
AreaEffectSimulation::AesType::inner_enum::AtExplosionArea,
AreaEffectSimulation::AesType::inner_enum::AtExplosionSingle,
AreaEffectSimulation::AesType::inner_enum::AtMineArea,
AreaEffectSimulation::AesType::inner_enum::AtFunctionalArea,
AreaEffectSimulation::AesType::inner_enum::AtBoobyTrap,
AreaEffectSimulation::AesType::inner_enum::AtOffRouteMine,
AreaEffectSimulation::AesType::inner_enum::AtExplosionCluster};
if (!validTypes.contains(data.aesTypeId()))
return;
switch (modType)
{
case DataModification::eAdded:
if (!allInternalAesItemIds().contains(data.aesItemId()))
return;
m_allCentralAesItemViews.insert(data.aesItemId(), data);
break;
case DataModification::eModified:
{
if (!m_allCentralAesItemViews.contains(data.aesItemId()))
return;
m_allCentralAesItemViews[data.aesItemId()] = data;
break;
}
case DataModification::eRemoved:
// AesItemViews are removed from m_allCentralAesItemViews in HandlerOcCentralAesData::onAesItemChanged
break;
}
}
void HandlerOcCentralAesData::handleSituation()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QMutexLocker l(&m_accessMutex);
if (m_radius == 0)
{
INFO_LOG(m_logger) << "Do not send Situation as radius is 0, centerEquipmentId " << m_centerEquipmentId
<< ", m_position " << m_centerPosition;
QTimer::singleShot(0, this, &HandlerOcCentralAesData::aliveTimeout); // ending thread
return;
}
QSet<qint32> oldAesItemsInRange = m_aesItemsInRange;
fetchAesItemsInRange();
// 50 is the max number of AesItems which are allowed to be handled on the ABT.
// So we have to just take the 50 nearest items.
bool hasOverload = m_aesItemsInRange.size() > 50;
if (hasOverload)
{
DEBUG_LOG(m_logger) << "Got more than 50 AesItems in range. " << m_aesItemsInRange.size() << " in total --> Sorting out.";
QMultiMap<qreal, qint32> distances;
for (qint32 currentAesItemId : qAsConst(m_aesItemsInRange))
{
qreal distance = calculateDistanceToCircleCenter(currentAesItemId);
if (distance >= 0.0)
distances.insert(distance, currentAesItemId);
}
DEBUG_LOG(m_logger) << "We have " << distances.size() << " valid distances.";
m_aesItemsInRange.clear();
qint32 counter = 0;
for (auto it = distances.constBegin(); (it != distances.constEnd()) && (counter < 50); ++it, ++counter)
m_aesItemsInRange.insert(it.value());
DEBUG_LOG(m_logger) << "We now have " << m_aesItemsInRange.size() << " AES Items to send.";
}
QSet<qint32> validIds = m_aesItemsInRange - oldAesItemsInRange;
DEBUG_LOG(m_logger) << "After sorting out old items, we have " << validIds.size() << " items left.";
removeKickedOutAesItems(oldAesItemsInRange);
addAesItemsToBuilder(m_allCentralEffectClusters, validIds);
addAesItemsToBuilder(m_allCentralNContaminations, validIds);
addAesItemsToBuilder(m_allCentralBCContaminations, validIds);
addAesItemsToBuilder(m_allCentralExplosionsArea, validIds);
addAesItemsToBuilder(m_allCentralExplosionsSingle, validIds);
addAesItemsToBuilder(m_allCentralMineAreas, validIds);
addAesItemsToBuilder(m_allCentralFunctionalAreas, validIds);
addAesItemsToBuilder(m_allCentralBoobyTraps, validIds);
addAesItemsToBuilder(m_allCentralOffRouteMines, validIds);
addAesItemsToBuilder(m_allCentralExplosionClusters, validIds);
if (m_dataQueue->hasMessages())
{
QByteArray payload;
LtsUtils::set1Byte(m_bInitialDataTransfer ? OcSituation::TelegramId::CentralAesDataInitial : OcSituation::TelegramId::CentralAesData, payload);
LtsUtils::set1Byte(hasOverload, payload); // info, if there are more than 50 aes items in range
m_bInitialDataTransfer = false;
QByteArray sendingData = m_dataQueue->getSendingData(Settings::maxNumOfBytePerTxCycle());
payload += m_bWithCompression ? qCompress(sendingData) : sendingData;
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(m_equipmentId);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
DEBUG_LOG(m_logger) << "Sending " << req.payload().size() << " Bytes payload.";
emit sendOcData(req);
}
else
DEBUG_LOG(m_logger) << "Sending timeout, but nothing to send.";
}
void HandlerOcCentralAesData::resetAliveTimer()
{
m_aliveTimer->start();
}
void HandlerOcCentralAesData::aliveTimeout()
{
INFO_LOG(m_logger) << "O/C-Thread keep alive timeout for O/C-Laptop-Id: " << m_equipmentId
<< " - Thread will be finished id: " << QThread::currentThreadId();
QThread::currentThread()->quit();
}
void HandlerOcCentralAesData::onResetInternalData()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
DEBUG_LOG(m_logger) << "Resetting internal data!";
m_aesItemsInRange.clear();
m_dataQueue->clear();
m_bInitialDataTransfer = true;
}
void HandlerOcCentralAesData::fetchAesItemsInRange()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
if (m_radius == 0)
return;
const QPointF relCenterPos = PositionConverter::relativeFromGeographic(m_centerPosition, m_baseCoordinate);
if (m_centerType == OcSituation::SituationCenterType::Equipment)
INFO_LOG(m_logger) << "Fetching AesItems in radius around equipment " << m_centerEquipmentId
<< " with position " << relCenterPos.x() << "/" << relCenterPos.y() << " and radius: " << m_radius << ", " << m_baseCoordinate.mgrs()
<< ", " << m_centerPosition.lon() << ", " << m_centerPosition.lat();
else
INFO_LOG(m_logger) << "Fetching AesItems in radius of position " << relCenterPos.x() << "/" << relCenterPos.y() << " radius: " << m_radius;
m_aesItemsInRange.clear();
qint32 effectClustersInRange = fetchAesItemsInCircle(m_allCentralEffectClusters);
qint32 nContaminationsInRange = fetchAesItemsInCircle(m_allCentralNContaminations);
qint32 bCContaminationsInRange = fetchAesItemsInCircle(m_allCentralBCContaminations);
qint32 explosionsAreaInRange = fetchAesItemsInCircle(m_allCentralExplosionsArea);
qint32 explosionsSingleInRange = fetchAesItemsInCircle(m_allCentralExplosionsSingle);
qint32 mineAreasInRange = fetchAesItemsInCircle(m_allCentralMineAreas);
qint32 functionalAreasInRange = fetchAesItemsInCircle(m_allCentralFunctionalAreas);
qint32 boobyTrapsInRange = fetchAesItemsInCircle(m_allCentralBoobyTraps);
qint32 offRouteMinesInRange = fetchAesItemsInCircle(m_allCentralOffRouteMines);
qint32 explosionClustersInRage = fetchAesItemsInCircle(m_allCentralExplosionClusters);
TRACE_LOG(m_logger) << "Got " << effectClustersInRange << " EffectClusters in range.";
TRACE_LOG(m_logger) << "Got " << nContaminationsInRange << " NContaminations in range.";
TRACE_LOG(m_logger) << "Got " << bCContaminationsInRange << " BCContaminations in range.";
TRACE_LOG(m_logger) << "Got " << explosionsAreaInRange << " ExplosionsArea in range.";
TRACE_LOG(m_logger) << "Got " << explosionsSingleInRange << " ExplosionsSingle in range.";
TRACE_LOG(m_logger) << "Got " << mineAreasInRange << " MineAreas in range.";
TRACE_LOG(m_logger) << "Got " << functionalAreasInRange << " FunctionalAreas in range.";
TRACE_LOG(m_logger) << "Got " << boobyTrapsInRange << " BoobyTraps in range.";
TRACE_LOG(m_logger) << "Got " << offRouteMinesInRange << " OffRouteMines in range.";
TRACE_LOG(m_logger) << "Got " << explosionClustersInRage << " ExplosionClusters in range.";
}
template<typename T>
qint32 HandlerOcCentralAesData::fetchAesItemsInCircle(const QHash<qint32, T> &aesItems)
{
qint32 aesItemsInRangeCount = 0;
for (auto it = aesItems.constBegin(); it != aesItems.constEnd(); ++it)
{
if (!m_allCentralAesItemViews.contains(it.key()))
{
WARNING_LOG(m_logger) << "Got AesItem::id " << it.key() << " but there is no corresponding AesItemView.";
continue;
}
AreaEffectSimulation::AesItemView aesItemView = m_allCentralAesItemViews.value(it.key());
if (it.value().effectStateId() == AreaEffectSimulation::AesEffectState::inner_enum::EsExpired ||
it.value().effectStateId() == AreaEffectSimulation::AesEffectState::inner_enum::EsRemoved)
{
TRACE_LOG(m_logger) << "AesItem of type " << aesItemView.aesTypeId() << ", with id " << it.key() << " is removed or expired --> skipping.";
continue;
}
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
if (polygonFromForm(aesItemView.form()).intersected(m_situationCircle).size() > 0)
#else
if (polygonFromForm(aesItemView.form()).intersects(m_situationCircle))
#endif
{
m_aesItemsInRange.insert(it.key());
++aesItemsInRangeCount;
}
}
return aesItemsInRangeCount;
}
QPolygonF HandlerOcCentralAesData::polygonFromForm(const std::vector<Core::GeoPosition> &form) const
{
QPolygonF result;
result.reserve(form.size());
for (const Core::GeoPosition &currentPosition : qAsConst(form))
result.append(PositionConverter::relativeFromGeographic({currentPosition.lon(), currentPosition.lat()}, m_baseCoordinate));
return result;
}
QList<qint32> HandlerOcCentralAesData::allInternalAesItemIds() const
{
QList<qint32> result;
result.reserve(m_allCentralEffectClusters.size() +
m_allCentralNContaminations.size() +
m_allCentralBCContaminations.size() +
m_allCentralExplosionsArea.size() +
m_allCentralExplosionsSingle.size() +
m_allCentralMineAreas.size() +
m_allCentralFunctionalAreas.size() +
m_allCentralBoobyTraps.size() +
m_allCentralOffRouteMines.size() +
m_allCentralExplosionClusters.size());
for (auto it = m_allCentralEffectClusters.constBegin(); it != m_allCentralEffectClusters.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralNContaminations.constBegin(); it != m_allCentralNContaminations.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralBCContaminations.constBegin(); it != m_allCentralBCContaminations.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralExplosionsArea.constBegin(); it != m_allCentralExplosionsArea.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralExplosionsSingle.constBegin(); it != m_allCentralExplosionsSingle.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralMineAreas.constBegin(); it != m_allCentralMineAreas.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralFunctionalAreas.constBegin(); it != m_allCentralFunctionalAreas.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralBoobyTraps.constBegin(); it != m_allCentralBoobyTraps.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralOffRouteMines.constBegin(); it != m_allCentralOffRouteMines.constEnd(); ++it)
result.append(it.key());
for (auto it = m_allCentralExplosionClusters.constBegin(); it != m_allCentralExplosionClusters.constEnd(); ++it)
result.append(it.key());
return result;
}
void HandlerOcCentralAesData::removeKickedOutAesItems(const QSet<qint32> &aesItemsSentBefore)
{
QSet<qint32> kickedOutAesItems = aesItemsSentBefore - m_aesItemsInRange;
DEBUG_LOG(m_logger) << "Found " << kickedOutAesItems.size() << " AesItems to kick out.";
AreaEffectSimulation::AesType aesType = AreaEffectSimulation::AesType::inner_enum::AtUnknown;
for (qint32 currentAesItemId : qAsConst(kickedOutAesItems))
{
if (m_allCentralAesItemViews.contains(currentAesItemId))
aesType = m_allCentralAesItemViews.value(currentAesItemId).aesTypeId();
else if (m_allCentralEffectClusters.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtEffectCluster;
else if (m_allCentralNContaminations.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtNContamination;
else if (m_allCentralBCContaminations.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtBCContamination;
else if (m_allCentralExplosionsArea.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtExplosionArea;
else if (m_allCentralExplosionsSingle.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtExplosionSingle;
else if (m_allCentralMineAreas.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtMineArea;
else if (m_allCentralFunctionalAreas.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtFunctionalArea;
else if (m_allCentralBoobyTraps.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtBoobyTrap;
else if (m_allCentralOffRouteMines.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtOffRouteMine;
else if (m_allCentralExplosionClusters.contains(currentAesItemId))
aesType = AreaEffectSimulation::AesType::inner_enum::AtExplosionCluster;
else
{
WARNING_LOG(m_logger) << "Could not remove AesItem with id " << currentAesItemId << ". There is no way to find out its AesType";
continue;
}
m_dataQueue->addRemoveMessage(currentAesItemId, aesType);
}
}
void HandlerOcCentralAesData::buildSituationCircle()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
// building the situation "circle" --> we use a regular polygon with 8 edges
m_situationCircle.clear();
QLineF tempLine;
const qint32 edges = 8;
const qreal angle = 360.0 / edges;
tempLine.setP1(PositionConverter::relativeFromGeographic(m_centerPosition, m_baseCoordinate));
tempLine.setLength(m_radius);
tempLine.setAngle(angle);
m_situationCircle << tempLine.p2();
for (qint32 counter = 1; counter < edges; ++counter)
{
tempLine.setAngle(tempLine.angle() + angle);
m_situationCircle << tempLine.p2();
}
}
qreal HandlerOcCentralAesData::calculateDistanceToCircleCenter(qint32 aesItemId) const
{
if (!m_allCentralAesItemViews.contains(aesItemId))
{
WARNING_LOG(m_logger) << "Could not calculate distance to circle center, because there is no AesItemView for AesItem::id " << aesItemId;
return -1.0;
}
QPolygonF aesItemForm = polygonFromForm(m_allCentralAesItemViews.value(aesItemId).form());
QLineF distanceLine(aesItemForm.boundingRect().center(), m_situationCircle.boundingRect().center());
return distanceLine.length();
}
template<typename T>
void HandlerOcCentralAesData::onAesItemChanged(QHash<qint32, T> &container, const T &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
if (!data.isCentral())
return;
container.insert(data.id(), data);
return;
case DataModification::eModified:
container[data.id()] = data;
if (m_aesItemsInRange.contains(data.id()))
{
if (data.effectStateId() == AreaEffectSimulation::AesEffectState::inner_enum::EsExpired
|| data.effectStateId() == AreaEffectSimulation::AesEffectState::inner_enum::EsRemoved)
{
m_aesItemsInRange.remove(data.id());
m_dataQueue->addRemoveMessage(data);
}
else
m_dataQueue->addAesItem(data);
}
return;
case DataModification::eRemoved:
container.remove(data.id());
if (m_allCentralAesItemViews.contains(data.id()))
{
AreaEffectSimulation::AesItemView aesItemView = m_allCentralAesItemViews.take(data.id());
if (m_aesItemsInRange.contains(data.id()))
{
m_aesItemsInRange.remove(data.id());
m_dataQueue->addRemoveMessage(data.id(), aesItemView.aesTypeId());
}
}
return;
}
}
template<typename T>
void HandlerOcCentralAesData::addAesItemsToBuilder(const QHash<qint32, T> &aesItems, const QSet<qint32> &validIds)
{
for (auto it = aesItems.constBegin(); it != aesItems.constEnd(); ++it)
{
if (validIds.contains(it.key()))
m_dataQueue->addAesItem(it.value());
}
}
} // namespace OcSituationServer
@@ -0,0 +1,202 @@
/**
* Handler for all oc situation data at the server per client
* $Id: handler_oc_situation_data.h 57655 2022-07-20 11:38:28Z karwat $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Services/OcSituationServer/handler_oc_situation_data.h $
* $Author: karwat $
* Created by Philipp Dassow, NLST, 27-02-2018
*/
#pragma once
#include <QMutex>
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QVector>
#include <QPolygonF>
#include <QSet>
#include <AreaEffectSimulation/aes_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include "main_handler.h"
#include "position_converter.h"
#include "oc_situation.h"
#include "logger.h"
namespace OcSituationServer
{
class HandlerOcCentralAesData;
class CentralAesDataQueue;
class ThreadOcCentralAesData : public QThread
{
Q_OBJECT
public:
explicit ThreadOcCentralAesData(MainHandler *mainHandler,
int equipmentId,
int centerEquipmentId,
int radius);
public slots:
void requestOcCentralAesData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &position,
int radius,
bool initial);
signals:
void sendOcData(const TrainingAreaBase::SendOcDataRequest &);
protected:
virtual void run();
private:
OcSituation::SituationCenterType m_centerType = OcSituation::SituationCenterType::Equipment; // which kind of center is used for the situation
Core::GeoPosition2D m_position; // center position a situation can be requested around
LtsCoordTrans::UtmCoordinate m_baseCoordinate;
int m_equipmentId{0}; // id from requesting OC-laptop
int m_centerEquipmentId{0}; // equipment in the center of situation
int m_radius{0}; // observer radius in meter
MainHandler *m_mainHandler{nullptr}; // provides TAB FTC and PPM DTC
HandlerOcCentralAesData *m_handler{nullptr}; // OC-Thread-Object
QMutex m_accessMutex; // Mutex-Object
LOGGER_T &m_logger; // Logger-Object
};
class HandlerOcCentralAesData : public QObject
{
Q_OBJECT
public:
explicit HandlerOcCentralAesData(const std::shared_ptr<DdsHandler> &dds,
const LtsCoordTrans::UtmCoordinate &baseCoordinate,
int equipmentId);
bool withCompression() const { return m_bWithCompression; }
void setWithCompression(bool bOn) { m_bWithCompression = bOn; }
bool withVBGTime() const { return m_bWithVBGTime; }
void setWithVBGTime(bool bOn) { m_bWithVBGTime = bOn; }
private:
void fetchAesItemsInRange();
template <typename T>
qint32 fetchAesItemsInCircle(const QHash<qint32, T> &aesItems);
QPolygonF polygonFromForm(const std::vector<Core::GeoPosition> &form) const;
QList<qint32> allInternalAesItemIds() const;
template <typename T>
void onAesItemChanged(QHash<qint32, T> &container, const T &data, DataModification modType);
template <typename T>
void addAesItemsToBuilder(const QHash<qint32, T> &aesItems, const QSet<qint32> &validIds);
void removeKickedOutAesItems(const QSet<qint32> &aesItemsSentBefore);
void buildSituationCircle();
qreal calculateDistanceToCircleCenter(qint32 aesItemId) const;
public slots:
void setRequestCentralAesData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &centerPosition,
int radius,
bool initial);
private slots:
void onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType);
void onPartitionTimeChanged(qint64 partitionTime) { m_partitionTime = partitionTime; }
void onEffectClusterChanged(const AreaEffectSimulation::EffectCluster &data, DataModification modType);
void onNContaminationChanged(const AreaEffectSimulation::NContamination &data, DataModification modType);
void onBCContaminationChanged(const AreaEffectSimulation::BCContamination &data, DataModification modType);
void onExplosionAreaChanged(const AreaEffectSimulation::ExplosionArea &data, DataModification modType);
void onExplosionSingleChanged(const AreaEffectSimulation::ExplosionSingle &data, DataModification modType);
void onMineAreaChanged(const AreaEffectSimulation::MineArea &data, DataModification modType);
void onFunctionalAreaChanged(const AreaEffectSimulation::FunctionalArea &data, DataModification modType);
void onBoobyTrapChanged(const AreaEffectSimulation::BoobyTrap &data, DataModification modType);
void onOffRouteMineChanged(const AreaEffectSimulation::OffRouteMine &data, DataModification modType);
void onExplosionClusterChanged(const AreaEffectSimulation::ExplosionCluster &data, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType);
void handleSituation();
void resetAliveTimer();
void aliveTimeout();
void onResetInternalData();
signals:
void sendOcData(const TrainingAreaBase::SendOcDataRequest &);
void resetInternalData();
private:
//helper struct
struct AesItemInfo
{
int32_t id;
AreaEffectSimulation::AesType type;
AesItemInfo()
: id(-1)
, type(AreaEffectSimulation::AesType::inner_enum::AtUnknown)
{}
AesItemInfo(int32_t id, AreaEffectSimulation::AesType type)
: id(id)
, type(type)
{}
};
LtsCoordTrans::UtmCoordinate m_baseCoordinate;
QPolygonF m_situationCircle;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
// request data
OcSituation::SituationCenterType m_centerType{OcSituation::SituationCenterType::Equipment}; // which kind of center is used for the situation
Core::GeoPosition2D m_centerPosition; // center position a situation can be requested around
int m_equipmentId{0}; // id from requesting OC-laptop
int m_centerEquipmentId{0}; // equipment in the center of situation
int m_centerParticipantId{0};
int m_radius{0}; // observer radius in meter
int m_retryCount{0};
bool m_participantOverloaded{false};
// internal aes data
QHash<qint32, AreaEffectSimulation::EffectCluster> m_allCentralEffectClusters;
QHash<qint32, AreaEffectSimulation::NContamination> m_allCentralNContaminations;
QHash<qint32, AreaEffectSimulation::BCContamination> m_allCentralBCContaminations;
QHash<qint32, AreaEffectSimulation::ExplosionArea> m_allCentralExplosionsArea;
QHash<qint32, AreaEffectSimulation::ExplosionSingle> m_allCentralExplosionsSingle;
QHash<qint32, AreaEffectSimulation::MineArea> m_allCentralMineAreas;
QHash<qint32, AreaEffectSimulation::FunctionalArea> m_allCentralFunctionalAreas;
QHash<qint32, AreaEffectSimulation::BoobyTrap> m_allCentralBoobyTraps;
QHash<qint32, AreaEffectSimulation::OffRouteMine> m_allCentralOffRouteMines;
QHash<qint32, AreaEffectSimulation::ExplosionCluster> m_allCentralExplosionClusters;
QHash<qint32, AreaEffectSimulation::AesItemView> m_allCentralAesItemViews; // <aesItemId, AesItemView>
QSet<qint32> m_aesItemsInRange;
// options, must be in sync with osc
bool m_bWithCompression{true};
bool m_bWithVBGTime{true};
bool m_bInitialDataTransfer{true};
qint64 m_partitionTime;
// thread management
QMutex m_accessMutex; // Mutex-Object
QTimer *m_aliveTimer{nullptr}; // alive timer
QTimer *m_txTimer{nullptr}; // tx cycle timer
CentralAesDataQueue *m_dataQueue;
LOGGER_T &m_logger;
};
} // namespace OcSituationServer
+633
View File
@@ -0,0 +1,633 @@
/**
* Handler for all oc data at the server
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 14-02-2018
*/
#include "handler_oc_data.h"
#include <QMutexLocker>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include "main_handler.h"
#include "oc_situation.h"
#include "handler_oc_aes_data.h"
#include "handler_oc_situation_data.h"
#include "handler_oc_central_aes_data.h"
#include "settings.h"
#include "logger.h"
namespace OcSituationServer
{
HandlerOcData::HandlerOcData(MainHandler *mainHandler,
QObject *parent)
: QObject(parent)
, Interface::Fts::SendEquipmentMessageOcRequestHandler(mainHandler->dds()->fts())
, Interface::Fts::EquipmentErrorRequestHandler(mainHandler->dds()->fts())
, m_mainHandler(mainHandler)
, m_handlerOcAesData(new HandlerOcAesData(mainHandler, this))
, m_dts(mainHandler->dds()->dts())
, m_fts(mainHandler->dds()->fts())
, m_evlDtc(mainHandler->evlDtc())
, m_evlFtc(mainHandler->evlFtc())
, m_msgFtc(mainHandler->msgFtc())
, m_ppmDtc(mainHandler->ppmDtc())
, m_ppmFtc(mainHandler->ppmFtc())
, m_tabDtc(mainHandler->tabDtc())
, m_tabFtc(mainHandler->tabFtc())
, m_currentActiveEvaluationSchemaId(0)
, m_stateInformationTimer(new QTimer(this))
, m_logger(getLogger("HandlerOcData"))
{}
HandlerOcData::~HandlerOcData()
{}
bool HandlerOcData::init()
{
// EVL DTC enable
if (!m_evlDtc->enableEvaluationSchema())
{
ERROR_LOG(m_logger) << "Enabling EVL Data-Topics failed.";
return false;
}
connect(m_evlDtc.get(), &Evaluations::Interface::Dtc::evaluationSchemaChanged, this, &HandlerOcData::onEvaluationSchemaChanged);
auto cb = [this](const Evaluations::EvaluationSchema & schema)
{
onEvaluationSchemaChanged(schema, DataModification::eAdded);
};
m_evlDtc->evaluationSchemas(cb);
// EVL FTC enable
if (!m_evlFtc->enableEvlCommentModify())
{
ERROR_LOG(m_logger) << "Enabling EVL Function-Topics failed.";
return false;
}
// Msg FTC enable
if (!m_msgFtc->enableCreateOperatorMessage())
{
ERROR_LOG(m_logger) << "Enabling MSG Function-Topics failed.";
return false;
}
if (!m_ppmDtc->enableParticipant())
{
ERROR_LOG(m_logger) << "Enabling PPM Data-Topics failed.";
return false;
}
if (!m_ppmFtc->enableRegisterParticipant())
{
ERROR_LOG(m_logger) << "Enabling PPM FTC-Topics failed.";
return false;
}
// TAB DTC enable
if (!m_tabDtc->enableOcData())
{
ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed.";
return false;
}
// TAB DTC enable
if (!m_tabDtc->enableConnections())
{
ERROR_LOG(m_logger) << "Enabling TAB Data-Topics failed.";
return false;
}
connect(m_tabDtc.get(), &TrainingAreaBase::Interface::Dtc::ocDataChanged, this, &HandlerOcData::onOcDataChanged);
// TAB FTC enable
if (!m_tabFtc->enableSendOcData())
{
ERROR_LOG(m_logger) << "Enabling TAB Function-Topics failed.";
return false;
}
// Handler aes data
if (!m_handlerOcAesData->init())
{
ERROR_LOG(m_logger) << "Could not initialise HandlerOcAesData.";
return false;
}
connect(m_stateInformationTimer, &QTimer::timeout, this, &HandlerOcData::sendStateInformation);
m_stateInformationTimer->setInterval(Settings::stateInformationCycleTime());
INFO_LOG(m_logger) << "Starting StateInformation timer with interval: " << m_stateInformationTimer->interval();
m_stateInformationTimer->start();
INFO_LOG(m_logger) << "Initialisation HandlerOcData succesful.";
return true;
}
void HandlerOcData::onEvlCommentModifyReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Evaluation could not be transmitted to EVL: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Evaluation successfully transmitted to EVL!";
}
void HandlerOcData::onCreateOperatorMessageReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Message could not be created by MSG: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Message created successfully by MSG!";
}
void HandlerOcData::onSendOcDataReply(const Core::FunctionReplyObject &reply)
{
if (reply.error())
ERROR_LOG(m_logger) << "Oc data could not be send: " << reply.translated();
else
DEBUG_LOG(m_logger) << "Oc data send successfully to TAB!";
}
void HandlerOcData::onRegisterParticipantReply(const ParticipantManagement::RegisterReply &reply)
{
if (reply.errorString().size())
ERROR_LOG(m_logger) << "Modify Participant data could not be send: " << reply.errorString();
else
DEBUG_LOG(m_logger) << "Modify Participant data send successfully to ppm!";
}
void HandlerOcData::onEvaluationSchemaChanged(const Evaluations::EvaluationSchema &data, DataModification modType)
{
if ((modType != DataModification::eAdded) && (modType != DataModification::eModified))
return;
if (data.sendToTa())
m_currentActiveEvaluationSchemaId = data.id();
}
void HandlerOcData::onOcDataChanged(const TrainingAreaBase::OcData &data, DataModification modType)
{
if ((modType != DataModification::eAdded) && (modType != DataModification::eModified))
return;
if (data.sampleTime() < QDateTime::currentMSecsSinceEpoch() - 1000 * 60 * 10) // older than 10 mins -> ignore
return;
const QByteArray payload = LtsUtils::toQByteArray(data.payload());
unsigned int telegramId;
LtsUtils::get1Byte(payload, telegramId);
DEBUG_LOG(m_logger) << "New OcData telegramId: " << telegramId;
switch (telegramId)
{
case OcSituation::TelegramId::Message:
{
handleMessage(payload);
break;
}
case OcSituation::TelegramId::DemandEvaluationSchema:
{
handleDemandEvaluationSchema(payload, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::Evaluation:
{
handleEvaluation(payload);
break;
}
case OcSituation::TelegramId::DemandSituation:
{
handleDemandSituation(payload, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::DemandAesItems:
{
OcSituation::AesType aesType;
size_t pos = 1;
LtsUtils::get1Byte(payload, pos, aesType);
m_handlerOcAesData->demandAesItems(aesType, data.equipmentNumber());
break;
}
case OcSituation::TelegramId::EquipmentErrorRegistration:
{
handleEquipmentErrorRegistration(payload);
break;
}
case OcSituation::TelegramId::EquipmentRegistration:
{
handleEquipmentRegistration(payload);
break;
}
default:
{
INFO_LOG(m_logger) << "OC-data with telegram ID: " << telegramId << " is not by HandlerOcData OSS!";
break;
}
}
}
void HandlerOcData::onSendOcData(const TrainingAreaBase::SendOcDataRequest &req)
{
m_tabFtc->sendOcDataRequest(req, this);
}
void HandlerOcData::sendStateInformation()
{
QByteArray payload;
LtsUtils::set1Byte(OcSituation::TelegramId::StateInformation, payload);
LtsUtils::set2Byte(m_currentActiveEvaluationSchemaId, payload);
LtsUtils::set2Byte(m_handlerOcAesData->dangerAreaResetCnt(), payload);
LtsUtils::set2Byte(m_handlerOcAesData->iedResetCnt(), payload);
TrainingAreaBase::SendOcDataRequest out;
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
/*!
* \details Only if Tetra is connected, the OcData telegram will be send
* If we not check = A bunch of telegrams will be queued/stored until Tetra is connected, than Tetra is in Overload
*/
QMapIterator<qint32, TrainingAreaBase::Connections> conIt(m_tabDtc->connectionss());
while (conIt.hasNext())
{
conIt.next();
for (TrainingAreaBase::ConnectionState conState : conIt.value().connections())
{
if (conState.name() == "TETRA" && conState.state() == false)
return;
}
}
onSendOcData(out);
}
void HandlerOcData::onSituationThreadExit()
{
ThreadOcSituationData *t = qobject_cast<ThreadOcSituationData *>(sender());
INFO_LOG(m_logger) << "Exit O/C-Thread for O/C-Laptop-Id: " << m_situationThreads.key(t);
QMutexLocker l(&m_accessMutex);
m_situationThreads.remove(m_situationThreads.key(t));
t->deleteLater();
}
void HandlerOcData::onCentralAesThreadExit()
{
ThreadOcCentralAesData *t = qobject_cast<ThreadOcCentralAesData *>(sender());
INFO_LOG(m_logger) << "Exit CentralAesData-Thread for O/C-Laptop-Id: " << m_centralAesDataThreads.key(t);
QMutexLocker l(&m_accessMutex);
m_centralAesDataThreads.remove(m_centralAesDataThreads.key(t));
t->deleteLater();
}
void HandlerOcData::onSendEquipmentMessageOcRequest(const rti::core::SampleIdentity &cookie, const SendEquipmentMessageRequest &request)
{
QByteArray preparedPayload;
LtsUtils::set1Byte(OcSituation::TelegramId::Message, preparedPayload);
LtsUtils::set2Byte(0, preparedPayload); // dummy receiverId is individually set for each eqId in list
LtsUtils::set1Byte(Messaging::AddressType::ObserverController, preparedPayload);
LtsUtils::set2Byte(request.sender(), preparedPayload);
LtsUtils::set1Byte(request.senderType().underlying(), preparedPayload);
LtsUtils::set2Byte(m_mainHandler->exerciseId(), preparedPayload);
preparedPayload.append(QByteArray::fromStdString(request.subject()));
preparedPayload.append('\n'); // separating subject from message
preparedPayload.append(QByteArray::fromStdString(request.message()));
for (const auto equipmentNumber : request.equipmentNumbers())
{
TrainingAreaBase::SendOcDataRequest out;
out.equipmentNumber(equipmentNumber);
// replace 2 bytes with current receiver
QByteArray receiverId;
LtsUtils::set2Byte(equipmentNumber, receiverId);
QByteArray payload = preparedPayload.replace(1, 2, receiverId);
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
onSendOcData(out);
}
m_fts->sendEquipmentMessageOcReply(cookie, Core::FunctionReplyObject(request.requestId()));
}
void HandlerOcData::onEquipmentErrorRequest(const rti::core::SampleIdentity &cookie, const EquipmentErrorRequest &request)
{
ERROR_LOG(m_logger) << "onEquipmentErrorRequest" << request.equipmentNumber();
EquipmentErrorRegistration out;
// out.id(request.requestId());
out.sampleTime(QDateTime::currentMSecsSinceEpoch());
out.participant(QString::number(request.equipmentNumber()).toStdString());
out.device(request.device());
out.text(request.text());
m_dts->publish(out);
m_fts->equipmentErrorReply(cookie, Core::FunctionReplyObject(request.requestId()));
}
void HandlerOcData::handleDemandSituation(const QByteArray &data, int equipmentId)
{
OcSituation::SituationCenterType centerType;
int centerEquipmentId = 0, radius = 0;
Core::GeoPosition2D position;
position.lon(std::nan(""));
position.lat(std::nan(""));
bool initial = false;
size_t pos = 1;
LtsUtils::get1Byte(data, pos, centerType);
if (centerType == OcSituation::SituationCenterType::Equipment)
{
LtsUtils::get2Byte(data, pos, centerEquipmentId);
LtsUtils::get2Byte(data, pos, radius);
DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId
<< " centerEquipmentId: " << centerEquipmentId << " radius: " << radius;
}
else
{
double tmp;
LtsUtils::get8Byte(data, pos, tmp);
position.lon(tmp);
LtsUtils::get8Byte(data, pos, tmp);
position.lat(tmp);
LtsUtils::get2Byte(data, pos, radius);
DEBUG_LOG(m_logger) << "Request from O/C-Laptop-Id: " << equipmentId
<< " center: " << position << " radius: " << radius;
}
bool filterActive = false;
LtsUtils::get1Byte(data, pos, initial);
LtsUtils::get1Byte(data, pos, filterActive);
QSet<ParticipantManagement::TypeClass::type> typeClassList{};
QSet<int> partyList{};
if (filterActive)
{
// get partyTypes
int pCount = 0;
LtsUtils::get1Byte(data, pos, pCount);
for (int i = 0; i < pCount; i++)
{
int partyId;
LtsUtils::get2Byte(data, pos, partyId);
partyList.insert(partyId);
}
//get types
int typeCount = 0;
LtsUtils::get1Byte(data, pos, typeCount);
for (int i = 0; i < typeCount; i++)
{
int typClass;
LtsUtils::get1Byte(data, pos, typClass);
typeClassList.insert((ParticipantManagement::TypeClass::type)typClass);
}
}
QMutexLocker l(&m_accessMutex);
// Situation Thread
ThreadOcSituationData *ocThread = m_situationThreads.value(equipmentId);
bool bInitialSituation;
if (!ocThread)
{
ocThread = new ThreadOcSituationData(m_mainHandler, equipmentId, centerEquipmentId, radius, filterActive, partyList, typeClassList);
m_situationThreads.insert(equipmentId, ocThread);
connect(ocThread, &QThread::finished, this, &HandlerOcData::onSituationThreadExit);
connect(ocThread, &ThreadOcSituationData::sendOcData, this, &HandlerOcData::onSendOcData);
ocThread->start();
bInitialSituation = true;
}
else
bInitialSituation = initial;
ocThread->requestOcSituationData(centerType, centerEquipmentId, position, radius, filterActive, partyList, typeClassList, bInitialSituation);
// Central AES Data Thread
bool bInitialAes;
ThreadOcCentralAesData *centralAesDataThread = m_centralAesDataThreads.value(equipmentId);
if (!centralAesDataThread)
{
centralAesDataThread = new ThreadOcCentralAesData(m_mainHandler, equipmentId, centerEquipmentId, radius);
m_centralAesDataThreads.insert(equipmentId, centralAesDataThread);
connect(centralAesDataThread, &QThread::finished, this, &HandlerOcData::onCentralAesThreadExit);
connect(centralAesDataThread, &ThreadOcCentralAesData::sendOcData, this, &HandlerOcData::onSendOcData);
centralAesDataThread->start();
bInitialAes = true;
}
else
bInitialAes = initial;
centralAesDataThread->requestOcCentralAesData(centerType, centerEquipmentId, position, radius, bInitialAes);
}
void HandlerOcData::handleDemandEvaluationSchema(const QByteArray &data, int equipmentNumber)
{
Q_UNUSED(data);
// ignoring requested schema ID, as we only have one active schema to be send to TA
Evaluations::EvaluationSchema schema = m_evlDtc->evaluationSchema(m_currentActiveEvaluationSchemaId);
DEBUG_LOG(m_logger) << "Sending to equipmentnumber: " << equipmentNumber << " evaluation schmema: " << m_currentActiveEvaluationSchemaId;
TrainingAreaBase::SendOcDataRequest out;
out.equipmentNumber(equipmentNumber);
QByteArray payload;
LtsUtils::set1Byte(OcSituation::TelegramId::EvaluationSchema, payload);
LtsUtils::set2Byte(schema.id(), payload);
QByteArray compressed = qCompress(QByteArray::fromStdString(schema.schemadata()));
payload.append(compressed);
out.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
onSendOcData(out);
}
void HandlerOcData::handleEvaluation(const QByteArray &data)
{
Evaluations::EvlCommentModifyRequest out;
size_t pos = 1;
unsigned int tmp;
// get schema id
LtsUtils::get2Byte(data, pos, tmp);
out.schemaId(tmp);
// get sender operator id
LtsUtils::get4Byte(data, pos, tmp);
out.senderOperatorId(tmp);
// get receiver operator id
LtsUtils::get4Byte(data, pos, tmp);
if (tmp > 0)
out.receiverOperatorId(dds::core::optional<int32_t>(tmp));
// get timeStamp
int64_t timeStamp;
LtsUtils::get8Byte(data, pos, timeStamp);
out.ts(timeStamp);
// get hierarchy string
unsigned int hierarchyLength;
LtsUtils::get2Byte(data, pos, hierarchyLength);
out.hierarchy(QByteArray(data.mid(pos, hierarchyLength)).toStdString());
pos += hierarchyLength;
// get text string
unsigned int textLength;
LtsUtils::get2Byte(data, pos, textLength);
out.text(QByteArray(data.mid(pos, textLength)).toStdString());
DEBUG_LOG(m_logger) << "Got new evl comment: " << out;
m_evlFtc->evlCommentModifyRequest(out, this);
}
void HandlerOcData::handleMessage(const QByteArray &data)
{
Messaging::CreateOperatorMessageRequest out;
size_t pos = 1;
unsigned int tmp = 0;
unsigned int addressType;
// get receiver
LtsUtils::get2Byte(data, pos, tmp);
std::vector<int32_t> recipientIds;
recipientIds.push_back(tmp);
// get address type
LtsUtils::get1Byte(data, pos, addressType);
out.recipientType(static_cast<Messaging::AddressType::inner_enum>(addressType));
if (addressType == Messaging::AddressType::ObserverController)
{
int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp);
if (participantId > 0)
recipientIds[0] = participantId;
else
{
ERROR_LOG(m_logger) << "Recipient equipment number: " << tmp << " is unknown. Aborting creating message.";
return;
}
}
out.recipientIds(recipientIds);
// get sender
LtsUtils::get2Byte(data, pos, tmp);
LtsUtils::get1Byte(data, pos, addressType);
out.senderType(static_cast<Messaging::AddressType::inner_enum>(addressType));
if (addressType == Messaging::AddressType::ObserverController)
{
int participantId = m_ppmDtc->participantIdByEquipmentNumber(tmp);
if (participantId > 0)
out.senderId(participantId);
else
{
WARNING_LOG(m_logger) << "Sender equipment number: " << tmp << " is unknown. Setting senderId to 0.";
out.senderId(0);
out.senderType(Messaging::AddressType::ObserverController);
}
}
else
{
// no need to check if operator still exists, there is no harm if not
out.senderId(tmp);
}
// get exerciseId
LtsUtils::get2Byte(data, pos, tmp);
if (tmp == 0)
WARNING_LOG(m_logger) << "Could'n set ExerciseId" << " " << tmp << " set to 0";
out.exerciseId(tmp);
// get address type
const QByteArray msg(data.right(data.size() - pos));
int idx = msg.indexOf('\n');
if (idx < 0)
{
out.subject((tr("Message from %1").arg(out.senderId())).toStdString());
out.message(msg.constData());
}
else
{
out.subject(msg.left(idx).constData());
out.message(msg.mid(idx + 1).constData());
}
DEBUG_LOG(m_logger) << "Got new message: " << out;
m_msgFtc->createOperatorMessageRequest(out, this);
}
void HandlerOcData::handleEquipmentErrorRegistration(const QByteArray &data)
{
EquipmentErrorRegistration out;
size_t pos = 1;
unsigned int tmp;
// equipmentNumber
LtsUtils::get2Byte(data, pos, tmp);
out.equipmentNumber(tmp);
// participant
out.participant(data.mid(pos, 36).toStdString());
pos += 36;
// device
out.device(data.mid(pos, 36).toStdString());
pos += 36;
// length
LtsUtils::get1Byte(data, pos, tmp);
// text
out.text(data.mid(pos, tmp).toStdString());
out.sampleTime(QDateTime::currentMSecsSinceEpoch());
DEBUG_LOG(m_logger) << "Got new error registration: " << out;
m_dts->publish(out);
}
void HandlerOcData::handleEquipmentRegistration(const QByteArray &data)
{
size_t pos = 1;
unsigned int tmp;
// participantID
LtsUtils::get2Byte(data, pos, tmp);
int participantId = tmp;
// equipmentNumber
LtsUtils::get2Byte(data, pos, tmp);
int equipmentNumber = tmp;
ParticipantManagement::Participant part = m_ppmDtc->participant(participantId);
if (part.equipmentNumber().is_set() && equipmentNumber > 0)
{
// we need a deregistration first!
ParticipantManagement::RegisterParticipantRequest dereq;
dereq.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
dereq.equipmentNumber(0);
dereq.participantId(participantId);
dereq.info(part.info());
dereq.identification(part.identification());
m_ppmFtc->registerParticipantRequest(dereq, this);
}
ParticipantManagement::RegisterParticipantRequest req;
if (equipmentNumber > 0)
req.assignState(ParticipantManagement::ParticipantAssignState::HandoutExcon);
else
req.assignState(ParticipantManagement::ParticipantAssignState::NotAssigned);
req.equipmentNumber(equipmentNumber);
req.participantId(participantId);
req.info(part.info());
req.identification(part.identification());
DEBUG_LOG(m_logger) << "Try to register part with id: " << req.participantId() << " and eqNumeber " << req.equipmentNumber();
m_ppmFtc->registerParticipantRequest(req, this);
}
} // Namespace OcSituationServer
+91
View File
@@ -0,0 +1,91 @@
/**
* Handler for all oc data at the server
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 14-02-2018
*/
#pragma once
#include "main_handler.h"
#include "logger.h"
#include <QMap>
#include <QMutex>
#include <QTimer>
namespace OcSituationServer
{
class ThreadOcCentralAesData;
class ThreadOcSituationData;
class HandlerOcAesData;
class HandlerOcData : public QObject
, protected Interface::Fts::SendEquipmentMessageOcRequestHandler
, protected Interface::Fts::EquipmentErrorRequestHandler
, protected Evaluations::Interface::Ftc::EvlCommentModifyReplyHandler
, protected Messaging::Interface::Ftc::CreateOperatorMessageReplyHandler
, protected ParticipantManagement::Interface::Ftc::RegisterParticipantReplyHandler
, protected TrainingAreaBase::Interface::Ftc::SendOcDataReplyHandler
{
Q_OBJECT
public:
explicit HandlerOcData(MainHandler *mainHandler,
QObject *parent = nullptr);
~HandlerOcData();
bool init();
protected:
void onEvlCommentModifyReply(const Core::FunctionReplyObject &reply) override final;
void onCreateOperatorMessageReply(const Core::FunctionReplyObject &reply) override final;
void onSendOcDataReply(const Core::FunctionReplyObject &reply) override final;
void onRegisterParticipantReply(const ParticipantManagement::RegisterReply &reply) override final;
private slots:
void onEvaluationSchemaChanged(const Evaluations::EvaluationSchema &data, DataModification modType);
void onOcDataChanged(const TrainingAreaBase::OcData &data, DataModification modType);
void onSendOcData(const TrainingAreaBase::SendOcDataRequest &req);
void sendStateInformation();
void onSituationThreadExit();
void onCentralAesThreadExit();
private:
void onSendEquipmentMessageOcRequest(const rti::core::SampleIdentity &cookie,
const SendEquipmentMessageRequest &request) override final;
void onEquipmentErrorRequest(const rti::core::SampleIdentity &cookie,
const EquipmentErrorRequest &request) override final;
void handleDemandSituation(const QByteArray &data, int equipmentId);
void handleDemandEvaluationSchema(const QByteArray &data, int equipmentNumber);
void handleEvaluation(const QByteArray &data);
void handleMessage(const QByteArray &data);
void handleEquipmentErrorRegistration(const QByteArray &data);
void handleEquipmentRegistration(const QByteArray &data);
MainHandler *m_mainHandler;
HandlerOcAesData *m_handlerOcAesData;
std::shared_ptr<Interface::Dts> m_dts;
std::shared_ptr<Interface::Fts> m_fts;
std::shared_ptr<Evaluations::Interface::Dtc> m_evlDtc;
std::shared_ptr<Evaluations::Interface::Ftc> m_evlFtc;
std::shared_ptr<Messaging::Interface::Ftc> m_msgFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_tabDtc;
std::shared_ptr<TrainingAreaBase::Interface::Ftc> m_tabFtc;
int m_currentActiveEvaluationSchemaId;
QMap<int, ThreadOcSituationData *> m_situationThreads; // equipmentId, thread for situation data handling
QMap<int, ThreadOcCentralAesData *> m_centralAesDataThreads; // equipmentId, thread for central aes data handling
QMutex m_accessMutex; // Mutex-Object
QTimer *m_stateInformationTimer;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,982 @@
/**
* Handler for all oc situation data at the server per client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 27-02-2018
*/
#include "handler_oc_situation_data.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsUtilsCore/outdated_position.h>
#include <ParticipantManagement/ppm_common.h>
#include "settings.h"
#include <algorithm>
namespace OcSituationServer
{
//
// ThreadOcSituationData
//
ThreadOcSituationData::ThreadOcSituationData(MainHandler *mainHandler,
int equipmentId,
int centerEquipmentId,
int radius,
bool filterActive,
QSet<int> partys,
QSet<ParticipantManagement::TypeClass::type> typeClasses)
: m_baseCoordinate(mainHandler->positionConverter()->baseCoordinate())
, m_equipmentId(equipmentId)
, m_centerEquipmentId(centerEquipmentId)
, m_radius(radius)
, m_partyList(partys)
, m_filterActive(filterActive)
, m_typeClassList(typeClasses)
, m_mainHandler(mainHandler)
, m_logger(getLogger(QStringLiteral("ThreadOcSituationData.") + QString::number(equipmentId)))
{}
void ThreadOcSituationData::requestOcSituationData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &position,
int radius,
bool filterActive,
QSet<int> partyList,
QSet<ParticipantManagement::TypeClass::type> typeClassList,
bool initial)
{
INFO_LOG(m_logger) << "OC-Situation Request for centerEquipmentId: " << centerEquipmentId
<< " from O/C-laptop-Id: " << m_equipmentId
<< " with radius of " << radius
<< "m, initial: " << initial;
QMutexLocker l(&m_accessMutex);
m_centerType = centerType;
m_centerEquipmentId = centerEquipmentId;
m_radius = radius;
m_position = position;
m_filterActive = filterActive;
m_partyList = partyList;
m_typeClassList = typeClassList;
if (m_handler)
m_handler->setRequestOcSituationData(centerType, centerEquipmentId, position, radius, filterActive, partyList, typeClassList, initial);
else
DEBUG_LOG(m_logger) << "Thread doesn't exist up to now! Do nothing.";
}
void ThreadOcSituationData::run()
{
static QMutex s_ddsAccesMutex;
{
QMutexLocker l(&m_accessMutex);
QMutexLocker m(&s_ddsAccesMutex);
DEBUG_LOG(m_logger) << "Instance new OC-Thread for O/C-Laptop-Id: " << m_equipmentId;
m_handler = new HandlerOcSituationData(m_mainHandler->dds(),
m_baseCoordinate,
m_equipmentId);
connect(m_handler, &HandlerOcSituationData::sendOcData, this, &ThreadOcSituationData::sendOcData, Qt::QueuedConnection);
m_handler->setRequestOcSituationData(m_centerType, m_centerEquipmentId, m_position, m_radius, m_filterActive, m_partyList, m_typeClassList, true);
}
INFO_LOG(m_logger) << "OC-Thread started for O/C-Laptop-Id: " << m_equipmentId;
exec();
{
QMutexLocker l(&m_accessMutex);
QMutexLocker m(&s_ddsAccesMutex);
delete m_handler;
m_handler = nullptr;
}
}
//
// HandlerOcSituationData
//
HandlerOcSituationData::HandlerOcSituationData(const std::shared_ptr<DdsHandler> &dds,
const LtsCoordTrans::UtmCoordinate &baseCoordinate,
int equipmentId)
: m_baseCoordinate(baseCoordinate)
, m_ppmDtc(ParticipantManagement::Interface::Dtc::create(dds->dds()))
, m_aesDtc(AreaEffectSimulation::Interface::Dtc::create(dds->dds()))
, m_gimDtc(GuezIedManagement::Interface::Dtc::create(dds->dds()))
, m_equipmentId(equipmentId)
, m_partitionTime(QDateTime::currentMSecsSinceEpoch())
, m_aliveTimer(new QTimer(this))
, m_txTimer(new QTimer(this))
, m_logger(getLogger(QStringLiteral("HandlerOcSituationData.") + QString::number(equipmentId)))
{
INFO_LOG(m_logger) << "Create O/C-Handler for O/C-Laptop-Id: " << equipmentId
<< " aliveTimeout: " << Settings::aliveTimeout() << " sec"
<< " txCyleTime: " << Settings::txCycleTime() << " sec"
<< " maxTransferBytes: " << Settings::maxNumOfBytePerTxCycle();
connect(dds.get(), &DdsHandler::partitionTimeChanged, this, &HandlerOcSituationData::onPartitionTimeChanged);
connect(m_aliveTimer, &QTimer::timeout, this, &HandlerOcSituationData::aliveTimeout);
m_aliveTimer->setSingleShot(true);
m_aliveTimer->start(Settings::aliveTimeout());
connect(m_txTimer, &QTimer::timeout, this, &HandlerOcSituationData::handleSituation);
m_txTimer->start(Settings::txCycleTime());
connect(this, &HandlerOcSituationData::resetInternalData,
this, &HandlerOcSituationData::onResetInternalData, Qt::QueuedConnection);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &HandlerOcSituationData::onParticipantChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::dynamicParticipantDataChanged,
this, &HandlerOcSituationData::onDynamicParticipantDataChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantPositionChanged,
this, &HandlerOcSituationData::onParticipantPositionChanged);
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::aesItemViewChanged,
this, &HandlerOcSituationData::onAesItemViewChanged);
// REGG-5592 - Von DUS-Minen ausgefallene Teilnehmer werden in der Lagedarstellung nicht mit einer
// Trefferanzeige in der Farbe der Sperre sondern in der Farbe des Ltg/Ausb-Dienstes (Ocker) dargestellt.
// to handle race condition between DynamicParticipantData and DusMineState:
connect(m_aesDtc.get(), &AreaEffectSimulation::Interface::Dtc::dusMineStateChanged,
this, &HandlerOcSituationData::onDusMineStateChanged);
m_ppmDtc->enableParticipant();
m_ppmDtc->enableDynamicParticipantData();
m_ppmDtc->enableParticipantPosition();
m_ppmDtc->enableType();
m_aesDtc->enableAesItemView();
}
void HandlerOcSituationData::setRequestOcSituationData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &centerPosition,
int radius,
bool filterActive,
QSet<int> partys,
QSet<ParticipantManagement::TypeClass::type> typeClasses,
bool initial)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QMutexLocker l(&m_accessMutex);
DEBUG_LOG(m_logger) << "Requested CenterEquipmentId: " << centerEquipmentId << " radius: " << radius;
bool endThread = false;
m_centerType = centerType;
m_centerEquipmentId = centerEquipmentId;
m_centerPosition = centerPosition;
m_centerParticipantId = 0;
m_radius = radius;
m_partyList.clear();
m_typeClassList.clear();
m_partyList = partys;
m_typeClassList = typeClasses;
m_filterActive = filterActive;
if (centerType == OcSituation::SituationCenterType::Equipment)
{
if (m_centerEquipmentId > 0)
{
ParticipantManagement::Participant centerParticipant;
for (const auto &participant : qAsConst(m_allParticipants))
{
if (participant.equipmentNumber().is_set() && participant.equipmentNumber().get() == m_centerEquipmentId)
{
centerParticipant = participant;
break;
}
};
if (centerParticipant.id() <= 0)
{
ERROR_LOG(m_logger) << "Participant with EqId " << m_centerEquipmentId
<< " as center of the oc situation request does not exist!";
endThread = true;
}
else
{
const ParticipantManagement::ParticipantPosition &partPosition = m_allParticipantPositions.value(centerParticipant.id());
if (partPosition.participantId() == 0)
{
ERROR_LOG(m_logger) << "Participant with EqId " << m_centerEquipmentId
<< " as center of the oc situation request has no position!";
endThread = true;
}
else
{
m_centerPosition = partPosition.pos();
m_centerParticipantId = centerParticipant.id();
}
}
if (endThread && ++m_retryCount < 5)
{
INFO_LOG(m_logger) << "Retry setting data - maybe participant is not yet available... " << m_retryCount;
QTimer::singleShot(1000 * m_retryCount, this, [this]()
{
// must be initial here
setRequestOcSituationData(m_centerType, m_centerEquipmentId, m_centerPosition, m_radius, m_filterActive, m_partyList, m_typeClassList, true);
});
return;
}
}
else
{
INFO_LOG(m_logger) << "Situation is not requested anymore -> end thread";
endThread = true;
}
}
if (!endThread)
{
if (centerType == OcSituation::SituationCenterType::Equipment)
{
if (m_centerParticipantId == 0)
endThread = true;
}
else if ((centerPosition.lon() == std::nan("")) || (centerPosition.lat() == std::nan("")))
endThread = true;
}
m_retryCount = 0;
if (endThread == true)
m_radius = 0; // ==> no data transfer on handleSituation() and quit
QTimer::singleShot(0, this, &HandlerOcSituationData::resetAliveTimer);
if (initial || endThread)
{
// thread change ...
QTimer::singleShot(0, this, &HandlerOcSituationData::handleSituation);
emit resetInternalData();
}
}
void HandlerOcSituationData::onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
if (modType == DataModification::eRemoved)
return;
const int participantId = data.id();
m_allParticipants[participantId] = data;
if (!m_participants.contains(participantId))
{
DEBUG_LOG(m_logger) << "m_participants does not contain participant id: " << participantId;
return; // participant is not of interest (now)
}
// old data to check against
const ParticipantManagement::Participant &oldParticipantData = m_participants[participantId];
// check structure
if (oldParticipantData.equipmentNumber() != data.equipmentNumber() ||
oldParticipantData.superiorId() != data.superiorId() ||
oldParticipantData.symbol() != data.symbol() ||
oldParticipantData.typeId() != data.typeId() ||
oldParticipantData.partyId() != data.partyId())
setModified(participantId, OcSituation::ModificationFlag::eStructure);
// check designator
if (oldParticipantData.designator() != data.designator() ||
oldParticipantData.tacticalIdentifier() != data.tacticalIdentifier())
setModified(participantId, OcSituation::ModificationFlag::eDesignator);
m_participants[participantId] = data;
DEBUG_LOG(m_logger) << "updated data in m_participants" << data;
if (m_modifiedFlags.contains(participantId))
{
INFO_LOG(m_logger) << "setModified for: " << m_participantId2EqId[participantId]
<< " / " << participantId
<< " bits: 0x" << QString::number(m_modifiedFlags[participantId], 16);
}
}
static inline int getEquipmentStateBits(const ParticipantManagement::DynamicParticipantData &data)
{
int ret = 0;
ret |= int(data.hasNbcProtectionConfigured()) << 1;
ret |= int(data.hasNbcProtectionActive()) << 2;
ret |= int(data.manipulation().size() > 0) << 7;
return ret;
}
void HandlerOcSituationData::onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
if (modType == DataModification::eRemoved)
return;
const int participantId = data.participantId();
m_allDynamicParticpantData[participantId] = data;
if (!m_participants.contains(participantId))
{
DEBUG_LOG(m_logger) << "m_participants does not contain participant id: " << participantId;
return; // participant does not interest us yet;
}
// old data to check against
const ParticipantManagement::DynamicParticipantData &oldDynamicData = m_dynamicParticpantData[participantId];
// check injury state
ParticipantManagement::StateInformation newYoungestInformation = ParticipantManagement::youngestStateInformation(data.stateHistory());
ParticipantManagement::StateInformation oldYoungestInformation = ParticipantManagement::youngestStateInformation(oldDynamicData.stateHistory());
if (oldYoungestInformation.stateTime() != newYoungestInformation.stateTime() ||
oldYoungestInformation.vulnerabilityState() != newYoungestInformation.vulnerabilityState() ||
oldYoungestInformation.vulnerabilityBits() != newYoungestInformation.vulnerabilityBits() || // TODO: do we really want to send those bit?
m_allParticipants.value(oldYoungestInformation.originatorId()).partyId() != m_allParticipants.value(newYoungestInformation.originatorId()).partyId())
setModified(participantId, OcSituation::ModificationFlag::eVBG);
// check special bits
if (getEquipmentStateBits(oldDynamicData) != getEquipmentStateBits(data))
setModified(participantId, OcSituation::ModificationFlag::eState);
m_dynamicParticpantData[participantId] = data;
DEBUG_LOG(m_logger) << "updated data in m_dynamicParticpantData" << data;
if (m_modifiedFlags.contains(participantId))
{
INFO_LOG(m_logger) << "setModified for: " << m_participantId2EqId[participantId]
<< " / " << participantId
<< " bits: 0x" << QString::number(m_modifiedFlags[participantId], 16);
}
}
void HandlerOcSituationData::onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
if (modType == DataModification::eRemoved)
return;
const int participantId = data.participantId();
m_allParticipantPositions[participantId] = data;
// update current center position when we're following a participant
if (m_centerType == OcSituation::SituationCenterType::Equipment &&
participantId == m_centerParticipantId)
m_centerPosition = data.pos();
if (!m_participants.contains(participantId))
{
DEBUG_LOG(m_logger) << "m_participants does not contain participant id: " << participantId;
return; // participant does not interest us yet;
}
// old data to check against
const ParticipantManagement::ParticipantPosition &oldPosition = m_participantPositions[participantId];
// check for ePosition / ePosition3D
if (oldPosition.sampleTime() <= data.sampleTime())
{
//bool isNullOld = qFuzzyIsNull(oldPosition.pos().z());
bool isNullNew = qFuzzyIsNull(data.pos().z());
// if (isNullOld && isNullNew)
if (isNullNew)
setModified(participantId, OcSituation::ModificationFlag::ePosition2D);
else
setModified(participantId, OcSituation::ModificationFlag::ePosition3D);
/*
if (isNullOld && isNullNew)
setModified(participantId, OcSituation::ModificationFlag::ePosition2D);
else if ((isNullOld && !isNullNew) ||
(!isNullOld && isNullNew))
setModified(participantId, OcSituation::ModificationFlag::ePosition3D);
else if (!qFuzzyCompare(oldPosition.pos().z(),
data.pos().z()))
setModified(participantId, OcSituation::ModificationFlag::ePosition3D);
else
setModified(participantId, OcSituation::ModificationFlag::ePosition2D);
*/
}
m_participantPositions[participantId] = data;
DEBUG_LOG(m_logger) << "updated data in m_participantPositions" << data;
if (m_modifiedFlags.contains(participantId))
{
INFO_LOG(m_logger) << "setModified for: " << m_participantId2EqId[participantId]
<< " / " << participantId
<< " bits: 0x" << QString::number(m_modifiedFlags[participantId], 16);
}
}
void HandlerOcSituationData::onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType)
{
switch (modType)
{
case DataModification::eAdded:
case DataModification::eModified:
if (data.partyId().is_set())
m_aesId2PartyId[data.id()] = data.partyId().get();
else
m_aesId2PartyId.remove(data.id());
break;
case DataModification::eRemoved:
m_aesId2PartyId.remove(data.id());
break;
}
}
void HandlerOcSituationData::onDusMineStateChanged(const AreaEffectSimulation::DusMineState &/*data*/, DataModification /*modType*/)
{
auto copy = m_dynamicParticpantData;
for (const ParticipantManagement::DynamicParticipantData &dynPartData : qAsConst(copy))
{
if (!m_participantWithMaybeMissingPartyId.contains(dynPartData.participantId()))
continue;
onDynamicParticipantDataChanged(dynPartData, DataModification::eModified);
}
}
void HandlerOcSituationData::handleSituation()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QMutexLocker l(&m_accessMutex);
if (m_radius == 0)
{
INFO_LOG(m_logger) << "Do not send Situation as radius is 0, centerEquipmentId " << m_centerEquipmentId
<< ", m_position " << m_centerPosition;
QTimer::singleShot(0, this, &HandlerOcSituationData::aliveTimeout); // ending thread
return;
}
fetchParticipantsInRange();
auto prioQueue = createPrioQueue();
const QByteArray data = createDataStream(Settings::maxNumOfBytePerTxCycle(), prioQueue);
if (data.isEmpty())
{
DEBUG_LOG(m_logger) << "Don't send empty data to TAB.";
return;
}
QByteArray payload;
LtsUtils::set1Byte(m_bInitialDataTransfer ? OcSituation::TelegramId::SituationDataInitial : OcSituation::TelegramId::SituationData, payload);
LtsUtils::set1Byte(m_participantOverloaded, payload);
m_bInitialDataTransfer = false;
payload += data;
updatePrioOfNotSendParticipants();
TrainingAreaBase::SendOcDataRequest req;
req.equipmentNumber(m_equipmentId);
req.payload(LtsUtils::convert<std::vector<uint8_t>>(payload));
emit sendOcData(req);
}
void HandlerOcSituationData::resetAliveTimer()
{
m_aliveTimer->start();
}
void HandlerOcSituationData::aliveTimeout()
{
INFO_LOG(m_logger) << "O/C-Thread keep alive timeout for O/C-Laptop-Id: " << m_equipmentId
<< " - Thread will be finished id: " << QThread::currentThreadId();
QThread::currentThread()->quit();
}
void HandlerOcSituationData::onResetInternalData()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
INFO_LOG(m_logger) << "Resetting internal data!";
m_participants.clear();
m_dynamicParticpantData.clear();
m_participantPositions.clear();
m_modifiedFlags.clear();
m_participantsToSend.clear();
m_outdatedParticipants.clear();
m_newlyOutdatedParticipants.clear();
m_participantsNotSend.clear();
m_participantId2EqId.clear();
m_participantWithMaybeMissingPartyId.clear();
m_bInitialDataTransfer = true;
}
void HandlerOcSituationData::fetchParticipantsInRange()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
const QPointF relCenterPos = PositionConverter::relativeFromGeographic(m_centerPosition, m_baseCoordinate);
if (m_centerType == OcSituation::SituationCenterType::Equipment)
INFO_LOG(m_logger) << "Fetching participants in radius around equipment " << m_centerEquipmentId
<< " with position " << relCenterPos.x() << "/" << relCenterPos.y() << " and radius: " << m_radius << ", " << m_baseCoordinate.mgrs()
<< ", " << m_centerPosition.lon() << ", " << m_centerPosition.lat();
else
INFO_LOG(m_logger) << "Fetching participants in radius of position " << relCenterPos.x() << "/" << relCenterPos.y() << " radius: " << m_radius;
const double rangeSqr = m_radius * m_radius;
QMultiMap<double, int> curParticipantsInRange;
QSet<int> curParticipants;
const QHash<int, ParticipantManagement::Participant> oldParticipants = m_participants;
for (const auto &position : qAsConst(m_allParticipantPositions))
{
if (!LtsUtils::isPositionTimeOutdated(m_partitionTime, position.sampleTime())) // only if position is not outdated
{
ParticipantManagement::Participant participant = m_allParticipants.value(position.participantId());
if (!participant.equipmentNumber().is_set() || participant.partyId() == 0)
continue; // no units and participants without equipmentnumber or party: pool
if (m_filterActive)
{
ParticipantManagement::TypeClass typeClass = m_ppmDtc->type(participant.typeId()).typeClass();
if ((m_partyList.count() > 0 && m_partyList.contains(participant.partyId()) == false) ||
(m_typeClassList.count() > 0 && m_typeClassList.contains(typeClass.underlying()) == false))
{
DEBUG_LOG(m_logger) << participant.id() << " filtered out";
continue;
}
}
const QPointF relPartPos(PositionConverter::relativeFromGeographic(position.pos(), m_baseCoordinate));
double x = relCenterPos.x() - relPartPos.x();
double y = relCenterPos.y() - relPartPos.y();
int distanceSqr = qRound(x * x + y * y);
if (distanceSqr > rangeSqr)
{
DEBUG_LOG(m_logger) << "Participant NOT in range equipment number: " << participant.equipmentNumber().get()
<< " pos: " << relPartPos.x() << "/" << relPartPos.y()
<< " distance: " << std::sqrt(distanceSqr) << " distanceSqr: " << distanceSqr;
continue;
}
DEBUG_LOG(m_logger) << "Participant in range equipment number: " << participant.equipmentNumber().get()
<< " pos: " << relPartPos.x() << "/" << relPartPos.y()
<< " distance: " << std::sqrt(distanceSqr) << " distanceSqr: " << distanceSqr;
curParticipantsInRange.insertMulti(distanceSqr, participant.id());
}
}
m_participantOverloaded = quint32(curParticipantsInRange.count()) > Settings::maxParticipantsInSituation();
// only the hundred nearest will be transmitted
unsigned int cnt = 0;
for (const int id : curParticipantsInRange)
{
handleParticipant(m_allParticipants.value(id));
curParticipants.insert(id);
DEBUG_LOG(m_logger) << "Added participant to hundred nearest: " << m_participantId2EqId[id]
<< " / " << id;
++cnt;
if (cnt >= Settings::maxParticipantsInSituation())
break;
}
// insert participants, that are now out of range/outdated
for (const auto &oldPart : oldParticipants)
{
const int id = oldPart.id();
if (!curParticipants.contains(id))
{
if (!m_outdatedParticipants.contains(id))
{
m_newlyOutdatedParticipants.insert(id);
m_outdatedParticipants.insert(id);
DEBUG_LOG(m_logger) << "Participant outdated: " << m_participantId2EqId[id]
<< " / " << id;
}
}
}
}
void HandlerOcSituationData::handleParticipant(const ParticipantManagement::Participant &participant)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
int participantId = participant.id();
if (participant.equipmentNumber().is_set())
m_participantId2EqId[participantId] = participant.equipmentNumber().get();
else
m_participantId2EqId.remove(participantId);
// now check if is a new participant
if (!m_participants.contains(participantId))
{
DEBUG_LOG(m_logger) << "Adding previously unknown participant to m_participants: " << m_participantId2EqId.value(participantId)
<< " / " << participantId;
// new participant -> all modified
setModified(participantId, OcSituation::ModificationFlag::eAll);
m_participants.insert(participantId, participant);
m_dynamicParticpantData.insert(participantId, m_allDynamicParticpantData.value(participantId));
m_participantPositions.insert(participantId, m_allParticipantPositions.value(participantId));
return;
}
else
{
// old participant, check if was outdated
if (m_outdatedParticipants.remove(participantId))
{
DEBUG_LOG(m_logger) << "Adding old player that is inside the radius again: " << m_participantId2EqId.value(participantId)
<< " / " << participantId;
if (m_modifiedFlags[participantId] == OcSituation::ModificationFlag::eNone)
setModified(participantId, OcSituation::ModificationFlag::ePosition2D);
}
}
}
static inline bool sortParticipantsForRange(const QPair<int, float> &p1,
const QPair<int, float> &p2)
{
return p1.second < p2.second;
}
QVector<QPair<int, float>> HandlerOcSituationData::createPrioQueue() const
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QVector<QPair<int, float>> participantsPrioQueue;
participantsPrioQueue.reserve(m_participantsToSend.size());
for (auto participantId : qAsConst(m_participantsToSend))
{
// ignore (currently) outdated players
if (m_outdatedParticipants.contains(participantId))
continue;
if (!m_participants.contains(participantId))
continue;
const ParticipantManagement::ParticipantPosition &position = m_participantPositions[participantId];
float x = m_centerPosition.lon() - position.pos().lon();
float y = m_centerPosition.lat() - position.pos().lon();
float r2 = x * x + y * y;
float prio = r2;
int cntNotSendDbg = 0;
QMap<int, int>::const_iterator participantIt = m_participantsNotSend.constFind(participantId);
if (participantIt != m_participantsNotSend.constEnd())
{
cntNotSendDbg = participantIt.value();
// Je nicht gesendeten -> 5 % abziehen, aber nie alles
const int cntNotSend = participantIt.value() > 20 ? 19 : participantIt.value();
DEBUG_LOG(m_logger) << "prio: " << prio << ", prio / 20 * cntNotSend: " << prio / 20 * cntNotSend;
prio -= (prio / 20 * cntNotSend);
}
participantsPrioQueue.push_back(qMakePair(participantId, prio));
DEBUG_LOG(m_logger) << "Radius from center for: " << m_participantId2EqId[participantId]
<< " / " << participantId << " = " << sqrt(r2) << "m, prio: " << prio
<< ", not sent: " << cntNotSendDbg << " time(s)";
}
std::sort(participantsPrioQueue.begin(), participantsPrioQueue.end(), sortParticipantsForRange);
return participantsPrioQueue;
}
QByteArray HandlerOcSituationData::createDataStream(int maxSize,
const QVector<QPair<int, float>> &participantsPrioQueue)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
QByteArray ret;
// do not send to much in one swoop
if (maxSize > 100)
maxSize -= 5;
// first: participants that are not in range any more
DEBUG_LOG(m_logger) << "m_newlyOutdatedParticipants: " << m_newlyOutdatedParticipants.size();
QSet<int> copyOfOutdated = m_newlyOutdatedParticipants;
for (const auto participantId : qAsConst(m_newlyOutdatedParticipants))
{
addParticipantId(ret, participantId, OcSituation::ModificationFlag::eNone);
copyOfOutdated.remove(participantId);
if (ret.size() < maxSize)
continue;
QByteArray ba = m_bWithCompression ? qCompress(ret) : ret;
DEBUG_LOG(m_logger) << "Added info about oudated player " << m_participantId2EqId[participantId]
<< " / " << participantId
<< " data size: " << ret.size()
<< " compressed size: " << ba.size();
if (ba.size() > maxSize)
{
m_newlyOutdatedParticipants = copyOfOutdated;
TRACE_LOG(m_logger) << "Uncompressed Data: " << ret.toHex(' ');
return ba;
}
}
m_newlyOutdatedParticipants = copyOfOutdated;
// Data updates
DEBUG_LOG(m_logger) << "participantsPrioQueue: " << participantsPrioQueue.size();
for (auto prioPair : participantsPrioQueue)
{
int participantId = prioPair.first;
if (!m_modifiedFlags.contains(participantId))
continue;
OcSituation::ModificationBits modifiedFlags = m_modifiedFlags[participantId];
// should never happen
if (modifiedFlags == OcSituation::ModificationFlag::eNone)
continue;
const ParticipantManagement::Participant &data = m_participants[participantId];
const ParticipantManagement::DynamicParticipantData &dynamicParticipantData = m_dynamicParticpantData[participantId];
const ParticipantManagement::ParticipantPosition &participantPosition = m_participantPositions[participantId];
// default DynamicParticipantData crashes the tablet software. we skip it ... for now
if (dynamicParticipantData == ParticipantManagement::DynamicParticipantData())
{
INFO_LOG(m_logger) << "There is only default DynamicParticipantData for participant with id " << participantId << " --> skipping";
continue;
}
addParticipantId(ret, participantId, modifiedFlags);
addPosition2D(ret, participantPosition, (modifiedFlags & OcSituation::ModificationFlag::ePosition2D) == OcSituation::ModificationFlag::ePosition2D);
addPosition3D(ret, participantPosition, (modifiedFlags & OcSituation::ModificationFlag::ePosition3D) == OcSituation::ModificationFlag::ePosition3D);
addVBG(ret, dynamicParticipantData, (modifiedFlags & OcSituation::ModificationFlag::eVBG) == OcSituation::ModificationFlag::eVBG);
addState(ret, dynamicParticipantData, (modifiedFlags & OcSituation::ModificationFlag::eState) == OcSituation::ModificationFlag:: eState);
addStructure(ret, data, (modifiedFlags & OcSituation::ModificationFlag::eStructure) == OcSituation::ModificationFlag::eStructure);
addDesignator(ret, data, (modifiedFlags & OcSituation::ModificationFlag::eDesignator) == OcSituation::ModificationFlag::eDesignator);
addSymbolSidc(ret, data, (modifiedFlags & OcSituation::ModificationFlag::eSymbol) == OcSituation::ModificationFlag::eSymbol);
clearModified(participantId);
if (ret.size() < maxSize)
continue;
QByteArray ba = m_bWithCompression ? qCompress(ret) : ret;
DEBUG_LOG(m_logger) << "Added info about changed player " << m_participantId2EqId[participantId]
<< " / " << participantId
<< " bits: 0x" << QString::number(modifiedFlags, 16)
<< " data size: " << ret.size()
<< " compressed: " << ba.size();
if (ba.size() > maxSize)
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << ret.toHex(' ');
return ba;
}
}
if (!ret.isEmpty())
{
DEBUG_LOG(m_logger) << "Uncompressed Data: " << ret.toHex(' ');
return m_bWithCompression ? qCompress(ret) : ret;
}
else
return ret;
}
void HandlerOcSituationData::updatePrioOfNotSendParticipants()
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
// add a prio bonus to all participants not send this cycle
QMap<int, OcSituation::ModificationBits>::const_iterator it = m_modifiedFlags.constBegin();
for (; it != m_modifiedFlags.constEnd(); ++it)
{
const int participantId = it.key();
QMap<int, int>::iterator participantIt = m_participantsNotSend.find(participantId);
int notSentSince = 0;
if (participantIt == m_participantsNotSend.end())
{
m_participantsNotSend.insert(participantId, 1);
notSentSince = 1;
}
else
{
participantIt.value()++;
notSentSince = participantIt.value();
}
DEBUG_LOG(m_logger) << "Player " << m_participantId2EqId[participantId]
<< " / " << participantId << " not sent for " << notSentSince << " time(s)";
}
}
void HandlerOcSituationData::setModified(int participantId, OcSituation::ModificationBits bits)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
DEBUG_LOG(m_logger) << "setModified " << bits << " for: " << m_participantId2EqId[participantId]
<< " / " << participantId;
m_participantsToSend.insert(participantId);
if (!m_modifiedFlags.contains(participantId))
{
// new entry
m_modifiedFlags.insert(participantId, bits);
return;
}
m_modifiedFlags[participantId] |= bits;
}
void HandlerOcSituationData::clearModified(int participantId)
{
TRACE_LOG(m_logger) << "Thread " << QThread::currentThreadId() << " function: " << LOGFUNCTION;
DEBUG_LOG(m_logger) << "clearModified for: " << m_participantId2EqId[participantId]
<< " / " << participantId;
m_modifiedFlags.remove(participantId);
m_participantsNotSend.remove(participantId);
m_participantsToSend.remove(participantId);
}
void HandlerOcSituationData::addParticipantId(QByteArray &buf, const int participantId, OcSituation::ModificationBits modifiedFlags) const
{
LtsUtils::set2Byte(participantId, buf);
LtsUtils::set1Byte(modifiedFlags, buf);
}
void HandlerOcSituationData::addPosition2D(QByteArray &buf, const ParticipantManagement::ParticipantPosition &data, bool bAdd) const
{
if (!bAdd)
return;
LtsUtils::set8Byte(data.pos().lon(), buf);
LtsUtils::set8Byte(data.pos().lat(), buf);
// no timestamp, as it is set in the client anyway
}
void HandlerOcSituationData::addPosition3D(QByteArray &buf, const ParticipantManagement::ParticipantPosition &data, bool bAdd) const
{
if (!bAdd)
return;
addPosition2D(buf, data, bAdd);
LtsUtils::set8Byte(data.pos().z(), buf);
LtsUtils::set2Byte(data.buildingId(), buf);
LtsUtils::set4Byte(data.roomId(), buf);
}
void HandlerOcSituationData::addVBG(QByteArray &buf, const ParticipantManagement::DynamicParticipantData &data, bool bAdd) const
{
if (!bAdd)
return;
const ParticipantManagement::StateInformation &youngestStateInformation = ParticipantManagement::youngestStateInformation(data.stateHistory(), true, false);
LtsUtils::set1Byte(youngestStateInformation.vulnerabilityState().underlying(), buf);
LtsUtils::set4Byte(youngestStateInformation.vulnerabilityKindId(), buf);
LtsUtils::set2Byte(youngestStateInformation.vulnerabilityBits(), buf);
int injuryPartyId = 1;
switch (youngestStateInformation.originatorType().underlying())
{
case ProjectDataManagement::StateChangeOriginator_def::Unknown:
case ProjectDataManagement::StateChangeOriginator_def::ExerciseControl:
case ProjectDataManagement::StateChangeOriginator_def::ObserverController:
case ProjectDataManagement::StateChangeOriginator_def::Initialization:
injuryPartyId = 1; // O/C
break;
case ProjectDataManagement::StateChangeOriginator_def::Participant:
injuryPartyId = m_allParticipants.value(youngestStateInformation.originatorId()).partyId();
if (injuryPartyId == 0)
injuryPartyId = 1;
break;
case ProjectDataManagement::StateChangeOriginator_def::AreaEffectSimulation:
case ProjectDataManagement::StateChangeOriginator_def::FunctionalArea:
{
auto it = m_aesId2PartyId.constFind(youngestStateInformation.originatorId());
if (it != m_aesId2PartyId.constEnd())
injuryPartyId = it.value();
break;
}
case ProjectDataManagement::StateChangeOriginator_def::DusMine:
{
int partyId = m_aesDtc->partyForDusMine(youngestStateInformation.originatorId());
if (partyId >= 0)
injuryPartyId = partyId;
if (partyId == -1)
m_participantWithMaybeMissingPartyId.insert(data.participantId());
else
m_participantWithMaybeMissingPartyId.remove(data.participantId());
break;
}
case ProjectDataManagement::StateChangeOriginator_def::ImprovisedExplosiveDevice:
{
const auto ied = m_gimDtc->iedForEquipmentNumber(youngestStateInformation.originatorId());
if (ied.equipmentNumber() != 0)
injuryPartyId = ied.partyId();
break;
}
case ProjectDataManagement::StateChangeOriginator_def::WoundTreatmentDevice:
case ProjectDataManagement::StateChangeOriginator_def::MaintainanceDevice:
case ProjectDataManagement::StateChangeOriginator_def::Sanitation:
case ProjectDataManagement::StateChangeOriginator_def::MedicalTimeout:
// not yet used
break;
}
LtsUtils::set1Byte(injuryPartyId, buf);
if (m_bWithVBGTime)
LtsUtils::set4Byte(youngestStateInformation.stateTime() / 1000, buf);
//add vitality and sanitation result
// Quick fix: If vitality is
uint8_t vitRes = (youngestStateInformation.vitality() / 10);
if (vitRes > 10)
{
WARNING_LOG(m_logger) << "Vitality of Participant " << data.participantId() << " is not between 0 and 100%. Putting to 100%";
vitRes = 10;
}
vitRes += ((int)youngestStateInformation.sanitationResult().underlying() << 4);
LtsUtils::set1Byte(vitRes, buf);
//finall 1 Byte for originator and maybe more
LtsUtils::set1Byte(youngestStateInformation.originatorType().underlying(), buf);
}
void HandlerOcSituationData::addState(QByteArray &buf, const ParticipantManagement::DynamicParticipantData &data, bool bAdd) const
{
if (!bAdd)
return;
LtsUtils::set1Byte(getEquipmentStateBits(data), buf);
}
void HandlerOcSituationData::addStructure(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const
{
if (!bAdd)
return;
LtsUtils::set2Byte(data.equipmentNumber().is_set() ? data.equipmentNumber().get() : 0, buf);
LtsUtils::set2Byte(data.typeId(), buf);
LtsUtils::set2Byte(data.partyId(), buf);
#ifndef GUEZ_MODE
LtsUtils::set2Byte(data.superiorId(), buf);
#endif
}
void HandlerOcSituationData::addDesignator(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const
{
if (!bAdd)
return;
const size_t len1 = std::min(data.designator().size(), size_t(15));
const size_t len2 = std::min(data.tacticalIdentifier().size(), size_t(15));
unsigned char len = ((len1 & 0x0F) | ((len2 & 0x0F) << 4));
LtsUtils::set1Byte(len, buf);
buf.append(data.designator().c_str(), len1);
buf.append(data.tacticalIdentifier().c_str(), len2);
}
void HandlerOcSituationData::addSymbolSidc(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const
{
if (!bAdd)
return;
const auto type = m_ppmDtc->type(data.typeId());
if (type.symbol() == data.symbol())
{
LtsUtils::set1Byte(0, buf); // 0 bytes -> marker for not sending the symbol sidc since it can be taken from the type
return;
}
if (data.symbol().size() > 255)
return;
const size_t len1 = std::min(data.symbol().size(), size_t(255));
LtsUtils::set1Byte(len1, buf);
buf.append(data.symbol().c_str(), len1);
}
} // namespace OcSituationServer
+197
View File
@@ -0,0 +1,197 @@
/**
* Handler for all oc situation data at the server per client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 27-02-2018
*/
#pragma once
#include <QMap>
#include <QMutex>
#include <QObject>
#include <QPair>
#include <QSet>
#include <QTimer>
#include <QThread>
#include <QVector>
#include <AreaEffectSimulation/aes_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include "main_handler.h"
#include "position_converter.h"
#include "oc_situation.h"
#include "logger.h"
namespace OcSituationServer
{
class HandlerOcSituationData;
class ThreadOcSituationData : public QThread
{
Q_OBJECT
public:
explicit ThreadOcSituationData(MainHandler *mainHandler,
int equipmentId,
int centerEquipmentId,
int radius,
bool filterActive,
QSet<int> partys,
QSet<ParticipantManagement::TypeClass::type> typeClasses);
public slots:
void requestOcSituationData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &position,
int radius,
bool filterActive,
QSet<int> partyList,
QSet<ParticipantManagement::TypeClass::type> typeClassList,
bool initial);
signals:
void sendOcData(const TrainingAreaBase::SendOcDataRequest &);
protected:
virtual void run();
private:
OcSituation::SituationCenterType m_centerType = OcSituation::SituationCenterType::Equipment; // which kind of center is used for the situation
Core::GeoPosition2D m_position; // center position a situation can be requested around
LtsCoordTrans::UtmCoordinate m_baseCoordinate;
int m_equipmentId{0}; // id from requesting OC-laptop
int m_centerEquipmentId{0}; // equipment in the center of situation
int m_radius{0}; // observer radius in meter
QSet<int> m_partyList;
bool m_filterActive;
QSet<ParticipantManagement::TypeClass::type> m_typeClassList;
MainHandler *m_mainHandler{nullptr}; // provides TAB FTC and PPM DTC
HandlerOcSituationData *m_handler{nullptr}; // OC-Thread-Object
QMutex m_accessMutex; // Mutex-Object
LOGGER_T &m_logger; // Logger-Object
};
class HandlerOcSituationData : public QObject
{
Q_OBJECT
public:
explicit HandlerOcSituationData(const std::shared_ptr<DdsHandler> &dds,
const LtsCoordTrans::UtmCoordinate &baseCoordinate,
int equipmentId);
bool withCompression() const { return m_bWithCompression; }
void setWithCompression(bool bOn) { m_bWithCompression = bOn; }
bool withVBGTime() const { return m_bWithVBGTime; }
void setWithVBGTime(bool bOn) { m_bWithVBGTime = bOn; }
private:
void fetchParticipantsInRange();
void handleParticipant(const ParticipantManagement::Participant &participant);
QVector<QPair<int, float>> createPrioQueue() const;
QByteArray createDataStream(int maxSize, const QVector<QPair<int, float>> &participantsPrioQueue);
void updatePrioOfNotSendParticipants();
void setModified(int participantId, OcSituation::ModificationBits bits);
void clearModified(int participantId);
// Building Byte Stream
void addParticipantId(QByteArray &buf, const int participantId, OcSituation::ModificationBits modifiedFlags) const;
void addPosition2D(QByteArray &buf, const ParticipantManagement::ParticipantPosition &data, bool bAdd) const;
void addPosition3D(QByteArray &buf, const ParticipantManagement::ParticipantPosition &data, bool bAdd) const;
void addVBG(QByteArray &buf, const ParticipantManagement::DynamicParticipantData &data, bool bAdd) const;
void addState(QByteArray &buf, const ParticipantManagement::DynamicParticipantData &data, bool bAdd) const;
void addStructure(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const;
void addDesignator(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const;
void addSymbolSidc(QByteArray &buf, const ParticipantManagement::Participant &data, bool bAdd) const;
public slots:
void setRequestOcSituationData(OcSituation::SituationCenterType centerType,
int centerEquipmentId,
const Core::GeoPosition2D &centerPosition,
int radius, bool filterActive,
QSet<int> partys,
QSet<ParticipantManagement::TypeClass::type> typeClasses,
bool initial);
private slots:
void onPartitionTimeChanged(qint64 partitionTime) { m_partitionTime = partitionTime; }
void onParticipantChanged(const ParticipantManagement::Participant &data, DataModification modType);
void onDynamicParticipantDataChanged(const ParticipantManagement::DynamicParticipantData &data, DataModification modType);
void onParticipantPositionChanged(const ParticipantManagement::ParticipantPosition &data, DataModification modType);
void onAesItemViewChanged(const AreaEffectSimulation::AesItemView &data, DataModification modType);
void onDusMineStateChanged(const AreaEffectSimulation::DusMineState &data, DataModification modType);
void handleSituation();
void resetAliveTimer();
void aliveTimeout();
void onResetInternalData();
signals:
void sendOcData(const TrainingAreaBase::SendOcDataRequest &);
void resetInternalData();
private:
LtsCoordTrans::UtmCoordinate m_baseCoordinate;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<GuezIedManagement::Interface::Dtc> m_gimDtc;
// request data
OcSituation::SituationCenterType m_centerType{OcSituation::SituationCenterType::Equipment}; // which kind of center is used for the situation
Core::GeoPosition2D m_centerPosition; // center position a situation can be requested around
int m_equipmentId{0}; // id from requesting OC-laptop
int m_centerEquipmentId{0}; // equipment in the center of situation
int m_centerParticipantId{0};
int m_radius{0}; // observer radius in meter
int m_retryCount{0};
bool m_participantOverloaded{false};
// internal participant data
QHash<int, ParticipantManagement::Participant> m_participants;
QHash<int, ParticipantManagement::DynamicParticipantData> m_dynamicParticpantData;
QHash<int, ParticipantManagement::ParticipantPosition> m_participantPositions;
QHash<int, ParticipantManagement::Participant> m_allParticipants;
QHash<int, ParticipantManagement::DynamicParticipantData> m_allDynamicParticpantData;
QHash<int, ParticipantManagement::ParticipantPosition> m_allParticipantPositions;
mutable QSet<int> m_participantWithMaybeMissingPartyId; // <participantId> originatorType==DusMine
QHash<int, int> m_aesId2PartyId;
// situation management data
QMap<int, OcSituation::ModificationBits> m_modifiedFlags;
QSet<int> m_participantsToSend;
QSet<int> m_outdatedParticipants;
QSet<int> m_newlyOutdatedParticipants;
QMap<int, int> m_participantsNotSend; // map <particpant Id, count of not send>
QMap<int, int> m_participantId2EqId; // for debugging purposes
QSet<ParticipantManagement::TypeClass::type> m_typeClassList;
QSet<int> m_partyList;
bool m_filterActive;
// options, must be in sync with osc
bool m_bWithCompression{true};
bool m_bWithVBGTime{true};
bool m_bInitialDataTransfer{true};
qint64 m_partitionTime;
// thread management
QMutex m_accessMutex; // Mutex-Object
QTimer *m_aliveTimer{nullptr}; // alive timer
QTimer *m_txTimer{nullptr}; // tx cycle timer
LOGGER_T &m_logger;
};
} // namespace OcSituationServer
+90
View File
@@ -0,0 +1,90 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: OcSituationServer
###########################################################
#log4j.logger.OSS=DEBUG
#log4j.logger.OSS.main=DEBUG
#log4j.logger.OSS.DdsHandler=DEBUG
#log4j.logger.OSS.MainHandler=DEBUG
#log4j.logger.OSS.HandlerOcAesData=DEBUG
#log4j.logger.OSS.HandlerOcData=INFO
#log4j.logger.OSS.HandlerOcSituationData=INFO
#log4j.logger.OSS.ThreadOcSituationData=INFO
#log4j.logger.OSS.HandlerOcSituationData.2501=INFO
#log4j.logger.OSS.ThreadOcSituationData.2501=INFO
#log4j.logger.OSS.PositionConverter=DEBUG
#log4j.logger.OSS.HandlerOcCentralAesData=DEBUG
#log4j.logger.OSS.ThreadOcCentralAesData=DEBUG
#log4j.logger.OSS.CentralAesDataQueue=DEBUG
#
# Lts::RollingFileAppender
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
+37
View File
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace OcSituationServer
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "OSS.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace OcSituationServer
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "oss_export.h"
namespace OcSituationServer
{
using LOGGER_T = NGL::INGLLogger &;
OSS_COMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace OcSituationServer
+42
View File
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* main for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include "main_handler.h"
#include "logger.h"
#include "settings.h"
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
LtsCommandLineParser::LtsCommandLineParser options;
if (!options.init())
return -1;
app.createLogger("OSS", options.exerciseId());
OcSituationServer::Settings::setIniFile(options.iniFile());
int iRet = -3;
{
std::unique_ptr<OcSituationServer::MainHandler> mainHandler(new OcSituationServer::MainHandler);
if (!mainHandler->init(options.domainId(), options.exerciseId()))
return iRet;
STARTUP_LOG(app.baseLogger());
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
+138
View File
@@ -0,0 +1,138 @@
/**
* MainHandler for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 14-02-2018
*/
#include "main_handler.h"
#include <DDS/Core/pacemaker.h>
#include "handler_oc_data.h"
#include "position_converter.h"
#include "settings.h"
namespace OcSituationServer
{
MainHandler::MainHandler(QObject *parent)
: QObject(parent)
, m_exerciseId(0)
, m_ddsHandler(nullptr)
, m_dbHandler(nullptr)
, m_handlerOcData(nullptr)
, m_positionConverter(nullptr)
, m_aesDtc(nullptr)
, m_evlDtc(nullptr)
, m_evlFtc(nullptr)
, m_gimDtc(nullptr)
, m_msgFtc(nullptr)
, m_ppmDtc(nullptr)
, m_ppmFtc(nullptr)
, m_tabDtc(nullptr)
, m_tabFtc(nullptr)
, m_logger(getLogger("MainHandler"))
{}
MainHandler::~MainHandler()
{
m_handlerOcData = nullptr;
m_positionConverter = nullptr;
m_aesDtc = nullptr;
m_evlDtc = nullptr;
m_evlFtc = nullptr;
m_gimDtc = nullptr;
m_msgFtc = nullptr;
m_ppmDtc = nullptr;
m_ppmFtc = nullptr;
m_tabDtc = nullptr;
m_tabFtc = nullptr;
m_ddsHandler = nullptr;
m_dbHandler = nullptr;
}
bool MainHandler::init(int domainId, int exerciseId)
{
m_exerciseId = exerciseId;
// register TrainingAreaBase::SendOcDataRequest for signal/slot connection from OC-threads
qRegisterMetaType<ParticipantManagement::Participant>("ParticipantManagement::Participant");
qRegisterMetaType<ParticipantManagement::DynamicParticipantData>("ParticipantManagement::DynamicParticipantData");
qRegisterMetaType<ParticipantManagement::ParticipantPosition>("ParticipantManagement::ParticipantPosition");
qRegisterMetaType<TrainingAreaBase::SendOcDataRequest>("TrainingAreaBase::SendOcDataRequest");
qRegisterMetaType<DataModification>("DataModification");
// initialize database
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(exerciseId, Settings::settings()))
{
ERROR_LOG(m_logger) << "Initialisation dbHandler failed.";
return false;
}
DEBUG_LOG(m_logger) << "Initialisation dbHandler succesful.";
// initialize DDS
m_ddsHandler = std::make_shared<DdsHandler>(domainId, exerciseId);
if (!m_ddsHandler->init())
{
ERROR_LOG(m_logger) << "Initialisation ddsHandler failed.";
return false;
}
DEBUG_LOG(m_logger) << "Initialisation ddsHandler succesful.";
// create AES Dtc
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created AES Dtc";
// create EVL Dtc
m_evlDtc = Evaluations::Interface::Dtc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created EVL Dtc";
// create EVL Ftc
m_evlFtc = Evaluations::Interface::Ftc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created EVL Ftc";
// create GIM Dtc
m_gimDtc = GuezIedManagement::Interface::Dtc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created GIM Dtc";
// create MSG Ftc
m_msgFtc = Messaging::Interface::Ftc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created Msg Ftc";
// create PPM Dtc
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created PPM Dtc";
// create PPM Ftc
m_ppmFtc = ParticipantManagement::Interface::Ftc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created PPM Dtc";
// create TAB Dtc
m_tabDtc = TrainingAreaBase::Interface::Dtc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created TAB Dtc";
// create TAB Ftc
m_tabFtc = TrainingAreaBase::Interface::Ftc::create(m_ddsHandler->dds());
DEBUG_LOG(m_logger) << "Created TAB Ftc";
// create position converter
m_positionConverter = std::make_shared<PositionConverter>(this);
// create handler oc-data
m_handlerOcData = std::make_shared<HandlerOcData>(this, this);
if (!m_handlerOcData->init())
{
ERROR_LOG(m_logger) << "Initialisation HandlerOcData failed.";
return false;
}
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
return true;
}
} // namespace OcSituationServer
+85
View File
@@ -0,0 +1,85 @@
/**
* MainHandler for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 14-02-2018
*/
#pragma once
#include <QObject>
#include <memory>
#include <AreaEffectSimulation/aes_dtc.h>
#include <Evaluations/evl_dtc.h>
#include <Evaluations/evl_ftc.h>
#include <GuezIedManagement/gim_dtc.h>
#include <Messaging/msg_ftc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ParticipantManagement/ppm_ftc.h>
#include <TrainingAreaBase/tab_dtc.h>
#include <TrainingAreaBase/tab_ftc.h>
#include "dds_handler.h"
#include <ngldb/db_service_handler.h>
#include "oss_db.h"
#include "logger.h"
namespace NGLDB
{
template <typename DB> class DbServiceHandler;
}
namespace OcSituationServer
{
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class HandlerOcData;
class PositionConverter;
class MainHandler : public QObject
{
Q_OBJECT
public:
explicit MainHandler(QObject *parent = nullptr);
~MainHandler();
bool init(int domainId, int exerciseId);
std::shared_ptr<DdsHandler> dds() const { return m_ddsHandler; }
std::shared_ptr<DatabaseInterface> db() const { return m_dbHandler->db(); }
std::shared_ptr<PositionConverter> positionConverter() const { return m_positionConverter; }
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> aesDtc() const { return m_aesDtc; }
std::shared_ptr<Evaluations::Interface::Dtc> evlDtc() const { return m_evlDtc; }
std::shared_ptr<Evaluations::Interface::Ftc> evlFtc() const { return m_evlFtc; }
std::shared_ptr<GuezIedManagement::Interface::Dtc> gimDtc() const { return m_gimDtc; }
std::shared_ptr<Messaging::Interface::Ftc> msgFtc() const { return m_msgFtc; }
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtc() const { return m_ppmDtc; }
std::shared_ptr<ParticipantManagement::Interface::Ftc> ppmFtc() const { return m_ppmFtc; }
std::shared_ptr<TrainingAreaBase::Interface::Dtc> tabDtc() const { return m_tabDtc; }
std::shared_ptr<TrainingAreaBase::Interface::Ftc> tabFtc() const { return m_tabFtc; }
int exerciseId() const { return m_exerciseId; }
private:
int m_exerciseId;
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<HandlerOcData> m_handlerOcData;
std::shared_ptr<PositionConverter> m_positionConverter;
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<Evaluations::Interface::Dtc> m_evlDtc;
std::shared_ptr<Evaluations::Interface::Ftc> m_evlFtc;
std::shared_ptr<GuezIedManagement::Interface::Dtc> m_gimDtc;
std::shared_ptr<Messaging::Interface::Ftc> m_msgFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ParticipantManagement::Interface::Ftc> m_ppmFtc;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_tabDtc;
std::shared_ptr<TrainingAreaBase::Interface::Ftc> m_tabFtc;
NGL::INGLLogger &m_logger;
};
} // namespace OcSituationServer
+76
View File
@@ -0,0 +1,76 @@
/**
* Types to be used for transparent OC communication
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLSE, 2018-02-20
*/
#pragma once
namespace OcSituation
{
enum TelegramId
{
DemandSituation = 131,
SituationData = 132,
StateInformation = 133,
SituationDataInitial = 134,
CentralAesData = 135,
CentralAesDataInitial = 136,
DemandEvaluationSchema = 111,
EvaluationSchema = 148,
Evaluation = 112,
Message = 130,
DemandAesItems = 114,
AesItems = 166,
EquipmentErrorRegistration = 113,
EquipmentRegistration = 115,
SingleMine = 117,
MineClearancePositions = 167,
MineClearanceArea = 168
};
enum CentralAesChangeType
{
AddMod = 1,
Remove = 2
};
enum ModificationFlag
{
eNone = 0, // == Player is out of range -> do not display
ePosition2D = (1 << 0), // x,y,sampleTime
ePosition3D = (1 << 1), // x,y,z,sampleTime
eVBG = (1 << 2), // injuryState, partei_zustand_aenderung, sampleTime
eState = (1 << 3), // bits, totalStateInformationBits
eStructure = (1 << 4), // equipmentNumber, typeId, partyId
eDesignator = (1 << 5), // designator, tacticalIdentifier
eSymbol = (1 << 6), // SIDC string (20Bytes)
eAll = (ePosition3D | eVBG | eState | eStructure | eDesignator | eSymbol)
};
typedef int ModificationBits;
enum AesType
{
Ied = 1,
IedPositions = 2,
ExplosionSingleSMart = 11,
ExplosionAreaSMart = 12,
DusMineArea = 13,
DangerArea = 14,
};
enum SituationCenterType
{
Equipment = 1,
Position = 2
};
} // namespace OcSituation
+52
View File
@@ -0,0 +1,52 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-04
*/
#pragma once
#include <QtCore/qglobal.h>
#ifndef OSS_COMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define OSS_COMMON_EXPORT
# else
# if defined(MAKE_SVCOSSCOMMON_LIB)
# define OSS_COMMON_EXPORT Q_DECL_EXPORT
# ifndef WIN32
# ifdef NDDSUSERDllExport
# undef NDDSUSERDllExport
# endif
# define NDDSUSERDllExport Q_DECL_EXPORT
# endif
# else
# define OSS_COMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef OSS_CLIENT_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define OSS_CLIENT_EXPORT
# else
# if defined(MAKE_SVCOSSCLIENT_LIB)
# define OSS_CLIENT_EXPORT Q_DECL_EXPORT
# else
# define OSS_CLIENT_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef OSS_SERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define OSS_SERVER_EXPORT
# else
# if defined(MAKE_SVCOSSSERVER_LIB)
# define OSS_SERVER_EXPORT Q_DECL_EXPORT
# else
# define OSS_SERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
+5
View File
@@ -0,0 +1,5 @@
-- Initial reset counter an creation
-- IED reset counter
INSERT INTO oss_reset_counter (aes_type_id, reset_counter) VALUES (1, 0);
-- Danger area reset counter
INSERT INTO oss_reset_counter (aes_type_id, reset_counter) VALUES (14, 0);
+53
View File
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<service namespace="OcSituationServer" abbreviation="OSS" procNames="OcSituationServer" exerciseDependency="Exercise" groups="ExerciseExecution,ExercisePaused" prio="">
<rcs>
<Id Id="$Id: ea_export_script.txt 41311 2018-04-23 09:43:45Z ehrlicher $"/>
<HeadURL HeadURL="$HeadURL: svn://slssvn/TDS/branches/product/devel/trunk/Lts/Common/CodeGen/ea_export_script.txt $"/>
<Author Author="$Author: ehrlicher $"/>
</rcs>
<tables>
<table name="oss_reset_counter">
<column name="aes_type_id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="reset_counter" datatype="integer"/>
</table>
</tables>
<views/>
<dbServiceStructs>
<serviceStruct name="OssResetCounter" table="oss_reset_counter"/>
<table name="oss_reset_counter"/>
</dbServiceStructs>
<topicTypes>
<topicType name="EquipmentErrorRegistration" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="0" recordTopic="1" baseValueType="Core::BaseEntity">
<column name="equipmentNumber" idlType="long"/>
<column name="sampleTime" idlType="long long"/>
<column name="participant" idlType="string"/>
<column name="device" idlType="string"/>
<column name="text" idlType="string"/>
</topicType>
<topicType name="EquipmentErrorRequest">
<column name="requestId" idlType="long" isKey="1"/>
<column name="equipmentNumber" idlType="long"/>
<column name="device" idlType="string"/>
<column name="text" idlType="string"/>
</topicType>
<topicType name="SendEquipmentMessageRequest">
<column name="requestId" idlType="long" isKey="1"/>
<column name="equipmentNumbers" idlType="sequence&lt;long&gt;"/>
<column name="sender" idlType="long"/>
<column name="senderType" idlType="Messaging::AddressType"/>
<column name="subject" idlType="string"/>
<column name="message" idlType="string"/>
</topicType>
</topicTypes>
<functionTopics>
<functionTopic name="EquipmentError" requestTopicType="EquipmentErrorRequest" replyTopicType="Core::FunctionReply" requestTopicName="EquipmentErrorRequest" replyTopicName="EquipmentErrorReply" routeInReplay="0"/>
<functionTopic name="SendEquipmentMessageOc" requestTopicType="SendEquipmentMessageRequest" replyTopicType="Core::FunctionReply" requestTopicName="SendEquipmentMessageOcRequest" replyTopicName="SendEquipmentMessageOcReply" routeInReplay="0"/>
</functionTopics>
<enumerations>
<enumeration name="DummyEnum">
<attribute key="dummy" value="0"/>
</enumeration>
</enumerations>
</service>
+82
View File
@@ -0,0 +1,82 @@
/**
* Converting positions relative to the training area
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 21-04-2018
*/
#include "position_converter.h"
#include <AssetManagement/asm_dtc.h>
#include <ExerciseManagement/exm_dtc.h>
#include <QMutex>
namespace OcSituationServer
{
PositionConverter::PositionConverter(MainHandler *m_mainHandler)
: m_mainHandler(m_mainHandler)
, m_baseCoord(LtsCoordTrans::UtmCoordinate())
, m_asmDtc(AssetManagement::Interface::Dtc::create(m_mainHandler->dds()->dds()))
, m_exmDtc(ExerciseManagement::Interface::Dtc::create(m_mainHandler->dds()->dds()))
, m_logger(getLogger("PositionConverter"))
{
m_exmDtc->enableExercise();
m_asmDtc->enableTrainingArea();
}
LtsCoordTrans::UtmCoordinate PositionConverter::baseCoordinate()
{
initCoordConversion();
return m_baseCoord;
}
bool PositionConverter::initCoordConversion()
{
static QMutex mutex;
QMutexLocker l(&mutex);
if (!m_exmDtc)
return true;
if (!m_exmDtc->waitForHistoricalDataExercise(15000))
{
ERROR_LOG(m_logger) << "Error initializing ExerciseManagement DTC";
return false;
}
const auto ex = m_exmDtc->exercise(m_mainHandler->exerciseId());
if (ex.id() <= 0)
{
ERROR_LOG(m_logger) << "Can not retrieve exercise information for id " << m_mainHandler->exerciseId();
return false;
}
if (!m_asmDtc->waitForHistoricalDataTrainingArea(10000))
{
ERROR_LOG(m_logger) << "Error initializing AssetManagement DTC";
return false;
}
const auto ta = m_asmDtc->trainingArea(ex.trainingAreaId());
if (ta.id() <= 0)
{
ERROR_LOG(m_logger) << "Can not retrieve training are information for id " << ex.trainingAreaId();
return false;
}
// calc tetra base point from training area center
const LtsCoordTrans::UtmCoordinate bottomLeft(LtsCoordTrans::UtmCoordinate::fromGeographic(ta.bottomLeft().lon(), ta.bottomLeft().lat()));
const QPointF mid(bottomLeft.geographic(ta.width() / 2, ta.height() / 2));
// our center point is NNxYY 00000 00000 by definition
// (we can also use current ta center but the it's hard to debug the coordinates in Tetra LogViewer)
const QString mgrs(LtsCoordTrans::mgrsFromGeographic(mid.x(), mid.y(), -1) + QStringLiteral("00000000000000"));
// now we've the correct tetra base point
m_baseCoord = LtsCoordTrans::UtmCoordinate::fromMgrs(mgrs);
m_asmDtc.reset();
m_exmDtc.reset();
return true;
}
} // namespace OcSituationServer
+63
View File
@@ -0,0 +1,63 @@
/**
* Converting positions relative to the training area
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 21-04-2018
*/
#pragma once
#include <LtsCoordTrans/lts_coord_trans.h>
#include <Core/core.hpp>
#include "main_handler.h"
#include "logger.h"
namespace AssetManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ExerciseManagement
{
namespace Interface
{
class Dtc;
}
}
namespace OcSituationServer
{
class DdsHandler;
class PositionConverter
{
public:
explicit PositionConverter(MainHandler *m_mainHandler);
LtsCoordTrans::UtmCoordinate baseCoordinate();
inline QPointF relativeFromGeographic(const Core::GeoPosition2D &pos)
{
return relativeFromGeographic(pos, baseCoordinate());
}
inline static QPointF relativeFromGeographic(const Core::GeoPosition2D &pos,
const LtsCoordTrans::UtmCoordinate &baseCoord)
{
// convert lon/lat to utm with the given zone from the center point
const LtsCoordTrans::UtmCoordinate point(LtsCoordTrans::UtmCoordinate::fromGeographic(pos.lon(), pos.lat(), baseCoord.zone));
// now calc distance from point to base point == relative position
return QPointF(point.x - baseCoord.x, point.y - baseCoord.y);
}
private:
bool initCoordConversion();
MainHandler *m_mainHandler;
LtsCoordTrans::UtmCoordinate m_baseCoord;
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
LOGGER_T &m_logger;
};
} // namespace OcSituationServer
+82
View File
@@ -0,0 +1,82 @@
/**
* Settings for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
*/
#include "settings.h"
#include <QtCore/QSettings>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
namespace OcSituationServer
{
class NGLSettingsClass : public NGL::Settings
{
public:
NGLSettingsClass()
: NGL::Settings(s_iniFile.isEmpty() ? NGL::findProjectConfigFile() : s_iniFile)
{}
static void setIniFile(const QString &fn)
{
s_iniFile = fn;
}
static QString s_iniFile;
};
QString NGLSettingsClass::s_iniFile;
Q_GLOBAL_STATIC(NGLSettingsClass, s)
//-----------------------------------------------------------------------------
NGL::Settings *Settings::settings()
{
return s();
}
//-----------------------------------------------------------------------------
void Settings::setIniFile(const QString &ini)
{
NGLSettingsClass::setIniFile(ini);
}
quint32 Settings::aliveTimeout()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/AliveTimeout"), 5).toUInt() * 60 * 1000; // 5 minutes
}
quint32 Settings::txCycleTime()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/TxCycleTime"), 10).toUInt() * 1000; // 10 seconds
}
quint32 Settings::maxNumOfBytePerTxCycle()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/MaxNumOfBytePerTxCycle"), 166).toUInt();
}
quint32 Settings::maxParticipantsInSituation()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/MaxParticipantsInSituation"), 100).toUInt();
}
quint32 Settings::stateInformationCycleTime()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/StateInformationCycleTime"), 30).toInt() * 1000; // 30 seconds
}
qint32 Settings::maxNumOfBytePerAesTxCycle()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/MaxNumOfBytePerAesTxCycle"), 200).toInt();
}
quint32 Settings::aesDemandCollectionTime()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/AesDemandCollectionTime"), 10).toInt() * 1000; // 10 seconds
}
quint32 Settings::iedPositionCycleTime()
{
return settings()->settings()->value(QLatin1String("OcSituationParameter/IedPositionCycleTime"), 30).toInt() * 1000; // 30 seconds
}
} // namespace OcSituationServer
+35
View File
@@ -0,0 +1,35 @@
/**
* Settings for OcSituationServer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QString>
namespace NGL
{
class Settings;
}
namespace OcSituationServer
{
namespace Settings
{
NGL::Settings *settings();
void setIniFile(const QString &ini);
// OC situation parameter
quint32 aliveTimeout();
quint32 txCycleTime();
quint32 maxNumOfBytePerTxCycle();
quint32 maxParticipantsInSituation();
quint32 stateInformationCycleTime();
qint32 maxNumOfBytePerAesTxCycle();
quint32 aesDemandCollectionTime();
quint32 iedPositionCycleTime();
} // namespace Settings
} // namespace OcSituationServer