// ************************************************************************** // // (C) Copyright 2009 Swiss SIMTEC AG // // ************************************************************************** // // $HeadURL$ // $LastChangedBy$ // $LastChangedDate$ // $LastChangedRevision$ // // ************************************************************************** // manual class headers #include "CoPrtCnvRadioTelegram.hpp" #include #include using N_CoPrtCnv::CoPrtCnvRadioTelegram; using namespace N_CoPrtCnv; // static member initialization // waiting for c++17 ... 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; // --------------------------------------------------------------- // constructor for a Register-Ind telegram // --------------------------------------------------------------- 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")) { // prepare a SdsGw-Register-Ind message // ------------------ setHeader(m_data, e_RegisterInd); setSrcISSI(a_sourceISSI); } // --------------------------------------------------------------- // CoPrtCnvRadioTelegram ctor for group monitoring // --------------------------------------------------------------- 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); // fill parameters 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; } } // --------------------------------------------------------------- // constructor for a Report-Ind telegram // --------------------------------------------------------------- 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")) { // prepare a SdsGw-Report-Ind/Req message // -------------------- setHeader(m_data, e_ReportIndReq); // fill parameters setSrcISSI(a_sourceISSI); setDestISSI(a_sourceISSI); set1Byte(m_data, 13, s_protocolIdentifier); // protocol identifier set1Byte(m_data, 14, 0); // message reference set1Byte(m_data, 15, 0); // area selection set1Byte(m_data, 16, a_delivery); // delivery } // --------------------------------------------------------------- // Constructor for received telegrams (Data-Ind/Req, PDU type // = 2) // --------------------------------------------------------------- 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")) { } // --------------------------------------------------------------- // the telegraminformation will be set // --------------------------------------------------------------- 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")) { // prepare a SdsGw-Data-Ind/Req message // ------------------ 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); // payload-header (fragment-counter, CRC-indication-bit) // fill telegram-information (payload-data) set1Byte(m_data, m_PayloadIndex++, a_telegramId); // payload telegrammkennung set1Byte(m_data, m_PayloadIndex++, 0); // payload teilnehmer set1Byte(m_data, m_PayloadIndex--, 0); // payload teilnehmer setIdentity(a_identity); m_PayloadIndex += 2; } // --------------------------------------------------------------- // for login-telegram // --------------------------------------------------------------- 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")) { // prepare a Login-Telegram // -------------- 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); // length in bits 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; // we also have to set the payload status here set1Byte(m_data, m_PayloadIndex++, e_NotFragmented + e_LoginTelegram); // payload-header (fragment-counter, CRC-indication-bit) // fill telegram-information (payload-data) set1Byte(m_data, m_PayloadIndex++, (a_identity >> 8) & 0x00FF); // payload teilnehmer (high-byte) set1Byte(m_data, m_PayloadIndex++, a_identity & 0x00FF); // payload teilnehmer (low-byte) set1Byte(m_data, m_PayloadIndex++, a_loginTelegramStatus); // payload login-status } void CoPrtCnvRadioTelegram::setLoginIssis(int hostIssi, int groupIssi1, int groupIssi2) { // special hack for testing multiple exercises in one physical radio // ISSI for central setPayload3Byte(hostIssi); // GSSI 1 = 5020 setPayload3Byte(groupIssi1); // GSSI 2 = 5021 setPayload3Byte(groupIssi2); } // --------------------------------------------------------------- // for generating fragmented telegrams. // a_error will be true, if the fragmented telegrams cannot be // generated. // --------------------------------------------------------------- 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")) { // first, middle or last fragmented telegram? 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; // without payload-status-byte 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; // alles unter MAX_TELEGRAM_SIZE wird schon 5 Zeilen weiter oben abgeblockt 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) { // middle fragment payloadStatus = e_MiddleFragment | (nbrOfFragmentedTelegrams - a_fragmentIndex); } else { // last fragment payloadStatus = e_LastFragment; size = payloadSizeLast; } } size += PAYLOAD_STATUS_LENGTH; // allocate and prepare the fragmented telegram 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); // payload-header // fill paylaod-data in fragmented telegram 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 { // if the code above is correct, this should never happen! 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"; } // --------------------------------------------------------------- // Sets the payload-data. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Sets the paylaod-data. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // The telegram ids from ETS are +1 everywhere, should be called before crc creation // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Returns the telegramId of radio-telegram. // --------------------------------------------------------------- gef_telegramm_id_et CoPrtCnvRadioTelegram::getTelID() const { return static_cast(static_cast(m_data.at(DATA_IND_REQ_HEADER_LENGTH))); } // --------------------------------------------------------------- // Returns the counter. // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::zaehler() const { return static_cast(m_data.at(1)); } // --------------------------------------------------------------- // Sets the counter. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setZaehler(const uint8_t a_counter) { set1Byte(m_data, 1, a_counter); } // --------------------------------------------------------------- // Returns a paylaod-value. Its size depends on a_size. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Returns a 1-byte paylaod-value. // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::getPayloadDataUInt8(size_t a_offset) const { return static_cast(m_data.at(DATA_IND_REQ_HEADER_LENGTH + a_offset)); } // --------------------------------------------------------------- // Returns the destISSI of radio-telegram. // --------------------------------------------------------------- uint32_t CoPrtCnvRadioTelegram::getDestISSI() const { return getUInt32(9); } // --------------------------------------------------------------- // Returns the identiy of radio-telegram. // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getIdentity() const { return isLoginTelegram() ? getPayloadDataUInt32(0, 2) : getPayloadDataUInt32(1, 2); } // --------------------------------------------------------------- // Sets the srcISSI. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setSrcISSI(const uint32_t a_ISSI) { setSrcISSI_Internal(m_data, a_ISSI); } // --------------------------------------------------------------- // Returns true if it is broadcast-telegram, which has not yet // set a broadcast- index. // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::mustBroadcastBeSet() const { // broadcast must be set when identity is 0 and broadcast-index is 0 too // exception: if telegram is Umgebungsmeldung if (isNormalTelegram()/*e_DataIndReq == m_Data[4]*/) return getIdentity() == 0 && getBroadcastIndex() == 0; return false; } // --------------------------------------------------------------- // Sets the broadcast-index. // --------------------------------------------------------------- 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); } // --------------------------------------------------------------- // Set the dstISSI. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setDestISSI(const uint32_t a_ISSI) { setDestISSI_Internal(m_data, a_ISSI); } // --------------------------------------------------------------- // Returns the PDU-type of radio-telegram. // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_PDU_Type CoPrtCnvRadioTelegram::getPDU_Type() const { return static_cast(m_data.at(4)); } // --------------------------------------------------------------- // Returns the fragment-status of radio-telegram. // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_FragmentStatus CoPrtCnvRadioTelegram::getFragmentStatus() const { return static_cast(m_data.at(26) & e_FragmentStatusMask); } // --------------------------------------------------------------- // Returns the fragment-counter of radio-telegram. // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::getFragmentCounter() const { return static_cast(m_data.at(26) & e_CounterMask); } // --------------------------------------------------------------- // If CRC-bit is set, it checks the CRC and removes CRC-stuff // from telegram. // If CRC-bit is not set, simply true will be returned // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isCrcOk() const { if ((m_data.at(26) & e_Checksum) == e_Checksum) { // check the checksum, which is simply a sum 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++; // remove the checksum-stuff 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; // fixme } } return true; } // --------------------------------------------------------------- // Returns the srcISSI of radio-telegram. // --------------------------------------------------------------- uint32_t CoPrtCnvRadioTelegram::getSrcISSI() const { return getUInt32(5); } // --------------------------------------------------------------- // returns true if the it is a special login telegram // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isLoginTelegram() const { if (e_DataIndReq == getPDU_Type()) return (e_LoginTelegram == ((e_FragmentStatusMask | e_CounterMask) & m_data.at(26))); return false; } // --------------------------------------------------------------- // returns true if the it is a special login telegram // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Returns a pointer to payload-data on a certain position of // radio-telegram. // --------------------------------------------------------------- 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]); } // --------------------------------------------------------------- // the size of payload-data without payload-status-header // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getPayloadSize() const { return static_cast((getPDU_Type() == e_DataIndReq) ? m_data.size() - DATA_IND_REQ_HEADER_LENGTH : 0); } // --------------------------------------------------------------- // Converts the list of fragmented telegrams into 1 big radio- // telegram. // The function allocates a block of data. The caller is // responsible to delete this block of data, when no longer // used. // If conversion fails nullptr will be returned // --------------------------------------------------------------- QByteArray CoPrtCnvRadioTelegram::getUnfragmentedData(const std::multimap &a_fragTelegramList) { NGL::INGLLogger &l = NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CoPrtCnvRadioTelegram"); // check whether all blocks exist 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(); // this size is including all the headers 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(); // this size is without payload-status-header } 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; } } } // allocate and fill the new unfragmented block of data 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); // generate the telegram-header 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); // copy the data from the fragments 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 { // if the code is correct, the program should never be here ERROR_LOG(l) << "Internal error: size of allocated datablock is too small"; return QByteArray(); } } // NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, data, totalSize); return data; } ERROR_LOG(l) << "Internal error while building unfragmented radio-telegram: fragTelegram=" << firstFragment << " totalSize=" << totalSize; return QByteArray(); } // --------------------------------------------------------------- // length without frame-header (4 byte) // --------------------------------------------------------------- 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)); } // --------------------------------------------------------------- // returns true if the it is a normal, unfragmented telegram // (but not a login- telegram) // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isNormalTelegram() const { return (e_DataIndReq == getPDU_Type() && (e_NormalTelegram == ((e_FragmentStatusMask | e_CounterMask) & m_data.at(26)))); } // --------------------------------------------------------------- // Returns the priority of radio-telegram. // --------------------------------------------------------------- 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; // the following telegrams will only need a priority for logging 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."; //NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, m_Data, m_Size); #ifdef DEBUG abort(); #endif } } else if (isLoginTelegram()) return 4; return 10; } // --------------------------------------------------------------- // Returns the broadcast-index of radio-telegram. // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getBroadcastIndex() const { if (isNormalTelegram() && m_data.size() > (DATA_IND_REQ_HEADER_LENGTH + 4)) return getPayloadDataUInt32(3, 2); return 0; } // --------------------------------------------------------------- // Sets the paylaod-data based on a_index with size a_size. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setPayloadDataWithIndex(const uint16_t a_index, const uint32_t a_value, const uint16_t a_size) { // m_Data[DATA_IND_REQ_HEADER_LENGTH+a_index] = ((a_value >> 8) & 0xFF); // m_Data[DATA_IND_REQ_HEADER_LENGTH+a_index+1] = (a_value & 0xFF); 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; } } // --------------------------------------------------------------- // Sets the complete paylaod-data. // --------------------------------------------------------------- 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()); } // --------------------------------------------------------------- // ATTENTION with use of this function, because it is a // (little) dirty trick to resize the sammel-telegrams without // memory-allocation. // It is the responsibity of the user to not overwrite // memory!!! // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::resetPayloadSize(const uint16_t a_newPayloadSize) { m_data.resize(a_newPayloadSize + DATA_IND_REQ_HEADER_LENGTH); setHeader(m_data, getPDU_Type()); // the following length in bits and bytes it the length of payload-data, including payload-status uint16_t size = m_data.size(); set2Byte(m_data, 22, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH) * 8); // length in bits) set2Byte(m_data, 24, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH)); // length in bytes) } // --------------------------------------------------------------- // Set the identity. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setIdentity(const uint16_t a_identity) { setPayloadDataWithIndex(isLoginTelegram() ? 0 : 1, a_identity, 2); } // --------------------------------------------------------------- // Sets the protocol-identifier. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setProtocolIdentifier(const E_ProtocolIdentifier a_ProtocolIdentifier) { if (e_DataIndReq == getPDU_Type()) set1Byte(m_data, 13, a_ProtocolIdentifier); } // --------------------------------------------------------------- // Sets the message-reference. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setMessageReference(const uint8_t a_MessageReference) { if (isSDSTL()) set1Byte(m_data, 14, a_MessageReference); } // --------------------------------------------------------------- // returns true if the it is a SDS/TL telegram // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isSDSTL() const { return (e_DataIndReq == getPDU_Type() && (e_UsingSDS_TL == (e_UsingSDS_TL & getProtocolIdentifier()))); } // --------------------------------------------------------------- // Returns the registration-status of TETRA-header of SdsGw- // Register-Rsp telegram // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_RegistrationStatus CoPrtCnvRadioTelegram::getRegistrationStatus() const { return static_cast(m_data.at(5)); } // --------------------------------------------------------------- // Returns the registration-status of TETRA-header of SdsGw- // Register-Rsp telegram in text-form // --------------------------------------------------------------- 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()); } // --------------------------------------------------------------- // Returns the protocolIdentifier of radio-telegram. // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_ProtocolIdentifier CoPrtCnvRadioTelegram::getProtocolIdentifier() const { return static_cast(m_data.at(13)); } // --------------------------------------------------------------- // Returns the protocolIdentifier of radio-telegram in text- // form // --------------------------------------------------------------- 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()); } // --------------------------------------------------------------- // Returns the message-reference of radio-telegram. // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::getMessageReference() const { return static_cast(m_data.at(14)); } // --------------------------------------------------------------- // Returns the area selection of TETRA-Header // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::getAreaSelection() const { return static_cast(m_data.at(15)); } // --------------------------------------------------------------- // Returns the validity period of radio-telegram. // --------------------------------------------------------------- uint8_t CoPrtCnvRadioTelegram::getValidityPeriod() const { return static_cast(m_data.at(16)); } // --------------------------------------------------------------- // Returns the forward-address of TETRA-Header // --------------------------------------------------------------- uint32_t CoPrtCnvRadioTelegram::getForwardAddress() const { return getUInt32(17); } // --------------------------------------------------------------- // Returns the flags of TETRA-Header // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_Flags CoPrtCnvRadioTelegram::getFlags() const { return static_cast(m_data.at(21)); } // --------------------------------------------------------------- // Returns the flags of TETRA-Header as text // --------------------------------------------------------------- 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(); } // --------------------------------------------------------------- // Returns the length in bits-field of TETRA-Header // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getLengthInBits() const { return getUInt16(22); } // --------------------------------------------------------------- // Returns the length in bytes-field of TETRA-Header // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getLengthInBytes() const { return getUInt16(24); } // --------------------------------------------------------------- // Returns true when the Telegram has a crc checksum // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::getWithCrc() const { if (getSize() > 26) return ((m_data.at(26) & e_Checksum) == e_Checksum); return false; } // --------------------------------------------------------------- // Returns the delivery-field of TETRA-Header of SdsGw-Report- // Ind/Req telegrams. // --------------------------------------------------------------- CoPrtCnvRadioTelegram::E_DeliveryStatusForTL_Report CoPrtCnvRadioTelegram::getDelivery() const { return static_cast(m_data.at(16)); } // --------------------------------------------------------------- // Returns the delivery-field of TETRA-Header of SdsGw-Report- // Ind/Req telegrams as text. // --------------------------------------------------------------- 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(); } // --------------------------------------------------------------- // Converts a signed 3-byte value into a signed 4-byte-value. // I.e. used for positions (north and east). // --------------------------------------------------------------- int32_t CoPrtCnvRadioTelegram::getPayloadDataSInt24(uint16_t a_offset) const { uint32_t value = getPayloadDataUInt32(a_offset, 3); // correct if negative if (value & 0x800000) value |= 0xFF800000; return value; } // --------------------------------------------------------------- // Returns true if the telegram needs licensing. // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::needsLicensing() const { if (isNormalTelegram()) { switch (getTelID()) { // the following telegrams do not need a licensing case GEF_UMGEBUNGS_MELDUNG2: case GEF_LIZENSIERUNG_TNE: case GEF_AUFFORDERUNG_BITE_MELDUNG: case GEF_ALLGEMEINE_NACHRICHT: return false; default: // nothing break; } return true; } return false; } // --------------------------------------------------------------- // returns true when the telegram has an expiry-date and is // expired. // Returns false if the telegram has no expiry-date // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::hasExpired() const { uint32_t expireTime = getExpireTime(); if (expireTime > 0) { uint32_t now = time(nullptr); return (now >= expireTime); } return false; // not expired } // --------------------------------------------------------------- // Returns the expire-time of the telegram. // Returns 0 if the telegram has not expire-time. // --------------------------------------------------------------- uint32_t CoPrtCnvRadioTelegram::getExpireTime() const { if (isNormalTelegram()) { uint32_t wirkBegin = 0; uint32_t wirkDauer = 0; // in seconds switch (getTelID()) { case GEF_WIRKFELD_EINZELGESCHOSS: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = 5; break; case GEF_WIRKFELD_FLAECHENDECKENDES_FEUER: wirkBegin = getPayloadDataUInt32(11 + 5, 4); // attention: consider specialcase witk wirkBegin of 0 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; // 10 min break; case GEF_WIRKFELD_C_KAMPFMITTEL: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 1) * 60; // min break; case GEF_WIRKFELD_MINENSPERRE: wirkBegin = getPayloadDataUInt32(11 + 5, 4); wirkDauer = getPayloadDataUInt32(15 + 5, 1) * 60 * 60; // std break; default: return 0; // no expiry-time } // if wirkDauer == 0 => unlimited if (0 == wirkDauer) { return 0; // 0 => no expire time } // calc the expire-time (if wirkBegin == 0 => specialcase of flaechendeckendes-feuer: not yet active, will be activated with fortsetzen if (wirkBegin > 0) return (wirkBegin + wirkDauer); } return 0; // no expiry-time } // --------------------------------------------------------------- // checks whether the content of telegram is vaild, but only a // few data will be checked, i.e. the kennung of wirkfeld and // funktionsraum. // Returns true if content of telegram is vaild (or will not // be checked) // --------------------------------------------------------------- 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)) // 1 to 5 or 8 (new DUS single mine simulator) { if (wirkfeldkennung > 0 && wirkfeldkennung <= 32767) return true; // valid ERROR_LOG(m_logger) << "Wirkfeldkennung (" << wirkfeldkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..32768."; return false; // wirkfeldkennung out of range } if (GEF_WIRKFELD_AENDERUNG_BEZEICHNER == getTelID() && wirkfeldkennung > 0) return true; // valid // DeaktivierungWirkfeld with wirkfeldtyp 6 and 7 is not meaningful, because for that reason we have the telegram // DeaktivierenFunktionsraum, therefore we do not allow this. ERROR_LOG(m_logger) << "Wirkfeldtyp (" << wirkfeldtyp << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..5 or 8."; return false; // wirkfeldtyp out of range 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; // wirkfeldkennung out of range (>32767) } } return true; // valid } return false; // length not ok case GEF_TECHNIK_FUNKTIONSRAUM: case GEF_FUNKTIONSRAUM: case GEF_DEAKTIVIEREN_FUNKTIONSRAUM: { uint32_t funktionsraumkennung = getPayloadDataUInt32(5, 2); if (funktionsraumkennung > 0) return true; // valid ERROR_LOG(m_logger) << "Funktionsraumkennung (" << funktionsraumkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..65535."; return false; // funktionsraumkennung out of range } case GEF_EINTRETEN_FUNKTIONSRAUM: if (isPayloadLengthOk(15)) { uint32_t funktionsraumkennung = getPayloadDataUInt32(13, 2); if (funktionsraumkennung > 0) return true; // valid ERROR_LOG(m_logger) << "Funktionsraumkennung (" << funktionsraumkennung << ") of telegram with telId=" << getTelID() << " is invalid. Must be 1..65535."; return false; // funktionsraumkennung out of range } return false; // length not ok case GEF_SPRACHAUSWAHL: { uint8_t sprache = getPayloadDataUInt8(5); if (sprache <= 3) return true; // valid ERROR_LOG(m_logger) << "Sprache (" << sprache << ") of telegram with telId=" << getTelID() << " is invalid. Must be 0..3."; return false; // wirkfeldkennung out of range } // check length of telegrams coming from Tetra // =========================================== 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 = ((m_Data[26] & e_Checksum) == e_Checksum) ? 9 : 7; 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: // min 15, variable @14, variable size ist 1 return isPayloadLengthOk(15, 14, 1); case GEF_DATENVERKEHR_TGTC_TDS: // min 8, variable @7, variable size ist 1 return isPayloadLengthOk(8, 7, 1); case GEF_GERAETEEREIGNIS: // min 25, variable @24, variable size ist 1 return isPayloadLengthOk(25, 24, 1); case GEF_WAFFENZUORDNUNG: // min 8, variable @7, variable size ist 6 return isPayloadLengthOk(8, 7, 6); case GEF_MUNITIONSVORRAT_EINER_ZUGEORDNETEN_WAFFE: // min 13, variable @12, variable size ist 5 return isPayloadLengthOk(13, 12, 5); case GEF_IPS_POSITIONSDATEN: // min 8, variable @7, variable size ist 2 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: // no warning, is also called for outgoing telegrams //WARNING_LOG(m_logger) << "Payload length for telegram id " << getTelID() << " is not checked"; break; } } return true; // nothing will be checked => vaild } // --------------------------------------------------------------- // checks the payload-length of telegram. Returns true if the // length is ok. // Logs an error if length not ok. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // returns true if it is time to send this telegram as normal // broadcast. // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::mustBeSentAsBroadcast(time_t a_currentTime, bool &a_wasLastBroadcast, const bool a_updateBroadcastCounter) { a_wasLastBroadcast = false; //INFO_LOG(m_logger) << "telId=" << getTelID() << " m_NextSendTime=" << m_NextSendTime << " actualTime=" << a_actualTime << " m_BroadcastCounter=" << m_BroadcastCounter << " BC=" << getBroadcastIndex() << " z=" << zaehler(); if (m_BroadcastCounter < s_maxBroadcastSendCnt && m_NextSendTime <= a_currentTime) { if (a_updateBroadcastCounter) { // update the broadcastCounter 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; } // --------------------------------------------------------------- // Set the delay-times for 2nd and 3rd broadcast. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setBroadcastDelayTimes(int a_2ndDelay, int a_3rdDelay) { s_2ndDelay = a_2ndDelay; s_3rdDelay = a_3rdDelay; } // --------------------------------------------------------------- // Set the max count a broadcast telegram will be send // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setMaxBroadcastSendCnt(int a_maxSendCnt) { s_maxBroadcastSendCnt = a_maxSendCnt; } // --------------------------------------------------------------- // returns true if it is a broadcast-telegram and it is moved // to broadcast-list // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isMovedToBroadcastList() const { return (m_BroadcastCounter >= 1); } // --------------------------------------------------------------- // Sets the broadcast-only-flag. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setBroadcastOnly(const bool a_doSet) { m_IsBroadcastOnly = a_doSet; } // --------------------------------------------------------------- // Returns the broadcast-only-flag. // --------------------------------------------------------------- bool CoPrtCnvRadioTelegram::isBroadcastOnly() const { return m_IsBroadcastOnly; } // --------------------------------------------------------------- // Defines the telegram which will be sent as normal broadcast // instead a Sammeltelegram. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Returns the telegram which will be sent as normal broadcast // instead a Sammeltelegram. // --------------------------------------------------------------- std::shared_ptr CoPrtCnvRadioTelegram::getBroadcastonlyTelegram() const { return m_BroadcastOnlyTelegram; } // --------------------------------------------------------------- // Set a 1-byte value at offset a_offset. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Set a 2-byte value at offset a_offset. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Set a 4-byte value at offset a_offset. // --------------------------------------------------------------- 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; } // --------------------------------------------------------------- // Returns a 2-byte value in little-endian-format at position // a_offset (based on whole telegram, including TETRA- and // frame-headers). Use this for reading out TETRA-header- // fields. // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getUInt16(const uint16_t a_offset) const { const uint16_t *ptr = reinterpret_cast(&m_data.data()[a_offset]); return *ptr; } // --------------------------------------------------------------- // Returns a 4-byte value in little-endian-format at position // a_offset (based on whole telegram, including TETRA- and // frame-headers). Use this for reading out TETRA-header- // fields. // --------------------------------------------------------------- uint32_t CoPrtCnvRadioTelegram::getUInt32(const uint16_t a_offset) const { const uint32_t *ptr = reinterpret_cast(&m_data.data()[a_offset]); return *ptr; } // --------------------------------------------------------------- // Sets the header with magic-number and size. The length will // be 0. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setHeader(QByteArray &data, E_PDU_Type pduType) { // fill header set1Byte(data, 0, MAGIC_NUMBER); set1Byte(data, 1, 0); // counter will be set when sent set2Byte(data, 2, data.size() - sizeof(PrtCnvRadioGwHeader)); set1Byte(data, 4, pduType); } // --------------------------------------------------------------- // Fills the header with SrcISSI, DstISSI, length in bits and // length in bytes. PDU will be set for DataIndicationRequest. // // ProtocolIdentifier, message reference, area selection, // forward address, flags will be filled with default-values. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::fillDataIndReqHeader(QByteArray &a_data) { set1Byte(a_data, 4, e_DataIndReq); // PDU-type setSrcISSI_Internal(a_data, 0); // will be set when sending setDestISSI_Internal(a_data, 0); // will be set when sending set1Byte(a_data, 13, s_protocolIdentifier); // Protocol identifier set1Byte(a_data, 14, 0); // message reference set1Byte(a_data, 15, 0); // area selection set1Byte(a_data, 16, 0); // validity period (not used) set1Byte(a_data, 17, 0); // forward adddress set1Byte(a_data, 18, 0); // forward adddress set1Byte(a_data, 19, 0); // forward adddress set1Byte(a_data, 20, 0); // forward adddress set1Byte(a_data, 21, e_FlagsNone); // flags // the following length in bits and bytes it the length of payload-data, including payload-status uint16_t size = a_data.size(); set2Byte(a_data, 22, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH) * 8); // length in bits set2Byte(a_data, 24, (size - DATA_IND_REQ_HEADER_LENGTH + PAYLOAD_STATUS_LENGTH)); // length in bytes } // --------------------------------------------------------------- // Internal function to set the srcISSI. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setSrcISSI_Internal(QByteArray &data, const uint32_t issi) { set4Byte(data, 5, issi); } // --------------------------------------------------------------- // nbr of times already sent this normal broadcast (only used // for broadcast- telegrams) // --------------------------------------------------------------- uint16_t CoPrtCnvRadioTelegram::getBroadcastCounter() const { return m_BroadcastCounter; } // --------------------------------------------------------------- // Internal function to set the dstISSI. // --------------------------------------------------------------- void CoPrtCnvRadioTelegram::setDestISSI_Internal(QByteArray &data, const uint32_t issi) { set4Byte(data, 9, issi); }