Files
qt5-legatus-legacy/Lts/Proxies/TrainingAreaBase/send_to_dds.cpp
T
2026-08-13 18:33:10 +02:00

1404 lines
53 KiB
C++
Executable File

/**
* Handle incoming ETS Tetra Telegrams and convert them to dds data topics
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-04-14
*/
#include "send_to_dds.h"
#include "main_handler.h"
#include "settings.h"
#include <DDS/DTS/generic_dts.h>
#include <QTimer>
namespace TrainingAreaBase
{
static constexpr const int s_PartyIdOffset = 1; // ICD: 1 - first Party, TDS internal: 2 - first party (1 is O/C)
SendToDds::SendToDds(MainHandler *mainHandler)
: m_mainHandler(mainHandler)
, m_dtsTab(mainHandler->ddsHandler()->dtsTab())
, m_aesStateHandler(m_mainHandler->aesHandler() ? mainHandler->aesHandler()->aesStateHandler() : nullptr)
, m_aesHandler(m_mainHandler->aesHandler())
, m_posHandler(mainHandler->posHandler())
, m_logger(getLogger("SendToDds"))
{}
template<typename S, typename T>
static inline void assignBase(const S &src, T &dest)
{
dest.equipmentNumber(src.header.equipmentNumber);
dest.sampleTime(src.header.timeStamp); // msec
}
void SendToDds::rxData(const TetraIcd::ManipulationReport &inData)
{
TrainingAreaBase::EquipmentOperationalState outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.isManipulation(inData.isManipulation);
outData.equipmentFunctionId(inData.equipmentFunctionId);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentOperationalState &inData)
{
TrainingAreaBase::EquipmentOperationalState outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.nbcProtectionActive(inData.nbcProtectionActive);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentConfigurationReport &inData)
{
TrainingAreaBase::EquipmentConfigurationReport outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.equipmentNumber(inData.equipmentNumber); // is doubled in the telegram
outData.canStartupTime(inData.canStartUpTime);
outData.equipmentType(inData.equipmentType);
// login parameter
outData.loginWaittimeBitweenTrys(inData.loginWaittimeBitweenTrys);
outData.loginMaxRetryCount(inData.loginMaxRetryCnt);
outData.loginMasteringFlag(inData.loginMasteringFlag);
// can device configuration
outData.canDeviceConfiguration(inData.canDeviceConfiguration);
// configuration voiceoutput/language
outData.displayDeviceVoiceOutput(inData.displayDeviceVoiceOutput);
outData.displayDeviceLanguage(static_cast<ProjectDataManagement::LanguageConfiguration::inner_enum>(inData.displayDeviceLanguage));
// pyrotechnics
outData.pyrotechnics(inData.pyrotechnics);
// configuration mine/handgrenade sensor
outData.mineHandgrenadeSensorMode(static_cast<TrainingAreaBase::MineHandgrenadeSensorMode::inner_enum>(inData.mhgrsMode));
outData.mineHandgrenadeSensorEnergySaving(inData.mhgrsEnergySaving);
// configuration special sensors
outData.firMode(static_cast<TrainingAreaBase::FirMode::inner_enum>(inData.specialSensorFir));
outData.minelayerMode(static_cast<TrainingAreaBase::MinelayerMode::inner_enum>(inData.specialSensorMinelayer));
outData.specialSensorStandard(static_cast<TrainingAreaBase::SpecialSensorStandardMode::inner_enum>(inData.specialSensorStandard));
// configuration agdus hw <- old stuff have to ask Christian Backeberg why its not deleted from the telegram
outData.agdusHwMode(static_cast<TrainingAreaBase::AgdusHwMode::inner_enum>(inData.agdusHwMode));
// position update intervall
outData.positionUpdateIntervall(static_cast<TrainingAreaBase::PositionUpdateIntervall::inner_enum>(inData.positionUpdateInterval));
// configuration display text
outData.displayTextMainGroup(inData.displayTextMainGroup);
outData.displayTextSubGroup(inData.displayTextSubGroup);
// vulnerability model
outData.vulnerabilityModelAgdus(inData.vulnerabilityModelAgdus);
outData.vulnerabilityModelMineIed(inData.vulnerabilityModelMineIed);
outData.vulnerabilityModelAreaFire(inData.vulnerabilityModelAreaFire);
outData.vulnerabilityModelOperatingResource(inData.vulnerabilityModelOperatingResource);
outData.vulnerabilityModelACWeaponry(inData.vulnerabilityModelAcWeaponry);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::CtaDataError &inData)
{
TrainingAreaBase::CtaDataError outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.reason(static_cast<ProjectDataManagement::DataErrorReason::inner_enum>(inData.reason));
outData.originTelegramId(inData.originTelegramId);
publish(outData);
}
template <typename S, typename T>
void setOptionalBuildingRoomId(S &in, const T &out)
{
if (out > 0)
in = out;
}
void SendToDds::rxData(const TetraIcd::PositionReport &inData)
{
TrainingAreaBase::EquipmentPosition outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if (!setPosition(outData, inData.geoPosition, inData.relPosition, inData.opsPosition, inData.header.timeStamp))
{
ctasErrorPosition(inData);
return;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::PositionReportWithMountedEquipments &inData)
{
TrainingAreaBase::EquipmentPosition outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if (!setPosition(outData, inData.geoPosition, inData.relPosition, inData.opsPosition, inData.header.timeStamp))
{
ctasErrorPosition(inData);
return;
}
// also set to zero if empty to be able to distinguish between position and position with participants
outData.mountedParticipants() = inData.mountedEquipments;
publish(outData);
}
void SendToDds::rxData(const TetraIcd::MineTriggered &inData)
{
TrainingAreaBase::MineTriggered outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.mineAreaId(inData.mineAreaId);
outData.mineIndex(inData.mineIndex);
// get Charge Type from mineAreaId, if 0 put it to 213:
if (inData.chargeTypeId != 0)
outData.chargeTypeId(inData.chargeTypeId);
if (inData.relPositionValid)
{
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
}
publish(outData);
}
template<typename S, typename T>
static inline void assignIedBaseData(const S &inData, T &outData)
{
assignBase(inData, outData);
outData.iedId(inData.iedId);
outData.tacticalState(static_cast<int>(inData.tacticalState));
outData.statePyrotechnics(inData.statePyrotechnics);
}
void SendToDds::rxData(const TetraIcd::DefinitionIed &inData)
{
TrainingAreaBase::DefinitionIed outData;
checkTimeStamp(inData);
assignIedBaseData(inData, outData);
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
outData.type(static_cast<int>(inData.iedType));
outData.remoteTrigger((inData.trigger & static_cast<int>(TetraIcd::DefinitionIed::TriggerTypes::Remote)) ? true : false);
outData.switchTrigger((inData.trigger & static_cast<int>(TetraIcd::DefinitionIed::TriggerTypes::Switch)) ? true : false);
outData.lightBarrierTrigger((inData.trigger & static_cast<int>(TetraIcd::DefinitionIed::TriggerTypes::LightBarrier)) ? true : false);
outData.tautWireTrigger((inData.trigger & static_cast<int>(TetraIcd::DefinitionIed::TriggerTypes::TautWire)) ? true : false);
outData.explosiveForce(inData.explosiveForce);
outData.pairingState(static_cast<int>(inData.pairingState));
if (inData.pairingState != TrainingAreaBase::TetraIcd::IedPairingState::NotPaired)
outData.pairedId(inData.pairedId);
if (inData.partyId != 0)
outData.partyId(inData.partyId + s_PartyIdOffset);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::StateIed &inData)
{
TrainingAreaBase::StateIed outData;
checkTimeStamp(inData);
assignIedBaseData(inData, outData);
outData.batteryWarnings(inData.batteryWarnings);
outData.errorStateElectronics(static_cast<int>(inData.errorStateElectronics));
outData.errorStatePyrotechnics1(static_cast<int>(inData.errorStatePyrotechnics1));
outData.errorStatePyrotechnics2(static_cast<int>(inData.errorStatePyrotechnics2));
outData.errorStatePyrotechnics3(static_cast<int>(inData.errorStatePyrotechnics3));
publish(outData);
}
void SendToDds::rxData(const TetraIcd::DusSingleMineState &inData)
{
TrainingAreaBase::DusSingleMineState outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.mineId(inData.mineId);
outData.effectStart(inData.effectStart);
setPosition2d(outData, inData.relPosition);
outData.chargeTypeId(inData.chargeTypeId);
outData.state(inData.state);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::StateChangeDirect &inData)
{
// Resolve Jira NLDEVCTRL-631 Gesundschiessen durch Laser Range Finder
// Es wird nur der Laser Range Code gefiltert.
if (Settings::laserRangeCode() == inData.chargeTypeId)
{
DEBUG_LOG(m_logger) << "laserRangeCode chargeTypeId=" << inData.chargeTypeId
<< "vulnerabilityState=" << static_cast<int>(inData.vulnerabilityState);
return;
}
TrainingAreaBase::EquipmentState outData;
checkTimeStamp(inData);
if (!assignStateChangeBase(inData, outData))
return;
const auto ct(pdm()->chargeTypeFromId(inData.chargeTypeId));
// Only set position if it is a direct firing - No Sekundaerwirkung (indirect)
if (!pdm()->isIndirect(inData.chargeTypeId))
if (!setPosition(outData, inData.geoPosition, inData.relPosition, inData.opsPosition, inData.header.timeStamp))
ctasErrorPosition(inData);
// ct.id() => available ChargeType (ULEIS Basic), inData.chargeTypeId => Original ChargeType
const auto gearClass(pdm()->gearClassFromChargeTypeId(inData.chargeTypeId));
// direct state changes from a minefield are triggered by real mines
// -> we must fake a MineTriggered telegram with a position
// otherwise AES must subscribe to StateChangeDirect which is somewhat stupid
if (gearClass == ProjectDataManagement::GearClass::MineSingle ||
gearClass == ProjectDataManagement::GearClass::MineArea)
{
TetraIcd::MineTriggered mt;
mt.header.timeStamp = inData.header.timeStamp;
if (outData.position().is_set())
{
const TrainingAreaBase::TabPosition pos = outData.position().get();
mt.relPosition = m_posHandler->geographicToRelative(pos.position());
mt.relPositionValid = true;
mt.chargeTypeId = ct.id();
mt.mineIndex = inData.originatorId;
rxData(mt);
}
outData.originatorType(ProjectDataManagement::StateChangeOriginator::DusMine);
}
else
{
// NOTE: igniterTypeId/IgniterFunction = 0 as long as inData does not contain values
if (pdm()->isIedChargeType({ct.id(),0,0,}))
outData.originatorType(ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice);
else // it's not an IED so it's either a participant or party. in case it's none it will fallback to participant and thus be negated later
{
if (m_mainHandler->ppmHandler()->getDtc()->participantIdByEquipmentNumber(inData.originatorId) <= 0) // returns -1 if not found!
{
if (m_mainHandler->ppmHandler()->getDtc()->party(inData.originatorId).id())
outData.originatorType(ProjectDataManagement::StateChangeOriginator::Party);
else
outData.originatorType(ProjectDataManagement::StateChangeOriginator::Participant); //Fallback
}
else
outData.originatorType(ProjectDataManagement::StateChangeOriginator::Participant);
}
}
outData.originatorId(inData.originatorId);
// For sending out use the original ChargeTypeId again!
outData.originatorChargeTypeId(inData.chargeTypeId);
if (outData.originatorType() == ProjectDataManagement::StateChangeOriginator::DusMine)
// in case of originator==DusMine a race condition may occur.
// (PPM::DynamicParticipantData vs. AES::DusMineState)
// To reduce the likelyhood, delay the major topic EquipmentState, which
// triggers PPM to publish DynamicParticipantData
QTimer::singleShot(10, this, [this, outData]()
{
publish(outData);
});
else
publish(outData);
}
void SendToDds::rxData(const TetraIcd::StateChangeIndirect &inData)
{
TrainingAreaBase::EquipmentState outData;
checkTimeStamp(inData);
if (!assignStateChangeBase(inData, outData))
return;
if (!setPosition(outData, inData.geoPosition, inData.relPosition, inData.opsPosition, inData.header.timeStamp))
ctasErrorPosition(inData);
outData.originatorType(ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
//outData.originatorId(m_aesStateHandler->getAesDdsId(inData.originatorId));
outData.originatorId(inData.originatorId);
if (m_aesHandler)
{
// [GUEZE-3031] Differentiation between direct hits on a participant or secondary impact through a building hit
// The offset of 500 is used as a criterion for secondary impact on buildings to add the entry (indirectly) in the participant history
int offsetIndirectHit = inData.isSecondaryEffect ? 500 : 0;
outData.originatorChargeTypeId(offsetIndirectHit + m_aesHandler->aesChargeId(outData.originatorId()));
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::OperatorAction &inData)
{
TrainingAreaBase::OperatorAction outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.operatorId(inData.operatorId);
outData.action(inData.action);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::StateChange &inData)
{
TrainingAreaBase::EquipmentState outData;
checkTimeStamp(inData);
if (!assignStateChangeBase(inData, outData))
return;
switch (inData.originatorType)
{
case TrainingAreaBase::TetraIcd::OriginatorType::ObserverController:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::ObserverController);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::ExerciseControl:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::ExerciseControl);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::MedicTimeout:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::MedicalTimeout);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::Sanitation:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::Sanitation);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::MedicDecontamination:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::WoundTreatmentDevice);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::MaintainanceRepair:
case TrainingAreaBase::TetraIcd::OriginatorType::MaintainanceDecontamination:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::MaintainanceDevice);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::FunctionalArea:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::FunctionalArea);
break;
case TrainingAreaBase::TetraIcd::OriginatorType::Initialisation:
outData.originatorType(ProjectDataManagement::StateChangeOriginator::Initialization);
break;
}
outData.originatorId(inData.originatorId);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::FunctionalAreaEntry &inData)
{
TrainingAreaBase::FunctionalAreaEntry outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if (!setPosition2d(outData, inData.relPosition))
ctasErrorPosition(inData);
outData.aesItemId(inData.aesDdsId);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::SpecialSensorData &inData)
{
TrainingAreaBase::SpecialSensorData outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if ((inData.relPosition.offsetX != 0 || inData.relPosition.offsetY != 0) && !setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
int operationalState = inData.operationalState & 0xFF; // 1 byte according to ICD
if (inData.airSensor)
operationalState |= 1 << 8;
if (inData.tireSensor)
operationalState |= 1 << 9;
if (inData.pointSensor)
operationalState |= 1 << 10;
if (inData.solidMatterSampleExtractor)
operationalState |= 1 << 11;
// type is always CSF(4) according to ICD52, but can be 0..8 -> 4 bits
// CSF type can be 0..3 --> 2 bits
operationalState |= (inData.type & 0x0F) << 12;
operationalState |= (inData.nbcType & 0x03) << 16;
outData.operationalState(operationalState);
publish(outData);
}
template<typename T>
static inline void assignMinelayerData(T &inData, TrainingAreaBase::MinelayerTrailerData &outData)
{
assignBase(inData, outData);
outData.barrierId(inData.barrierId);
outData.layingDensity(inData.layingDensity);
outData.effectStart(inData.effectStart);
outData.effectDuration(inData.effectDuration);
outData.mineCount(inData.mineCount);
}
void SendToDds::rxData(const TetraIcd::MinelayerTrailerData &inData)
{
TrainingAreaBase::MinelayerTrailerData outData;
checkTimeStamp(inData);
assignMinelayerData(inData, outData);
outData.chargeTypeId(m_mainHandler->pdmHandler()->chargeTypeFromId(inData.indirectChargeTasCode).id());
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::MinelayerThrowerData &inData)
{
TrainingAreaBase::MinelayerThrowerData outData;
checkTimeStamp(inData);
assignMinelayerData(inData, outData);
outData.chargeTypeId(m_mainHandler->pdmHandler()->chargeTypeFromId(inData.indirectChargeTasCode).id());
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
outData.throwingType(static_cast<TrainingAreaBase::ThrowingType::inner_enum>(inData.throwingType));
publish(outData);
}
void SendToDds::rxData(const TetraIcd::MinelayerTrailerPosition &inData)
{
TrainingAreaBase::MinelayerTrailerPosition outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.dusSerialNo(inData.dusSerialNo);
outData.chargeTypeId(m_mainHandler->pdmHandler()->chargeTypeFromId(inData.indirectChargeTasCode).id());
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::BiteReport &inData)
{
TrainingAreaBase::EquipmentBite outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.registerType(static_cast<int>(inData.loginType));
outData.biteState(inData.biteState);
outData.configState(inData.configState);
outData.batteryState(inData.batteryState);
outData.configError(inData.configError);
outData.biteErrors().resize(inData.biteErrors.size());
TrainingAreaBase::EquipmentBiteError ebe;
for (size_t i = 0; i < inData.biteErrors.size(); ++i)
{
const TetraIcd::BiteError &be = inData.biteErrors.at(i);
ebe.canDeviceNumber(be.canDeviceNumber);
ebe.errorNumber(be.errorNumber);
ebe.uniqueId(be.uniqueId);
ebe.subdeviceNumber(be.subdeviceNumber);
outData.biteErrors()[i] = ebe;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentEvent &inData)
{
TrainingAreaBase::EquipmentEvent outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if (!setPosition2d(outData, inData.relPosition))
ctasErrorPosition(inData);
TrainingAreaBase::Equipment src;
src.equipmentFunctionId(inData.equipmentFunctionId);
src.subDeviceId(inData.subDeviceId);
src.unqiueId(inData.uniqueId);
outData.source(src);
outData.code(inData.code);
outData.data() = inData.payload;
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentGearAssignment &inData)
{
TrainingAreaBase::EquipmentGearAssignment outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.gears().resize(inData.gears.size());
TrainingAreaBase::Gear tabGear;
for (size_t i = 0; i < inData.gears.size(); ++i)
{
const TetraIcd::Gear &gear = inData.gears.at(i);
tabGear.gearId(gear.gearSerialNumber);
tabGear.gearTypeId(pdm()->gearTypeIdFromTasGearTasCode(gear.gearTasCode));
tabGear.chargeCounting(gear.chargeCounting);
outData.gears()[i] = tabGear;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentGearChargeState &inData)
{
TrainingAreaBase::EquipmentGearChargeState outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.charge().resize(inData.charges.size());
TrainingAreaBase::GearCharge tabGr;
for (size_t i = 0; i < inData.charges.size(); ++i)
{
const TetraIcd::GearCharge &gr = inData.charges.at(i);
tabGr.gearId(gr.gearSerialNumber);
tabGr.gearTypeId(pdm()->gearTypeIdFromTasGearTasCode(gr.gearTasCode));
tabGr.remain(gr.remain);
tabGr.chargeTypeId(pdm()->chargeTypeIdFromGearAndChargeTasCode(tabGr.gearTypeId(), gr.chargeTypeId));
// Hack for old systems which neither send gearTasCode/gearType nor gearSerialNumber/gearId
// 1 = Wanne, 2 = Turm
if (gr.gearSerialNumber == 0 && gr.gearTasCode == 0)
tabGr.gearId(gr.location);
outData.charge()[i] = tabGr;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentGearEvent &inData)
{
TrainingAreaBase::EquipmentGearEvent outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.chargeAmount(inData.chargeAmount);
TrainingAreaBase::GearCharge tabGr;
tabGr.gearId(inData.charge.gearSerialNumber);
tabGr.gearTypeId(pdm()->gearTypeIdFromTasGearTasCode(inData.charge.gearTasCode));
tabGr.remain(inData.charge.remain);
tabGr.chargeTypeId(pdm()->chargeTypeIdFromGearAndChargeTasCode(tabGr.gearTypeId(), inData.charge.chargeTypeId));
outData.charge(tabGr);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::ParticipantSwVersions &inData)
{
TrainingAreaBase::ParticipantSwVersions outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.equipments().resize(inData.equipments.size());
TrainingAreaBase::EquipmentSwVersion eqpmt;
for (size_t i = 0; i < inData.equipments.size(); ++i)
{
const TetraIcd::EquipmentSwVersion data = inData.equipments.at(i);
eqpmt.equipmentFunctionId(data.equipmentFunctionId);
eqpmt.subDeviceId(data.subDeviceId);
eqpmt.unqiueId(data.uniqueId);
eqpmt.swVersion(data.swVersion);
outData.equipments()[i] = eqpmt;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EquipmentMessageOC &inData)
{
TrainingAreaBase::EquipmentMessage outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.subject(inData.subject);
outData.text(inData.message);
outData.sender(inData.senderId);
outData.receiver(inData.receiverId);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::OpaquePayloadDataIn &inData)
{
switch (inData.payloadType)
{
case TetraIcd::OpaquePayloadDataIn::Type::FireSmokeMarker:
{
TrainingAreaBase::FireSmokeMarkerData outData;
assignBase(inData, outData);
outData.payload() = inData.payload;
publish(outData);
break;
}
case TetraIcd::OpaquePayloadDataIn::Type::Geopairing:
{
TrainingAreaBase::GeopairingData outData;
checkTimeStamp(inData);
assignBase(inData, outData);
if (!setPosition2d(outData, inData.relPosition))
{
ctasErrorPosition(inData);
return;
}
outData.payload() = inData.payload;
publish(outData);
break;
}
case TetraIcd::OpaquePayloadDataIn::Type::Target:
{
TrainingAreaBase::TargetData outData;
assignBase(inData, outData);
outData.payload() = inData.payload;
publish(outData);
break;
}
case TetraIcd::OpaquePayloadDataIn::Type::OC:
{
TrainingAreaBase::OcData outData;
assignBase(inData, outData);
outData.payload() = inData.payload;
publish(outData);
}
}
}
void SendToDds::rxData(const TetraIcd::ParticipantModeReport &inData)
{
TrainingAreaBase::ParticipantModeReport outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.language(static_cast<ProjectDataManagement::LanguageConfiguration::inner_enum>(inData.language));
outData.operationMode(static_cast<ProjectDataManagement::OperationMode::inner_enum>(inData.operationMode));
outData.reactivationDetoxication(inData.reactivationDetoxication);
outData.reactivationMaintenance(inData.reactivationMaintenance);
outData.reactivationMedic(inData.reactivationMedic);
outData.protectionModeVest(static_cast<ProjectDataManagement::WphMode::inner_enum>(inData.modeVest));
outData.protectionModeHelmet(static_cast<ProjectDataManagement::WphMode::inner_enum>(inData.modeHelmet));
outData.personellProtectionMode(static_cast<ProjectDataManagement::PersonalProtection::inner_enum>(inData.personellProtectionMode));
outData.protectionLevel(static_cast<ProjectDataManagement::ProtectionLevel::inner_enum>(inData.protectionLevel));
outData.handgrenadeSearchMode(static_cast<ProjectDataManagement::HandgrenadeSearchMode::inner_enum>(inData.handgrenadeSearchMode));
outData.internationalMode(static_cast<ProjectDataManagement::InternationalMode::inner_enum>(inData.internationalMode));
outData.abcProtectionMode(static_cast<ProjectDataManagement::ABCProtectionMode::inner_enum>(inData.abcProtectionMode));
publish(outData);
}
void SendToDds::rxData(const TetraIcd::ParticipantConfigurationReport &inData)
{
TrainingAreaBase::ParticipantConfigurationReport outData;
checkTimeStamp(inData);
assignBase(inData, outData);
outData.positionIntervallVehicle(inData.positionIntervallVehicle);
outData.positionIntervallPersonell(inData.positionIntervallPersonell);
outData.positionIntervallAircraft(inData.positionIntervallAircraft);
outData.positionTransmissionIntervall(inData.positionTransmissionIntervall);
outData.heightbarrier(inData.heightbarrier);
outData.faultPositionHandling(static_cast<TrainingAreaBase::FaultPositionHandling::inner_enum>(inData.faultPositionHandling));
outData.switchingPositionIntervall(static_cast<TrainingAreaBase::SwitichingPositionIntervall::inner_enum>(inData.switchingPositionIntervall));
outData.waittimeAdditionalBroadcastRequest(inData.waittimeAdditionalBroadcastDemand);
outData.factorWaittimeIncreaseHighload(inData.factorWaittimeIncreaseAtHighload);
outData.waittimeAutomaticVulnerbilityIncreaseLevel1(inData.waittimeAutomaticVulnerabilityIncreaseLevel1);
outData.waittimeAutomaticVulnerbilityIncreaseLevel2(inData.waittimeAutomaticVulnerabilityIncreaseLevel2);
outData.waittimeAutomaticVulnerbilityIncreaseLevel3(inData.waittimeAutomaticVulnerabilityIncreaseLevel3);
outData.opticalCode(static_cast<TrainingAreaBase::OpticalCode::inner_enum>(inData.opticalCode));
outData.vulnerabilityModel(inData.vulnerabilityModel);
outData.validTimeOps(inData.validTimeOps);
publish(outData);
}
void SendToDds::rxData(const TetraIcd::EmergencyCall &inData)
{
TrainingAreaBase::EmergencyCall outData;
outData.equipmentNumber(inData.issi);
outData.sampleTime(inData.timeStamp); // msec
const Core::GeoPosition2D pos(inData.absPosition.lon, inData.absPosition.lat);
outData.position(pos);
m_mainHandler->ddsHandler()->emergencyCall()->write(outData);
QTimer *t = m_emergencyCallTimer[inData.issi];
if (!t)
{
t = new QTimer(this);
t->setInterval(30 * 1000);
const int issi = inData.issi;
connect(t, &QTimer::timeout, this, [this, issi]() { onEmergencyCallTimerTimeout(issi); });
m_emergencyCallTimer[inData.issi] = t;
}
t->start();
}
void SendToDds::rxData(const TetraIcd::SanitationRepair &inData)
{
TrainingAreaBase::ParticipantSanitationRepair sanRepair;
sanRepair.equipmentNumber(inData.header.equipmentNumber);
sanRepair.sampleTime(inData.header.timeStamp);
sanRepair.originatorType(static_cast<ProjectDataManagement::StateChangeOriginator::inner_enum>(inData.originatorType));
sanRepair.equipmentNumberOrigin(inData.equipmentNumberOrigin);
sanRepair.stateStart(static_cast<ProjectDataManagement::VulnerabilityState::inner_enum>(inData.stateStart));
sanRepair.typeOfTreatment(inData.typeOfTreatment);
sanRepair.triage(inData.triage);
sanRepair.tyeOfDamage(inData.tyeOfDamage);
sanRepair.stateResult(static_cast<ProjectDataManagement::VulnerabilityState::inner_enum>(inData.stateResult));
sanRepair.treatmentResult(static_cast<TrainingAreaBase::TreatmentResult::inner_enum>(inData.treatmentResult));
sanRepair.triagePriority(inData.triagePriority);
sanRepair.fullState(inData.fullState);
sanRepair.timeOfTreatment(inData.timeOfTreatment);
sanRepair.timeOfRecovery(inData.timeOfRecovery);
publish(sanRepair);
}
void SendToDds::rxData(const TetraIcd::SanitaryMaterialStockReport &inData)
{
TrainingAreaBase::ParticipantSanitaryMaterialStockReport sanReport;
sanReport.equipmentNumber(inData.header.equipmentNumber);
sanReport.sampleTime(inData.header.timeStamp);
sanReport.sanitaryMaterialCount(inData.sanitaryMaterialCount);
sanReport.sanitaryMaterialStock(inData.sanitaryMaterialStock);
sanReport.uniqueId(inData.uniqueId);
sanReport.originatorEquipmentNumber(inData.originatorEquipmentNumber);
sanReport.qualification(static_cast<TrainingAreaBase::Qualification::inner_enum>(inData.qualification));
publish(sanReport);
DEBUG_LOG(m_logger) << "SendToDds::rxData(const TetraIcd::SanitaryMaterialStockReport for " << sanReport.equipmentNumber();
if (sanReport.qualification() == TrainingAreaBase::Qualification::NotDefined)
{
//check if we already have the uniqe id in our map. if yes AND it's not the same equipNo we had a remap;
int oldEquipNo = m_uniqueId2EquipmentNumber.value(sanReport.uniqueId());
if (oldEquipNo > 0)
{
TrainingAreaBase::ParticipantSanitation sanitation;
sanitation.equipmentNumber(oldEquipNo);
sanitation.sampleTime(sanReport.sampleTime());
sanitation.proceduresTaken(0);
sanitation.uniqueId(sanReport.uniqueId());
sanitation.originatorEquipmentNumber(0);
sanitation.qualification(TrainingAreaBase::Qualification::NotDefined);
sanitation.sanitationResult(TrainingAreaBase::SanitationResult::NoEffect);
sanitation.sanitationEvaluation(TrainingAreaBase::SanitationEvaluation::NotApplicable);
publish(sanitation);
}
m_uniqueId2EquipmentNumber.remove(sanReport.uniqueId());
}
onPvSanUniqueIdMapping(sanReport.uniqueId(), sanReport.equipmentNumber(), sanReport.sampleTime());
}
void SendToDds::rxData(const TetraIcd::Sanitation &inData)
{
TrainingAreaBase::ParticipantSanitation sanitation;
sanitation.equipmentNumber(inData.header.equipmentNumber);
sanitation.sampleTime(inData.header.timeStamp);
sanitation.proceduresTaken(inData.proceduresTaken);
sanitation.uniqueId(inData.uniqeId);
sanitation.originatorEquipmentNumber(inData.originatorEquipmentNumber);
sanitation.qualification(static_cast<TrainingAreaBase::Qualification::inner_enum>(inData.qualification));
sanitation.sanitationResult(static_cast<TrainingAreaBase::SanitationResult::inner_enum>(inData.sanitationResult));
sanitation.sanitationEvaluation(static_cast<TrainingAreaBase::SanitationEvaluation::inner_enum>(inData.sanitationEvaluation));
publish(sanitation);
DEBUG_LOG(m_logger) << "SendToDds::rxData(const TetraIcd::Sanitation for " << sanitation.equipmentNumber();
if (sanitation.qualification() == TrainingAreaBase::Qualification::NotDefined)
{
TrainingAreaBase::ParticipantSanitaryMaterialStockReport sanReport;
sanReport.equipmentNumber(sanitation.equipmentNumber());
sanReport.sampleTime(sanitation.sampleTime());
sanReport.sanitaryMaterialCount(0);
sanReport.sanitaryMaterialStock(std::vector<int>());
sanReport.uniqueId(sanitation.uniqueId());
sanReport.originatorEquipmentNumber(0);
sanReport.qualification(TrainingAreaBase::Qualification::NotDefined);
publish(sanReport);
m_uniqueId2EquipmentNumber.remove(sanitation.uniqueId());
}
else
onPvSanUniqueIdMapping(sanitation.uniqueId(), sanitation.equipmentNumber(), sanitation.sampleTime());
}
void SendToDds::rxData(const TetraIcd::ApacheLaserDesignation &inData)
{
TrainingAreaBase::LaserDesignation laserDesignation;
laserDesignation.equipmentNumber(inData.header.equipmentNumber);
laserDesignation.sampleTime(inData.header.timeStamp);
laserDesignation.spotLocation(Core::GeoPosition2D(inData.spotLocation.lon, inData.spotLocation.lat));
laserDesignation.outputPower(inData.outputPower);
laserDesignation.code(inData.code);
laserDesignation.gearId(inData.gearId);
publish(laserDesignation);
}
template<typename S>
void SendToDds::ctasErrorPosition(const S &data)
{
TrainingAreaBase::CtaDataError error;
error.equipmentNumber(data.header.equipmentNumber);
error.sampleTime(data.header.timeStamp);
error.reason(ProjectDataManagement::DataErrorReason_def::InvalidPosition);
error.originTelegramId(data.id());
publish(error);
}
void SendToDds::rxData(const TetraIcd::BatteryInfo &inData)
{
TrainingAreaBase::BatteryInfo batteryInfo;
batteryInfo.equipmentNumber(inData.header.equipmentNumber);
batteryInfo.sampleTime(inData.header.timeStamp);
std::vector<TrainingAreaBase::BatteryState> batteryStates;
batteryStates.reserve(inData.batteryStates.size());
for(const TetraIcd::BatteryState &batState : qAsConst(inData.batteryStates))
{
TrainingAreaBase::BatteryState batStateOut;
batStateOut.batteryPort(batState.batteryPort);
batStateOut.batteryPercent(batState.batteryPercent);
batStateOut.current(batState.current);
batStateOut.frequency(batState.frequency);
batStateOut.generic(batState.generic);
batStateOut.serialNumber(batState.serialNumber.toStdString());
batStateOut.switchOrValue(batState.switchOrValue);
batStateOut.temperature(batState.temperature);
batStateOut.voltage(batState.voltage);
batteryStates.push_back(batStateOut);
}
batteryInfo.batteryStates(batteryStates);
publish(batteryInfo);
}
void SendToDds::rxData(const TetraIcd::BatteryAlarm &inData)
{
TrainingAreaBase::BatteryAlarm batAlarm;
batAlarm.acknowledge(inData.acknowledge);
batAlarm.alarmTime(inData.alarmTime);
batAlarm.condition(inData.condition);
batAlarm.description(inData.description.toStdString());
batAlarm.batteryPort(inData.id);
batAlarm.uniqueAlarmNumber(inData.uniqueAlarmNumber);
batAlarm.equipmentNumber(inData.header.equipmentNumber);
batAlarm.sampleTime(inData.header.timeStamp);
publish(batAlarm);
}
void SendToDds::rxData(const TetraIcd::ServiceControl &inData)
{
TrainingAreaBase::ServiceControl serviceControl;
serviceControl.equipmentNumber(inData.header.equipmentNumber);
serviceControl.sampleTime(inData.header.timeStamp);
serviceControl.serviceControlState(static_cast<TrainingAreaBase::ServiceControlEnum::inner_enum>(inData.serviceControlState));
publish(serviceControl);
}
void SendToDds::rxData(const TetraIcd::RestoreRadioReadiness &inData)
{
struct PositionStruct
{
int equipmentNumber;
Core::GeoPosition2D position;
};
RestoreRadioReadiness restoreRadioReadiness;
restoreRadioReadiness.equipmentNumber(inData.header.equipmentNumber);
restoreRadioReadiness.sampleTime(inData.header.timeStamp);
restoreRadioReadiness.readinessLossPosition(m_posHandler->relativeToGeographic(inData.readinessLossPosition));
restoreRadioReadiness.readinessLossTime(inData.readinessLossTime);
restoreRadioReadiness.readinessRestorePosition(m_posHandler->relativeToGeographic(inData.readinessRestorePosition));
restoreRadioReadiness.readinessRestoreTime(inData.readinessRestoreTime);
publish(restoreRadioReadiness);
}
void SendToDds::rxData(const TetraIcd::UAVRadioLinkConnectionStatus &inData)
{
TrainingAreaBase::UASRadioLinkConnectionState outData;
assignBase(inData, outData);
//TODO make it better!
switch (inData.uavConnectionState)
{
case TetraIcd::UAVRadioLinkConnectionStatusType::Connected:
outData.connectionStatus(ProjectDataManagement::UasState::On);
break;
case TetraIcd::UAVRadioLinkConnectionStatusType::Disconnected:
outData.connectionStatus(ProjectDataManagement::UasState::Off);
break;
case TetraIcd::UAVRadioLinkConnectionStatusType::Connecting:
outData.connectionStatus(ProjectDataManagement::UasState::Init);
break;
case TetraIcd::UAVRadioLinkConnectionStatusType::Diconnecting: //Yes there is a Typo
outData.connectionStatus(ProjectDataManagement::UasState::Shutdown);
break;
case TetraIcd::UAVRadioLinkConnectionStatusType::Error:
outData.connectionStatus(ProjectDataManagement::UasState::Undefined);
break;
case TetraIcd::UAVRadioLinkConnectionStatusType::Undefined:
outData.connectionStatus(ProjectDataManagement::UasState::Undefined);
break;
}
publish(outData);
}
void SendToDds::rxData(const TetraIcd::UAVMissionStatus &inData)
{
TrainingAreaBase::UASMissionStatus outData;
assignBase(inData, outData);
switch (inData.currentAction)
{
case TetraIcd::UAVActionTask::Undefined: outData.currentTask(ProjectDataManagement::UasActionTask::Undefined); break;
case TetraIcd::UAVActionTask::Disarm: outData.currentTask(ProjectDataManagement::UasActionTask::Disarm); break;
case TetraIcd::UAVActionTask::Arm: outData.currentTask(ProjectDataManagement::UasActionTask::Arm); break;
case TetraIcd::UAVActionTask::Takeoff: outData.currentTask(ProjectDataManagement::UasActionTask::Takeoff); break;
case TetraIcd::UAVActionTask::Connect: outData.currentTask(ProjectDataManagement::UasActionTask::Connect); break;
case TetraIcd::UAVActionTask::Loiter: outData.currentTask(ProjectDataManagement::UasActionTask::Loiter); break;
case TetraIcd::UAVActionTask::Return: outData.currentTask(ProjectDataManagement::UasActionTask::Return); break;
case TetraIcd::UAVActionTask::Observe: outData.currentTask(ProjectDataManagement::UasActionTask::Observe); break;
}
outData.currentMissionName(inData.currentMissionName.toStdString());
outData.missionEnded(inData.missionEnded.toStdString());
publish(outData);
}
void SendToDds::rxData(const TetraIcd::UAVStatus &inData)
{
TrainingAreaBase::UASStatus outData;
assignBase(inData, outData);
//TODO make it better!
switch (inData.uavState)
{
case TetraIcd::UAVState::On:
outData.uasState(ProjectDataManagement::UasState::On);
break;
case TetraIcd::UAVState::Off:
outData.uasState(ProjectDataManagement::UasState::Off);
break;
case TetraIcd::UAVState::Init:
outData.uasState(ProjectDataManagement::UasState::Init);
break;
case TetraIcd::UAVState::Shutdown:
outData.uasState(ProjectDataManagement::UasState::Shutdown);
break;
case TetraIcd::UAVState::Calibrating:
outData.uasState(ProjectDataManagement::UasState::Calibrating);
break;
case TetraIcd::UAVState::Manual:
outData.uasState(ProjectDataManagement::UasState::Manual);
break;
case TetraIcd::UAVState::Maintenance:
outData.uasState(ProjectDataManagement::UasState::Maintenance);
break;
case TetraIcd::UAVState::Standby:
outData.uasState(ProjectDataManagement::UasState::Standby);
break;
case TetraIcd::UAVState::Normal:
outData.uasState(ProjectDataManagement::UasState::Normal);
break;
case TetraIcd::UAVState::Auto:
outData.uasState(ProjectDataManagement::UasState::Auto);
break;
case TetraIcd::UAVState::Undefined:
outData.uasState(ProjectDataManagement::UasState::Undefined);
break;
}
publish(outData);
}
template <typename T>
bool SendToDds::setPosition(T &data,
const TetraIcd::AbsolutePosition &geoPos,
const TetraIcd::RelativePosition &relPos,
const TetraIcd::OpsPosition &opsPos,
const qint64 timeStamp) const
{
bool bRet = true;
bool moutCheck = true; // GPS-freier Raum
TrainingAreaBase::TabPosition tabPos;
if (geoPos.isValid())
{
tabPos.position(Core::GeoPosition(geoPos.lon, geoPos.lat, geoPos.z));
setOptionalBuildingRoomId(tabPos.moutObjectId(), opsPos.moutObjectId);
setOptionalBuildingRoomId(tabPos.moutRoomId(), opsPos.moutRoomId);
// We need to inform CES to not do any calculation for this participant
// anymore since it's somehow no longer in the WLS area. This is done by
// setting the reporting source to something other than Wls since this
// is the trigger for CES
const bool bWlsLocationLost = opsPos.moutObjectId == -2 &&
opsPos.moutRoomId == -2;
if (bWlsLocationLost)
{
TrainingAreaBase::TabPosition lastTabPos =
m_posHandler->lastPosition(data.equipmentNumber());
if (lastTabPos.reportingSource() == Core::PositionReportingSource::Wls ||
std::isnan(lastTabPos.position().lon()))
tabPos.reportingSource(Core::PositionReportingSource::Gps);
else
std::swap(tabPos, lastTabPos); // already a Gps position, use this
}
else
tabPos.reportingSource(Core::PositionReportingSource::Wls);
}
else if (opsPos.opsId == 0) // -> RelPos must be set and valid.
{
bool changeReportSource = true;
DEBUG_LOG(m_logger) << "ops == 0 so check for marked invalid to use last position!";
if (PositionHandler::isPositionMarkedInvalid(relPos))
{
// use the last hit position if the state change is a reaction on CES hit request
// -> the participant may not have a valid position
tabPos = m_posHandler->lastPosition(data.equipmentNumber());
// Problem:
// Hit lines in the playback for mounted participants had the last pos before mounting as position.
//
// In the WLS area, the position should be taken to see a hit line, because the participant does not know his own position,
// you have to take the last position, which comes from the WLS every second and is therefore always up to date.
// NEW 2021: sometimes a participant loses contact to an IPS during AES, in this case use the last Wls OR Ops:Tunnel position
DEBUG_LOG(m_logger) << "ops == 0 got last Position its source is " << tabPos.reportingSource();
if (tabPos.reportingSource() != Core::PositionReportingSource::Wls
&& tabPos.reportingSource() != Core::PositionReportingSource::Ops
&& tabPos.reportingSource() != Core::PositionReportingSource::OpsTunnel)
tabPos = TrainingAreaBase::TabPosition();
else if (tabPos.reportingSource() == Core::PositionReportingSource::Ops
|| tabPos.reportingSource() == Core::PositionReportingSource::OpsTunnel)
changeReportSource = false;
DEBUG_LOG(m_logger) << "ops == 0 changeSource is " << changeReportSource ;
}
else
{
// Check training area bounds
if (!m_posHandler->isInTrainingArea(relPos))
{
DEBUG_LOG(m_logger) << "bret = false";
bRet = false;
}
tabPos.position(m_posHandler->relativeToGeographic(relPos));
setOptionalBuildingRoomId(tabPos.moutObjectId(), opsPos.moutObjectId);
setOptionalBuildingRoomId(tabPos.moutRoomId(), opsPos.moutRoomId);
// Check, if position is valid after leaving a building the participant
// needs a minimun distance to the building-footprint
TrainingAreaBase::TabPosition lastPosition = m_posHandler->lastPosition(data.equipmentNumber());
{
DEBUG_LOG(m_logger) << "lastPosition.reportingSource=" << lastPosition.reportingSource();
}
if (lastPosition.reportingSource() == Core::PositionReportingSource::Ops &&
lastPosition.moutObjectId().is_set())
{
DEBUG_LOG(m_logger) << "Check leave Building equipmentNumber=" << data.equipmentNumber();
DEBUG_LOG(m_logger) << "relPos=" << relPos.offsetX << "/" << relPos.offsetY;
DEBUG_LOG(m_logger) << "posInExtendedBuildingFootprint";
const Core::GeoPosition &position = tabPos.position();
int moutObjectId = lastPosition.moutObjectId().get();
const int lastMountPointTypeId = m_posHandler->lastMountPointTypeId(data.equipmentNumber());
if (ism()->posInExtendedBuildingFootprint(moutObjectId, position, lastMountPointTypeId))
{
moutCheck = false;
bRet = false;
}
else
{
DEBUG_LOG(m_logger) << "lastMountPointTypeId =" << m_posHandler->lastMountPointTypeId(data.equipmentNumber());
if ( m_posHandler->lastMountPointTypeId(data.equipmentNumber()) == InfrastructureManagement::MountingPointType_def::IndoorIPS)
{
// Checking whether the speed of the participant is plausible
double speed = m_posHandler->speed(relPos, data.equipmentNumber(), timeStamp);
bool checkSpeed = !m_posHandler->isTimeToPerformSpeedCheckExceeded(data.equipmentNumber(),
timeStamp,
Settings::maxSpeedCheckTimeout());
DEBUG_LOG(m_logger) << "Check Speed speed=" << speed
<< " MaxSoldierSpeed=" << Settings::maxSoldierSpeed()
<< " checkSpeed=" << checkSpeed;
if (checkSpeed && speed > Settings::maxSoldierSpeed())
{
DEBUG_LOG(m_logger) << "Check Speed bRet = false";
bRet = false;
moutCheck = false;
}
}
else
{
DEBUG_LOG(m_logger) << "Kein Speed check!";
}
}
}
}
// Warum muss das hier im WLS oder generell auf GPS geschaltet werden?
if (changeReportSource)
tabPos.reportingSource(Core::PositionReportingSource::Gps);
}
else
{
INFO_LOG(m_logger) << "Valid OpsData: " << opsPos.opsId;
IsmHandler::OpsData opsData;
if (ism())
{
opsData = ism()->dataFromOpsCode(opsPos.opsId);
//int mountingPointTypeId = m_mainHandler->ismHandler()->getAssetTypeIdFromStructureElementId(opsData.structureElementId);
DEBUG_LOG(m_logger) << "equipmentNumber=" << data.equipmentNumber()
<< " AssetId=" << opsData.assetId
<< " opsData.structureElementId=" << opsData.structureElementId
<< " opsData.mseId=" << opsData.mainStructureElementId
<< " opsData.mptTypeId=" << opsData.mountingPointTypeId;
if ( opsData.structureElementId != 0)
m_posHandler->setLastMountPointTypeId(data.equipmentNumber(), opsData.mountingPointTypeId);
}
if (opsData.opsCode != opsPos.opsId)
{
WARNING_LOG(m_logger) << "found no position data for ops id " << opsPos.opsId;
bRet = false;
}
else if (opsData.structureElementId == 0)
{
WARNING_LOG(m_logger) << "ops id " << opsPos.opsId << " is not mounted";
bRet = false;
}
else
{
DEBUG_LOG(m_logger) << "ops id " << opsPos.opsId;
tabPos.moutObjectId(opsData.mainStructureElementId);
tabPos.moutRoomId(opsData.roomId);
tabPos.position(opsData.position);
m_posHandler->setLastBuildingId(data.equipmentNumber(), opsData.mainStructureElementId);
m_posHandler->setLastRoomId(data.equipmentNumber(), opsData.roomId);
}
if (opsData.opsTunnel)
tabPos.reportingSource(Core::PositionReportingSource::OpsTunnel);
else
tabPos.reportingSource(Core::PositionReportingSource::Ops);
}
if (!bRet)
{
if ( moutCheck )
{
DEBUG_LOG(m_logger) << "POS = ungueltig";
tabPos.position().lon(nan(""));
tabPos.position().lat(nan(""));
}
else
{
DEBUG_LOG(m_logger) << "POS = ungueltig => alte Pos mit aktueller Zeit verschicken";
TrainingAreaBase::TabPosition lastPosition = m_posHandler->lastPosition(data.equipmentNumber());
tabPos.position().lon(lastPosition.position().lon());
tabPos.position().lat(lastPosition.position().lat());
tabPos.position().z(lastPosition.position().z());
tabPos.reportingSource(Core::PositionReportingSource::OpsBuilding);
tabPos.moutObjectId(m_posHandler->lastBuildingId(data.equipmentNumber()));
tabPos.moutRoomId(m_posHandler->lastRoomId(data.equipmentNumber()));
bRet = true;
}
}
else
{
DEBUG_LOG(m_logger) << "set last Position";
if (tabPos.reportingSource() == Core::PositionReportingSource::Ops)
DEBUG_LOG(m_logger) << "ReportingSource=OPS";
else if (tabPos.reportingSource() == Core::PositionReportingSource::Gps)
DEBUG_LOG(m_logger) << "ReportingSource=GPS";
DEBUG_LOG(m_logger) << "tabPos.reportingSource=" << tabPos.reportingSource()
<< " timeStamp=" << timeStamp;
m_posHandler->setLastPosition(data.equipmentNumber(), tabPos);
m_posHandler->setLastTime(data.equipmentNumber(), timeStamp);
}
data.position(tabPos);
return bRet;
}
template <typename T>
bool SendToDds::setPosition2d(T &data,
const TetraIcd::RelativePosition &relPos) const
{
TrainingAreaBase::TabPosition tabPos;
bool bRet = true;
if (PositionHandler::isPositionMarkedInvalid(relPos))
tabPos = m_posHandler->lastPosition(data.equipmentNumber());
else
{
// Check training area bounds
if (!m_posHandler->isInTrainingArea(relPos))
bRet = false;
DEBUG_LOG(m_logger) << "tabPos.reportingSource=" << tabPos.reportingSource();
tabPos.position(m_posHandler->relativeToGeographic(relPos));
m_posHandler->setLastPosition(data.equipmentNumber(), tabPos);
}
Core::GeoPosition2D pos2d;
pos2d.lat(tabPos.position().lat());
pos2d.lon(tabPos.position().lon());
data.position(pos2d);
return bRet;
}
template<typename S, typename T>
bool SendToDds::assignStateChangeBase(const S &inData, T &outData)
{
assignBase(inData, outData);
// Plausibility check ProjectDataManagement::VulnerabilityState
if (inData.vulnerabilityState < TrainingAreaBase::TetraIcd::VulnerabilityState::CombatReady ||
inData.vulnerabilityState > TrainingAreaBase::TetraIcd::VulnerabilityState::VulnerabilityBitsChanged)
{
TrainingAreaBase::CtaDataError error;
assignBase(inData, error);
error.reason(ProjectDataManagement::DataErrorReason_def::InvalidTacticalState);
error.originTelegramId(inData.id());
publish(error);
return false;
}
outData.vulnerabilityState(static_cast<ProjectDataManagement::VulnerabilityState::inner_enum>(inData.vulnerabilityState));
outData.vulnerabilityKindId(pdm()->vulnerabilityKindId(inData.tasVulnerabilityKindId, inData.header.equipmentNumber));
outData.vitality(inData.tasVitality);
outData.vulnerabilityBits(inData.vulnerabilityBits);
return true;
}
template <typename S>
void SendToDds::checkTimeStamp(const S &data)
{
const auto partTime = m_mainHandler->ddsHandler()->partitionTime();
if (data.header.timeStamp > (partTime + Settings::timeDelayAhead()) ||
data.header.timeStamp < (partTime - Settings::timeDelayBack()))
{
TrainingAreaBase::CtaDataError error;
error.equipmentNumber(data.header.equipmentNumber);
error.sampleTime(data.header.timeStamp);
error.reason(ProjectDataManagement::DataErrorReason_def::InvalidTime);
error.originTelegramId(data.id());
publish(error);
}
}
std::shared_ptr<IsmHandler> SendToDds::ism() const
{
return m_mainHandler->ismHandler();
}
std::shared_ptr<PdmHandler> SendToDds::pdm() const
{
return m_mainHandler->pdmHandler();
}
template <typename T>
void SendToDds::publish(const T &data)
{
try
{
m_dtsTab->publish(data);
}
catch (const std::exception &ex)
{
WARNING_LOG(m_logger) << "Could not send telegram: " << ex.what();
}
}
void SendToDds::onEmergencyCallTimerTimeout(int issi)
{
auto it = m_emergencyCallTimer.find(issi);
if (it == m_emergencyCallTimer.end())
return;
delete it.value();
m_emergencyCallTimer.erase(it);
// 1a: Following works on common/default dts, which is not used for this topic:
// see also DdsHandler::init()
// m_mainHandler->ddsHandler()->dtsTab()->removeEmergencyCallByKey(issi);
// 1b: Instead, remove it from special 'dts':
if (m_mainHandler->ddsHandler()->emergencyCall())
{
TrainingAreaBase::EmergencyCall emergCallData;
emergCallData.equipmentNumber(issi);
if (!m_mainHandler->ddsHandler()->emergencyCall()->remove(emergCallData))
ERROR_LOG(m_logger) << LOGFUNCTION << " removing of TrainingAreaBase::EmergencyCall failed, issi = " << issi;
}
}
void SendToDds::onPvSanUniqueIdMapping(int uniqueId, int equipNo, qint64 sampleTime)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << "start pvSan mapping " << equipNo << " uniqueId " << uniqueId;
//first check if we the mapping is the same, nothing to do
if (m_uniqueId2EquipmentNumber.value(uniqueId) == equipNo)
return;
//check if we already have the uniqe id in our map. if not: insert
if (!m_uniqueId2EquipmentNumber.contains(uniqueId))
{
m_uniqueId2EquipmentNumber.insert(uniqueId, equipNo);
return;
}
//check if we already have the uniqe id in our map. if yes AND it's not the same equipNo we had a remap;
int oldEquipNo = m_uniqueId2EquipmentNumber.value(uniqueId);
if (oldEquipNo > 0)
{
TrainingAreaBase::ParticipantSanitaryMaterialStockReport sanReport;
sanReport.equipmentNumber(oldEquipNo);
sanReport.sampleTime(sampleTime);
sanReport.sanitaryMaterialCount(0);
sanReport.sanitaryMaterialStock(std::vector<int>());
sanReport.uniqueId(uniqueId);
sanReport.originatorEquipmentNumber(0);
sanReport.qualification(TrainingAreaBase::Qualification::NotDefined);
publish(sanReport);
TrainingAreaBase::ParticipantSanitation sanitation;
sanitation.equipmentNumber(oldEquipNo);
sanitation.sampleTime(sampleTime);
sanitation.proceduresTaken(0);
sanitation.uniqueId(uniqueId);
sanitation.originatorEquipmentNumber(0);
sanitation.qualification(TrainingAreaBase::Qualification::NotDefined);
sanitation.sanitationResult(TrainingAreaBase::SanitationResult::NoEffect);
sanitation.sanitationEvaluation(TrainingAreaBase::SanitationEvaluation::NotApplicable);
publish(sanitation);
m_uniqueId2EquipmentNumber.insert(uniqueId, equipNo);
}
}
} // namespace TrainingAreaBase