Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLSE, 2017-03-27
#
#
# Data Plugin
#
NGL_ADD_LIBRARY(WS TlnDataPlugin MODULE tln_data_plugin.cpp tln_data_plugin.h)
NGL_TARGET_LINK_LIBRARIES(TlnDataPlugin
Qt5::Core
Qt5::WebSockets
LtsSessionDataDds
NGLLogger
nglsettings
svcEemClient
TlnDataPluginInterface
)
NGL_INSTALL_TARGET(TlnDataPlugin)
+352
View File
@@ -0,0 +1,352 @@
/**
* @RDE_HEADER@
* Data Plugin for The Timeline - DDS => GUI
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-27
*/
#include "tln_data_plugin.h"
#include <DDS/DDSConnector/dds_connector.h>
#include <LtsSession/lts_session_data_dds.h>
#include <nglsettings/nglsettings_connections.h>
#include <ExerciseEventManagement/eem_db_structs.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QTimer>
#include <QUrlQuery>
#include <QWebSocket>
namespace TimeLineManagement
{
std::shared_ptr<DataPluginBase::Interface> DataPlugin::createDataInterface(LtsSession::LtsSessionDataDds *session)
{
return std::make_shared<DataInterfaceImpl>(session);
}
DataInterfaceImpl::DataInterfaceImpl(LtsSession::LtsSessionDataDds *session)
: DataInterface()
, m_foreign(std::make_shared<ForeignInterfaceImpl>(session))
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Plugins.Timeline.Data"))
{}
ForeignInterfaceImpl::ForeignInterfaceImpl(LtsSession::LtsSessionDataDds *session)
: m_session(session)
, m_webSocketScm(nullptr)
, m_webSocketEem(nullptr)
, m_liveHistoryBufferTimer(new QTimer(this))
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Plugins.Timeline.Data"))
{
connect(m_liveHistoryBufferTimer, &QTimer::timeout, this, &ForeignInterfaceImpl::onClearHistoryBuffer);
m_liveHistoryBufferTimer->setInterval(5000);
m_liveHistoryBufferTimer->start();
// open ws socket to exercise event management
QTimer::singleShot(0, this, &ForeignInterfaceImpl::connectToScm);
}
ForeignInterfaceImpl::~ForeignInterfaceImpl()
{
m_pendingRequests.clear();
m_liveHistoryBuffer.clear();
cleanupSocket(m_webSocketScm);
cleanupSocket(m_webSocketEem);
}
static ExerciseEventManagement::Event db2dds(const ExerciseEventManagement::Database::EemEvent &evDb)
{
ExerciseEventManagement::Event ev;
ev.id(evDb.id & 0xffffffff);
ev.sampleTime(evDb.sampleTime);
ev.eventTypeId(evDb.eventTypeId);
ev.participantId(evDb.participantId);
ev.partyId(evDb.partyId);
ev.equipmentNumber(evDb.equipmentNumber);
ev.properties(evDb.properties.toUtf8().data());
// Add Originator to Hit Building
if ( ev.eventTypeId() == ProjectDataManagement::EventType_def::HitBuilding)
{
QJsonObject properties = QJsonDocument::fromJson(QString::fromStdString(ev.properties()).toUtf8()).object();
if ( properties.contains(QStringLiteral("AesName")))
{
properties.insert(QStringLiteral("Originator"), properties.value(QStringLiteral("AesName")).toString());
} else if ( properties.contains(QStringLiteral("OriginatorEquipmentNumber")))
{
QJsonParseError parseError;
QJsonDocument doc = QJsonDocument::fromJson(QByteArray::fromStdString(ev.properties()), &parseError);
long originatorEquipmentNumber = properties.value(QStringLiteral("OriginatorEquipmentNumber")).toInt();
QString originatorEquipmentNumberStr = QString::number(originatorEquipmentNumber);
properties.insert(QStringLiteral("Originator"),originatorEquipmentNumberStr);
}
QJsonDocument doc(properties);
//QString strJson(QStringLiteral( doc.toJson(QJsonDocument::Compact)));
std::string propertiesStd = QJsonDocument(properties).toJson(QJsonDocument::Compact).toStdString();
ev.properties(propertiesStd);
}
ev.position(Core::GeoPosition(evDb.position.x(), evDb.position.y(), evDb.position.z()));
return ev;
}
TimelineDataProvider::TimelineDataProvider(int requestId, const QUrl &url,
qint64 from, qint64 to,
const QVector<int> &eventTypeIds,
const QJsonObject &filter)
: m_webSocketEem(new QWebSocket)
, m_requestId(requestId)
, m_bFinshed(false)
, m_requestMin(from)
, m_requestMax(to)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("Plugins.Timeline.Data"))
{
connect(m_webSocketEem, &QWebSocket::disconnected,
this, &TimelineDataProvider::disconnectedFromEem);
connect(m_webSocketEem, &QWebSocket::textMessageReceived,
this, &TimelineDataProvider::textMessageFromEem);
if (from > to)
std::swap(from, to);
QUrl realUrl(url);
realUrl.setPath(QStringLiteral("/request"));
QUrlQuery query;
query.addQueryItem(QStringLiteral("from"), QString::number(from));
query.addQueryItem(QStringLiteral("to"), QString::number(to));
query.addQueryItem(QStringLiteral("requestId"), QString::number(requestId));
if (!eventTypeIds.isEmpty())
{
QStringList sl;
sl.reserve(eventTypeIds.size());
for (int id : eventTypeIds)
sl += QString::number(id);
query.addQueryItem(QStringLiteral("eventTypeId"), sl.join(QLatin1Char(',')));
}
if (!filter.isEmpty())
{
const QJsonDocument doc(filter);
query.addQueryItem(QStringLiteral("filter"), QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
}
realUrl.setQuery(query);
m_webSocketEem->open(realUrl);
DEBUG_LOG(m_logger) << "requesting data from EEM: " << realUrl.toString();
}
TimelineDataProvider::~TimelineDataProvider()
{
m_webSocketEem->disconnect(this);
m_webSocketEem->close();
m_webSocketEem->deleteLater();
}
void TimelineDataProvider::disconnectedFromEem()
{
m_webSocketEem->close();
if (m_bFinshed)
return;
m_bFinshed = true;
emit finished();
}
void TimelineDataProvider::textMessageFromEem(const QString &message)
{
if (message.isEmpty() || message == QStringLiteral("{}"))
{
DEBUG_LOG(m_logger) << "textMessageFromEem - finished";
disconnectedFromEem();
return;
}
const QJsonDocument doc(QJsonDocument::fromJson(message.toUtf8()));
const QJsonArray arr = doc.array();
DEBUG_LOG(m_logger) << "textMessageFromEem - #events: " << arr.size();
m_eventData.reserve(m_eventData.size() + arr.size());
using namespace ExerciseEventManagement::Database;
std::transform(arr.begin(), arr.end(),
std::back_inserter(m_eventData),
[](const QJsonValue &val) { return db2dds(fromJson<EemEvent>(val.toObject())); });
}
TimelineDataProviderInterface *ForeignInterfaceImpl::requestData(qint64 from, qint64 to, const QVector<int> &eventTypeIds, const QJsonObject &filter)
{
if (!m_webSocketEem)
{
WARNING_LOG(m_logger) << "No connection to EEM - can not request data!";
return nullptr;
}
static int s_requestId = 0;
++s_requestId;
int curRequestId = s_requestId;
TimelineDataProvider *tldp = new TimelineDataProvider(curRequestId, eemUrl(), from, to, eventTypeIds, filter);
connect(tldp, &TimelineDataProvider::finished, this, &ForeignInterfaceImpl::requestFinished);
connect(tldp, &TimelineDataProvider::destroyed, this, [this, curRequestId]() { m_pendingRequests.remove(curRequestId);});
m_pendingRequests[curRequestId] = tldp;
DEBUG_LOG(m_logger) << "add to m_pendingRequests: " << curRequestId << ", " << tldp;
return tldp;
}
QVector<ExerciseEventManagement::Event> ForeignInterfaceImpl::liveHistory() const
{
QVector<ExerciseEventManagement::Event> ret;
for (const QVector<ExerciseEventManagement::Event> &vec : m_liveHistoryBuffer)
ret += vec;
return ret;
}
void ForeignInterfaceImpl::connectToScm()
{
cleanupSocket(m_webSocketScm);
m_webSocketScm = new QWebSocket;
connect(m_webSocketScm, &QWebSocket::connected,
this, &ForeignInterfaceImpl::connectedToScm);
connect(m_webSocketScm, &QWebSocket::textMessageReceived,
this, &ForeignInterfaceImpl::textMessageFromScm);
const QUrl scmUrl(QStringLiteral("ws://%1:%2")
.arg(NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET))
.arg(NGL::Connections::port(NGL::Connections::eConnection::SCM_WEBSOCKET)));
DEBUG_LOG(m_logger) << "Trying to connect to SCM at " << scmUrl.toString();
m_webSocketScm->open(scmUrl);
}
void ForeignInterfaceImpl::connectedToScm()
{
DEBUG_LOG(m_logger) << "Connected to SCM...";
m_webSocketScm->sendTextMessage(QStringLiteral("{\"type\": \"servicePortsInformationRequest\",\"uuid\": \"7777\"}"));
}
void ForeignInterfaceImpl::textMessageFromScm(const QString &message)
{
DEBUG_LOG(m_logger) << "textMessageFromScm from scm: " << message;
const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
const QJsonArray exercises = doc.object().value(QStringLiteral("data")).toArray();
for (const QJsonValue &valEx : exercises)
{
const QJsonObject &exercise = valEx.toObject();
const int exerciseId = exercise.value(QStringLiteral("exerciseId")).toInt();
DEBUG_LOG(m_logger) << "textMessageFromScm from scm - exerciseId " << exerciseId;
if (exerciseId != m_session->exerciseId())
continue;
const QJsonArray services = exercise.value(QStringLiteral("services")).toArray();
for (const QJsonValue &valSvc : services)
{
const QJsonObject &service = valSvc.toObject();
const QString svcName = service.value(QStringLiteral("nameAbbrevation")).toString().toUpper();
DEBUG_LOG(m_logger) << "textMessageFromScm from scm - svcName " << svcName;
if (svcName != QStringLiteral("EEM"))
continue;
const QJsonArray ports = service.value(QStringLiteral("ports")).toArray();
for (const QJsonValue &valPort : ports)
{
const QJsonObject &port = valPort.toObject();
int typeId = port.value(QStringLiteral("typeId")).toInt();
if (typeId == Core::PortType::WebSocketServer)
{
m_eemPort = port.value(QStringLiteral("number")).toInt();
connectToEem();
cleanupSocket(m_webSocketScm);
m_webSocketScm = nullptr;
return;
}
}
}
}
}
void ForeignInterfaceImpl::connectToEem()
{
cleanupSocket(m_webSocketEem);
m_webSocketEem = new QWebSocket;
connect(m_webSocketEem, &QWebSocket::connected,
this, &ForeignInterfaceImpl::connectedToEem);
connect(m_webSocketEem, &QWebSocket::disconnected,
this, &ForeignInterfaceImpl::disconnectedFromEem);
connect(m_webSocketEem, &QWebSocket::textMessageReceived,
this, &ForeignInterfaceImpl::textMessageFromEem);
QUrl url(eemUrl());
url.setPath(QStringLiteral("/live"));
DEBUG_LOG(m_logger) << "Trying to connect to EEM at " << url.toString();
m_webSocketEem->open(url);
}
void ForeignInterfaceImpl::connectedToEem()
{
DEBUG_LOG(m_logger) << "Connected to EEM...";
}
void ForeignInterfaceImpl::disconnectedFromEem()
{
WARNING_LOG(m_logger) << "EEM connection closed - try to reconnect";
QTimer::singleShot(500, this, &ForeignInterfaceImpl::connectToScm);
}
void ForeignInterfaceImpl::textMessageFromEem(const QString &message)
{
TRACE_LOG(m_logger) << "textMessageFromEem: " << message;
const QJsonObject obj(QJsonDocument::fromJson(message.toUtf8()).object());
using namespace ExerciseEventManagement::Database;
ExerciseEventManagement::Event ev(db2dds(fromJson<EemEvent>(obj)));
emit liveEvent(ev);
m_liveHistoryBuffer[ev.sampleTime() / 1000].push_back(std::move(ev));
}
void ForeignInterfaceImpl::requestFinished()
{
TimelineDataProvider *tldp = qobject_cast<TimelineDataProvider *>(sender());
if (!tldp)
{
WARNING_LOG(m_logger) << "requestFinished without valid sender";
return;
}
auto it = m_pendingRequests.find(tldp->requestId());
if (it == m_pendingRequests.end())
{
WARNING_LOG(m_logger) << "requestFinished but request not found in pending requests: " << tldp->requestId() << ", " << tldp;
return;
}
DEBUG_LOG(m_logger) << "emit requestedDataAvailable for " << tldp->requestId() << ", #events: " << tldp->eventData().size() << ", " << tldp;
emit requestedDataAvailable(it.value());
it.value()->deleteLater();
m_pendingRequests.erase(it);
}
void ForeignInterfaceImpl::cleanupSocket(QWebSocket *s)
{
if (!s)
return;
s->disconnect(this);
s->close();
s->deleteLater();
}
const QUrl ForeignInterfaceImpl::eemUrl() const
{
return QUrl(QStringLiteral("ws://%1:%2")
.arg(NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET))
.arg(m_eemPort));
}
void ForeignInterfaceImpl::onClearHistoryBuffer()
{
qint64 toRemove = time(nullptr) - 10;
auto it = m_liveHistoryBuffer.begin();
while (it != m_liveHistoryBuffer.end() && it.key() < toRemove)
it = m_liveHistoryBuffer.erase(it);
}
} // namespace TimeLineManagement
+131
View File
@@ -0,0 +1,131 @@
/**
* @RDE_HEADER@
* Data Plugin for The Timeline - DDS => GUI
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-27
*/
#pragma once
#include <QObject>
#include <PluginInterfaces/data_plugin_interface.h>
#include <Plugins/Timeline/tln_data_plugin_interface.h>
#include <ExerciseEventManagement/eem.hpp>
#include <QHash>
#include <QMap>
#include <QVector>
class QTimer;
class QWebSocket;
namespace TimeLineManagement
{
class DataPlugin final : public QObject, public DataPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "data:3:tln")
Q_INTERFACES(DataPluginBase::Plugin)
Q_OBJECT
public:
DataPlugin() = default;
~DataPlugin() = default;
QString pluginName() const override { return QStringLiteral("Timeline"); }
std::shared_ptr<DataPluginBase::Interface> createDataInterface(LtsSession::LtsSessionDataDds *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay;
}
};
class DataInterfaceImpl final : public QObject, public DataInterface
{
Q_OBJECT
public:
explicit DataInterfaceImpl(LtsSession::LtsSessionDataDds *session);
std::shared_ptr<TimeLineManagement::ForeignDataInterface> getForeignDataInterface() override { return m_foreign; }
private:
std::shared_ptr<TimeLineManagement::ForeignDataInterface> m_foreign;
NGL::INGLLogger &m_logger;
};
class TimelineDataProvider final : public QObject, public TimelineDataProviderInterface
{
Q_OBJECT
public:
TimelineDataProvider(int requestId, const QUrl &url,
qint64 from, qint64 to,
const QVector<int> &eventTypeIds = QVector<int>(),
const QJsonObject &filter = QJsonObject());
~TimelineDataProvider();
const QVector<ExerciseEventManagement::Event> &eventData() const override { return m_eventData; }
int requestId() const override { return m_requestId; }
void getRequestTime(qint64 &requestMin /* out */, qint64 &requestMax /* out */) const override { requestMin = m_requestMin; requestMax = m_requestMax; }
signals:
void finished();
private:
void disconnectedFromEem();
void textMessageFromEem(const QString &message);
private:
QVector<ExerciseEventManagement::Event> m_eventData;
QWebSocket *m_webSocketEem;
int m_requestId;
bool m_bFinshed;
qint64 m_requestMin;
qint64 m_requestMax;
NGL::INGLLogger &m_logger;
};
class ForeignInterfaceImpl final : public ForeignDataInterface
{
Q_OBJECT
public:
explicit ForeignInterfaceImpl(LtsSession::LtsSessionDataDds *session);
~ForeignInterfaceImpl();
TimelineDataProviderInterface *requestData(qint64 from,
qint64 to,
const QVector<int> &eventTypeIds = QVector<int>(),
const QJsonObject &filter = QJsonObject()) override;
QVector<ExerciseEventManagement::Event> liveHistory() const override;
const QUrl eemUrl() const override;
private:
void connectToScm();
void connectedToScm();
void textMessageFromScm(const QString &message);
void connectToEem();
void connectedToEem();
void disconnectedFromEem();
void textMessageFromEem(const QString &message);
void requestFinished();
void cleanupSocket(QWebSocket *s);
void onClearHistoryBuffer();
private:
QMap<qint64, QVector<ExerciseEventManagement::Event>> m_liveHistoryBuffer;
QHash<int, TimelineDataProvider *> m_pendingRequests; // requestId
LtsSession::LtsSessionDataDds *m_session;
QWebSocket *m_webSocketScm;
QWebSocket *m_webSocketEem;
QTimer *m_liveHistoryBufferTimer;
quint16 m_eemPort;
NGL::INGLLogger &m_logger;
};
} // namespace TimeLineManagement