Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Services/OcSituationClient/handler_oc_situation_data.cpp
T

611 lines
21 KiB
C++
Executable File

/**
* Handler for all oc situation data at the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, NLST, 01-03-2018
*/
#include "handler_oc_situation_data.h"
#include <LtsUtilsCore/qbytearray_byteaccess.h>
#include <LtsUtilsCore/dds_vector.h>
#include "main_handler.h"
#include "settings.h"
namespace OcSituationClient
{
HandlerOcSituationData::HandlerOcSituationData(const std::shared_ptr<DdsHandler> &dds,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ParticipantManagement::Interface::Ftc> &ppmFtc,
const std::shared_ptr<TrainingAreaBase::Interface::Dts> &tabDts,
const std::shared_ptr<TrainingAreaBaseOC::Interface::Dtc> &tabocDtc,
const std::shared_ptr<TrainingAreaBaseOC::Interface::Dts> &tabocDts,
QObject *parent)
: QObject(parent)
, m_dds(dds)
, m_ppmDtc(ppmDtc)
, m_ppmFtc(ppmFtc)
, m_tabDts(tabDts)
, m_tabocDtc(tabocDtc)
, m_tabocDts(tabocDts)
, m_logger(getLogger("HandlerOcSituationData"))
{}
HandlerOcSituationData::~HandlerOcSituationData()
{}
bool HandlerOcSituationData::init()
{
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::partyChanged,
this, &HandlerOcSituationData::onPartyChanged);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::participantChanged,
this, &HandlerOcSituationData::onParticipantChanged);
// PPM DTC enable
if (!m_ppmDtc->enableParticipant() ||
!m_ppmDtc->enableParty() ||
!m_ppmDtc->enableType())
{
ERROR_LOG(m_logger) << "Enabling PPM Data-Topics failed.";
return false;
}
// PPM FTC enable
if (!m_ppmFtc->enableModifyParticipantData())
{
ERROR_LOG(m_logger) << "Enabling PPM Data-Topics failed.";
return false;
}
// TAB DTS enable
if (!m_tabDts->enableEquipmentPosition() ||
!m_tabDts->enableEquipmentState() ||
!m_tabDts->enableEquipmentOperationalState() ||
!m_tabDts->enableParticipantSanitation() ||
!m_tabDts->enableSpecialSensorData())
{
ERROR_LOG(m_logger) << "Enabling TAB DTS Data-Topics failed.";
return false;
}
// TAOBOC DTC enable
if (!m_tabocDtc->enableSpecificsEquipment())
{
ERROR_LOG(m_logger) << "Enabling TABOC Data-Topics failed.";
return false;
}
if (!m_tabocDts->enableParticipantsOverload())
{
ERROR_LOG(m_logger) << "Enabling TABOC fts-Topics failed.";
return false;
}
DEBUG_LOG(m_logger) << "Initialisation HandlerOcSituationData succesful.";
return true;
}
void HandlerOcSituationData::process(const QByteArray &data, bool bIsInitial)
{
if (bIsInitial)
{
DEBUG_LOG(m_logger) << "Processing initial situation data.";
int ownEquipmentNumber = m_tabocDtc->specificsEquipment(1).equipmentNumber;
int ownParticipantId = -1;
for (const int &participantId : qAsConst(m_participantId2EqId))
{
if (m_participantId2EqId.value(participantId) == ownEquipmentNumber)
{
ownParticipantId = participantId;
break;
} // if
} // for
removeAllPositions();
m_participantData.clear();
m_equipmentPositions.clear();
m_equipmentStates.clear();
m_equipmentOperationalStates.clear();
m_specialSensorData.clear();
m_participantId2EqId.clear();
if (ownParticipantId == -1)
ERROR_LOG(m_logger) << "Mapping of own participant id and equipment number is unknown.";
else
m_participantId2EqId.insert(ownParticipantId, ownEquipmentNumber);
} // if
if (!decodeSituationData(data))
return;
if (!m_participantData.isEmpty())
sendParticipantData(++m_requestId);
sendTrainingAreaBaseData();
}
void HandlerOcSituationData::removeAllPositions()
{
TrainingAreaBase::EquipmentPosition invalidPos;
invalidPos.position.position.lon = nan("");
invalidPos.position.position.lat = nan("");
invalidPos.position.reportingSource = Core::PositionReportingSource::SetByOperator; // SetByOperator -> nan is allowed
invalidPos.sampleTime = m_dds->currentPartitionTime();
for (const auto eqNumber : qAsConst(m_participantId2EqId))
{
invalidPos.equipmentNumber = eqNumber;
m_tabDts->publish(invalidPos);
}
}
void HandlerOcSituationData::setInvalidPositionForParticipant(int participantId)
{
TrainingAreaBase::EquipmentPosition invalidPos;
invalidPos.position.position.lon = nan("");
invalidPos.position.position.lat = nan("");
invalidPos.position.reportingSource = Core::PositionReportingSource::SetByOperator; // SetByOperator -> nan is allowed
invalidPos.sampleTime = m_dds->currentPartitionTime();
invalidPos.equipmentNumber = -participantId; // negative -> treat equipNo as participantId
m_tabDts->publish(invalidPos);
}
void HandlerOcSituationData::onModifyParticipantDataReply(const Core::FunctionReplyObject &reply)
{
if (!m_modifiyRequests.contains(reply.id()))
{
ERROR_LOG(m_logger) << "Got a ModifyParticipantDataReply without matching ID!";
return;
}
if (reply.error())
ERROR_LOG(m_logger) << "ModifyParticipantDataRequest " << reply.id() << " could not be handled: " << reply.str();
else
{
DEBUG_LOG(m_logger) << "ModifyParticipantDataRequest " << reply.id() << " handled successfully by PPM!";
delete (m_modifiyRequests.value(reply.id()));
m_modifiyRequests.remove(reply.id());
sendTrainingAreaBaseData(); // send TAB data for new participants
}
}
bool HandlerOcSituationData::decodeSituationData(const QByteArray &data)
{
if (data.isEmpty())
{
ERROR_LOG(m_logger) << "Got an empty QByteArray to decode.";
return false;
}
bool participantOverloaded = false;
LtsUtils::get1Byte(data, participantOverloaded);
//send overload topic
TrainingAreaBaseOC::ParticipantsOverload participantsOverload;
participantsOverload.id = 1;
participantsOverload.hasOverload = participantOverloaded;
m_tabocDts->publish(participantsOverload);
DEBUG_LOG(m_logger) << "publish overload: " << participantOverloaded;
QByteArray data2 = data;
data2.remove(0, 1);
// shouldn't be needed in 99,9% of all cases but just to be sure
m_ppmDtc->waitForHistoricalDataParticipant(5000);
m_ppmDtc->waitForHistoricalDataParty(5000);
m_ppmDtc->waitForHistoricalDataType(5000);
QByteArray uncompressed = MainHandler::tryUncompress(data2, m_bWithCompression);
if (uncompressed.isEmpty())
{
INFO_LOG(m_logger) << "Got an empty QByteArray after uncompression.";
return false;
}
TRACE_LOG(m_logger) << "Uncompressed Data: " << uncompressed.toHex(' ');
DEBUG_LOG(m_logger) << " m_equipmentPositions size before decode: " << m_equipmentPositions.size()
<< " m_equipmentStates size before decode: " << m_equipmentStates.size()
<< " m_equipmentOperationalStates size before decode: " << m_equipmentOperationalStates.size()
<< " m_specialSensorData size before decode: " << m_specialSensorData.size();
size_t pos = 0;
int participantId = 0;
OcSituation::ModificationBits modifiedFlag;
int modId = 1000000;
while (getParticipantId(uncompressed, pos, participantId, modifiedFlag) && participantId != 0)
{
INFO_LOG(m_logger) << "Participant: " << participantId << " is modified: " << modifiedFlag;
// outdated handling
if (modifiedFlag == OcSituation::ModificationFlag::eNone)
{
// directly remove and go on with next participant
setInvalidPositionForParticipant(participantId);
continue;
}
// position
bool bRet = false;
TrainingAreaBase::EquipmentPosition position;
position.position.reportingSource = Core::PositionReportingSource::Gps; // we don't know better
bRet |= getPosition2D(uncompressed, pos, position, (modifiedFlag & OcSituation::ModificationFlag::ePosition2D) == OcSituation::ModificationFlag::ePosition2D);
bRet |= getPosition3D(uncompressed, pos, position, (modifiedFlag & OcSituation::ModificationFlag::ePosition3D) == OcSituation::ModificationFlag::ePosition3D);
if (bRet)
m_equipmentPositions.insert(participantId, position);
// state
TrainingAreaBase::EquipmentState state;
TrainingAreaBase::ParticipantSanitation sanitaion;
bRet = getVBG(uncompressed, pos, state, sanitaion, (modifiedFlag & OcSituation::ModificationFlag::eVBG) == OcSituation::ModificationFlag::eVBG);
if (bRet)
{
DEBUG_LOG(m_logger) << "Adding EquipmentState to internal map: " << state;
m_equipmentStates.insert(participantId, state);
DEBUG_LOG(m_logger) << "Adding ParticipantSanitation to internal map: " << sanitaion;
m_participantSanitations.insert(participantId, sanitaion);
}
// sensorData and operationalState
TrainingAreaBase::SpecialSensorData sensorData;
TrainingAreaBase::EquipmentOperationalState opState;
bRet = getState(uncompressed, pos, sensorData, opState, (modifiedFlag & OcSituation::ModificationFlag::eState) == OcSituation::ModificationFlag::eState);
if (bRet)
{
DEBUG_LOG(m_logger) << "Adding SpecialSensorData to internal map: " << sensorData;
DEBUG_LOG(m_logger) << "Adding EquipmentOperationalState to internal map: " << opState;
m_specialSensorData.insert(participantId, sensorData);
m_equipmentOperationalStates.insert(participantId, opState);
}
// participantData
bRet = false;
ParticipantManagement::ParticipantData participantData = m_ppmDtc->convertParticipantToParticipantData(m_participants.value(participantId));
bRet |= getStructure(uncompressed, pos, participantData, (modifiedFlag & OcSituation::ModificationFlag::eStructure) == OcSituation::ModificationFlag::eStructure);
bRet |= getDesignator(uncompressed, pos, participantData, (modifiedFlag & OcSituation::ModificationFlag::eDesignator) == OcSituation::ModificationFlag::eDesignator);
bRet |= getSymbolSidc(uncompressed, pos, participantData, (modifiedFlag & OcSituation::ModificationFlag::eSymbol) == OcSituation::ModificationFlag::eSymbol);
//sanity
bool bInsane = false;
if (bRet && (participantData.partyId <= 0 || participantData.id <= 0 || participantData.typeId <= 0))
bInsane = true;
if (bRet && !bInsane)
{
int unitId = Settings::unitOffset() + participantData.partyId;
if (participantData.id == 0) // if new participant
{
participantData.id = participantId;
participantData.modId = modId++;
participantData.hierarchyNumber = participantId; // it just needs to be unqiue here
}
//always set superiorModId! changeing party Id only won't work
participantData.superiorModId = unitId;
participantData.assignState = ParticipantManagement::ParticipantAssignState::HandoutExcon; // set always as we can deduce it anyway //TODO cant reasign EQ nr
if (!m_participantData.contains(unitId)) // Adding Unit to participantData, if not already added
{
m_participantData.insert(unitId, m_ppmDtc->convertParticipantToParticipantData(m_participants.value(unitId)));
DEBUG_LOG(m_logger) << "Adding unit to m_participantData: " << m_participantData.value(unitId);
}
m_participantData.insert(participantId, participantData);
DEBUG_LOG(m_logger) << "Adding ParticipantData to internal map: " << participantData;
if (participantData.equipmentNumber.is_set())
m_participantId2EqId[participantId] = participantData.equipmentNumber.get();
else
m_participantId2EqId[participantId] = 0; // so the positions can be removed, see e.g. removeAllPositions()
}
// no position update for ourself. Easier to filter here than in server but a bit overhead in TETRA
if (m_tabocDtc->specificsEquipment(1).equipmentNumber == m_participantId2EqId.value(participantId))
m_equipmentPositions.remove(participantId);
}
DEBUG_LOG(m_logger) << " m_equipmentPositions size after decode: " << m_equipmentPositions.size()
<< " m_equipmentStates size after decode: " << m_equipmentStates.size()
<< " m_equipmentOperationalStates size after decode: " << m_equipmentOperationalStates.size()
<< " m_specialSensorData size after decode: " << m_specialSensorData.size();
return true;
}
void HandlerOcSituationData::sendParticipantData(int requestId)
{
if (m_modifiyRequests.contains(requestId))
{
WARNING_LOG(m_logger) << "Resending modfiyParticipantDataRequest: " << requestId;
m_ppmFtc->modifyParticipantDataRequest(*m_modifiyRequests.value(requestId), this);
return;
}
ParticipantManagement::ModifyParticipantDataRequest *newRequest = new ParticipantManagement::ModifyParticipantDataRequest();
newRequest->requestId = requestId;
newRequest->data = LtsUtils::convert<std::vector<ParticipantManagement::ParticipantData>>(m_participantData);
newRequest->forceAddWithId = true;
m_modifiyRequests.insert(requestId, newRequest);
m_participantData.clear();
INFO_LOG(m_logger) << "Sending modfiyParticipantDataRequest: " << requestId;
m_ppmFtc->modifyParticipantDataRequest(*newRequest, this);
}
template <typename T>
void HandlerOcSituationData::publishMap(QMap<int, T> &map)
{
for (auto it = map.begin(); it != map.end();)
{
if (m_participants.value(it.key()).id > 0) // publishing only for existing participants
{
auto topic = it.value();
topic.equipmentNumber = m_participantId2EqId.value(it.key());
m_tabDts->publish(topic);
it = map.erase(it);
}
else
++it;
}
}
void HandlerOcSituationData::sendTrainingAreaBaseData()
{
publishMap<TrainingAreaBase::EquipmentPosition>(m_equipmentPositions);
publishMap<TrainingAreaBase::EquipmentState>(m_equipmentStates);
publishMap<TrainingAreaBase::EquipmentOperationalState>(m_equipmentOperationalStates);
publishMap<TrainingAreaBase::SpecialSensorData>(m_specialSensorData);
publishMap<TrainingAreaBase::ParticipantSanitation>(m_participantSanitations);
}
bool HandlerOcSituationData::getParticipantId(const QByteArray &buf, size_t &pos, int &participantId, OcSituation::ModificationBits &modifiedFlags) const
{
bool bRet = true;
bRet &= LtsUtils::get2Byte(buf, pos, participantId);
bRet &= LtsUtils::get1Byte(buf, pos, modifiedFlags);
return bRet;
}
bool HandlerOcSituationData::getPosition2D(const QByteArray &buf, size_t &pos, TrainingAreaBase::EquipmentPosition &position, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// lon, lat, sampleTime
double tmp;
bRet &= LtsUtils::get8Byte(buf, pos, tmp);
position.position.position.lon = tmp;
bRet &= LtsUtils::get8Byte(buf, pos, tmp);
position.position.position.lat = tmp;
position.sampleTime = m_dds->currentPartitionTime();
return bRet;
}
bool HandlerOcSituationData::getPosition3D(const QByteArray &buf, size_t &pos, TrainingAreaBase::EquipmentPosition &position, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// lon, lat, sampleTime
bRet &= getPosition2D(buf, pos, position, bAvailable);
// z
double tmp;
int roomId = -1;
int buildingId = -1;
bRet &= LtsUtils::get8Byte(buf, pos, tmp);
position.position.position.z = tmp;
bRet &= LtsUtils::get2Byte(buf, pos, buildingId);
bRet &= LtsUtils::get4Byte(buf, pos, roomId);
if (buildingId > 0 && roomId > 0)
{
position.position.moutRoomId.set(roomId);
position.position.moutObjectId.set(buildingId);
position.position.reportingSource = (tmp >= 0.0) ? Core::PositionReportingSource::Ops : Core::PositionReportingSource::OpsTunnel;
}
return bRet;
}
bool HandlerOcSituationData::getVBG(const QByteArray &buf, size_t &pos, TrainingAreaBase::EquipmentState &equipmentState, TrainingAreaBase::ParticipantSanitation &sanitation, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// vulnerabilityState, vulnerabilityBits, originatorParty, sampleTime
uint64_t tmp;
bRet &= LtsUtils::get1Byte(buf, pos, tmp);
equipmentState.vulnerabilityState = static_cast<ProjectDataManagement::VulnerabilityState>(tmp);
bRet &= LtsUtils::get4Byte(buf, pos, tmp);
equipmentState.vulnerabilityKindId = tmp;
bRet &= LtsUtils::get2Byte(buf, pos, tmp);
equipmentState.vulnerabilityBits = tmp;
bRet &= LtsUtils::get1Byte(buf, pos, tmp);
if (!m_knownParties.contains(tmp))
{
WARNING_LOG(m_logger) << "Party: " << tmp << " unknown, setting equipmentState.originatorId() to "
<< Settings::unitOffset() + 1;
//Sollte nicht passieren, doch falls ja gibt es goldene Kreuze beim Ausfall
tmp = 1;
}
equipmentState.originatorId = Settings::unitOffset() + tmp;
if (m_bWithVBGTime)
{
bRet &= LtsUtils::get4Byte(buf, pos, tmp);
equipmentState.sampleTime = tmp * 1000UL;
}
else
equipmentState.sampleTime = QDateTime::currentMSecsSinceEpoch();
quint8 vitRest;
bRet &= LtsUtils::get1Byte(buf, pos, vitRest);
if (bRet)
{
int vit = (vitRest & 0x0F) * 10;
equipmentState.vitality = vit;
TrainingAreaBase::SanitationResult result = static_cast<TrainingAreaBase::SanitationResult>(vitRest >> 4);
sanitation.sanitationResult = result;
sanitation.proceduresTaken = 1;
sanitation.qualification = TrainingAreaBase::Qualification::SANPers;
}
qint8 originator = 0;
bRet &= LtsUtils::get1Byte(buf, pos, originator);
if (bRet)
equipmentState.originatorType = static_cast<ProjectDataManagement::StateChangeOriginator>(originator);
return bRet;
}
bool HandlerOcSituationData::getState(const QByteArray &buf, size_t &pos, TrainingAreaBase::SpecialSensorData &specialSensorData, TrainingAreaBase::EquipmentOperationalState &equipmentOperationalState, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// operationBits
int tmp;
bRet &= LtsUtils::get1Byte(buf, pos, tmp);
specialSensorData.sampleTime = m_dds->currentPartitionTime();
equipmentOperationalState.nbcProtectionActive = (tmp & 0x02) || (tmp & 0x04);
equipmentOperationalState.isManipulation = tmp & 0x02;
equipmentOperationalState.sampleTime = m_dds->currentPartitionTime();
return bRet;
}
bool HandlerOcSituationData::getStructure(const QByteArray &buf, size_t &pos, ParticipantManagement::ParticipantData &participant, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// equipmentNumber, typeId, partyId
int tmp;
bRet &= LtsUtils::get2Byte(buf, pos, tmp);
participant.equipmentNumber = dds::core::optional<int32_t>(tmp);
bRet &= LtsUtils::get2Byte(buf, pos, tmp);
participant.typeId = tmp;
bRet &= LtsUtils::get2Byte(buf, pos, tmp);
if (m_knownParties.contains(tmp))
participant.partyId = tmp;
else
{
WARNING_LOG(m_logger) << "Not setting new party: " << tmp << " for participant: " << participant.id << " as the party is unknown -> do a base download.";
bRet = false;
}
#ifndef GUEZ_MODE
bRet &= LtsUtils::get2Byte(buf, pos, tmp);
if (m_participants.contains(tmp))
participant.superiorModId(tmp);
else
WARNING_LOG(m_logger) << "Not setting new superioir ID: " << tmp << " for participant: " << participant.id() << " as the superior is unknown -> do a base download.";
#endif
return bRet;
}
bool HandlerOcSituationData::getDesignator(const QByteArray &buf, size_t &pos, ParticipantManagement::ParticipantData &participant, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
// designator, tacticalIdentifier
int tmp;
bRet &= LtsUtils::get1Byte(buf, pos, tmp);
size_t len1 = tmp & 0x0F;
size_t len2 = (tmp & 0xF0) >> 4;
//hier wurde direkt auf dem Buffer zugegriffen ohne vorher zu prüfen ob es überhaupt passt
if ((size_t)buf.size() < pos + len1)
bRet = false;
if (bRet)
participant.designator = buf.mid(pos, len1).toStdString();
pos += len1;
if ((size_t)buf.size() < pos + len2)
bRet = false;
if (bRet)
participant.tacticalIdentifier = buf.mid(pos, len2).toStdString();
pos += len2;
return bRet;
}
bool HandlerOcSituationData::getSymbolSidc(const QByteArray &buf, size_t &pos, ParticipantManagement::ParticipantData &participant, bool bAvailable) const
{
if (!bAvailable)
return false;
bool bRet = true;
size_t len;
bRet &= LtsUtils::get1Byte(buf, pos, len);
if (len == 0)
{
// get symbol from type id
const auto type = m_ppmDtc->type(participant.typeId);
participant.symbol = type.symbol;
return bRet;
}
if ((size_t)buf.size() < pos + len)
bRet = false;
if (bRet)
participant.symbol = buf.mid(pos, len).toStdString();
pos += len;
return bRet;
}
void HandlerOcSituationData::onPartyChanged(const ParticipantManagement::Party &party, DataModification mod)
{
switch (mod)
{
case DataModification::eAdded:
case DataModification::eModified:
m_knownParties.insert(party.id);
break;
case DataModification::eRemoved:
m_knownParties.remove(party.id);
break;
}
}
void HandlerOcSituationData::onParticipantChanged(const ParticipantManagement::Participant &participant, DataModification mod)
{
switch (mod)
{
case DataModification::eAdded:
case DataModification::eModified:
m_participants[participant.id] = participant;
// if there is a participant but it has (currently) no equipno, remove it from sitmap by setting invalid position
if (!m_participantId2EqId.contains(participant.id))
setInvalidPositionForParticipant(participant.id);
break;
case DataModification::eRemoved:
m_participants.remove(participant.id);
break;
}
}
} // namespace OcSituationClient