#include "CoPrtCnvRadioTelegram.hpp" #include #include using N_CoPrtCnv::CoPrtCnvRadioTelegram; using namespace N_CoPrtCnv; constexpr uint8_t CoPrtCnvRadioTelegram::MAGIC_NUMBER; constexpr uint16_t CoPrtCnvRadioTelegram::DATA_IND_REQ_WITH_TL_HEADER_LENGTH; constexpr uint16_t CoPrtCnvRadioTelegram::DATA_IND_REQ_HEADER_LENGTH; constexpr uint16_t CoPrtCnvRadioTelegram::DATA_IND_REQ_NO_TL_HEADER_LENGTH; constexpr uint16_t CoPrtCnvRadioTelegram::MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH; constexpr uint16_t CoPrtCnvRadioTelegram::PAYLOAD_STATUS_LENGTH; constexpr uint8_t CoPrtCnvRadioTelegram::MAX_NBR_OF_FRAGMENTS; int CoPrtCnvRadioTelegram::s_2ndDelay = 60; int CoPrtCnvRadioTelegram::s_3rdDelay = 60; int CoPrtCnvRadioTelegram::s_maxBroadcastSendCnt = 0; bool CoPrtCnvRadioTelegram::s_etsMode = false; static const N_CoPrtCnv::CoPrtCnvRadioTelegram::E_ProtocolIdentifier s_protocolIdentifier = N_CoPrtCnv::CoPrtCnvRadioTelegram::e_TDSMessageWithoutSDS_TL; CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const uint32_t a_sourceISSI) : m_data(9, Qt::Uninitialized), m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { setHeader(m_data, e_RegisterInd); setSrcISSI(a_sourceISSI); } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const uint32_t a_sourceISSI, const std::set &groups) : m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { uint16_t len = groups.size(); if (len > 5) len = 5; m_data.resize(12 + len * 4); setHeader(m_data, e_GroupMonitorReq); setSrcISSI(a_sourceISSI); uint8_t mode = 1; set1Byte(m_data, 9, mode); set2Byte(m_data, 10, len); std::set::iterator it = groups.begin(); for (uint16_t i = 0; i < len; i++) { set4Byte(m_data, 12 + i * 4, *it); ++it; } } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const uint32_t a_sourceISSI, const E_DeliveryStatusForTL_Report a_delivery) : m_data(17, Qt::Uninitialized), m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { setHeader(m_data, e_ReportIndReq); setSrcISSI(a_sourceISSI); setDestISSI(a_sourceISSI); set1Byte(m_data, 13, s_protocolIdentifier); set1Byte(m_data, 14, 0); set1Byte(m_data, 15, 0); set1Byte(m_data, 16, a_delivery); } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const QByteArray &a_data) : m_data(a_data), m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const gef_telegramm_id_et a_telegramId, const uint16_t a_payloadSize, const uint16_t a_identity) : m_data(a_payloadSize + DATA_IND_REQ_HEADER_LENGTH, '\0'), m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { setHeader(m_data, e_DataIndReq); fillDataIndReqHeader(m_data); m_PayloadIndex = DATA_IND_REQ_HEADER_LENGTH - PAYLOAD_STATUS_LENGTH; set1Byte(m_data, m_PayloadIndex++, e_NotFragmented); set1Byte(m_data, m_PayloadIndex++, a_telegramId); set1Byte(m_data, m_PayloadIndex++, 0); set1Byte(m_data, m_PayloadIndex--, 0); setIdentity(a_identity); m_PayloadIndex += 2; } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const E_LoginStatus a_loginTelegramStatus, const uint16_t a_identity, uint32_t a_issi) : m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { const bool withGroupIssis = (a_loginTelegramStatus & e_LoginOtherExercise) == e_LoginOtherExercise; m_data.resize(DATA_IND_REQ_HEADER_LENGTH + (withGroupIssis ? 12 : 3)); setHeader(m_data, e_DataIndReq); fillDataIndReqHeader(m_data); setDestISSI(a_issi); uint16_t size = m_data.size(); set2Byte(m_data, DATA_IND_REQ_WITH_TL_HEADER_LENGTH, 8 * (size - DATA_IND_REQ_HEADER_LENGTH_WITHOUT_PAYLOAD_HEADER)); m_PayloadIndex = DATA_IND_REQ_HEADER_LENGTH - PAYLOAD_STATUS_LENGTH; set1Byte(m_data, m_PayloadIndex++, e_NotFragmented + e_LoginTelegram); set1Byte(m_data, m_PayloadIndex++, (a_identity >> 8) & 0x00FF); set1Byte(m_data, m_PayloadIndex++, a_identity & 0x00FF); set1Byte(m_data, m_PayloadIndex++, a_loginTelegramStatus); } void CoPrtCnvRadioTelegram::setLoginIssis(int hostIssi, int groupIssi1, int groupIssi2) { setPayload3Byte(hostIssi); setPayload3Byte(groupIssi1); setPayload3Byte(groupIssi2); } CoPrtCnvRadioTelegram::CoPrtCnvRadioTelegram(const std::shared_ptr &a_bigTelegram, const uint8_t a_fragmentIndex, bool &a_error) : m_data(), m_PayloadIndex(0), m_BroadcastCounter(0), m_NextSendTime(0), m_IsBroadcastOnly(false), m_BroadcastOnlyTelegram(nullptr), m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram")) { static const uint16_t MAX_TELEGRAM_SIZE = MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH - PAYLOAD_STATUS_LENGTH; const int payloadSize = a_bigTelegram->getSize() - DATA_IND_REQ_HEADER_LENGTH; const uint8_t nbrOfFragmentedTelegrams = uint8_t((payloadSize - 1) / MAX_TELEGRAM_SIZE); if (nbrOfFragmentedTelegrams > (MAX_NBR_OF_FRAGMENTS - 1)) { ERROR_LOG(m_logger) << "Radio-telegram is too big (" << nbrOfFragmentedTelegrams + 1 << " fragments). There are max " << MAX_NBR_OF_FRAGMENTS << " fragments of size=" << MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH << " (=" << MAX_NBR_OF_FRAGMENTS *MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH << " bytes) possible."; a_error = true; return; } if (nbrOfFragmentedTelegrams == 0) { ERROR_LOG(m_logger) << "Radio-telegram is too small for fragmentation. It must have at least a size of " << (MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + 1); a_error = true; return; } uint8_t payloadStatus = e_FirstFragment | nbrOfFragmentedTelegrams; const uint16_t payloadSizeNormal = uint16_t(payloadSize / (nbrOfFragmentedTelegrams + 1)); const uint16_t payloadSizeLast = uint16_t(payloadSize - (nbrOfFragmentedTelegrams * payloadSizeNormal)); uint16_t size = payloadSizeNormal; if (0 != a_fragmentIndex) { if (nbrOfFragmentedTelegrams != a_fragmentIndex) { payloadStatus = e_MiddleFragment | (nbrOfFragmentedTelegrams - a_fragmentIndex); } else { payloadStatus = e_LastFragment; size = payloadSizeLast; } } size += PAYLOAD_STATUS_LENGTH; m_data.resize(size + DATA_IND_REQ_WITH_TL_HEADER_LENGTH + sizeof(PrtCnvRadioGwHeader)); setHeader(m_data, e_DataIndReq); fillDataIndReqHeader(m_data); m_PayloadIndex = DATA_IND_REQ_HEADER_LENGTH - PAYLOAD_STATUS_LENGTH; set1Byte(m_data, m_PayloadIndex++, payloadStatus); if (size > 1) { const char *srcData = a_bigTelegram->getData() + DATA_IND_REQ_HEADER_LENGTH + a_fragmentIndex * payloadSizeNormal; const uint8_t *ptr = reinterpret_cast(srcData); setPayloadData(ptr, size - 1); } else { ERROR_LOG(m_logger) << "Internal error: size must be bigger than 1"; a_error = true; return; } a_error = false; } CoPrtCnvRadioTelegram::~CoPrtCnvRadioTelegram() { if (m_BroadcastOnlyTelegram != nullptr) ERROR_LOG(m_logger) << "Internal error: m_BroadcastOnlyTelegram was not nullptr while deletion of radio-telegram"; } void CoPrtCnvRadioTelegram::setPayload1Byte(const uint8_t a_value) { set1Byte(m_data, m_PayloadIndex, a_value); m_PayloadIndex += 1; } void CoPrtCnvRadioTelegram::setPayload2Byte(const uint16_t a_value) { setPayloadData(a_value, 2); } void CoPrtCnvRadioTelegram::setPayload3Byte(const uint32_t a_value) { setPayloadData(a_value, 3); } void CoPrtCnvRadioTelegram::setPayload4Byte(const uint32_t a_value) { setPayloadData(a_value, 4); } void CoPrtCnvRadioTelegram::setPayloadData(uint32_t a_value, const uint8_t a_size) { for (int i = m_PayloadIndex + a_size - 1; i >= m_PayloadIndex; i--) { set1Byte(m_data, i, a_value & 0xFF); a_value >>= 8; } m_PayloadIndex += a_size; } void CoPrtCnvRadioTelegram::setPayloadData(const uint8_t *a_data, const uint16_t a_size) { uint8_t *ptr = reinterpret_cast(m_data.data()); memcpy(&ptr[m_PayloadIndex], a_data, a_size); m_PayloadIndex += a_size; } void CoPrtCnvRadioTelegram::fixupTelegramIdForEts(bool convertToEtsId) { if (!s_etsMode) return; char *ptr = &m_data.data()[DATA_IND_REQ_HEADER_LENGTH]; if (convertToEtsId) ++(*ptr); else --(*ptr); } void CoPrtCnvRadioTelegram::setEtsMode(bool bIsEtsMode) { s_etsMode = bIsEtsMode; } gef_telegramm_id_et CoPrtCnvRadioTelegram::getTelID() const { return static_cast(static_cast(m_data.at(DATA_IND_REQ_HEADER_LENGTH))); } uint8_t CoPrtCnvRadioTelegram::zaehler() const { return static_cast(m_data.at(1)); } void CoPrtCnvRadioTelegram::setZaehler(const uint8_t a_counter) { set1Byte(m_data, 1, a_counter); } uint32_t CoPrtCnvRadioTelegram::getPayloadDataUInt32(size_t a_offset, uint16_t a_size) const { uint32_t value = 0; const uint8_t *ptr = reinterpret_cast(m_data.data()); uint8_t *ptrValue = reinterpret_cast(&value); a_offset += DATA_IND_REQ_HEADER_LENGTH; switch (a_size) { case 4: ptrValue[3] = ptr[a_offset++]; Q_FALLTHROUGH(); case 3: ptrValue[2] = ptr[a_offset++]; Q_FALLTHROUGH(); case 2: ptrValue[1] = ptr[a_offset++]; Q_FALLTHROUGH(); case 1: ptrValue[0] = ptr[a_offset++]; break; default: Q_ASSERT(false); ERROR_LOG(m_logger) << "a_size must be between 1..4"; } return value; } uint8_t CoPrtCnvRadioTelegram::getPayloadDataUInt8(size_t a_offset) const { return static_cast(m_data.at(DATA_IND_REQ_HEADER_LENGTH + a_offset)); } uint32_t CoPrtCnvRadioTelegram::getDestISSI() const { return getUInt32(9); } uint16_t CoPrtCnvRadioTelegram::getIdentity() const { return isLoginTelegram() ? getPayloadDataUInt32(0, 2) : getPayloadDataUInt32(1, 2); } void CoPrtCnvRadioTelegram::setSrcISSI(const uint32_t a_ISSI) { setSrcISSI_Internal(m_data, a_ISSI); } bool CoPrtCnvRadioTelegram::mustBroadcastBeSet() const { if (isNormalTelegram()) return getIdentity() == 0 && getBroadcastIndex() == 0; return false; } void CoPrtCnvRadioTelegram::setBroadcastIndex(const uint16_t a_broadcastIndex) { set1Byte(m_data, DATA_IND_REQ_HEADER_LENGTH + 3, (a_broadcastIndex >> 8) & 0xFF); set1Byte(m_data, DATA_IND_REQ_HEADER_LENGTH + 4, a_broadcastIndex & 0xFF); } void CoPrtCnvRadioTelegram::setDestISSI(const uint32_t a_ISSI) { setDestISSI_Internal(m_data, a_ISSI); } CoPrtCnvRadioTelegram::E_PDU_Type CoPrtCnvRadioTelegram::getPDU_Type() const { return static_cast(m_data.at(4)); } CoPrtCnvRadioTelegram::E_FragmentStatus CoPrtCnvRadioTelegram::getFragmentStatus() const { return static_cast(m_data.at(26) & e_FragmentStatusMask); } uint8_t CoPrtCnvRadioTelegram::getFragmentCounter() const { return static_cast(m_data.at(26) & e_CounterMask); } bool CoPrtCnvRadioTelegram::isCrcOk() const { if ((m_data.at(26) & e_Checksum) == e_Checksum) { uint32_t sum = getProtocolIdentifier(); for (int i = DATA_IND_REQ_HEADER_LENGTH - PAYLOAD_STATUS_LENGTH; i < m_data.size() - 2; i++) sum += static_cast(m_data.at(i)); uint16_t checksum = getUInt8(m_data.size() - 2) * 256 + getUInt8(m_data.size() - 1); checksum = ~checksum; checksum++; auto that = const_cast(this); that->m_data.resize(m_data.size() - 2); that->m_data[26] = (m_data.at(26) & ~e_Checksum); if (sum != checksum) { ERROR_LOG(m_logger) << "Checksum of telegram is wrong: " << QString::number(checksum, 16) << " (expected: " << QString::number(sum, 16) << "), issi: " << getUInt32(5); return true; } } return true; } uint32_t CoPrtCnvRadioTelegram::getSrcISSI() const { return getUInt32(5); } bool CoPrtCnvRadioTelegram::isLoginTelegram() const { if (e_DataIndReq == getPDU_Type()) return (e_LoginTelegram == ((e_FragmentStatusMask | e_CounterMask) & m_data.at(26))); return false; } bool CoPrtCnvRadioTelegram::mustBeFragmented() const { if (e_DataIndReq == getPDU_Type()) return size_t(getSize()) > (MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + DATA_IND_REQ_WITH_TL_HEADER_LENGTH + sizeof(PrtCnvRadioGwHeader)); return false; } const uint8_t *CoPrtCnvRadioTelegram::getPayloadDataPtr(uint16_t a_offset) const { return reinterpret_cast(&m_data.data()[DATA_IND_REQ_HEADER_LENGTH + a_offset]); } uint8_t *CoPrtCnvRadioTelegram::getPayloadDataPtr(uint16_t a_offset) { return reinterpret_cast(&m_data.data()[DATA_IND_REQ_HEADER_LENGTH + a_offset]); } uint16_t CoPrtCnvRadioTelegram::getPayloadSize() const { return static_cast((getPDU_Type() == e_DataIndReq) ? m_data.size() - DATA_IND_REQ_HEADER_LENGTH : 0); } QByteArray CoPrtCnvRadioTelegram::getUnfragmentedData(const std::multimap &a_fragTelegramList) { NGL::INGLLogger &l = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram"); uint16_t totalSize = 0; bool first = true; uint8_t expectedNextFragment = 0; CoPrtCnvRadioTelegram *firstFragment = nullptr; std::multimap::const_reverse_iterator rit; for (rit = a_fragTelegramList.rbegin(); rit != a_fragTelegramList.rend(); ++rit) { CoPrtCnvRadioTelegram *fragRadioTelegram = rit->second; if (first) { if (e_FirstFragment == fragRadioTelegram->getFragmentStatus()) { totalSize += fragRadioTelegram->getSize(); expectedNextFragment = fragRadioTelegram->getFragmentCounter() - 1; firstFragment = fragRadioTelegram; first = false; } else { ERROR_LOG(l) << "The fragment with the highest counter is not marked as first fragment"; return nullptr; } } else { if (e_MiddleFragment == fragRadioTelegram->getFragmentStatus() || e_LastFragment == fragRadioTelegram->getFragmentStatus()) { if (expectedNextFragment-- == fragRadioTelegram->getFragmentCounter()) { if (e_LastFragment == fragRadioTelegram->getFragmentStatus() && 0 != fragRadioTelegram->getFragmentCounter()) { ERROR_LOG(l) << "The last fragment must have a counter of 0, but the counter is " << (int)fragRadioTelegram->getFragmentCounter(); return nullptr; } totalSize += fragRadioTelegram->getPayloadSize(); } else { ERROR_LOG(l) << "The fragmented Radio-Telegram from TNE with id=" << fragRadioTelegram->getIdentity() << " with fragment-counter=" << (expectedNextFragment + 1) << " was lost"; return nullptr; } } else { ERROR_LOG(l) << "A fragment which is not the first one, is not marked as middle or last fragment"; return nullptr; } } } if (firstFragment && totalSize <= (DATA_IND_REQ_HEADER_LENGTH + MAX_NBR_OF_FRAGMENTS * (MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH - PAYLOAD_STATUS_LENGTH)) && totalSize > DATA_IND_REQ_HEADER_LENGTH) { QByteArray data(totalSize, Qt::Uninitialized); setHeader(data, e_DataIndReq); fillDataIndReqHeader(data); setSrcISSI_Internal(data, firstFragment->getSrcISSI()); setDestISSI_Internal(data, firstFragment->getDestISSI()); uint16_t index = DATA_IND_REQ_HEADER_LENGTH - PAYLOAD_STATUS_LENGTH; set1Byte(data, index++, e_NotFragmented); for (rit = a_fragTelegramList.rbegin(); rit != a_fragTelegramList.rend(); ++rit) { CoPrtCnvRadioTelegram *fragRadioTelegram = rit->second; uint16_t fragSize = fragRadioTelegram->getPayloadSize(); if (index + fragSize <= totalSize) { memcpy(&data.data()[index], fragRadioTelegram->getPayloadDataPtr(0), fragSize); index += fragSize; } else { ERROR_LOG(l) << "Internal error: size of allocated datablock is too small"; return QByteArray(); } } return data; } ERROR_LOG(l) << "Internal error while building unfragmented radio-telegram: fragTelegram=" << firstFragment << " totalSize=" << totalSize; return QByteArray(); } bool CoPrtCnvRadioTelegram::isTelegramLengthOk(const uint16_t a_lengthWithoutFrame) { return (a_lengthWithoutFrame <= (MAX_RADIO_TELEGRAM_PAYLOAD_LENGTH + DATA_IND_REQ_HEADER_LENGTH - sizeof(PrtCnvRadioGwHeader) - PAYLOAD_STATUS_LENGTH)); } bool CoPrtCnvRadioTelegram::isNormalTelegram() const { return (e_DataIndReq == getPDU_Type() && (e_NormalTelegram == ((e_FragmentStatusMask | e_CounterMask) & m_data.at(26)))); } uint8_t CoPrtCnvRadioTelegram::getPriority() const { if (isNormalTelegram()) { switch (getTelID()) { case GEF_INIT: case GEF_DEAKTIVIERUNG_EINZELMINE: case GEF_TREFFER_EREIGNIS_SEKUNDAERWIRKUNG: case GEF_SANITAETSDIENST_INSTANDSETZUNG: case GEF_SANITAETSDIENST_INSTANDSETZUNG_PVSAN: return 2; case GEF_WIRKFELD_EINZELGESCHOSS: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_STOP: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN: case GEF_WIRKFELD_MINENSPERRE: case GEF_WIRKFELD_STATUS_MINENSPERRE: case GEF_WIRKFELD_A_KAMPFMITTEL: case GEF_WIRKFELD_C_KAMPFMITTEL: case GEF_DEAKTIVIERUNG_WIRKFELD: case GEF_REAKTIVIERUNG_DUS_MINE: case GEF_TECHNIK_FUNKTIONSRAUM: case GEF_FUNKTIONSRAUM: case GEF_DEAKTIVIEREN_FUNKTIONSRAUM: case GEF_DATENVERKEHR_TDS_GEOP: case GEF_WIRKFELD_EINZELMINENSIMULATOR: case GEF_POSITIONS_MELDUNG: return 3; case GEF_UMGEBUNGS_MELDUNG2: case GEF_AUFFORDERUNG_BITE_MELDUNG: case GEF_LIZENSIERUNG_TNE: case GEF_TREFFER_AUSLOESUNG: case GEF_SPRACHAUSWAHL: case GEF_BESTANDSMELDUNG_SANITAETSMATERIAL: case GEF_SELEKTIVE_AUFMUNITIONIERUNG: case GEF_SCHIEDSRICHTERKOMMANDO: return 4; case GEF_KONFIGURATION_WIRKMODELL: case GEF_ANFORDERUNG_KONFIGURATIONSDATEN: case GEF_WIRKFELD_AENDERUNG_BEZEICHNER: case GEF_DATENVERKEHR_TDS_SRT: case GEF_DATENVERKEHR_TDS_DMF: case GEF_DATENVERKEHR_TDS_TGTC: case GEF_TEILNEHMERMODE_SETZEN: case GEF_TEILNEHMER_KONFIG_ANFORDERN: case GEF_TEILNEHMER_KONFIG_SETZEN: return 5; case GEF_ALLGEMEINE_NACHRICHT: return 6; case GEF_SW_VERSIONSMELDUNG: return 7; case GEF_NOP: case GEF_WF_SAMMELDEAKTIVIERUNG: case GEF_WF_SAMMELDEAKTIVIERUNG_DUS_MINEN: case GEF_WF_SAMMELDEAKTIVIERUNG_FUNKTIONSRAUM: return 99; default: ERROR_LOG(m_logger) << "Priority of telegram with telId=" << getTelID() << " not defined."; #ifdef DEBUG abort(); #endif } } else if (isLoginTelegram()) return 4; return 10; } uint16_t CoPrtCnvRadioTelegram::getBroadcastIndex() const { if (isNormalTelegram() && m_data.size() > (DATA_IND_REQ_HEADER_LENGTH + 4)) return getPayloadDataUInt32(3, 2); return 0; } void CoPrtCnvRadioTelegram::setPayloadDataWithIndex(const uint16_t a_index, const uint32_t a_value, const uint16_t a_size) { if (1 == a_size) set1Byte(m_data, DATA_IND_REQ_HEADER_LENGTH + a_index, a_value); else { uint16_t savedPayloadIndex = m_PayloadIndex; m_PayloadIndex = DATA_IND_REQ_HEADER_LENGTH + a_index; setPayloadData(a_value, a_size); m_PayloadIndex = savedPayloadIndex; } } void CoPrtCnvRadioTelegram::setPayloadDataComplete(const QByteArray &payload) { m_data.resize(payload.size() + DATA_IND_REQ_HEADER_LENGTH); setHeader(m_data, getPDU_Type()); memcpy(&m_data.data()[DATA_IND_REQ_HEADER_LENGTH], payload.data(), payload.size()); } void CoPrtCnvRadioTelegram::resetPayloadSize(const uint16_t a_newPayloadSize) { m_data.resize(a_newPayloadSize + DATA_IND_REQ_HEADER_LENGTH); setHeader(m_data, getPDU_Type()); uint16_t size = m_data.size(); set2Byte(m_data, 22, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH) * 8); set2Byte(m_data, 24, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH)); } void CoPrtCnvRadioTelegram::setIdentity(const uint16_t a_identity) { setPayloadDataWithIndex(isLoginTelegram() ? 0 : 1, a_identity, 2); } void CoPrtCnvRadioTelegram::setProtocolIdentifier(const E_ProtocolIdentifier a_ProtocolIdentifier) { if (e_DataIndReq == getPDU_Type()) set1Byte(m_data, 13, a_ProtocolIdentifier); } void CoPrtCnvRadioTelegram::setMessageReference(const uint8_t a_MessageReference) { if (isSDSTL()) set1Byte(m_data, 14, a_MessageReference); } bool CoPrtCnvRadioTelegram::isSDSTL() const { return (e_DataIndReq == getPDU_Type() && (e_UsingSDS_TL == (e_UsingSDS_TL & getProtocolIdentifier()))); } CoPrtCnvRadioTelegram::E_RegistrationStatus CoPrtCnvRadioTelegram::getRegistrationStatus() const { return static_cast(m_data.at(5)); } std::string CoPrtCnvRadioTelegram::getRegistrationStatusAsText() const { switch (getRegistrationStatus()) { case e_IssiSuccessfulRegistered: return "IssiSuccessfulRegistered"; case e_DuplicateISSI: return "DuplicateISSI"; case e_InvalidISSI: return "InvalidISSI"; case e_InvalidIpAddress: return "InvalidIpAddress"; } return std::to_string(getRegistrationStatus()); } CoPrtCnvRadioTelegram::E_ProtocolIdentifier CoPrtCnvRadioTelegram::getProtocolIdentifier() const { return static_cast(m_data.at(13)); } std::string CoPrtCnvRadioTelegram::getProtocolIdentifierAsText() const { switch (getProtocolIdentifier()) { case e_SimpleTextMessagingWithoutSDS_TL: return "Simple Text Messaging w/o SDS/TL"; case e_SimpleGPS_WithoutSDS_TL: return "Simple GPS w/o SDS/TL"; case e_TDSMessageWithoutSDS_TL: return "TDS Message w/o SDS/TL"; case e_UsingSDS_TL: return "SDS/TL"; case e_SimpleTextMessagingUsingSDS_TL: return "Simple Text Messaging with SDS/TL"; case e_SimpleGPS_UsingSDS_TL: return "Simple GPS with SDS/TL"; case e_LoginTest: return "Login-Test / Hohenfels"; } return std::to_string(getProtocolIdentifier()); } uint8_t CoPrtCnvRadioTelegram::getMessageReference() const { return static_cast(m_data.at(14)); } uint8_t CoPrtCnvRadioTelegram::getAreaSelection() const { return static_cast(m_data.at(15)); } uint8_t CoPrtCnvRadioTelegram::getValidityPeriod() const { return static_cast(m_data.at(16)); } uint32_t CoPrtCnvRadioTelegram::getForwardAddress() const { return getUInt32(17); } CoPrtCnvRadioTelegram::E_Flags CoPrtCnvRadioTelegram::getFlags() const { return static_cast(m_data.at(21)); } std::string CoPrtCnvRadioTelegram::getFlagsAsText() const { switch (getFlags()) { case e_FlagsNone: return "No Flags"; case e_StorageAllowed: return "Storage Allowed"; case e_GroupServiceOrOnlyStandardReportAllowed: return "Group Service or only Standard Report Allowed"; case e_ReceivedConfirmationRequested: return "Received Confirmation Requested"; case e_ConsumedConfirmationRequested: return "Consumed Confirmation Requested"; } std::ostringstream str; str << getFlags(); return str.str(); } uint16_t CoPrtCnvRadioTelegram::getLengthInBits() const { return getUInt16(22); } uint16_t CoPrtCnvRadioTelegram::getLengthInBytes() const { return getUInt16(24); } bool CoPrtCnvRadioTelegram::getWithCrc() const { if (getSize() > 26) return ((m_data.at(26) & e_Checksum) == e_Checksum); return false; } CoPrtCnvRadioTelegram::E_DeliveryStatusForTL_Report CoPrtCnvRadioTelegram::getDelivery() const { return static_cast(m_data.at(16)); } std::string CoPrtCnvRadioTelegram::getDeliveryAsText() const { switch (getDelivery()) { case e_SDS_ReceiptAcknowledgedByDestination: return "SDS receipt acknowledged by destination"; case e_SDS_ConsumedByDestination: return "SDS consumed by destination"; case e_SourceIsNotAuthorizedForSDS: return "Source is not authorized for SDS"; case e_DestinationIsNotAuthorizedForSDS: return "Destination is not authorized for SDS"; case e_UnknownDestinationAddress: return "Unknown destination address"; case e_DeliveryFailed: return "Delivery failed"; case e_DestinationNotRegisteredOnSystem: return "Destination not registered on system"; case e_DestinationQueueFull: return "Destination queue full"; case e_MessageTooLong: return "Message too long"; case e_DestinationHostPC_NotConnected: return "Destination host PC not connected"; case e_ProtocolNotSupported: return "Protocol not supported"; case e_DataCodingSchemeNotSupported: return "Datat coding scheme not supported"; case e_DestinationNotReachable: return "Destination not reachable"; } std::ostringstream str; str << getDelivery(); return str.str(); } int32_t CoPrtCnvRadioTelegram::getPayloadDataSInt24(uint16_t a_offset) const { uint32_t value = getPayloadDataUInt32(a_offset, 3); if (value & 0x800000) value |= 0xFF800000; return value; } bool CoPrtCnvRadioTelegram::needsLicensing() const { if (isNormalTelegram()) { switch (getTelID()) { case GEF_UMGEBUNGS_MELDUNG2: case GEF_LIZENSIERUNG_TNE: case GEF_AUFFORDERUNG_BITE_MELDUNG: case GEF_ALLGEMEINE_NACHRICHT: return false; default: break; } return true; } return false; } bool CoPrtCnvRadioTelegram::hasExpired() const { uint32_t expireTime = getExpireTime(); if (expireTime > 0) { uint32_t now = time(nullptr); return (now >= expireTime); } return false; } uint32_t CoPrtCnvRadioTelegram::getExpireTime() const { if (isNormalTelegram()) { uint32_t wirkBegin = 0; uint32_t wirkDauer = 0; switch (getTelID()) { case GEF_WIRKFELD_EINZELGESCHOSS: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = 5; break; case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 2); break; case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN: wirkBegin = getPayloadDataUInt32(11, 4); wirkDauer = getPayloadDataUInt32(15, 2); break; case GEF_WIRKFELD_A_KAMPFMITTEL: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 2) * 60 * 10; break; case GEF_WIRKFELD_C_KAMPFMITTEL: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 1) * 60; break; case GEF_WIRKFELD_MINENSPERRE: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 1) * 60 * 60; break; default: return 0; } if (0 == wirkDauer) { return 0; } if (wirkBegin > 0) return (wirkBegin + wirkDauer); } return 0; } bool CoPrtCnvRadioTelegram::isContentValid() { if (isNormalTelegram()) { uint16_t wirkfeldkennung = 0; uint8_t wirkfeldtyp = 0; switch (getTelID()) { case GEF_WIRKFELD_EINZELGESCHOSS: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_STOP: case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER_FORTSETZEN: case GEF_WIRKFELD_MINENSPERRE: case GEF_WIRKFELD_STATUS_MINENSPERRE: case GEF_WIRKFELD_A_KAMPFMITTEL: case GEF_WIRKFELD_C_KAMPFMITTEL: case GEF_WIRKFELD_EINZELMINENSIMULATOR: wirkfeldkennung = getPayloadDataUInt32(5, 2); wirkfeldtyp = 3; Q_FALLTHROUGH(); case GEF_DEAKTIVIERUNG_WIRKFELD: case GEF_WIRKFELD_AENDERUNG_BEZEICHNER: if (GEF_DEAKTIVIERUNG_WIRKFELD == getTelID() || GEF_WIRKFELD_AENDERUNG_BEZEICHNER == getTelID()) { wirkfeldtyp = getPayloadDataUInt8(5); wirkfeldkennung = getPayloadDataUInt32(6, 2); } if ((wirkfeldtyp >= 1 && wirkfeldtyp <= 5) || (wirkfeldtyp == 8)) { if (wirkfeldkennung > 0 && wirkfeldkennung <= 32767) return true; ERROR_LOG(m_logger) << "Wirkfeldkennung (" << wirkfeldkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..32768."; return false; } if (GEF_WIRKFELD_AENDERUNG_BEZEICHNER == getTelID() && wirkfeldkennung > 0) return true; ERROR_LOG(m_logger) << "Wirkfeldtyp (" << wirkfeldtyp << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..5 or 8."; return false; case GEF_DEAKTIVIERUNG_EINZELMINE: if (isPayloadLengthOk(13)) { wirkfeldkennung = getPayloadDataUInt32(9, 2); { if (wirkfeldkennung > 32767) { ERROR_LOG(m_logger) << "DeaktivierungEinzelmine: wirkfeldkennung=" << wirkfeldkennung << " too big. Must be 1..32767"; return false; } } return true; } return false; case GEF_TECHNIK_FUNKTIONSRAUM: case GEF_FUNKTIONSRAUM: case GEF_DEAKTIVIEREN_FUNKTIONSRAUM: { uint32_t funktionsraumkennung = getPayloadDataUInt32(5, 2); if (funktionsraumkennung > 0) return true; ERROR_LOG(m_logger) << "Funktionsraumkennung (" << funktionsraumkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..65535."; return false; } case GEF_EINTRETEN_FUNKTIONSRAUM: if (isPayloadLengthOk(15)) { uint32_t funktionsraumkennung = getPayloadDataUInt32(13, 2); if (funktionsraumkennung > 0) return true; ERROR_LOG(m_logger) << "Funktionsraumkennung (" << funktionsraumkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..65535."; return false; } return false; case GEF_SPRACHAUSWAHL: { uint8_t sprache = getPayloadDataUInt8(5); if (sprache <= 3) return true; ERROR_LOG(m_logger) << "Sprache (" << sprache << ") of telegram with telId=" << getTelID() << " is invalid. Must be 0..3."; return false; } case GEF_MANIPULATIONSMELDUNG: return isPayloadLengthOk(12); case GEF_BITE_MELDUNG2: return isPayloadLengthOk(27, 19, 7); case GEF_BETRIEBSZUSTAND_AUSSTATTUNG: return isPayloadLengthOk(9); case GEF_FEHLERMELDUNG_GFD: return isPayloadLengthOk(9); case GEF_POSITIONS_MELDUNG: return isPayloadLengthOk(14); case GEF_POSITIONS_MELDUNG_MIT_TEILNEHMER: return isPayloadLengthOk(15, 14, 2); case GEF_POSITIONS_MELDUNG_LFZ: return isPayloadLengthOk(15); case GEF_TREFFER_EREIGNIS_DUS: return isPayloadLengthOk(23); case GEF_TREFFER_EREIGNIS_WIRKSIMULATION: return isPayloadLengthOk(22); case GEF_SCHIEDSRICHTEREINGRIFF: return isPayloadLengthOk(16); case GEF_SELEKTIVE_AUFMUNITIONIERUNG: return isPayloadLengthOk(8,7,8); case GEF_TAKTISCHER_STATUS: return isPayloadLengthOk(16); case GEF_DATENVERKEHR_SRT_TDS: case GEF_DATENVERKEHR_DMF_TDS: { uint16_t length = m_data.size() - DATA_IND_REQ_HEADER_LENGTH; const uint16_t MIN_LENGTH = 7; if (length >= MIN_LENGTH) return true; ERROR_LOG(m_logger) << "The payload-length of telegram with telId=" << getTelID() << " must be at least " << MIN_LENGTH << ", but it is only " << length; return false; } case GEF_NACHFORDERUNG_BROADCAST_TELEGRAMME: return isPayloadLengthOk(10); case GEF_SONDERSENSORIK_BETRIEBSZUSTAND: return isPayloadLengthOk(9); case GEF_SONDERSENSORIK_FAHRSPUR: return isPayloadLengthOk(15); case GEF_SONDERSENSORIK_DATEN: return isPayloadLengthOk(17); case GEF_SONDERSENSORIK_MIVANH_VERLEGEDATEN: return isPayloadLengthOk(24); case GEF_SONDERSENSORIK_WMIV_VERLEGEDATEN: return isPayloadLengthOk(25); case GEF_SONDERSENSORIK_MIVANH_VERLEGEPOSITION: return isPayloadLengthOk(16); case GEF_DATENVERKEHR_GEOP_TDS: return isPayloadLengthOk(15, 14, 1); case GEF_DATENVERKEHR_TGTC_TDS: return isPayloadLengthOk(8, 7, 1); case GEF_GERAETEEREIGNIS: return isPayloadLengthOk(25, 24, 1); case GEF_WAFFENZUORDNUNG: return isPayloadLengthOk(8, 7, 6); case GEF_MUNITIONSVORRAT_EINER_ZUGEORDNETEN_WAFFE: return isPayloadLengthOk(13, 12, 5); case GEF_IPS_POSITIONSDATEN: return isPayloadLengthOk(8, 7, 2); case GEF_TREFFER_EREIGNIS_SEKUNDAERWIRKUNG: return isPayloadLengthOk({13, 17}); case GEF_TEILNEHMERMODE_MELDUNG: return isPayloadLengthOk(15); case GEF_TEILNEHMER_KONFIG_ANTWORT: return isPayloadLengthOk(27); case GEF_DEFINITION_IED: return isPayloadLengthOk(24); case GEF_ZUSTAND_IED: return isPayloadLengthOk(16); case GEF_STATUS_EINZELMINENSIMULATOR: return isPayloadLengthOk(17); default: break; } } return true; } bool CoPrtCnvRadioTelegram::isPayloadLengthOk(uint16_t a_minLength, const uint16_t a_dataAmountOffset, const uint8_t a_variableDataElementSize) const { if (0 == a_variableDataElementSize) return isPayloadLengthOk(a_minLength); const uint16_t length = getPayloadSize(); if (length >= a_minLength) { uint8_t dataAmount = getPayloadDataUInt8(a_dataAmountOffset); uint16_t dataSize = a_variableDataElementSize * dataAmount; uint16_t expectedLength = dataSize + a_minLength; if (expectedLength != length) { WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << " must be " << expectedLength << ", but it is " << length << " (the amount of variable data in telegram was " << dataAmount << " => " << dataSize << " bytes)"; return false; } } else { WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << " must be at least " << a_minLength << ", but it is only " << length; return false; } return true; } bool CoPrtCnvRadioTelegram::isPayloadLengthOk(uint16_t a_minLength, uint16_t a_maxLength) const { if (a_maxLength == 0) return isPayloadLengthOk(a_minLength); const uint16_t length = getPayloadSize(); if (length < a_minLength) { WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << " must be greater or equal to " << a_minLength << ", but it is " << length; return false; } if (length > a_maxLength) { WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << " must be fewer than " << a_minLength << ", but it is " << length; return false; } return true; } bool CoPrtCnvRadioTelegram::isPayloadLengthOk(uint16_t a_length) const { const uint16_t length = getPayloadSize(); if (length == a_length) return true; WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << " must be " << a_length << ", but it is " << length; return false; } bool CoPrtCnvRadioTelegram::isPayloadLengthOk(const std::vector &a_lengths) const { const uint16_t length = getPayloadSize(); if (std::any_of(a_lengths.begin(), a_lengths.end(), [length](int len) { return len == length;})) return true; WARNING_LOG(m_logger) << "The payload-length of telegram with telId: " << getTelID() << ", is: " << length; return false; } bool CoPrtCnvRadioTelegram::mustBeSentAsBroadcast(time_t a_currentTime, bool &a_wasLastBroadcast, const bool a_updateBroadcastCounter) { a_wasLastBroadcast = false; if (m_BroadcastCounter < s_maxBroadcastSendCnt && m_NextSendTime <= a_currentTime) { if (a_updateBroadcastCounter) { switch (m_BroadcastCounter) { case 0: if (s_2ndDelay >= 0) { m_NextSendTime = time(nullptr) + s_2ndDelay; INFO_LOG(m_logger) << "1st Broadcast-Telegram telId=" << getTelID() << " BC=" << getBroadcastIndex(); break; } ++m_BroadcastCounter; Q_FALLTHROUGH(); case 1: if (s_3rdDelay >= 0) { m_NextSendTime = time(nullptr) + s_3rdDelay; INFO_LOG(m_logger) << "2nd Broadcast-Telegram telId=" << getTelID() << " BC=" << getBroadcastIndex(); break; } ++m_BroadcastCounter; Q_FALLTHROUGH(); default: INFO_LOG(m_logger) << "3rd Broadcast-Telegram telId=" << getTelID() << " BC=" << getBroadcastIndex(); m_NextSendTime = 0; a_wasLastBroadcast = true; } ++m_BroadcastCounter; } return true; } return false; } void CoPrtCnvRadioTelegram::setBroadcastDelayTimes(int a_2ndDelay, int a_3rdDelay) { s_2ndDelay = a_2ndDelay; s_3rdDelay = a_3rdDelay; } void CoPrtCnvRadioTelegram::setMaxBroadcastSendCnt(int a_maxSendCnt) { s_maxBroadcastSendCnt = a_maxSendCnt; } bool CoPrtCnvRadioTelegram::isMovedToBroadcastList() const { return (m_BroadcastCounter >= 1); } void CoPrtCnvRadioTelegram::setBroadcastOnly(const bool a_doSet) { m_IsBroadcastOnly = a_doSet; } bool CoPrtCnvRadioTelegram::isBroadcastOnly() const { return m_IsBroadcastOnly; } void CoPrtCnvRadioTelegram::setBroadcastonlyTelegram(const std::shared_ptr &a_radioTelegram) { if (a_radioTelegram != nullptr && m_BroadcastOnlyTelegram != nullptr) ERROR_LOG(m_logger) << "Internal error: m_BroadcastOnlyTelegram was not nullptr"; if (a_radioTelegram != nullptr) { INFO_LOG(m_logger) << "Set braodcastonly-telegram: telId=" << a_radioTelegram->getTelID() << " BC=" << a_radioTelegram->getBroadcastIndex(); } m_BroadcastOnlyTelegram = a_radioTelegram; } std::shared_ptr CoPrtCnvRadioTelegram::getBroadcastonlyTelegram() const { return m_BroadcastOnlyTelegram; } void CoPrtCnvRadioTelegram::set1Byte(QByteArray &a_data, uint16_t a_offset, uint8_t a_value) { Q_ASSERT(a_offset < a_data.size()); uint8_t *ptr = reinterpret_cast(&a_data.data()[a_offset]); *ptr = a_value; } void CoPrtCnvRadioTelegram::set2Byte(QByteArray &a_data, uint16_t a_offset, uint16_t a_value) { Q_ASSERT(a_offset < a_data.size()); uint16_t *ptr = reinterpret_cast(&a_data.data()[a_offset]); *ptr = a_value; } void CoPrtCnvRadioTelegram::set4Byte(QByteArray &a_data, uint16_t a_offset, uint32_t a_value) { Q_ASSERT(a_offset < a_data.size()); uint32_t *ptr = reinterpret_cast(&a_data.data()[a_offset]); *ptr = a_value; } uint16_t CoPrtCnvRadioTelegram::getUInt16(const uint16_t a_offset) const { const uint16_t *ptr = reinterpret_cast(&m_data.data()[a_offset]); return *ptr; } uint32_t CoPrtCnvRadioTelegram::getUInt32(const uint16_t a_offset) const { const uint32_t *ptr = reinterpret_cast(&m_data.data()[a_offset]); return *ptr; } void CoPrtCnvRadioTelegram::setHeader(QByteArray &data, E_PDU_Type pduType) { set1Byte(data, 0, MAGIC_NUMBER); set1Byte(data, 1, 0); set2Byte(data, 2, data.size() - sizeof(PrtCnvRadioGwHeader)); set1Byte(data, 4, pduType); } void CoPrtCnvRadioTelegram::fillDataIndReqHeader(QByteArray &a_data) { set1Byte(a_data, 4, e_DataIndReq); setSrcISSI_Internal(a_data, 0); setDestISSI_Internal(a_data, 0); set1Byte(a_data, 13, s_protocolIdentifier); set1Byte(a_data, 14, 0); set1Byte(a_data, 15, 0); set1Byte(a_data, 16, 0); set1Byte(a_data, 17, 0); set1Byte(a_data, 18, 0); set1Byte(a_data, 19, 0); set1Byte(a_data, 20, 0); set1Byte(a_data, 21, e_FlagsNone); uint16_t size = a_data.size(); set2Byte(a_data, 22, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH) * 8); set2Byte(a_data, 24, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH)); } void CoPrtCnvRadioTelegram::setSrcISSI_Internal(QByteArray &data, const uint32_t issi) { set4Byte(data, 5, issi); } uint16_t CoPrtCnvRadioTelegram::getBroadcastCounter() const { return m_BroadcastCounter; } void CoPrtCnvRadioTelegram::setDestISSI_Internal(QByteArray &data, const uint32_t issi) { set4Byte(data, 9, issi); }