1404 lines
53 KiB
C++
Executable File
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
|
|
|
|
|
|
|
|
|