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

1014 lines
40 KiB
C++

// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#include "LogViewGui.hpp"
#include "LogViewTelegram.hpp"
#include "LogViewXdrParser.hpp"
#include "LogViewTelegramDialog.hpp"
#include "LogViewOptionDialog.hpp"
#include <CoPrtCnv/CoPrtCnvRadioTelegram.hpp>
#include <QDir>
#include <QFile>
#include <QTimer>
#include <QAction>
#include <QApplication>
#include <QCheckBox>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QGroupBox>
#include <QHeaderView>
#include <QJsonArray>
#include <QLabel>
#include <QMessageBox>
#include <QScreen>
#include <QSortFilterProxyModel>
#include <QSplitter>
#include <QStatusBar>
#include <QTableView>
#include <QToolBar>
#include <QToolBox>
#include <QDateTime>
#include <QVBoxLayout>
#include <nglapplication/nglapplication.h>
#include "LogViewInjectionThread.hpp"
#include "LogViewTelegramEdit.hpp"
#include "LogViewTetraTelegramsParser.hpp"
#include <TrainingAreaBase/training_area_telegrams.h>
namespace N_LogView
{
// static member initialization
const QLatin1String LogViewGui::WINDOW_GROUP("MainWindow");
const QLatin1String LogViewGui::SIZE("Size");
const QLatin1String LogViewGui::SCREEN("Screen");
const QLatin1String LogViewGui::POS("Pos");
const QLatin1String LogViewGui::SPLITTPOS("SplittPos");
const QLatin1String LogViewGui::TOOLBOXINDEX("ToolBoxIndex");
// ---------------------------------------------------------------
// ---------------------------------------------------------------
LogViewGui::LogViewGui(QWidget* a_parent)
: QMainWindow(a_parent)
, RADIO_BUTTON_ICON_SIZE(16, 16)
, m_LogfileSize(0)
, m_DoReload(false)
, m_EditTelegramController(new TelegramEdit(this))
, m_TailTimer(nullptr)
, m_InjectionThread(nullptr)
, m_replayTimer(new QTimer(this))
, m_currentReplayRow(-1)
, m_logger(getLogger("LogViewGui"))
{
setWindowIcon(QIcon(QLatin1String(":icons/16x16/rde_16x16.png")));
QSettings settings;
settings.beginGroup(WINDOW_GROUP);
// status-bar
m_InjectionStatus = new QLabel(this);
m_InjectionStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
statusBar()->addPermanentWidget(m_InjectionStatus);
m_ParticipantStatus = new QLabel(this);
m_ParticipantStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
statusBar()->addPermanentWidget(m_ParticipantStatus);
m_TelegramStatus = new QLabel(this);
m_TelegramStatus->setFrameStyle(QFrame::Panel | QFrame::Sunken);
statusBar()->addPermanentWidget(m_TelegramStatus);
// tool-bar
// --------
QToolBar* toolBar = addToolBar(tr("Datei"));
toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
// tool-bar: new logfile
QString tipText;
QAction* action = new QAction(tr("Neue Log-Datei"), this);
action->setIcon(QIcon(QLatin1String(":icons/16x16/document-new.png")));
action->setShortcut(tr("Ctrl+N"));
tipText = tr("Eine neue Telegram-Log-Datei anzeigen (Ctrl-N)");
action->setStatusTip(tipText);
action->setToolTip(tipText);
connect(action, &QAction::triggered, this, &LogViewGui::newTelegramFile);
toolBar->addAction(action);
// tool-bar: add logfile
action = new QAction(tr("Log-Datei hinzu"), this);
action->setIcon(QIcon(QLatin1String(":icons/16x16/document-import.png")));
action->setShortcut(tr("Ctrl+O"));
tipText = tr("Eine weitere Telegramm-Log-Datei anzeigen (Ctrl-O)");
action->setStatusTip(tipText);
action->setToolTip(tipText);
connect(action, &QAction::triggered, this, &LogViewGui::loadTelegramFile);
toolBar->addAction(action);
// tool-bar: reload
m_reloadAction = new QAction(tr("&Reload"), this);
m_reloadAction->setCheckable(true);
m_reloadAction->setChecked(m_DoReload);
m_reloadAction->setIcon(QIcon(QLatin1String(":icons/16x16/edit-redo.png")));
m_reloadAction->setShortcut(tr("Ctrl+R"));
tipText = tr("Reload des Logfiles ein-/ausschalten (Ctrl-R)");
m_reloadAction->setStatusTip(tipText);
m_reloadAction->setToolTip(tipText);
connect(m_reloadAction, &QAction::triggered, this, &LogViewGui::reloadOnOff);
toolBar->addAction(m_reloadAction);
// tool-bar: settings
action = new QAction(tr("&Einstellungen"), this);
action->setIcon(QIcon(QLatin1String(":icons/16x16/configure.png")));
action->setShortcut(tr("Ctrl+E"));
tipText = tr("Verhalten des Programmes einstellen (Ctrl-E)");
action->setStatusTip(tipText);
action->setToolTip(tipText);
connect(action, &QAction::triggered, this, &LogViewGui::changeSettings);
toolBar->addAction(action);
// tool-bar: exit
toolBar->addSeparator();
action = new QAction(tr("&Beenden"), this);
action->setIcon(QIcon(QLatin1String(":icons/16x16/edit-delete.png")));
action->setShortcut(tr("Alt-F4"));
tipText = tr("LogView beenden (Alt-F4)");
action->setStatusTip(tipText);
action->setToolTip(tipText);
connect(action, &QAction::triggered, this, &LogViewGui::close);
toolBar->addAction(action);
createCentralSelectionWidget();
createTetraSelectionWidget();
createWlsSelectionWidget();
// participant-selection
// ---------------------
m_ParticipantTableView = new QTableView(this);
m_participantProxyModel = new QSortFilterProxyModel(this);
m_participantModel = new ParticipantModel(this);
m_participantProxyModel->setSourceModel(m_participantModel);
m_ParticipantTableView->setModel(m_participantProxyModel);
m_ParticipantTableView->setAlternatingRowColors(true);
m_ParticipantTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_ParticipantTableView->setSelectionMode(QAbstractItemView::MultiSelection);
m_ParticipantTableView->setSortingEnabled(true);
m_ParticipantTableView->setTabKeyNavigation(false);
m_ParticipantTableView->verticalHeader()->hide();
m_ParticipantTableView->verticalHeader()->setMinimumSectionSize(QApplication::font().pointSize() * 2.5);
m_ParticipantTableView->verticalHeader()->setDefaultSectionSize(QApplication::font().pointSize() * 2.5);
m_ParticipantTableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
m_ParticipantTableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
connect(m_ParticipantTableView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &LogViewGui::selectedParticipantChanged);
QVBoxLayout* participantBox = new QVBoxLayout;
participantBox->addWidget(m_ParticipantTableView);
m_ParticipantSelectionGroup = new QGroupBox(tr("Teilnehmer-Filter &aktivieren"), this);
m_ParticipantSelectionGroup->setCheckable(true);
m_ParticipantSelectionGroup->setChecked(false);
m_ParticipantSelectionGroup->setLayout(participantBox);
connect(m_ParticipantSelectionGroup, &QGroupBox::toggled, this, &LogViewGui::selectedParticipantChanged);
// left side (filters)
m_ToolBox = new QToolBox(this);
m_ToolBox->addItem(m_CentralSelectionGroup, QIcon(QLatin1String(":/image/Central.png")), tr("&Zentral-Telegramm-Filter"));
m_ToolBox->addItem(m_TetraSelectionGroup, QIcon(QLatin1String(":/image/Tetra.png")), tr("TET&RA-Telegramm-Filter"));
m_ToolBox->addItem(m_WlsSelectionGroup, QIcon(QLatin1String(":/image/Wls.png")), tr("&WLS-Telegramm-Filter"));
m_ToolBox->addItem(m_ParticipantSelectionGroup, QIcon(QLatin1String(":/image/TNE.png")), tr("Teilnehmer-&Filter"));
m_ToolBox->addItem(m_EditTelegramController->tableView(), QIcon(QLatin1String(":icons/16x16/document-edit.png")), tr("&Edit && Sende Telegramme"));
m_ToolBox->setCurrentIndex(settings.value(TOOLBOXINDEX).toInt());
// right side (logged telegrams)
// -----------------------------
m_logTelegramView = new QTableView(this);
m_logTelegramView->setAlternatingRowColors(true);
m_logTelegramView->setSelectionBehavior(QAbstractItemView::SelectRows);
m_logTelegramView->setSelectionMode(QAbstractItemView::ContiguousSelection);
m_logTelegramView->verticalHeader()->hide();
m_logTelegramView->verticalHeader()->setMinimumSectionSize(QApplication::font().pointSize() * 2.5);
m_logTelegramView->verticalHeader()->setDefaultSectionSize(QApplication::font().pointSize() * 2.5);
m_logTelegramView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
m_logTelegramView->setContextMenuPolicy(Qt::CustomContextMenu); //Custom Contextmenu enabled
connect(m_logTelegramView, &QTableView::customContextMenuRequested,
this, &LogViewGui::customMenuRequested); //connect table to contextmenu
m_telegramModel = new TelegramModel(m_logTelegramView);
m_telegramProxyModel = new TelegramProxyModel(m_logTelegramView);
m_telegramProxyModel->setSourceModel(m_telegramModel);
m_logTelegramView->setModel(m_telegramProxyModel);
connect(m_logTelegramView, &QTableView::doubleClicked,
this, &LogViewGui::onLogTelegramViewDoubleClicked);
m_logTelegramView->horizontalHeader()->setSortIndicatorShown(true);
m_logTelegramView->horizontalHeader()->setSortIndicator(3, Qt::AscendingOrder);
m_logTelegramView->setSortingEnabled(true);
QVBoxLayout* telegramBox = new QVBoxLayout;
telegramBox->addWidget(m_logTelegramView);
QGroupBox* telegramGroup = new QGroupBox(tr("&Log-Telegramme"), this);
telegramGroup->setLayout(telegramBox);
// splitter between toolbox and log-telegram-list
m_Splitter = new QSplitter(Qt::Horizontal, this);
m_Splitter->addWidget(m_ToolBox);
m_Splitter->addWidget(telegramGroup);
const QList<QVariant> splitterSizesAsVariant = settings.value(SPLITTPOS).toList();
QList<int> splitterSizes;
splitterSizes.reserve(splitterSizesAsVariant.size());
if (splitterSizesAsVariant.isEmpty())
{
splitterSizes << 235 << 1000;
}
else
{
for (const QVariant &item : splitterSizesAsVariant)
splitterSizes << item.toInt();
}
m_Splitter->setSizes(splitterSizes);
setCentralWidget(m_Splitter);
// initialize the telegram-filters (this should be done before load of telegrams, otherwise the telegram-list
// will be shown several times and this could be slow when the telegram-list is slow)
m_CentralSelectionGroup->setChecked(true);
m_ackCentralTelCheckBox->setChecked(true);
m_ackCentralTelCheckBox->setChecked(false);
m_PosCentralTelCheckBox->setChecked(true);
m_PosCentralTelCheckBox->setChecked(false);
m_SentCentralTelCheckBox->setChecked(true);
m_ReceivedCentralTelCheckBox->setChecked(true);
m_TetraSelectionGroup->setChecked(true);
m_SentTetraTelCheckBox->setChecked(true);
m_ReceivedTetraTelCheckBox->setChecked(true);
m_PosTetraTelCheckBox->setChecked(true);
m_PosTetraTelCheckBox->setChecked(false);
m_FragmentedTetraTelCheckBox->setChecked(true);
m_LoginTetraTelCheckBox->setChecked(true);
m_RegisterTetraTelCheckBox->setChecked(true);
m_ReportTetraTelCheckBox->setChecked(false);
m_AirInterfaceLoadTetraTelCheckBox->setChecked(false);
m_WlsSelectionGroup->setChecked(true);
m_PosWlsTelCheckBox->setChecked(true);
m_PosWlsTelCheckBox->setChecked(false);
m_SentWlsTelCheckBox->setChecked(true);
m_ReceivedWlsTelCheckBox->setChecked(true);
// set the size and position of main-window
QSize size = settings.value(SIZE, sizeHint()).toSize();
resize(size.isNull() ? QSize(1280, 768) : size);
move(settings.value(POS, QPoint(0, 0)).toPoint());
settings.endGroup();
connect(m_replayTimer, &QTimer::timeout, this, &LogViewGui::onReplayTimerTimeout);
QTimer::singleShot(0, this, &LogViewGui::loadSpecificationFiles);
startInjectionThread();
}
void LogViewGui::createCentralSelectionWidget()
{
// send-checkbox
m_SentCentralTelCheckBox = createRadioButton(tr("&gesendete Telegramme"), QLatin1String(":/image/P2C.png"));
// received-checkbox
m_ReceivedCentralTelCheckBox = createRadioButton(tr("&empfangene Telegramme"), QLatin1String(":/image/C2P.png"));
// pos-checkbox
m_PosCentralTelCheckBox = createRadioButton(tr("&Positionsmeldungen"), QLatin1String(":/image/CentralPos.png"));
// ack-checkbox
m_ackCentralTelCheckBox = createRadioButton(tr("&Bestaetigungstelegramme"), QLatin1String(":/image/CentralAck.png"));
QVBoxLayout* centralFilterBox = new QVBoxLayout;
centralFilterBox->addWidget(m_SentCentralTelCheckBox);
centralFilterBox->addWidget(m_ReceivedCentralTelCheckBox);
centralFilterBox->addWidget(m_PosCentralTelCheckBox);
centralFilterBox->addWidget(m_ackCentralTelCheckBox);
centralFilterBox->addStretch(1);
m_CentralSelectionGroup = new QGroupBox(tr("Telegramme anzeigen"), this);
m_CentralSelectionGroup->setCheckable(true);
m_CentralSelectionGroup->setChecked(false);
m_CentralSelectionGroup->setLayout(centralFilterBox);
connect(m_CentralSelectionGroup, &QGroupBox::toggled, this, &LogViewGui::showTelegramTable);
}
void LogViewGui::createTetraSelectionWidget()
{
// sent-checkbox
m_SentTetraTelCheckBox = createRadioButton(tr("&gesendete Telegramme"), QLatin1String(":/image/T2P.png"));
// received-checkbox
m_ReceivedTetraTelCheckBox = createRadioButton(tr("&empfangene Telegramme"), QLatin1String(":/image/P2T.png"));
// pos-checkbox
m_PosTetraTelCheckBox = createRadioButton(tr("&Positionsmeldungen"), QLatin1String(":/image/TetraPos.png"));
// fragmented-checkbox
m_FragmentedTetraTelCheckBox = createRadioButton(tr("&Fragmentierte Meldungen"), QLatin1String(":/image/TetraFragmented.png"));
// login-checkbox
m_LoginTetraTelCheckBox = createRadioButton(tr("&Loginmeldungen"), QLatin1String(":/image/TetraLogin.png"));
// register-checkbox
m_RegisterTetraTelCheckBox = createRadioButton(tr("&Registermeldungen"), QLatin1String(":/image/TetraRegister.png"));
// report-checkbox
m_ReportTetraTelCheckBox = createRadioButton(tr("&Reportmeldungen"), QLatin1String(":/image/TetraReport.png"));
// air-interface-load-checkbox
m_AirInterfaceLoadTetraTelCheckBox = createRadioButton(tr("&Air-Load Meldungen"), QLatin1String(":/image/TetraAirInterface.png"));
QVBoxLayout* tetraFilterBox = new QVBoxLayout;
tetraFilterBox->addWidget(m_SentTetraTelCheckBox);
tetraFilterBox->addWidget(m_ReceivedTetraTelCheckBox);
tetraFilterBox->addWidget(m_PosTetraTelCheckBox);
tetraFilterBox->addWidget(m_FragmentedTetraTelCheckBox);
tetraFilterBox->addWidget(m_LoginTetraTelCheckBox);
tetraFilterBox->addWidget(m_RegisterTetraTelCheckBox);
tetraFilterBox->addWidget(m_ReportTetraTelCheckBox);
tetraFilterBox->addWidget(m_AirInterfaceLoadTetraTelCheckBox);
tetraFilterBox->addStretch(1);
m_TetraSelectionGroup = new QGroupBox(tr("Telegramme anzeigen"), this);
m_TetraSelectionGroup->setCheckable(true);
m_TetraSelectionGroup->setChecked(false);
m_TetraSelectionGroup->setLayout(tetraFilterBox);
connect(m_TetraSelectionGroup, &QGroupBox::toggled, this, &LogViewGui::showTelegramTable);
}
void LogViewGui::createWlsSelectionWidget()
{
// send-checkbox
m_SentWlsTelCheckBox = createRadioButton(tr("&gesendete Telegramme"), QLatin1String(":/image/P2W.png"));
// received-checkbox
m_ReceivedWlsTelCheckBox = createRadioButton(tr("&empfangene Telegramme"), QLatin1String(":/image/W2P.png"));
// pos-checkbox
m_PosWlsTelCheckBox = createRadioButton(tr("&Positionsmeldungen"), QLatin1String(":/image/WlsPos.png"));
QVBoxLayout* wlsFilterBox = new QVBoxLayout;
wlsFilterBox->addWidget(m_SentWlsTelCheckBox);
wlsFilterBox->addWidget(m_ReceivedWlsTelCheckBox);
wlsFilterBox->addWidget(m_PosWlsTelCheckBox);
wlsFilterBox->addStretch(1);
m_WlsSelectionGroup = new QGroupBox(tr("Telegramme anzeigen"), this);
m_WlsSelectionGroup->setCheckable(true);
m_WlsSelectionGroup->setChecked(false);
m_WlsSelectionGroup->setLayout(wlsFilterBox);
connect(m_WlsSelectionGroup, &QGroupBox::toggled, this, &LogViewGui::showTelegramTable);
}
// ---------------------------------------------------------------
void LogViewGui::customMenuRequested(const QPoint &pos)
{
//create Menu with Actions
QMenu menu(this);
menu.addAction(tr("Add Telegram to EditList"), this, &LogViewGui::addTelegramToEdit);
menu.addAction(tr("Show HexData"), this, &LogViewGui::showHexData);
menu.addAction(tr("Export as CSV file"), this, &LogViewGui::exportAsCsv);
menu.addAction(tr("Export as JSon file"), this, &LogViewGui::exportAsJson);
#ifdef LTS_DEBUG
if (m_EditTelegramController && m_EditTelegramController->isInjectionPossible())
{
const QString str = m_replayTimer->isActive() ? tr("Stop replay") : tr("Start Replay from here");
menu.addAction(str, this, &LogViewGui::onStartLogReplay);
}
#endif
menu.exec(m_logTelegramView->viewport()->mapToGlobal(pos));
}
// ---------------------------------------------------------------
void LogViewGui::addTelegramToEdit()
{
for (const QModelIndex &orig: m_logTelegramView->selectionModel()->selectedRows())
{
const QModelIndex idx = m_telegramProxyModel->mapToSource(orig);
QSharedPointer<LogViewTelegram> tele = m_telegramModel->telegram(idx.row());
if (!tele)
continue;
m_EditTelegramController->appendTelegram(tele);
}
}
// ---------------------------------------------------------------
void LogViewGui::showHexData()
{
const QModelIndex idx = m_telegramProxyModel->mapToSource(m_logTelegramView->currentIndex());
QSharedPointer<LogViewTelegram> tele = m_telegramModel->telegram(idx.row());
if (!tele)
return;
LogViewTelegramDialog* showTelegramDialog = new LogViewTelegramDialog(this, true);
showTelegramDialog->showDataDump(tele, false);
}
// ---------------------------------------------------------------
void LogViewGui::exportAsCsv()
{
const int idx = m_ActualLogfile.lastIndexOf(QLatin1Char('.'));
const QString suggestion = m_ActualLogfile.left(idx) + QLatin1String(".csv");
const QString fn = QFileDialog::getSaveFileName(this, tr("Export logdata as CSV"), suggestion, tr("CSV file (*.csv)"));
if (fn.isEmpty())
return;
QFile f(fn);
if (!f.open(QIODevice::WriteOnly))
return;
for (int c = 0; c < m_telegramProxyModel->columnCount(); ++c)
{
f.write(m_telegramProxyModel->headerData(c, Qt::Horizontal).toString().toUtf8());
f.write(";");
}
f.write("ISSI\n");
const int rc = m_telegramProxyModel->rowCount();
for (int r = 0; r < rc; ++r)
{
QSharedPointer<LogViewTelegram> tele = m_telegramProxyModel->telegram(r);
if (!tele)
continue;
f.write(QByteArray::number(tele->getDestination()));
f.write(";");
f.write(tele->getName().toUtf8());
f.write(";");
f.write(QByteArray::number(tele->getParticipantId()));
f.write(";");
f.write(QByteArray::number(tele->getLogTimeMsec()));
f.write(";");
f.write(QByteArray::number(tele->getGpsTime()));
f.write(";");
f.write(QByteArray::number(tele->getDelay()));
f.write(";");
f.write(QByteArray::number(tele->getISSI()));
f.write("\n");
}
f.close();
}// ---------------------------------------------------------------
void LogViewGui::exportAsJson()
{
const int idx = m_ActualLogfile.lastIndexOf(QLatin1Char('.'));
const QString suggestion = m_ActualLogfile.left(idx) + QLatin1String(".csv");
const QString fn = QFileDialog::getSaveFileName(this, tr("Export logdata as Json"), suggestion, tr("JSon file (*.json)"));
if (fn.isEmpty())
return;
QFile f(fn);
if (!f.open(QIODevice::WriteOnly))
return;
QJsonArray arr;
const int rc = m_telegramProxyModel->rowCount();
for (int r = 0; r < rc; ++r)
{
QSharedPointer<LogViewTelegram> tele = m_telegramProxyModel->telegram(r);
if (!tele)
continue;
if (tele->jsonData().isEmpty())
continue;
arr += tele->jsonData();
}
f.write(QJsonDocument(arr).toJson(QJsonDocument::Indented));
f.close();
}
// ---------------------------------------------------------------
void LogViewGui::onStartLogReplay()
{
#ifndef LTS_DEBUG
return;
#endif
if (m_replayTimer->isActive())
{
m_replayTimer->stop();
return;
}
m_reloadAction->setChecked(false);
m_PosCentralTelCheckBox->setChecked(true);
if (!m_EditTelegramController || !m_EditTelegramController->isInjectionPossible())
return;
m_currentReplayRow = m_telegramProxyModel->mapToSource(m_logTelegramView->currentIndex()).row();
if (m_currentReplayRow < 0)
return;
m_replayTimer->start(0);
}
void LogViewGui::onReplayTimerTimeout()
{
auto findNextCentralTelegram = [&]()
{
static constexpr auto sendToCentralBits = N_CoPrtCnv::CoPrtCnvLogHeader::e_DestProtocolCentral |
N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend;
auto tele = m_telegramModel->telegram(m_currentReplayRow);
while (tele && (tele->getDestination() != sendToCentralBits ||
tele->getTelId() == GEF_BESTAETIGUNG))
tele = m_telegramModel->telegram(++m_currentReplayRow);
return tele;
};
QSharedPointer<LogViewTelegram> tele = findNextCentralTelegram();
auto checkTele = [&]()
{
if (!tele)
{
m_replayTimer->stop();
m_currentReplayRow = -1;
QMessageBox::information(this, tr("Sending finished"), tr("No more telegrams to send - stopping"));
return false;
}
return true;
};
if (!checkTele())
return;
const uint64_t curSendTime = tele->getLogTimeMsec();
auto teleCopy = QSharedPointer<LogViewTelegram>(new LogViewTelegram(*(tele.data())));
teleCopy->actualizeGpsTime();
m_InjectionThread->sendTelegram(teleCopy.data(), true);
++m_currentReplayRow;
tele = findNextCentralTelegram();
if (!checkTele())
return;
uint64_t nextSendTime = (tele->getLogTimeMsec() - curSendTime); // micro -> milli
DEBUG_LOG(m_logger) << "Next SendTime: " << nextSendTime
<< ", telId: " << tele->getTelId()
<< ", next row: " << m_currentReplayRow
<< ", equipNo: " << tele->getParticipantId();
QModelIndex idx = m_telegramModel->index(m_currentReplayRow, 0, QModelIndex());
m_logTelegramView->setCurrentIndex(m_telegramProxyModel->mapFromSource(idx));
m_replayTimer->start(nextSendTime);
}
// ---------------------------------------------------------------
void LogViewGui::loadSpecificationFiles()
{
QSettings settings;
settings.beginGroup(LogViewOptionDialog::SPEC_FILES_GROUP);
// load central-protocol spezification
QString defString = QCoreApplication::applicationDirPath() + QLatin1String("/training_area_telegrams.h");
QString filename = settings.value(LogViewOptionDialog::P0_FILENAME, defString).toString();
INFO_LOG(m_logger) << "Load central-protocol spezification-file: " << filename.toStdString();
if (!QFile::exists(filename))
{
QMessageBox::warning(this, tr("LogView"), tr("Die Spezifikations-Datei \'%1\' fuer Zentral-Telegramme existiert nicht!<br><br>Werde die Default-Datei verwenden...").arg(filename));
filename = defString;
}
LogViewTelegram::jsonParser()->parse(filename);
// load tetra-protocol spezification
defString = QCoreApplication::applicationDirPath() + QLatin1String("/zsksxd_tetraForLogView.x");
filename = settings.value(LogViewOptionDialog::P5_FILENAME, defString).toString();
INFO_LOG(m_logger) << "Load tetra-protocol spezification-file: " << filename.toStdString();
if (!QFile::exists(filename))
{
QMessageBox::warning(this, tr("LogView"), tr("Die Spezifikations-Datei \'%1\' fuer TETRA-Telegramme existiert nicht!<br><br>Werde die Default-Datei verwenden...").arg(filename));
filename = defString;
}
LogViewTelegram::tetraParser()->parseX_File(filename);
INFO_LOG(m_logger) << "Browse telegram-log-file";
newTelegramFile();
}
// ---------------------------------------------------------------
void LogViewGui::newTelegramFile()
{
if (m_TailTimer) m_TailTimer->stop();
// delete all participants
m_participantModel->clearParticipants();
m_telegramModel->clearTelegrams();
m_participantListTelCount.clear();
m_participantListIssi.clear();
m_ParticipantStatus->setText(tr("0 Teilnehmer"));
// load a telegram-file
loadTelegramFile();
}
// ---------------------------------------------------------------
void LogViewGui::loadTelegramFile()
{
const QLatin1String DIR("Dir");
QSettings settings;
settings.beginGroup(QLatin1String("LogFiles"));
QString filename = QFileDialog::getOpenFileName(this, tr("Bitte eine Log-Datei auswaehlen"), settings.value(DIR).toString(), LogViewOptionDialog::KRLOG_FILES/*tr("Log-Dateien (*.krlog)")*/);
if (filename.isEmpty())
return;
QDir dir;
settings.setValue(DIR, dir.filePath(filename));
readTelegramsFromLogFile(filename);
showTelegramTable();
startInjectionThread();
m_logTelegramView->resizeColumnsToContents();
m_ParticipantTableView->resizeColumnsToContents();
// start the time for checking whether new entries were added to the log-file
if (!m_TailTimer)
m_TailTimer = new QTimer(this);
connect(m_TailTimer, &QTimer::timeout, this, &LogViewGui::updateLogfile, Qt::QueuedConnection);
m_TailTimer->setInterval(1000);
m_TailTimer->start();
}
// ---------------------------------------------------------------
// Checks whether the size of actual logfile has changed. If
// yes, the new log-entries will be read and shown within
// table.
// ---------------------------------------------------------------
void LogViewGui::updateLogfile()
{
if(!m_DoReload)
return;
m_TailTimer->stop();
QFileInfo fileInfo = QFileInfo(m_ActualLogfile);
if (fileInfo.exists())
{
if (fileInfo.size() > m_LogfileSize)
{
try
{
readTelegramsFromLogFile(m_ActualLogfile, m_LogfileSize);
}
catch (const std::exception& e)
{
WARNING_LOG(m_logger) << "Error: " << e.what();
// simply read the telegram in the next update
}
}
else
{
//check if one newer file exists and load it
QString dateFromFile;
const QString format = QStringLiteral("yyMMdd_hh");
for (int iterator = fileInfo.baseName().size() - format.size(); iterator < fileInfo.baseName().size(); ++iterator)
dateFromFile.append(fileInfo.baseName().at(iterator));
QDateTime nextDateCalc = QDateTime::fromString(dateFromFile, format);
nextDateCalc = nextDateCalc.addYears(100);
nextDateCalc = nextDateCalc.addSecs(3600);
QString nextDate = nextDateCalc.toString(format);
nextDateCalc = nextDateCalc.addSecs(3600);
QString checkDate = nextDateCalc.toString(format);
QString filePath(fileInfo.absolutePath() + QLatin1Char('/'));
const QString baseName = fileInfo.baseName();
const QVector<QStringRef> parts = baseName.splitRef(QLatin1Char('_'));
for (int iterator = 0; iterator < parts.size() - 2; ++iterator) // ...ex_42_<date>...
filePath += parts.at(iterator).toString() + QLatin1Char('_');
QString nextFile(filePath + nextDate + QLatin1Char('.') + fileInfo.completeSuffix());
QString checkFile(filePath + checkDate + QLatin1Char('.') + fileInfo.completeSuffix());
QFileInfo infoNextFile(nextFile);
QFileInfo infoCheckFile(checkFile);
if (infoNextFile.exists() && infoNextFile.isFile() && !infoCheckFile.exists())
{
m_LogfileSize = 0;
readTelegramsFromLogFile(nextFile, m_LogfileSize);
}
}
}
m_TailTimer->start();
}
// ---------------------------------------------------------------
void LogViewGui::readTelegramsFromLogFile(const QString& a_fileName, const qint64 a_filePos)
{
// open the log-file
QFile telegramFile(a_fileName);
if (telegramFile.open(QIODevice::ReadOnly))
{
m_ActualLogfile = a_fileName;
telegramFile.seek(a_filePos);
setWindowTitle(QFileInfo(m_ActualLogfile).baseName());
m_logTelegramView->blockSignals(true);
m_logTelegramView->setUpdatesEnabled(false);
QVector<QSharedPointer<LogViewTelegram> > newTelegrams;
// read the header of telegram-entry
N_CoPrtCnv::CoPrtCnvLogHeader lh;
while (true)
{
if (sizeof(lh) == telegramFile.read((char*)&lh, sizeof(lh)))
{
if (lh.m_Version == 3 || lh.m_Version == 4)
{
// read the telegram itself
qint64 telegramLength = lh.m_Length - sizeof(lh);
QByteArray buf(telegramLength, Qt::Uninitialized);
if (telegramLength == telegramFile.read(buf.data(), telegramLength))
{
m_LogfileSize = telegramFile.pos();
if (lh.m_Version == 4)
buf = qUncompress(buf);
// add the telegram to telegram-list
N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol protocol =
static_cast<N_CoPrtCnv::CoPrtCnvLogHeader::E_CoPrtCnvProtocol>(lh.m_Protocol);
QSharedPointer<LogViewTelegram> telegram(new LogViewTelegram(protocol, lh.m_Time, buf));
// this checks the telegram and raises a messagebox if the telegram is wrong
if (!telegram->isValid())
{
ERROR_LOG(m_logger) << "Trying to read/parse unknown data from logfile '" << a_fileName
<< "' at position " << telegramFile.pos() - (telegramLength + sizeof(lh));
continue;
}
newTelegrams.push_back(telegram);
// add the pariticipant to the participant-list
addToParticipantList(telegram);
}
}
else
{
QMessageBox::warning(this, tr("Falsche Version"), tr("LogViewer kann Telegramm-Logs der Version %1 nicht einlesen").arg(lh.m_Version));
ERROR_LOG(m_logger) << "Trying to read/parse unknown data from logfile '" << a_fileName
<< "' at position " << telegramFile.pos() - sizeof(lh);
}
}
else
{
break;
}
}
syncParticipantList();
m_telegramModel->appendTelegrams(newTelegrams);
m_ParticipantStatus->setText(tr("%1 Teilnehmer").arg(m_participantModel->rowCount()));
m_logTelegramView->setUpdatesEnabled(true);
m_logTelegramView->blockSignals(false);
statusBar()->showMessage(tr("%1 Telegramme eingelesen").arg(m_telegramModel->rowCount()));
}
else
{
QMessageBox::warning(this, tr("Dateifehler"), tr("Konnte die Datei <b>%1</b> nicht oeffnen").arg(a_fileName));
}
telegramFile.close();
}
// ---------------------------------------------------------------
void LogViewGui::selectedParticipantChanged()
{
std::set<int> selectedParticipantIds;
const QModelIndexList selectedParticipants = m_ParticipantTableView->selectionModel()->selectedRows(0);
for (const QModelIndex &participantIdx : selectedParticipants)
{
// row 0 -> eqId
const QModelIndex idx = m_participantProxyModel->mapToSource(participantIdx);
selectedParticipantIds.insert(m_participantModel->data(idx).toInt());
}
m_telegramProxyModel->setAllowedEquipmentIds(selectedParticipantIds);
showTelegramTable();
}
static void updateFilteredPosTelegramIds(std::set<int> &teleIds, bool bEnable)
{
if (bEnable)
{
teleIds.erase(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG);
teleIds.erase(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG_LFZ);
teleIds.erase(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG_MIT_TEILNEHMER);
teleIds.erase(TrainingAreaBase::TetraIcd::IPS_POSITIONSDATEN);
}
else
{
teleIds.insert(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG);
teleIds.insert(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG_LFZ);
teleIds.insert(TrainingAreaBase::TetraIcd::POSITIONS_MELDUNG_MIT_TEILNEHMER);
teleIds.insert(TrainingAreaBase::TetraIcd::IPS_POSITIONSDATEN);
}
}
// ---------------------------------------------------------------
void LogViewGui::showTelegramTable()
{
updateFilteredPosTelegramIds(m_CentralTelegramFilterList, m_PosCentralTelCheckBox->isChecked());
updateFilteredPosTelegramIds(m_TetraTelegramFilterList, m_PosTetraTelCheckBox->isChecked());
updateFilteredPosTelegramIds(m_WlsTelegramFilterList, m_PosWlsTelCheckBox->isChecked());
if (m_ackCentralTelCheckBox->isChecked())
m_CentralTelegramFilterList.erase(TrainingAreaBase::TetraIcd::BESTAETIGUNG);
else
m_CentralTelegramFilterList.insert(TrainingAreaBase::TetraIcd::BESTAETIGUNG);
m_telegramProxyModel->setFilteredCentralTelegramIds(m_CentralTelegramFilterList);
m_telegramProxyModel->setFilteredTetraTelegramIds(m_TetraTelegramFilterList);
m_telegramProxyModel->setFilteredWlsTelegramIds(m_WlsTelegramFilterList);
m_telegramProxyModel->setFilterByEquipmentIds(m_ParticipantSelectionGroup->isChecked());
m_PosCentralTelCheckBox->setEnabled(m_SentCentralTelCheckBox->isChecked() && m_CentralSelectionGroup->isChecked());
m_PosTetraTelCheckBox->setEnabled(m_SentTetraTelCheckBox->isChecked() && m_TetraSelectionGroup->isChecked());
m_PosWlsTelCheckBox->setEnabled(m_ReceivedWlsTelCheckBox->isChecked() && m_WlsSelectionGroup->isChecked());
const bool bShowCentralTelegrams = m_CentralSelectionGroup->isChecked();
m_telegramProxyModel->setFilterCentralTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend,
!m_SentCentralTelCheckBox->isChecked() || !bShowCentralTelegrams);
m_telegramProxyModel->setFilterCentralTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive,
!m_ReceivedCentralTelCheckBox->isChecked() || !bShowCentralTelegrams);
const bool bShowTetraTelegrams = m_TetraSelectionGroup->isChecked();
m_telegramProxyModel->setFilterTetraTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend,
!m_SentTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive,
!m_ReceivedTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterInd,
!m_RegisterTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_RegisterRsp,
!m_RegisterTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_DataIndReq,
!bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_ReportIndReq,
!m_ReportTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirInterfaceLoadReq,
!m_AirInterfaceLoadTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraPDU(N_CoPrtCnv::CoPrtCnvRadioTelegram::e_AirLoadHyterra,
!m_AirInterfaceLoadTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraLogin(!m_LoginTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
m_telegramProxyModel->setFilterTetraFragmented(!m_FragmentedTetraTelCheckBox->isChecked() || !bShowTetraTelegrams);
const bool bShowWlsTelegrams = m_WlsSelectionGroup->isChecked();
m_telegramProxyModel->setFilterWlsTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestSend,
!m_SentWlsTelCheckBox->isChecked() || !bShowWlsTelegrams);
m_telegramProxyModel->setFilterWlsTelegrams(N_CoPrtCnv::CoPrtCnvLogHeader::e_DestReceive,
!m_ReceivedWlsTelCheckBox->isChecked() || !bShowWlsTelegrams);
m_TelegramStatus->setText(tr("%1 telegrams (unfiltered: %2)")
.arg(m_telegramProxyModel->rowCount())
.arg(m_telegramModel->rowCount()));
}
void LogViewGui::addToParticipantList(const QSharedPointer<LogViewTelegram> &a_telegram)
{
int partId = a_telegram->getParticipantId();
m_participantListTelCount[partId]++;
auto &issi = m_participantListIssi[partId];
if (issi == 0)
issi = a_telegram->getISSI();
}
void LogViewGui::syncParticipantList()
{
QVector<int> eqIds;
QVector<int> issis;
QVector<int> numberOfTelegrams;
eqIds.reserve(m_participantListTelCount.size());
issis.reserve(m_participantListTelCount.size());
numberOfTelegrams.reserve(m_participantListTelCount.size());
for (auto it = m_participantListTelCount.constBegin(); it != m_participantListTelCount.constEnd(); ++it)
{
eqIds.push_back(it.key());
issis.push_back(m_participantListIssi.value(it.key()));
numberOfTelegrams.push_back(it.value());
}
m_participantModel->addUpdateParticipants(eqIds, issis, numberOfTelegrams);
}
// ---------------------------------------------------------------
void LogViewGui::changeSettings()
{
LogViewOptionDialog optionDialog(this);
connect(&optionDialog, &LogViewOptionDialog::settingsChanged, this, &LogViewGui::onSettingsChanged);
optionDialog.exec();
}
// ---------------------------------------------------------------
QCheckBox* LogViewGui::createRadioButton(const QString& a_buttonName, const QString& a_iconName)
{
QCheckBox* checkBox = new QCheckBox(a_buttonName, this);
checkBox->setIcon(QIcon(a_iconName));
checkBox->setIconSize(RADIO_BUTTON_ICON_SIZE);
connect(checkBox, &QCheckBox::toggled, this, &LogViewGui::showTelegramTable);
return checkBox;
}
// ---------------------------------------------------------------
// starts the thread, which trys to connect to the injection-
// server of PrtCnv
// ---------------------------------------------------------------
void LogViewGui::startInjectionThread()
{
if (m_InjectionThread)
{
m_InjectionThread->quit();
m_InjectionThread->wait();
delete m_InjectionThread;
}
m_InjectionThread = new LogViewInjectionThread();
QSettings settings;
settings.beginGroup(QLatin1String("LogFiles"));
QString filePath = settings.value(QLatin1String("Dir")).toString();
QString exerciseName;
QVector<QStringRef> parts = filePath.splitRef(QLatin1Char('/'));
if (parts.size() >= 2)
exerciseName = QLatin1Char('_') + parts.at(parts.size() - 2).toString();
QString portFilePath = QCoreApplication::applicationDirPath() + QDir::separator() + QLatin1String("PrtCnv") + exerciseName + QLatin1String(".dat");
QFileInfo checkPortFile(portFilePath);
if(checkPortFile.exists() && checkPortFile.isFile())
{
QSettings portSetting(portFilePath, QSettings::IniFormat);
m_InjectionThread->setPort(portSetting.value(QLatin1String("Injection/Port")).toInt());
}
m_EditTelegramController->setInjectionThread(m_InjectionThread);
connect(m_InjectionThread, &LogViewInjectionThread::updateStatus,
this, &LogViewGui::updateInjectionStatus);
m_InjectionThread->start();
}
// ---------------------------------------------------------------
// Toggles whether the logfile must be periodically checked
// for changes
// ---------------------------------------------------------------
void LogViewGui::reloadOnOff()
{
m_DoReload = !m_DoReload;
}
// ---------------------------------------------------------------
void LogViewGui::closeEvent(QCloseEvent* a_closeEvent)
{
QSettings settings;
settings.beginGroup(WINDOW_GROUP);
// store the actual toolbox selection
settings.setValue(TOOLBOXINDEX, m_ToolBox->currentIndex());
// store the actual size
settings.setValue(SIZE, size());
QPoint position = pos();
settings.setValue(POS, position);
#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
int screenNbr = QApplication::desktop()->screenNumber(position);
#else
QScreen *scr = QGuiApplication::screenAt(position);
int screenNbr = QGuiApplication::screens().indexOf(scr);
#endif
settings.setValue(SCREEN, screenNbr);
// store the splitter-position
const QList<int> splitterSizes = m_Splitter->sizes();
QList<QVariant> splitterSizesAsVariant;
splitterSizesAsVariant.reserve(splitterSizes.size());
for (int item : splitterSizes)
splitterSizesAsVariant << QVariant(item);
settings.setValue(SPLITTPOS, splitterSizesAsVariant);
settings.endGroup();
QMainWindow::closeEvent(a_closeEvent);
}
// ---------------------------------------------------------------
void LogViewGui::updateInjectionStatus(const QString& a_picName, const QString& a_toopTipText)
{
m_InjectionStatus->setPixmap(QPixmap(a_picName));
m_InjectionStatus->setToolTip(a_toopTipText);
}
// ---------------------------------------------------------------
void LogViewGui::onLogTelegramViewDoubleClicked(const QModelIndex &index)
{
if (!m_telegramProxyModel || !m_telegramModel)
return;
QModelIndex idx = m_telegramProxyModel->mapToSource(index);
if (!idx.isValid())
return;
QSharedPointer<LogViewTelegram> telegram = m_telegramModel->telegram(idx.row());
if (!telegram)
return;
LogViewTelegramDialog* showTelegramDialog = new LogViewTelegramDialog(this, true);
showTelegramDialog->showDialog(telegram);
}
void LogViewGui::onSettingsChanged(uint32_t changedFlags)
{
if ((changedFlags & LogViewOptionDialog::SpecFilesGroup) == LogViewOptionDialog::SpecFilesGroup)
loadSpecificationFiles();
if ((changedFlags & LogViewOptionDialog::InjectionGroup) == LogViewOptionDialog::InjectionGroup)
startInjectionThread();
}
} // namespace N_LogView