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

1773 lines
63 KiB
C++

// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
#include "LogViewTelegram.hpp"
#include <CoPrtCnv/CoPrtCnvTelegramConverter.hpp>
#include <CoPrtCnv/CoPrtCnvCentralTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <CoPrtCnv/CoPrtCnvHexDumpHelper.hpp>
#include "LogViewXdrParser.hpp"
#include "LogViewXdrStruct.hpp"
#include "LogViewXdrValue.hpp"
#include "LogViewXdrType.hpp"
#include "LogViewTetraTelegramsParser.hpp"
#include "LogViewField.hpp"
#include <QDateTime>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
#include <QApplication>
#include <time.h>
namespace N_LogView
{
// static member initialization
const LogViewXdrParser* LogViewTelegram::m_Parser = nullptr;
const uint16_t LogViewTelegram::START_OFFSET_CENTRAL = 12;
const uint16_t LogViewTelegram::POS_CENTRAL_COUNTER = 4;
const uint16_t LogViewTelegram::POS_CENTRAL_TELID = 8;
const uint16_t LogViewTelegram::POS_CENTRAL_PARTICIPANTID = 12;
const uint16_t LogViewTelegram::START_OFFSET_TETRA_REGISTER = 5;
const uint16_t LogViewTelegram::START_OFFSET_TETRA_AIRLOAD = 5;
const uint16_t LogViewTelegram::START_OFFSET_TETRA_DATA = 28;
const uint16_t LogViewTelegram::START_OFFSET_TETRA_LOGIN = 27;
const uint16_t LogViewTelegram::START_OFFSET_TETRA_FRAGMENT = 26;
const uint16_t LogViewTelegram::SIZE_CENTRAL_DYN_ARRAY_SIZE = 4;
// ---------------------------------------------------------------
// Copy ctor
// ---------------------------------------------------------------
LogViewTelegram::LogViewTelegram(const LogViewTelegram &o)
: m_Name(o.m_Name)
, m_ToolTipText(o.m_ToolTipText)
, m_WaitTime(o.m_WaitTime)
, m_ParticipantId(o.m_ParticipantId)
, m_ISSI(o.m_ISSI)
, m_AllocatedSize(o.m_AllocatedSize)
, m_ActOffset(o.m_ActOffset)
, m_GpsFieldOffset(o.m_GpsFieldOffset)
, m_TelId(o.m_TelId)
, m_LastUnsignedValue(o.m_LastUnsignedValue)
, m_MaxArraySize(o.m_MaxArraySize)
, m_DynArraySizeMethod(o.m_DynArraySizeMethod)
, m_Destination(o.m_Destination)
, m_Validity(o.m_Validity)
, m_Protocol(o.m_Protocol)
, m_LogTime(o.m_LogTime)
, m_logTimeText(o.m_logTimeText)
, m_GpsTime(o.m_GpsTime)
, m_data(o.m_data)
, m_json(o.m_json)
, m_TetraTelegram(nullptr)
, m_logger(getLogger("LogViewTelegram"))
{
if (!m_data.isEmpty())
m_TetraTelegram = new N_CoPrtCnv::CoPrtCnvRadioTelegram(m_data);
}
// ---------------------------------------------------------------
// Constructor for new telegrams (only receive central-
// telegrams are allowed), which will be edited
// ---------------------------------------------------------------
LogViewTelegram::LogViewTelegram(const N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol a_destination,
int telegramId)
: m_WaitTime(1000)
, m_ParticipantId(0)
, m_ISSI(0)
, m_AllocatedSize(0)
, m_ActOffset(0)
, m_GpsFieldOffset(QVector<uint16_t>())
, m_TelId(telegramId)
, m_LastUnsignedValue(0)
, m_MaxArraySize(0)
, m_DynArraySizeMethod(LogViewXdrStandardType::e_DynArrayMsgLength)
, m_Destination(a_destination)
, m_Validity(e_NotYetEvaluated)
, m_Protocol(static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>(a_destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolMask))
, m_LogTime(0)
, m_GpsTime(0)
, m_TetraTelegram(nullptr)
, m_logger(getLogger("LogViewTelegram"))
{
const uint32_t &waitTime = qgetenv("LogViewWaitTime").toUInt();
if (waitTime > 0)
m_WaitTime = waitTime;
m_ToolTipText.resize(e_ColMax);
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
{
m_Name = jsonParser()->nameFromTelegramId(m_TelId);
m_json[QStringLiteral("type")] = m_Name;
m_json[QStringLiteral("typeId")] = m_TelId;
break;
}
default:
showMessageBox(QObject::tr("Nur Zentral-Telegramme sind erlaubt"));
}
}
// ---------------------------------------------------------------
// Constructor for existing telegrams
// ---------------------------------------------------------------
LogViewTelegram::LogViewTelegram(const N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol a_destination,
const quint64 a_unixTime,
const QByteArray &a_telegramData)
: m_WaitTime(1000)
, m_ParticipantId(0)
, m_ISSI(0)
, m_AllocatedSize(0)
, m_ActOffset(0)
, m_GpsFieldOffset(QVector<uint16_t>())
, m_TelId(0)
, m_LastUnsignedValue(0)
, m_MaxArraySize(0)
, m_DynArraySizeMethod(LogViewXdrStandardType::e_DynArrayMsgLength)
, m_Destination(a_destination)
, m_Validity(e_NotYetEvaluated)
, m_Protocol(static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>(a_destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolMask))
, m_LogTime(a_unixTime)
, m_GpsTime(0)
, m_data(a_telegramData)
, m_TetraTelegram(nullptr)
, m_logger(getLogger("LogViewTelegram"))
{
const uint32_t &waitTime = qgetenv("LogViewWaitTime").toUInt();
if (waitTime > 0)
m_WaitTime = waitTime;
m_ToolTipText.resize(e_ColMax);
// log-time
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls:
{
m_json = QJsonDocument::fromJson(a_telegramData).object();
m_TelId = m_json.value(QLatin1String("typeId")).toInt();//getUInt32(POS_CENTRAL_TELID);
// INFO_LOG(m_logger) << "m_TelId=" << m_TelId;
//m_Name = m_Parser[m_Protocol]->getTelegramName(getTelId());
m_Name = m_json.value(QLatin1String("type")).toString();
switch (getTelId())
{
case TrainingAreaBase::TetraIcd::BESTAETIGUNG:
break;
default:
// the participant-id can not be taken out of xyData.header.identitaet without knowing the exact Wrapper,
// because in some (unclear) cases an unknown 4-byte value is on expected position (maybe some C++ alignment stuff)
//m_ParticipantId = getUInt32(POS_CENTRAL_PARTICIPANTID);
m_ParticipantId = m_json.value(QLatin1String("data"))
.toObject().value(QLatin1String("header"))
.toObject().value(QLatin1String("equipmentNumber")).toInt();
break;
}
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra:
{
m_TetraTelegram = new N_CoPrtCnv::CoPrtCnvRadioTelegram(m_data);
// NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, m_Data, m_Size);
switch (m_TetraTelegram->getPDU_Type())
{
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd:
m_Name = QObject::tr("TETRA-Register-Indication");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
m_Name = QObject::tr("TETRA-Register-Response");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq: {
if (m_TetraTelegram->isLoginTelegram())
{
m_Name = QObject::tr("TETRA-Login-Telegramm");
m_ParticipantId = m_TetraTelegram->getIdentity();
}
else if (m_TetraTelegram->isNormalTelegram())
{
// unfragmented telegram
m_TelId = m_TetraTelegram->getTelID();
m_Name = tetraParser()->getTelegramName(m_TelId);
m_ParticipantId = m_TetraTelegram->getIdentity();
// get the GPS-time
if (getDirection() == N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend)
{
// fixme: mache dies konfigurierbar
uint16_t gpsTimeOffset = 2; // without telID
switch (m_TelId)
{
case GEF_DEAKTIVIERUNG_EINZELMINE:
gpsTimeOffset = 4; // without telID
break;
default:
gpsTimeOffset = 2; // without telID
}
m_GpsTime = getUInt32(START_OFFSET_TETRA_DATA+gpsTimeOffset);
}
}
else
{
// fragmented telegram
switch (m_TetraTelegram->getFragmentStatus())
{
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment:
m_Name = QObject::tr("1. Fragment eines TETRA-Telegrammes");
m_ParticipantId = m_TetraTelegram->getIdentity();
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_MiddleFragment:
m_Name = QObject::tr("%1.-letztes Fragment eines TETRA-Telegrammes").arg(m_TetraTelegram->getFragmentCounter()+1);
// fixme: get participantid over ISSI
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment:
m_Name = QObject::tr("Letztes Fragment eines TETRA-Telegrammes");
// fixme: get participantid over ISSI
break;
default:
m_Name = QObject::tr("TETRA-Telegramm mit Fragment-Status=%1 ist nicht implementiert").arg(m_TetraTelegram->getFragmentStatus());
break;
}
}
// set the ISSI
if ((m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestDirectionMask) == N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive)
{
m_ISSI = m_TetraTelegram->getDestISSI();
}
else
{
m_ISSI = m_TetraTelegram->getSrcISSI();
}
break;
}
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq:
m_Name = QObject::tr("Report-Indication/Request");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_GroupMonitorReq:
m_Name = QObject::tr("Group monitoring Request");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_StatusIndReq:
m_Name = QObject::tr("Status Indication/Request");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
m_Name = QObject::tr("AirInterfaceLoad-Request");
break;
default:
// Commented because of annoying message for Type 240
/* QMessageBox::warning(NULL,
QObject::tr("Unknown PDU-Type"),
QObject::tr("Handling for TETRA-Telegram mit PDU-Type=%1 is not implemented!").arg(m_TetraTelegram->getPDU_Type()));
*/
m_Name = QObject::tr("PDU-Type=%1").arg(m_TetraTelegram->getPDU_Type());
break;
}
}
break;
default:
showMessageBox(QObject::tr("Unknown protocol %1!</b>").arg(m_Protocol), QObject::tr("Unknown protocol"));
m_Validity = e_Invalid;
break;
}
}
LogViewTelegram::~LogViewTelegram()
{
delete m_TetraTelegram;
}
// ---------------------------------------------------------------
// Writes a_value with size a_size at actual position of
// m_Data (given with m_ActOffset). m_ActOffset will be
// updated after writing data.
// If the size of m_Data is not big enough, it will be made
// bigger.
// ---------------------------------------------------------------
void LogViewTelegram::setUIntX(const uint8_t a_size, quint64 a_value, const LogViewXdrStandardType::E_ByteOrder a_byteOrder)
{
// check whether m_Data is big enough
if (m_ActOffset + a_size > m_data.size())
m_data.resize(m_ActOffset + a_size);
// write the data
if (a_byteOrder == LogViewXdrStandardType::e_ByteOrderBigEndian)
{
for (int i = a_size - 1; i >= 0; i--)
{
setData(m_ActOffset+i, a_value & 0x000000FF);
a_value = a_value >> 8;
}
}
else
{
for (int i = 0; i < a_size; i++)
{
setData(m_ActOffset + i, a_value & 0x000000FF);
a_value = a_value >> 8;
}
}
m_ActOffset += a_size;
}
// ---------------------------------------------------------------
// Returns true if a_data could be written.
// Returns false if a_offset is outside of range and therefore
// a_data could not be written.
// ---------------------------------------------------------------
bool LogViewTelegram::setData(const uint16_t a_offset, const uint8_t a_data)
{
if (a_offset < m_data.size())
{
m_data[a_offset] = a_data;
return true;
}
return false;
}
quint64 LogViewTelegram::getLogTimeMsec() const
{
const quint64 logTimeSec = m_LogTime >> 32;
const quint64 logTimeMicroSec = m_LogTime & 0x00FFFFFFFFULL;
return (logTimeSec * 1000 + logTimeMicroSec / 1000);
}
// ---------------------------------------------------------------
// Returns the log-time of the telegram as pretty formatted
// text
// ---------------------------------------------------------------
QString LogViewTelegram::getLogTimeText() const
{
if (m_logTimeText.isEmpty())
{
const QDateTime dt(QDateTime::fromMSecsSinceEpoch(getLogTimeMsec()));
m_logTimeText = dt.toString(QLatin1String("ddd, MMM dd, yyyy hh:mm:ss.zzz"));
}
return m_logTimeText;
}
// ---------------------------------------------------------------
QString LogViewTelegram::getDestinationText() const
{
auto protocol = static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>
(m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolMask);
auto dest = static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>
(m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestDirectionMask);
switch (protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return QLatin1String("C > P");
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return QLatin1String("P > C");
default:
break;
}
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return QLatin1String("W > C");
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return QLatin1String("C > W");
default:
break;
}
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return QLatin1String("P > T");
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return QLatin1String("T > P");
default:
break;
}
break;
}
default:
break;
}
return QStringLiteral("<%1>").arg(m_Destination);
}
// ---------------------------------------------------------------
// Returns the GPS-time of the telegram as pretty formatted
// text. Only telegrams from TETRA to central have a GPS-time,
// for all other telegrams an empty string will be returned.
// ---------------------------------------------------------------
QString LogViewTelegram::getGpsTimeText() const
{
if (m_GpsTime != 0)
return QDateTime::fromMSecsSinceEpoch(m_GpsTime * 1000LL).toString(QLatin1String("hh:mm:ss"));
return QString();
}
// ---------------------------------------------------------------
// Returns the difference between GPS-time and log-time in
// seconds. Only telegrams from TETRA to central have a GPS-
// time (and therefore only for this telegrams a time-
// difference can be calculated), for all other telegrams 0
// will be returned.
// ---------------------------------------------------------------
int32_t LogViewTelegram::getDelay() const
{
if (m_GpsTime != 0)
{
quint64 logTimeSec = m_LogTime >> 32;
return logTimeSec - m_GpsTime;
}
return 0;
}
// ---------------------------------------------------------------
// Returns the tool-tip text for the with a_col defined column
// for this telegram. Its content depends on the type and
// subtype of telegram.
// ---------------------------------------------------------------
QString LogViewTelegram::getToolTipText(const E_Column a_col)
{
if (a_col < e_ColMax)
{
if(a_col == e_ColName && m_ToolTipText[e_ColName].isNull())
createToolTipTextForColName();
if(a_col == e_ColLogTime && m_ToolTipText[e_ColLogTime].isNull()) {
quint64 logTimeSec = m_LogTime >> 32;
quint64 logTimeMicroSec = m_LogTime & 0x00FFFFFFFFULL;
m_ToolTipText[e_ColLogTime] = QStringLiteral("<nobr>%1.%2 s</nobr>")
.arg(logTimeSec)
.arg(logTimeMicroSec);
}
if(a_col == e_ColGpsTime && m_ToolTipText[e_ColGpsTime].isNull()) {
if(m_GpsTime == 0) {
m_ToolTipText[e_ColGpsTime] = QLatin1String(""); // wichtig damit isNull() false ist!
} else {
const QDateTime dtGpsTime(QDateTime::fromMSecsSinceEpoch(m_GpsTime * 1000LL));
m_ToolTipText[e_ColGpsTime] = QStringLiteral("%1<br><nobr>Unixzeit: %2 <b>0x%3</b></nobr>")
.arg(dtGpsTime.toString(QLatin1String("ddd, MMM dd, yyyy hh:mm:ss")))
.arg(m_GpsTime)
.arg(m_GpsTime, 8, 16, QLatin1Char('0'));
}
}
return m_ToolTipText[a_col];
}
return QString();
}
// ---------------------------------------------------------------
void LogViewTelegram::createToolTipTextForColName()
{
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls:
{
const QString jsonStr = QString::fromUtf8(QJsonDocument(m_json).toJson(QJsonDocument::JsonFormat::Indented));
// set the tool-tip-text
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Telegram Id"), getTelId())
+ formatToolTipLine(QObject::tr("Counter"), getUInt32(POS_CENTRAL_COUNTER))
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ formatToolTipLine(QObject::tr("Json Data"), jsonStr, jsonStr.size())
+ QLatin1String("</table>");
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra:
{
switch (m_TetraTelegram->getPDU_Type())
{
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd:
// set the tool-tip-text
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Host ISSI"), m_TetraTelegram->getSrcISSI())
+ formatToolTipLine(QObject::tr("Counter"), m_TetraTelegram->zaehler())
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ QLatin1String("</table>");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Registration Status"), m_TetraTelegram->getRegistrationStatusAsText(), m_TetraTelegram->getRegistrationStatus())
+ formatToolTipLine(QObject::tr("Counter"), m_TetraTelegram->zaehler())
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ QLatin1String("</table>");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq: {
int frameSize = 1;
if(m_TetraTelegram->getWithCrc())
frameSize += 2;
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Source ISSI"), m_TetraTelegram->getSrcISSI())
+ formatToolTipLine(QObject::tr("Destination ISSI"), m_TetraTelegram->getDestISSI())
+ formatToolTipLine(QObject::tr("Protocol Identifier"), m_TetraTelegram->getProtocolIdentifierAsText(), m_TetraTelegram->getProtocolIdentifier())
+ formatToolTipLine(QObject::tr("Message Reference"), m_TetraTelegram->getMessageReference())
+ formatToolTipLine(QObject::tr("Area Selection"), m_TetraTelegram->getAreaSelection())
+ formatToolTipLine(QObject::tr("Validity Period"), m_TetraTelegram->getValidityPeriod())
+ formatToolTipLine(QObject::tr("Forward Address"), m_TetraTelegram->getForwardAddress())
+ formatToolTipLine(QObject::tr("Flags"), m_TetraTelegram->getFlagsAsText(), m_TetraTelegram->getFlags())
+ formatToolTipLine(QObject::tr("Complete Payload in Bits"), m_TetraTelegram->getLengthInBits())
+ formatToolTipLine(QObject::tr("Real Payload in Bytes"), m_TetraTelegram->getLengthInBytes() - frameSize)
+ formatToolTipLine(QObject::tr("Counter"), m_TetraTelegram->zaehler())
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ QLatin1String("</table>");
break;
}
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq:
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Source ISSI"), m_TetraTelegram->getSrcISSI())
+ formatToolTipLine(QObject::tr("Destination ISSI"), m_TetraTelegram->getDestISSI())
+ formatToolTipLine(QObject::tr("Protocol Identifier"), m_TetraTelegram->getProtocolIdentifierAsText(), m_TetraTelegram->getProtocolIdentifier())
+ formatToolTipLine(QObject::tr("Message Reference"), m_TetraTelegram->getMessageReference())
+ formatToolTipLine(QObject::tr("Area Selection"), m_TetraTelegram->getAreaSelection())
+ formatToolTipLine(QObject::tr("Delivery"), m_TetraTelegram->getDeliveryAsText(), m_TetraTelegram->getDelivery())
+ formatToolTipLine(QObject::tr("Counter"), m_TetraTelegram->zaehler())
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ QLatin1String("</table>");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
default:
m_ToolTipText[e_ColName] = QLatin1String("<table>")
+ formatToolTipLine(QObject::tr("Counter"), m_TetraTelegram->zaehler())
+ formatToolTipLine(QObject::tr("Size"), m_data.size())
+ QLatin1String("</table>");
break;
}
break;
}
default:
break;
}
}
union convertHackDouble
{
quint64 i;
double d;
};
union convertHackFloat
{
uint32_t i;
float f;
};
double LogViewTelegram::xdrToDouble(quint64 in)
{
convertHackDouble c;
c.i = in;
return c.d;
}
float LogViewTelegram::xdrToFloat(uint32_t in)
{
convertHackFloat c;
c.i = in;
return c.f;
}
quint64 LogViewTelegram::doubleToXdr(double in)
{
convertHackDouble c;
c.d = in;
return c.i;
}
uint32_t LogViewTelegram::floatToXdr(float in)
{
convertHackFloat c;
c.f = in;
return c.i;
}
// ---------------------------------------------------------------
void LogViewTelegram::resetOffset()
{
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
m_ActOffset = START_OFFSET_CENTRAL;
break;
default:
ERROR_LOG(m_logger) << "resetOffset is only for Central-Protocol implemented";
}
}
// ---------------------------------------------------------------
LogViewField* LogViewTelegram::getFieldTree()
{
// INFO_LOG(m_logger) << "type=" << getStructName().toStdString();
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls:
{
resetOffset();
const QString type = m_json.value(QStringLiteral("type")).toString();
QStringList objPath = {QStringLiteral("data")};
return getFieldTreeJson(m_json.value(QStringLiteral("data")),
type, jsonParser()->structFromType(type), objPath);
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra:
{
QString structName;
switch (m_TetraTelegram->getPDU_Type())
{
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd:
m_ActOffset = START_OFFSET_TETRA_REGISTER;
structName = QLatin1String("TETRA_register_indication_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp:
m_ActOffset = START_OFFSET_TETRA_REGISTER;
structName = QLatin1String("TETRA_register_response_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq:
if (m_TetraTelegram->isLoginTelegram())
{
m_ActOffset = START_OFFSET_TETRA_LOGIN;
structName = QLatin1String("TETRA_login_telegram_rt");
}
else if (m_TetraTelegram->isNormalTelegram())
{
structName = getStructName();
if (structName == QLatin1String("gef_automatic_conversion_rt"))
{
// use the central-structure for telegrams which information on tetra and central-side are identical
auto centralTelegram = N_CoPrtCnv::CoPrtCnvTelegramConverter::convertToCentral(m_TetraTelegram);
if (centralTelegram)
{
// create a temporary central-telegram for getting the field-tree
LogViewTelegram* tmpCentralTelegram = new LogViewTelegram(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral,
m_LogTime,
centralTelegram->data());
LogViewField* fieldTree = tmpCentralTelegram->getFieldTree();
delete tmpCentralTelegram;
delete centralTelegram;
return fieldTree;
}
}
else
{
// conversion to central-telegram was not successful => use tetra-structure
m_ActOffset = START_OFFSET_TETRA_DATA;
structName = getStructName();
}
}
else
{
// fragmented telegram
switch (m_TetraTelegram->getFragmentStatus())
{
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_FirstFragment:
m_ActOffset = START_OFFSET_TETRA_FRAGMENT;
structName = QLatin1String("TETRA_first_fragment_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_MiddleFragment:
m_ActOffset = START_OFFSET_TETRA_FRAGMENT;
structName = QLatin1String("TETRA_middle_fragment_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_LastFragment:
m_ActOffset = START_OFFSET_TETRA_FRAGMENT;
structName = QLatin1String("TETRA_last_fragment_rt");
break;
default:
break;
}
}
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_GroupMonitorReq:
m_ActOffset = START_OFFSET_TETRA_REGISTER;
structName = QLatin1String("TETRA_group_monitoring_ind_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_StatusIndReq:
m_ActOffset = START_OFFSET_TETRA_REGISTER;
structName = QLatin1String("TETRA_status_ind_req_rt");
break;
case N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq:
m_ActOffset = START_OFFSET_TETRA_AIRLOAD;
structName = QLatin1String("TETRA_air_interface_load_req_rt");
break;
default:
m_ActOffset = 0; // data begins at 0
structName = QLatin1String("TETRA_unknown_PDU_rt");
break;
}
return getFieldTree(structName, structName, 1);
}
default:
break;
}
return nullptr;
}
static inline QStringList objectPath(const LogViewField *parent, const QString &name, const QString &nameOverride)
{
auto ret = parent->jsonPath();
if (!nameOverride.isEmpty())
ret += nameOverride;
if (!name.isEmpty())
ret += name;
return ret;
}
LogViewField *LogViewTelegram::getFieldTreeJson(const QJsonValue &data,
const QString &name,
const StructInformation &si,
const QStringList &objectPathBase)
{
auto xdr = new LogViewXdrStruct(name, LogViewXdrElement::e_KindStructElement);
LogViewField *f = new LogViewField(name, LogViewField::e_FieldKindBorder, xdr);
f->setJsonPath(objectPathBase);
f->setDeleteXdr(true);
addFieldTreeStruct(data.toObject(), f, si, QString(), m_logger);
return f;
}
void LogViewTelegram::addFieldTreeStruct(const QJsonObject &parentObject,
LogViewField *parent,
const StructInformation &si,
const QString &objectNameArrayHint,
NGL::INGLLogger &logger)
{
DEBUG_LOG(logger) << "getFieldTreeJson for struct " << si.type << " with #members: " << si.member.size();
auto parser = jsonParser();
for (const MemberInformation &mi : si.member)
{
if (!mi.isVisible)
continue;
const QJsonValue val = parentObject.value(mi.name);
if (mi.isString || mi.isBinary)
{
addFieldTreeJsonString(val, parent, mi.name, mi.isBinary, objectNameArrayHint, LogViewField::e_FieldKindEdit, logger);
continue;
}
if (mi.isPod)
{
addFieldTreeJsonPod(val, parent, mi.name, mi.type, objectNameArrayHint, LogViewField::e_FieldKindEdit, logger);
continue;
}
if (mi.isEnum)
{
addFieldTreeJsonEnum(val, parent, mi.name, mi.type, objectNameArrayHint, LogViewField::e_FieldKindEdit, logger);
continue;
}
if (mi.isStruct)
{
const StructInformation si = parser->structFromType(mi.type);
auto xdr = new LogViewXdrStruct(mi.name, LogViewXdrElement::e_KindStructElement);
LogViewField *f = new LogViewField(mi.name, LogViewField::e_FieldKindBorder, xdr);
f->setJsonPath(objectPath(parent, mi.name, objectNameArrayHint));
f->setDeleteXdr(true);
addFieldTreeStruct(val.toObject(), f, si, objectNameArrayHint, logger);
parent->addChild(f);
continue;
}
if (mi.isArray)
{
const QString innerType = LogViewTetraTelegramsParser::getInnerType(mi.type);
const bool isPod = parser->isPod(innerType);
const bool isEnum = isPod ? false : parser->isEnum(innerType);
const bool isStruct = (isPod || isEnum) ? false : (parser->isStruct(mi.type) || parser->isStruct(innerType));
LogViewXdrElement *xdr;
if (isStruct)
{
xdr = new LogViewXdrStruct(mi.name, LogViewXdrElement::e_KindStruct);
}
else if (isEnum)
{
xdr = new LogViewXdrStruct(mi.name, LogViewXdrElement::e_KindEnum, 16);
}
else // isPod
{
const PodInformation pod = jsonParser()->podFromType(innerType);
auto stdType = LogViewXdrStandardType::e_Float;
if (pod.isBoolean)
stdType = LogViewXdrStandardType::e_Boolean;
else if (pod.isIntegral)
stdType = pod.isSigned ? LogViewXdrStandardType::e_Signed : LogViewXdrStandardType::e_Unsigned;
xdr = new LogViewXdrStandardType(mi.name,
pod.byteLen,
pod.byteLen,
stdType,
LogViewXdrStandardType::e_DynArrayMsgLength,
LogViewXdrStandardType::e_ByteOrderLittleEndian);
}
LogViewField *f = new LogViewField(mi.name, isStruct ? LogViewField::e_FieldKindTab : LogViewField::e_FieldKindPodArray, xdr);
const QString fieldName = isStruct ? QStringLiteral("array: ") + mi.name : mi.name;
f->setJsonPath(objectPath(parent, fieldName, objectNameArrayHint));
f->setDeleteXdr(true);
f->setMaxArraySize(200, true);
f->setType(LogViewTetraTelegramsParser::getInnerType(mi.type));
const QJsonArray arr = val.toArray();
const QString path(QStringLiteral("index: %1"));
if (isStruct)
{
auto si = parser->structFromType(mi.type); // custom struct
if (si.type.isEmpty())
si = parser->structFromType(innerType); // pod?
for (int i = 0; i < arr.size(); ++i)
{
LogViewField *tc = new LogViewField(mi.name + QStringLiteral("TabChild"), LogViewField::e_FieldKindTabChild, xdr);
tc->setJsonPath(f->jsonPath());
f->addChild(tc);
addFieldTreeStruct(arr.at(i).toObject(), tc, si, path.arg(i), logger);
}
}
else if (isPod)
{
for (const auto &ele : arr)
f->addValue(QString::number(ele.toInt()));
}
else if (isEnum)
{
for (const auto &ele : arr)
addFieldTreeJsonEnum(ele, f, QString(), innerType, objectNameArrayHint, LogViewField::e_FieldKindPodArrayChild, logger);
}
else
{
// assume string
for (int i = 0; i < arr.size(); ++i)
addFieldTreeJsonString(arr.at(i), f, QString(), false, path.arg(i), LogViewField::e_FieldKindPodArrayChild, logger);
}
parent->addChild(f);
}
}
}
// ---------------------------------------------------------------
void LogViewTelegram::addFieldTreeJsonString(const QJsonValue &val,
LogViewField *parent,
const QString &name,
bool isBinary,
const QString &objectNameArrayHint,
LogViewField::E_FieldKind fieldKind,
NGL::INGLLogger &logger)
{
DEBUG_LOG(logger) << "addFieldTreeJsonString for " << name << " and value: " << val.toString();
auto xdr = new LogViewXdrStandardType(name,
1,
1,
isBinary ? LogViewXdrStandardType::e_Data : LogViewXdrStandardType::e_Character,
LogViewXdrStandardType::e_DynArrayMsgLength,
LogViewXdrStandardType::e_ByteOrderLittleEndian);
LogViewField *f = new LogViewField(name, fieldKind, xdr);
f->setMaxArraySize(std::numeric_limits<uint16_t>::max(), true);
if (isBinary)
f->addValueBinaryHex(val.toString());
else
f->addValue(val.toString());
f->setJsonPath(objectPath(parent, name, objectNameArrayHint));
f->setDeleteXdr(true);
parent->addChild(f);
}
// ---------------------------------------------------------------
void LogViewTelegram::addFieldTreeJsonPod(const QJsonValue &val,
LogViewField *parent,
const QString &name,
const QString &type,
const QString &objectNameArrayHint,
LogViewField::E_FieldKind fieldKind,
NGL::INGLLogger &logger)
{
DEBUG_LOG(logger) << "addFieldTreeJsonPod for " << name << " and value: " << val.toDouble() << ", type: " << type;
const PodInformation pod = jsonParser()->podFromType(type);
auto stdType = LogViewXdrStandardType::e_Float;
if (pod.isBoolean)
stdType = LogViewXdrStandardType::e_Boolean;
else if (pod.isIntegral)
stdType = pod.isSigned ? LogViewXdrStandardType::e_Signed : LogViewXdrStandardType::e_Unsigned;
auto xdr = new LogViewXdrStandardType(name,
pod.byteLen,
pod.byteLen,
stdType,
LogViewXdrStandardType::e_DynArrayMsgLength,
LogViewXdrStandardType::e_ByteOrderLittleEndian);
LogViewField *f = new LogViewField(name, fieldKind, xdr);
if (pod.isBoolean)
f->addValue(QString::number(val.toBool()));
else if (pod.isIntegral)
f->addValue(QString::number(qRound64(val.toDouble())));
else
f->addValue(QStringLiteral("%1").arg(val.toDouble(), 0, 'f', 10));
f->setJsonPath(objectPath(parent, name, objectNameArrayHint));
f->setDeleteXdr(true);
parent->addChild(f);
}
// ---------------------------------------------------------------
void LogViewTelegram::addFieldTreeJsonEnum(const QJsonValue &val,
LogViewField *parent,
const QString &name,
const QString &type,
const QString &objectNameArrayHint,
LogViewField::E_FieldKind fieldKind,
NGL::INGLLogger &logger)
{
DEBUG_LOG(logger) << "addFieldTreeJsonEnum for " << name << " and value: " << val.toDouble();
const EnumInformation ei = jsonParser()->enumFromType(type);
if (ei.type.isEmpty())
{
WARNING_LOG(logger) << "Can not find enum information for " << type;
return;
}
const int enumVal = val.toInt();
auto xdr = new LogViewXdrStruct(name, LogViewXdrElement::e_KindEnum, 16);
LogViewField *f = new LogViewField(name, fieldKind, xdr);
bool bAddedDefault = false;
for (const EnumValue &ev : ei.values)
{
xdr->addElement(ev.key, ev.value, LogViewXdrElement::e_KindEnumElement);
if (ev.value == enumVal)
{
bAddedDefault = true;
f->addValue(ev.key);
}
}
if (!bAddedDefault)
f->addValue(QStringLiteral("Unknown (%1)").arg(enumVal));
f->setMaxArraySize(0, false);
f->setJsonPath(objectPath(parent, name, objectNameArrayHint));
f->setDeleteXdr(true);
parent->addChild(f);
}
// ---------------------------------------------------------------
QString LogViewTelegram::typeFromStructInformation(const StructInformation &si, const QString &name)
{
return memberFromStructInformation(si, name).type;
}
// ---------------------------------------------------------------
MemberInformation LogViewTelegram::memberFromStructInformation(const StructInformation &si, const QString &name)
{
for(const MemberInformation &mi : si.member)
{
if (mi.name == name)
return mi;
}
return {};
}
// ---------------------------------------------------------------
LogViewField* LogViewTelegram::getFieldTree(const QString& a_type, const QString& a_name, uint16_t a_arraySize, const LogViewXdrType* a_parentElement, const bool a_hasContent)
{
DEBUG_LOG(m_logger) << "a_type=" << a_type << " a_name=" << a_name << " a_arraySize=" << a_arraySize;
const LogViewXdrElement* xdrElement = tetraParser()->getElement(a_type);
if (!xdrElement)
return nullptr;
// xdrElement->dump();
//INFO_LOG(m_logger) << "name=" << xdrElement->getName().toStdString() << " type=" << xdrElement->getType().toStdString() << " value=" << xdrElement->getValue() << " kind=" << xdrElement->getKind() << " hasChilds=" << xdrElement->hasChilds() << " arraySize=" << xdrElement->getArraySize();
uint16_t maxArraySize = 0;
LogViewField* fieldTree = nullptr;
switch (xdrElement->getKind())
{
case LogViewXdrElement::e_KindStruct:
{
// add tab for arrays or border for normal structure
if (a_arraySize == 1)
{
// border for normal structure
fieldTree = new LogViewField(a_name, LogViewField::e_FieldKindBorder, xdrElement);
}
else
{
// static or dynamic array of structures => make tabs
fieldTree = new LogViewField(a_name, LogViewField::e_FieldKindTab, xdrElement);
if (0 != a_arraySize)
{
// static array of structures
fieldTree->setMaxArraySize(0, false);
}
}
// add the childs as struct-elements
uint32_t i = 0;
while (LogViewXdrType* child = static_cast<const LogViewXdrStruct*>(xdrElement)->getChildAsType(i++))
{
bool isDynArray = false;
int arraySize = child->getArraySize();
if (0 == arraySize && !isScalar(child))
{
INFO_LOG(m_logger) << "dyn. array of struc";
arraySize = getDynArraySize(m_ActOffset);
isDynArray = true;
}
else
{
// do not iterate scalar types
if (isScalar(child))
{
arraySize = 1;
}
m_MaxArraySize = child->getMaxArraySize();
}
// INFO_LOG(m_logger) << "struct-element: name=" << child->getName().toStdString() << " arraySize=" << arraySize << " maxArraySize=" << m_DynArrayOffset;
// handle specialcase: dyn. array of structures with actual count of 0 elements
if (0 == arraySize)
{
LogViewField* field = getFieldTree(child->getType(), child->getName(), child->getArraySize(), child);
field->setHasContent(false);
if (isDynArray) field->setMaxArraySize(child->getMaxArraySize(), true); // fixme: kommt weiter unten nochmals
fieldTree->addChild(field);
}
// now the normal-case begins
for (int k=0; k<arraySize; k++)
{
LogViewField* field = getFieldTree(child->getType(), /*childName*/child->getName(), child->getArraySize(), child);
if (isDynArray) field->setMaxArraySize(child->getMaxArraySize(), true); // fixme: kommt weiter oben auch schon
fieldTree->addChild(field);
}
}
break;
}
case LogViewXdrElement::e_KindTypedef:
// get the size of the element (with iterating down until we get a standard-type)
if (a_arraySize != 0) // fixme: dies wird nur bei dyn. arrays von skalaren typen funktionieren
{
// INFO_LOG(m_logger) << "typedef statisch";
a_arraySize = static_cast<const LogViewXdrType*>(xdrElement)->getArraySize();
}
else
{
// fixme
INFO_LOG(m_logger) << "typedef dynamisch";
}
maxArraySize = static_cast<const LogViewXdrType*>(xdrElement)->getMaxArraySize(); // fixme: vielleicht funktioniert das nicht in allen Fällen, funktioniert für allgemeine_nachricht
while (xdrElement->getKind() != LogViewXdrElement::e_KindStandardType)
{
xdrElement = tetraParser()->getElement(static_cast<const LogViewXdrType*>(xdrElement)->getType());
// xdrElement->dump();
// INFO_LOG(m_logger) << "====> typedef name=" << a_name.toStdString() << " type=" << a_type.toStdString() << " a_arraySize=" << a_arraySize << " xdr.name=" << xdrElement->getName().toStdString() << " xdr.isLeaf=" << xdrElement->isLeaf() << " kind=" << xdrElement->getKind();
if (LogViewXdrElement::e_KindEnum == xdrElement->getKind())
{
return getFieldTree(xdrElement->getName(), a_name, a_arraySize);
}
}
Q_FALLTHROUGH();
case LogViewXdrElement::e_KindStandardType:
{
//INFO_LOG(m_logger) << "standardtype: add edit-field a_name=" << a_name.toStdString() << " a_arraySize=" << a_arraySize << " maxArraySize=" << maxArraySize << " m_DynArrayOffset=" << m_DynArrayOffset << " <===========================";
if (a_hasContent)
{
fieldTree = getContent(static_cast<const LogViewXdrStandardType*>(xdrElement), m_ActOffset, a_arraySize);
fieldTree->setName(a_name);
}
else
{
fieldTree = new LogViewField(a_name, LogViewField::e_FieldKindEdit, xdrElement);
fieldTree->setHasContent(false);
// todo: mache test mit telegramm, welches ein dyn. array von strukture mit anzahl 0 enthält (z.B. statistik-nachforderung mit 0 teilnehmern)
}
// set the maximal size of array: static arrays => a_arraySize; dyn. arrays => maxArraySize
if (0 == a_arraySize)
{
// dynamic array
fieldTree->setMaxArraySize(0 == maxArraySize ? m_MaxArraySize : maxArraySize, true);
}
else
{
// static array or no array (== [1])
fieldTree->setMaxArraySize(a_arraySize, false);
}
break;
}
case LogViewXdrElement::e_KindEnum:
{
// evaluate the size
uint16_t size = static_cast<const LogViewXdrStruct*>(xdrElement)->getEnumSize();
uint16_t arraySize = a_arraySize;
if (0 == arraySize)
{
arraySize = getDynArraySize(m_ActOffset);
}
QString tooltipText;
bool isLast = true;
if (1 == arraySize)
{
uint32_t enumValue = getUIntX(m_ActOffset, size);
tooltipText = QStringLiteral("<b>%1 0x%2</b> 0b%3").arg(enumValue)
.arg(enumValue, 2, 16, QLatin1Char('0'))
.arg(enumValue, 8, 2, QLatin1Char('0'));
// only for Tetra and not for arrays: mask out unneeded bits (used for different bitfields within one value)
if (a_parentElement)
{
isLast = a_parentElement->maskValue(enumValue, size, tooltipText);
}
}
// allocate a new field
fieldTree = new LogViewField(a_name, LogViewField::e_FieldKindEdit, xdrElement);
fieldTree->setToolTipText(tooltipText);
// get the enums
for (int i=0; i<arraySize; i++)
{
uint32_t enumValue = getUIntX(m_ActOffset, size);
QString tmp;
if(a_parentElement)
a_parentElement->maskValue(enumValue, size, tmp);
// get the enum-text
QString enumText = QStringLiteral("<%1>").arg(enumValue);
int j=0;
while (LogViewXdrValue* child = (static_cast<const LogViewXdrStruct*>(xdrElement))->getChildAsValue(j++))
{
//INFO_LOG(m_logger) << "child name=" << child->getName().toStdString() << " type=" << child->getType().toStdString() << " value=" << child->getValue();
if (child->getValue() == enumValue)
{
enumText = LogViewField::makeClean(child->getName());
break;
}
}
// add the enum-array-element
fieldTree->addValue(enumText);
if (isLast) m_ActOffset += size;
}
// set the maximal size of array: static arrays => a_arraySize; dyn. arrays => maxArraySize
if (0 == a_arraySize)
{
// dynamic array
fieldTree->setMaxArraySize(0 == maxArraySize ? m_MaxArraySize : maxArraySize, true);
}
else
{
// static array or no array (== [1])
fieldTree->setMaxArraySize(a_arraySize, false);
}
break;
}
default:
break;
}
return fieldTree;
}
// ---------------------------------------------------------------
void LogViewTelegram::dump() const
{
//INFO_LOG(m_logger) << "m_ActOffset=" << m_ActOffset;
NGL::NGLLoggerHelper::hexDumpHelper(m_logger, NGL::LogLevel::Info, m_data.data(), m_data.size());
}
// ---------------------------------------------------------------
QString LogViewTelegram::getStructName() const
{
return isJsonTelegram() ? jsonParser()->nameFromTelegramId(getTelId())
: tetraParser()->getStructName(getTelId());;
}
// ---------------------------------------------------------------
uint16_t LogViewTelegram::getUInt16(const uint16_t a_offset) const
{
return (uint16_t)getUIntX(a_offset, 2);
}
// ---------------------------------------------------------------
uint32_t LogViewTelegram::getUInt32(const uint16_t a_offset) const
{
return getUIntX(a_offset, 4);
}
// ---------------------------------------------------------------
int32_t LogViewTelegram::getInt32(const uint16_t a_offset) const
{
return getUInt32(a_offset);
}
// ---------------------------------------------------------------
quint64 LogViewTelegram::getUIntX(const uint16_t a_offset, const uint8_t a_size, const LogViewXdrStandardType::E_ByteOrder a_byteOrder) const
{
quint64 factor = 1; // used for little endian
quint64 value = getData(a_offset);
for (int i=1; i<a_size; i++)
{
if (LogViewXdrStandardType::e_ByteOrderBigEndian == a_byteOrder)
{
value = value << 8;
value += getData(a_offset+i);
}
else
{
// little endian
factor = factor << 8;
value += factor * getData(a_offset+i);
}
}
return value;
}
// ---------------------------------------------------------------
int32_t LogViewTelegram::getSIntX(const uint16_t a_offset, const uint8_t a_size, const LogViewXdrStandardType::E_ByteOrder a_byteOrder) const
{
uint32_t value = getUIntX(a_offset, a_size, a_byteOrder);
switch (a_size)
{
case 1:
if (value & 0x00000080) value = value | 0xFFFFFF00;
break;
case 2:
if (value & 0x00008000) value = value | 0xFFFF0000;
break;
case 3:
if (value & 0x00800000) value = value | 0xFF000000;
break;
default:
break;
}
return value;
}
// ---------------------------------------------------------------
char LogViewTelegram::getChar(const uint16_t a_offset, const uint8_t a_size) const
{
char theChar = getData(a_offset+a_size-1);
return theChar;
}
// ---------------------------------------------------------------
bool LogViewTelegram::isValid()
{
switch (m_Validity)
{
default:
switch (m_Protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral:
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls:
{
const QJsonValue typeId(m_json.value(QStringLiteral("typeId")));
const QJsonValue data(m_json.value(QStringLiteral("data")));
const QJsonValue type(m_json.value(QStringLiteral("type")));
if (!typeId.isDouble() || !data.isObject() || !type.isString())
return false;
return true;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra:
{
// check whether magic-number is correct
uint8_t magicNumber = static_cast<uint8_t>(m_TetraTelegram->getData()[0]);
if (magicNumber != 0xA5)
{
showMessageBox(QObject::tr("TETRA-Magic-Number is 0x%1 but must be 0xA5").arg(magicNumber, 2, 16, QLatin1Char('0')));
m_Validity = e_Invalid;
return false;
}
// check telegram-length
int length = m_TetraTelegram->getSize();
if (length != m_data.size())
{
showMessageBox(QObject::tr("Telegram length is %1 but must be %2").arg(length+4).arg(m_data.size()));
m_Validity = e_Invalid;
return false;
}
m_Validity = e_Valid;
return true;
}
default:
m_Validity = e_Invalid;
return false;
}
case e_Valid:
return true;
case e_Invalid:
return false;
}
}
// ---------------------------------------------------------------
LogViewField* LogViewTelegram::getContent(const LogViewXdrStandardType* a_standardType, uint16_t& a_offset, const uint16_t a_arraySize)
{
// INFO_LOG(m_logger) << "name=" << a_standardType->getName() << " valueKind=" << a_standardType->getValueKind() << " valueSize=" << a_standardType->getValueSize() << " a_offset=" << a_offset << " a_arraySize=" << a_arraySize;
// allocate a new field
LogViewField* field = new LogViewField(QString(), LogViewField::e_FieldKindEdit, a_standardType);
field->setIsRightAligned(false);
// evaluate the size
const int size = a_standardType->getValueSize();
field->setSize(size);
uint16_t arraySize = a_arraySize;
if (0 == arraySize)
arraySize = getDynArraySize(a_offset);
// INFO_LOG(m_logger) << "name=" << a_standardType->getName().toStdString() << " size=" << size << " arraySize=" << arraySize;
switch (a_standardType->getValueKind())
{
case LogViewXdrStandardType::e_Unsigned:
// get the values
{
const LogViewXdrStandardType::E_ByteOrder byteOrder = a_standardType->getByteOrder();
uint32_t value = 0;
for (int i = 0; i < arraySize; i++)
{
value = getUIntX(i*size+a_offset, size, byteOrder);
field->addValue(QString::number(value));
}
// generate the tool-tip-text
if (arraySize <= 1)
{
if (a_standardType->getDynArraySizeMethod() == LogViewXdrStandardType::e_DynArrayIndex
|| a_standardType->getDynArraySizeMethod() == LogViewXdrStandardType::e_DynArrayIndexDiv8)
{
m_DynArraySizeMethod = a_standardType->getDynArraySizeMethod();
m_LastUnsignedValue = value; // used for dyn. array in TETRA-Protocol
}
}
field->setIsRightAligned(true);
}
break;
case LogViewXdrStandardType::e_Signed:
// get the signed values
{
const LogViewXdrStandardType::E_ByteOrder byteOrder = a_standardType->getByteOrder();
for (int i = 0; i < arraySize; i++)
{
int32_t value = getSIntX(i*size+a_offset, size, byteOrder);
field->addValue(QString::number(value));
}
field->setIsRightAligned(true);
}
break;
case LogViewXdrStandardType::e_Float:
// get the floating-point value
{
const LogViewXdrStandardType::E_ByteOrder byteOrder = a_standardType->getByteOrder();
for (int i = 0; i < arraySize; i++)
{
const quint64 value = getUIntX(i*size+a_offset, size, byteOrder);
if (8 == size) {
field->addValue(QString::number(xdrToDouble(value)));
INFO_LOG(m_logger) << "value=" << xdrToDouble(value);
} else {
field->addValue(QString::number(xdrToFloat(value)));
INFO_LOG(m_logger) << "value=" << xdrToFloat(value);
}
}
field->setIsRightAligned(true); // fixme: vielleicht wird isRightAligned gar nicht benötigt...
}
break;
case LogViewXdrStandardType::e_Character:
// generate the text
{
QByteArray text;
for (int i = 0; i < arraySize; i++)
{
char theChar = getChar(a_offset+(i*size), size);
if (0 == theChar)
break;
text += theChar;
}
field->addValue(QString::fromUtf8(text));
}
break;
case LogViewXdrStandardType::e_Data:
// generate the data-field
for (int i = 0; i < arraySize; i++)
field->addValue(QString::number(getData(a_offset+i)));
break;
default:
ERROR_LOG(m_logger) << "ValueKind " << a_standardType->getValueKind() << " not implemented!";
break;
}
uint16_t sizeOfContent = arraySize * size;
a_offset += sizeOfContent;
// dynamic arrays must have an overall size which is 4-byte aligned
if (0 == a_arraySize)
{
uint16_t rest = sizeOfContent % 4;
if (0 != rest)
{
a_offset += (4 - rest);
// INFO_LOG(m_logger) << "align offset to " << a_offset << " because it was an dyn. array content.";
}
}
return field;
}
// ---------------------------------------------------------------
N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol LogViewTelegram::getDirection() const
{
return static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>(m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestDirectionMask);
}
// ---------------------------------------------------------------
void LogViewTelegram::setJsonValue(const QStringList &path, const QJsonValue &val)
{
m_data.clear();
updateJsonValue(path, val, m_json);
if (path.size() && path.last() == QLatin1String("equipmentNumber"))
m_ParticipantId = m_json.value(QLatin1String("data"))
.toObject().value(QLatin1String("header"))
.toObject().value(QLatin1String("equipmentNumber")).toInt();
}
// ---------------------------------------------------------------
void LogViewTelegram::updateJsonValue(const QStringList &path, const QJsonValue &newVal, QJsonObject &obj)
{
if (path.isEmpty())
return;
if (path.size() > 1)
{
QString key = path.first();
static const QLatin1String arrayMarker(QLatin1String("array: "));
const bool isArray = key.startsWith(arrayMarker);
if (isArray)
{
key = key.mid(arrayMarker.size());
const QJsonValue val = obj.value(key);
QJsonArray arr = val.toArray();
const QStringList subPath = path.mid(1);
updateJsonValue(subPath, newVal, arr);
obj[key] = arr;
}
else
{
const QJsonValue val = obj.value(key);
QJsonObject child = val.toObject();
const QStringList subPath = path.mid(1);
updateJsonValue(subPath, newVal, child);
obj[key] = child;
}
}
else
{
obj[path.first()] = newVal;
}
}
// ---------------------------------------------------------------
void LogViewTelegram::updateJsonValue(const QStringList &path, const QJsonValue &newVal, QJsonArray &arr)
{
if (path.isEmpty())
return;
const QString &key = path.first();
static const QLatin1String indexMarker(QLatin1String("index: "));
if (!key.startsWith(indexMarker))
return;
const int arrayIdx = key.midRef(indexMarker.size()).toInt();
while (arr.size() <= arrayIdx)
arr.append(QJsonValue());
QJsonValue childVal = arr.at(arrayIdx);
if (childVal.isObject() || path.size() > 1)
{
QJsonObject child = childVal.toObject();
const QStringList subPath = path.mid(1);
updateJsonValue(subPath, newVal, child);
arr[arrayIdx] = child;
}
else
{
arr[arrayIdx] = newVal;
}
}
// ---------------------------------------------------------------
QIcon LogViewTelegram::getIcon() const
{
static const QIcon c2p(QLatin1String(":/image/C2P.png"));
static const QIcon p2c(QLatin1String(":/image/P2C.png"));
static const QIcon w2p(QLatin1String(":/image/W2P.png"));
static const QIcon p2w(QLatin1String(":/image/P2W.png"));
static const QIcon t2p(QLatin1String(":/image/T2P.png"));
static const QIcon p2t(QLatin1String(":/image/P2T.png"));
static const QIcon unk(QLatin1String(":/image/Unknown.png"));
N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol protocol, dest;
protocol =(N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol)(m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolMask);
dest = (N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol)(m_Destination & N_CoPrtCnv::CoPrtCnvLogHeader::e_DestDirectionMask);
switch (protocol)
{
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return c2p;
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return p2c;
default:
break;
}
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolWls: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return w2p;
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return p2w;
default:
break;
}
break;
}
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra: {
switch (dest) {
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive:
return p2t;
case N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend:
return t2p;
default:
break;
}
break;
}
default:
break;
}
return unk;
}
// ---------------------------------------------------------------
bool LogViewTelegram::isScalar(const LogViewXdrType* a_xdrType) const
{
const LogViewXdrElement* xdrElement = tetraParser()->getElement(a_xdrType->getType());
if (xdrElement)
{
switch (xdrElement->getKind())
{
case LogViewXdrElement::e_KindStandardType:
case LogViewXdrElement::e_KindEnum:
return true;
case LogViewXdrElement::e_KindStruct:
return false;
case LogViewXdrElement::e_KindTypedef:
return isScalar(static_cast<const LogViewXdrType*>(xdrElement));
/*
e_KindStructElement,
e_KindEnumElement,
e_KindDefine*/
default:
ERROR_LOG(m_logger) << "name=" << xdrElement->getName().toStdString() << " kind=" << xdrElement->getKind() << " case not implemented!";
return false;
}
}
return false;
}
// ---------------------------------------------------------------
void LogViewTelegram::showMessageBox(const QString& a_errorText, const QString& a_title) const
{
quint64 logTimeSec = m_LogTime >> 32;
quint64 logTimeMicroSec = m_LogTime & 0x00FFFFFFFFULL;
const QDateTime dt(QDateTime::fromMSecsSinceEpoch(logTimeSec * 1000 + logTimeMicroSec/1000));
const QString destStr((m_Destination&N_CoPrtCnv::CoPrtCnvLogHeader::e_DestDirectionMask)==N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend ?
QLatin1String("gesendete") : QLatin1String("gelesene"));
QMessageBox::warning(QApplication::topLevelWidgets()[0], a_title,
QString::fromUtf8("Das %5 Telegramm mit Protokoll %6 von %1 (Unix-Zeit: %2) und Länge %3 ist fehlerhaft:<br><br><b>%4</b>")
.arg(dt.toString(Qt::SystemLocaleLongDate)).arg(logTimeSec).arg(m_data.size()).arg(a_errorText, destStr).arg(m_Protocol));
}
// ---------------------------------------------------------------
uint16_t LogViewTelegram::getDynArraySize(uint16_t& a_offset) const
{
uint16_t arraySize;
if (N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolTetra == m_Protocol)
{
switch (m_DynArraySizeMethod) {
case LogViewXdrStandardType::e_DynArrayMsgLength:
arraySize = m_data.size() - m_MaxArraySize;
break;
case LogViewXdrStandardType::e_DynArrayIndexDiv8:
arraySize = (m_LastUnsignedValue+7)/8;
break;
default:
arraySize = m_LastUnsignedValue;
break;
}
}
else
{
// dynamic array => get the next 4 bytes, which contain the lengths of the dynamic array
arraySize = getUIntX(a_offset, SIZE_CENTRAL_DYN_ARRAY_SIZE);
a_offset += SIZE_CENTRAL_DYN_ARRAY_SIZE;
}
return arraySize;
}
// ---------------------------------------------------------------
QString LogViewTelegram::formatToolTipLine(const QString& a_name, const QString& a_text, const uint32_t a_value)
{
static const QString s1(QLatin1String("<tr><td><nobr>%1: </nobr></td><td align=right><b><nobr>%2</nobr></b></td><td align=right><nobr>0x%3</nobr></td><td align=right><nobr>0b%4</nobr></td></tr>"));
static const QString s2(QLatin1String("%1"));
QString num16 = s2.arg(a_value, 2, 16, QLatin1Char('0'));
QString num2 = s2.arg(a_value, 8, 2, QLatin1Char('0'));
QString ret = s1.arg(a_name, a_text, num16, num2);
ret = ret.replace(QLatin1Char('\n'), QLatin1String("<br/>"));
return ret;
}
// ---------------------------------------------------------------
const N_CoPrtCnv::CoPrtCnvRadioTelegram* LogViewTelegram::getTetraTelegram() const
{
return m_TetraTelegram;
}
// ---------------------------------------------------------------
LogViewTetraTelegramsParser *LogViewTelegram::jsonParser()
{
static LogViewTetraTelegramsParser s_parser;
return &s_parser;
}
// ---------------------------------------------------------------
LogViewXdrParser *LogViewTelegram::tetraParser()
{
static LogViewXdrParser s_parser;
return &s_parser;
}
// ---------------------------------------------------------------
// returns the data-byte at a_offset. Returns 0 if a_offset is
// outside of range.
// ---------------------------------------------------------------
uint8_t LogViewTelegram::getData(const uint16_t a_offset) const
{
if (a_offset < m_data.size())
return m_data.at(a_offset);
return 0;
}
// ---------------------------------------------------------------
QByteArray LogViewTelegram::getData(QJsonDocument::JsonFormat format) const
{
if (m_data.isEmpty() && !m_json.isEmpty())
return QJsonDocument(m_json).toJson(format);
return m_data;
}
// ---------------------------------------------------------------
void LogViewTelegram::setNextFieldAsGpsTimeField()
{
m_GpsFieldOffset.append(m_ActOffset);
}
// ---------------------------------------------------------------
void LogViewTelegram::actualizeGpsTime()
{
if (m_GpsFieldOffset.size() > 0)
{
if (N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral == m_Protocol)
{
uint16_t savedOffset = m_ActOffset;
for (QVector<uint16_t>::const_iterator i = m_GpsFieldOffset.cbegin(); i < m_GpsFieldOffset.cend(); ++i)
{
m_ActOffset = *i;
setUIntX(4, ::time(nullptr)); // todo nicht 4 sondern size des typs
}
m_ActOffset = savedOffset;
}
}
if (!m_json.isEmpty())
{
static const QString dataKey = QStringLiteral("data");
if (dataKey.contains(dataKey))
{
auto data = m_json.value(dataKey).toObject();
static const QString hdrKey = QStringLiteral("header");
if (data.contains(hdrKey))
{
auto hdr = data.value(hdrKey).toObject();
hdr[QStringLiteral("timeStamp")] = QDateTime::currentMSecsSinceEpoch();
data[hdrKey] = hdr;
}
m_json[dataKey] = data;
}
m_data.clear();
}
}
} // namespace N_LogView