Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+352
@@ -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
|
||||
Reference in new issue
Block a user