Files
qt5-legatus-legacy/TetraIF/CoPrtCnv/CoPrtCnvRadioTelegram.cpp
T
2026-08-13 18:33:10 +02:00

1794 lines
62 KiB
C++

// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "CoPrtCnvRadioTelegram.hpp"
#include <time.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
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<uint32_t> &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<uint32_t>::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<CoPrtCnvRadioTelegram> &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<const uint8_t *>(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<uint8_t *>(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<gef_telegramm_id_et>(static_cast<uint8_t>(m_data.at(DATA_IND_REQ_HEADER_LENGTH)));
}
// ---------------------------------------------------------------
// Returns the counter.
// ---------------------------------------------------------------
uint8_t CoPrtCnvRadioTelegram::zaehler() const
{
return static_cast<uint8_t>(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<const uint8_t *>(m_data.data());
uint8_t *ptrValue = reinterpret_cast<uint8_t *>(&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<uint8_t>(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<E_PDU_Type>(m_data.at(4));
}
// ---------------------------------------------------------------
// Returns the fragment-status of radio-telegram.
// ---------------------------------------------------------------
CoPrtCnvRadioTelegram::E_FragmentStatus CoPrtCnvRadioTelegram::getFragmentStatus() const
{
return static_cast<E_FragmentStatus>(m_data.at(26) & e_FragmentStatusMask);
}
// ---------------------------------------------------------------
// Returns the fragment-counter of radio-telegram.
// ---------------------------------------------------------------
uint8_t CoPrtCnvRadioTelegram::getFragmentCounter() const
{
return static_cast<uint8_t>(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<uint8_t>(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<CoPrtCnvRadioTelegram*>(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<const uint8_t *>(&m_data.data()[DATA_IND_REQ_HEADER_LENGTH + a_offset]);
}
uint8_t *CoPrtCnvRadioTelegram::getPayloadDataPtr(uint16_t a_offset)
{
return reinterpret_cast<uint8_t *>(&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<uint16_t>((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<uint16_t, CoPrtCnvRadioTelegram *> &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<uint16_t, CoPrtCnvRadioTelegram *>::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<E_RegistrationStatus>(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<E_ProtocolIdentifier>(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<uint8_t>(m_data.at(14));
}
// ---------------------------------------------------------------
// Returns the area selection of TETRA-Header
// ---------------------------------------------------------------
uint8_t CoPrtCnvRadioTelegram::getAreaSelection() const
{
return static_cast<uint8_t>(m_data.at(15));
}
// ---------------------------------------------------------------
// Returns the validity period of radio-telegram.
// ---------------------------------------------------------------
uint8_t CoPrtCnvRadioTelegram::getValidityPeriod() const
{
return static_cast<uint8_t>(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<E_Flags>(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<E_DeliveryStatusForTL_Report>(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<uint16_t> &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<N_CoPrtCnv::CoPrtCnvRadioTelegram> &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<N_CoPrtCnv::CoPrtCnvRadioTelegram> 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<uint8_t *>(&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<uint16_t *>(&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<uint32_t *>(&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<const uint16_t *>(&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<const uint32_t *>(&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);
}