/** * 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 #include 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 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(inData.displayDeviceLanguage)); // pyrotechnics outData.pyrotechnics(inData.pyrotechnics); // configuration mine/handgrenade sensor outData.mineHandgrenadeSensorMode(static_cast(inData.mhgrsMode)); outData.mineHandgrenadeSensorEnergySaving(inData.mhgrsEnergySaving); // configuration special sensors outData.firMode(static_cast(inData.specialSensorFir)); outData.minelayerMode(static_cast(inData.specialSensorMinelayer)); outData.specialSensorStandard(static_cast(inData.specialSensorStandard)); // configuration agdus hw <- old stuff have to ask Christian Backeberg why its not deleted from the telegram outData.agdusHwMode(static_cast(inData.agdusHwMode)); // position update intervall outData.positionUpdateIntervall(static_cast(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(inData.reason)); outData.originTelegramId(inData.originTelegramId); publish(outData); } template 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 static inline void assignIedBaseData(const S &inData, T &outData) { assignBase(inData, outData); outData.iedId(inData.iedId); outData.tacticalState(static_cast(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(inData.iedType)); outData.remoteTrigger((inData.trigger & static_cast(TetraIcd::DefinitionIed::TriggerTypes::Remote)) ? true : false); outData.switchTrigger((inData.trigger & static_cast(TetraIcd::DefinitionIed::TriggerTypes::Switch)) ? true : false); outData.lightBarrierTrigger((inData.trigger & static_cast(TetraIcd::DefinitionIed::TriggerTypes::LightBarrier)) ? true : false); outData.tautWireTrigger((inData.trigger & static_cast(TetraIcd::DefinitionIed::TriggerTypes::TautWire)) ? true : false); outData.explosiveForce(inData.explosiveForce); outData.pairingState(static_cast(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(inData.errorStateElectronics)); outData.errorStatePyrotechnics1(static_cast(inData.errorStatePyrotechnics1)); outData.errorStatePyrotechnics2(static_cast(inData.errorStatePyrotechnics2)); outData.errorStatePyrotechnics3(static_cast(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(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 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(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(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(inData.language)); outData.operationMode(static_cast(inData.operationMode)); outData.reactivationDetoxication(inData.reactivationDetoxication); outData.reactivationMaintenance(inData.reactivationMaintenance); outData.reactivationMedic(inData.reactivationMedic); outData.protectionModeVest(static_cast(inData.modeVest)); outData.protectionModeHelmet(static_cast(inData.modeHelmet)); outData.personellProtectionMode(static_cast(inData.personellProtectionMode)); outData.protectionLevel(static_cast(inData.protectionLevel)); outData.handgrenadeSearchMode(static_cast(inData.handgrenadeSearchMode)); outData.internationalMode(static_cast(inData.internationalMode)); outData.abcProtectionMode(static_cast(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(inData.faultPositionHandling)); outData.switchingPositionIntervall(static_cast(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(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(inData.originatorType)); sanRepair.equipmentNumberOrigin(inData.equipmentNumberOrigin); sanRepair.stateStart(static_cast(inData.stateStart)); sanRepair.typeOfTreatment(inData.typeOfTreatment); sanRepair.triage(inData.triage); sanRepair.tyeOfDamage(inData.tyeOfDamage); sanRepair.stateResult(static_cast(inData.stateResult)); sanRepair.treatmentResult(static_cast(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(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(inData.qualification)); sanitation.sanitationResult(static_cast(inData.sanitationResult)); sanitation.sanitationEvaluation(static_cast(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()); 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 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 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(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 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 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 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(inData.vulnerabilityState)); outData.vulnerabilityKindId(pdm()->vulnerabilityKindId(inData.tasVulnerabilityKindId, inData.header.equipmentNumber)); outData.vitality(inData.tasVitality); outData.vulnerabilityBits(inData.vulnerabilityBits); return true; } template 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 SendToDds::ism() const { return m_mainHandler->ismHandler(); } std::shared_ptr SendToDds::pdm() const { return m_mainHandler->pdmHandler(); } template 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()); 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