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

@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* The ClientConnection class represents a connection to a single client.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "client_connection.h"
#include "WsBiteInterface/Messages/ws_reply_message.h"
#include <QtWebSockets/QWebSocket>
#include <QtCore/QTimer>
using namespace WarehouseGuez::WsBiteInterface::Connection;
ClientConnection::ClientConnection(QWebSocket *socket, QObject *parent)
: QObject(parent)
, m_socket(socket)
, m_ip(socket->peerAddress())
, m_logger(getLogger(QLatin1String("ClientConnection")))
{}
ClientConnection::~ClientConnection()
{
if (m_socket)
{
m_socket->disconnect(this);
m_socket->close();
}
delete m_socket;
}
bool ClientConnection::init()
{
if (m_socket == nullptr)
{
ERROR_LOG(m_logger) << "Initialisation failed due to an invalid socket";
return false;
}
connect(m_socket, &QWebSocket::textMessageReceived, this, &ClientConnection::onNewTextMessageFromSocket);
connect(m_socket, &QWebSocket::connected, this, &ClientConnection::sendMessagesFromQueue);
connect(m_socket, &QWebSocket::disconnected, this, &ClientConnection::onSocketDisconnect);
return true;
}
QHostAddress ClientConnection::ip() const
{
return m_ip;
}
void ClientConnection::addMessage(const WarehouseGuez::WsBiteInterface::Messages::ReplyMessage &message)
{
if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState)
{
m_messageQueue.enqueue(message);
DEBUG_LOG(m_logger) << "Adding message to queue, size: " << m_messageQueue.size();
return;
}
const QString msg(message.toJsonString());
TRACE_LOG(m_logger) << "Send msg: " << msg;
m_socket->sendTextMessage(msg);
}
void ClientConnection::onNewTextMessageFromSocket(const QString &message)
{
DEBUG_LOG(m_logger) << "Recv msg: " << message;
emit newTextMessage(this, message);
}
void ClientConnection::sendMessagesFromQueue()
{
if (m_socket->state() != QAbstractSocket::ConnectedState)
return;
while (!m_messageQueue.isEmpty())
{
Messages::ReplyMessage message = m_messageQueue.dequeue();
const QString msg(message.toJsonString());
TRACE_LOG(m_logger) << "Send msg: " << msg;
m_socket->sendTextMessage(msg);
DEBUG_LOG(m_logger) << "Sent message from queue, new size: " << m_messageQueue.size();
}
}
void ClientConnection::onSocketDisconnect()
{
DEBUG_LOG(m_logger) << "Client with ip "
<< m_ip.toString()
<< " disconnected";
emit disconnected(this);
}
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* The ClientConnection class represents a connection to a single client.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtCore/QQueue>
#include <QtNetwork/QHostAddress>
#include "WsBiteInterface/Messages/message_transmitter.h"
#include "logger.h"
class QWebSocket;
class QTimer;
namespace WarehouseGuez
{
namespace WsBiteInterface
{
namespace Connection
{
class ClientConnection
: public QObject
, public Messages::MessageTransmitter
{
Q_OBJECT
public:
explicit ClientConnection(QWebSocket *socket, QObject *parent = nullptr);
~ClientConnection();
bool init();
QHostAddress ip() const;
void addMessage(const Messages::ReplyMessage &message) override final;
Q_SIGNALS:
void newTextMessage(MessageTransmitter *transmitter, const QString &message);
void disconnected(ClientConnection *conn);
private Q_SLOTS:
void onNewTextMessageFromSocket(const QString &message);
void sendMessagesFromQueue();
void onSocketDisconnect();
private: // methods
void sendMessages();
private: // attributes
QWebSocket *m_socket;
QQueue<Messages::ReplyMessage> m_messageQueue;
QHostAddress m_ip;
LOGGER_T m_logger;
}; // class ClientConnection
} // namespace Connection
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,113 @@
/**
* @RDE_HEADER@
* The ConnectionHandler class functions as a WebSocket server and manages incoming client connections.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "connection_handler.h"
#include "client_connection.h"
#include "WsBiteInterface/Messages/message_transmitter.h"
#include <nglsettings/nglsettings_connections.h>
#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/QWebSocket>
using namespace WarehouseGuez::WsBiteInterface::Connection;
ConnectionHandler::ConnectionHandler()
: m_server(nullptr)
, m_logger(getLogger("ConnectionHandler"))
{
}
ConnectionHandler::~ConnectionHandler()
{
for (auto s : qAsConst(m_connections))
s->disconnect(this);
qDeleteAll(m_connections);
}
bool ConnectionHandler::init()
{
if (m_server != nullptr)
{
m_server->disconnect();
delete m_server;
}
// TODO: get a server name from config ?
// TODO: get the SslMode from config ?
m_server = new QWebSocketServer(QStringLiteral("RDE-EXC"), QWebSocketServer::NonSecureMode, this);
connect(m_server, &QWebSocketServer::newConnection, this, &ConnectionHandler::onNewConnection);
connect(m_server, &QWebSocketServer::serverError, this, &ConnectionHandler::onServerError);
// TODO: read port from config?
int port = NGL::Connections::port(NGL::Connections::eConnection::LVS);
DEBUG_LOG(m_logger) << "Trying to listen to port " << port;
if (!m_server->listen(QHostAddress::Any, port))
{
ERROR_LOG(m_logger) << "Not possible to listen to port " << port;
return false;
}
DEBUG_LOG(m_logger) << "... ok - port: " << port;
return true;
}
void ConnectionHandler::onNewMessage(WarehouseGuez::WsBiteInterface::Messages::MessageTransmitter *transmitter, const QString &message)
{
emit newTextMessageFromClient(transmitter, message);
}
void ConnectionHandler::onServerError(QWebSocketProtocol::CloseCode closeCode)
{
ERROR_LOG(m_logger) << "Got server error with close code " << closeCode;
}
void ConnectionHandler::onNewConnection()
{
while (m_server->hasPendingConnections())
{
QWebSocket *socket = m_server->nextPendingConnection();
ClientConnection *newConnection = new ClientConnection(socket, this);
if (!newConnection->init())
{
ERROR_LOG(m_logger) << "Failed to initialise new client";
delete newConnection;
newConnection = nullptr;
continue;
}
connect(newConnection, &ClientConnection::newTextMessage, this, &ConnectionHandler::onNewMessage);
connect(newConnection, &ClientConnection::disconnected, this, &ConnectionHandler::onDisconnected);
m_connections.append(newConnection);
DEBUG_LOG(m_logger) << "New client with ip " << newConnection->ip().toString() << " connected";
}
}
void ConnectionHandler::onDisconnected(ClientConnection *conn)
{
bool removeCount = m_connections.removeOne(conn);
Q_ASSERT(removeCount == 1);
conn->deleteLater(); // use deleteLater() as the signal is emitted from with ClientConnection
if (m_connections.empty())
emit lastConnectionClosed();
emit clientDisconnected(conn);
}
/*
void ConnectionHandler::onPositions(const ::WsBiteInterface::Messages::ReplyMessage &positionsCnr)
{
for (ClientConnection *currentConnection : qAsConst(m_connections))
currentConnection->addMessage(positionsCnr);
}
*/
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* The ConnectionHandler class functions as a WebSocket server and manages incoming client connections.
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtNetwork/QHostAddress>
#include <QtWebSockets/qwebsocketprotocol.h>
#include "../Messages/ws_reply_message.h"
#include "logger.h"
class QWebSocketServer;
namespace WarehouseGuez
{
namespace WsBiteInterface
{
namespace Messages
{
class MessageTransmitter;
}
namespace Connection
{
class ClientConnection;
class ConnectionHandler
: public QObject
{
Q_OBJECT
public:
explicit ConnectionHandler();
~ConnectionHandler();
bool init();
Q_SIGNALS:
void newTextMessageFromClient(Messages::MessageTransmitter *transmitter, const QString &message);
void lastConnectionClosed();
void clientDisconnected(Messages::MessageTransmitter *transmitter);
private Q_SLOTS:
void onNewMessage(Messages::MessageTransmitter *transmitter, const QString &message);
void onServerError(QWebSocketProtocol::CloseCode closeCode);
void onNewConnection();
void onDisconnected(ClientConnection *conn);
private: // attributes
QWebSocketServer *m_server;
QVector<ClientConnection *> m_connections;
LOGGER_T m_logger;
};
} // namespace Connection
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* The MessageHandler class processes messages from the client an creates appropriate replys
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "message_handler.h"
#include "message_transmitter.h"
#include "ws_client_message.h"
#include <memory>
namespace WarehouseGuez
{
namespace WsBiteInterface
{
namespace Messages
{
MessageHandler::MessageHandler(DataManager *dataManager)
: m_dataManager(dataManager)
, m_logger(getLogger("MessageHandler"))
{
Q_ASSERT(dataManager);
}
void MessageHandler::onNewTextMessage(MessageTransmitter *transmitter, const QString &message)
{
Q_ASSERT(transmitter);
std::unique_ptr<ClientMessage> clientMessage(ClientMessage::create(message, m_logger));
if (!clientMessage)
return;
clientMessage->process(m_dataManager, transmitter);
}
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,43 @@
/**
* @RDE_HEADER@
* The MessageHandler class processes messages from the client an creates appropriate replys
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtCore/QObject>
#include "logger.h"
namespace WarehouseGuez
{
namespace WsBiteInterface
{
class DataManager;
namespace Messages
{
class MessageTransmitter;
class MessageHandler
: public QObject
{
Q_OBJECT
public:
explicit MessageHandler(DataManager *dataManager);
public Q_SLOTS:
void onNewTextMessage(MessageTransmitter *transmitter, const QString &message);
private:
DataManager *m_dataManager;
LOGGER_T m_logger;
};
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,26 @@
/**
* @RDE_HEADER@
* The MessageTransmitter Interface offers an extremely lightweight API for a class to just write textmessages to
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include "ws_reply_message.h"
namespace WarehouseGuez
{
namespace WsBiteInterface
{
namespace Messages
{
class MessageTransmitter
{
public:
virtual void addMessage(const ReplyMessage &message) = 0;
};
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,161 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id: client_message.cpp 58958 2023-02-13 11:26:20Z fritsche_h $
* $HeadURL: svn://slssvn/TDS/branches/product/stable/guez2/Lts/Services/WarehouseGuez/WsBiteInterface/Messages/client_message.cpp $
* $Author: fritsche_h $
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "ws_client_message.h"
#include "ws_reply_message.h"
#include "WsBiteInterface/bite_data_manager.h"
#include "WsBiteInterface/Messages/message_transmitter.h"
#include "WsBiteInterface/json_keys.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QUuid>
#include <chrono>
/*
*python testing:
import websocket
import json
import time
ws = websocket.create_connection("ws://127.0.0.1:2081")
d = { 'type' : 'getEssentials', 'uuid': 'blub' }
ws.send(json.dumps(d))
d = { 'type' : 'getConfigCNR', 'uuid': 'blub' }
ws.send(json.dumps(d))
d = { 'type' : 'getPositionsCNR', 'uuid': 'blub', 'data': {'repeat': 10}}
ws.send(json.dumps(d))
while True:
ws.recv()
*/
namespace WarehouseGuez
{
namespace WsBiteInterface
{
using namespace JsonKeys;
namespace Messages
{
// ####### ClientMessage begin #######
ClientMessage *ClientMessage::create(const QString &message, NGL::INGLLogger &logger)
{
ClientMessage *result = nullptr;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
{
ERROR_LOG(logger) << "Got message which is not JSON conform: " << message;
return new InvalidMessage(QString(), QStringLiteral("Got message which is not JSON conform"));
}
QJsonObject obj = doc.object();
QJsonObject dataPart = obj.value(DATA()).toObject();
QString type = obj.value(TYPE()).toString();
QString uuid = obj.value(UUID_()).toString();
if (type.isEmpty())
{
ERROR_LOG(logger) << "Got message without type: " << message;
result = new InvalidMessage(uuid, QStringLiteral("Message has no type"));
}
else if (type == ERROR_() && dataPart.contains(ERROR_MESSAGE()) && dataPart.contains(ERROR_CLASS()) && dataPart.contains(UUID_()))
{
ERROR_LOG(logger) << "Got error message: " << QJsonDocument(dataPart).toJson();
return nullptr;
}
else if (uuid.isEmpty())
{
ERROR_LOG(logger) << "Got message without uuid: " << message;
result = new InvalidMessage(QString(), QStringLiteral("Got message without uuid"));
}
else if (type == GET_BITE_DATA())
result = new GetBiteDataMessage(type, uuid, dataPart);
if (result == nullptr)
{
ERROR_LOG(logger) << "Got message with invalid type: " << message;
result = new InvalidMessage(uuid, QStringLiteral("Invalid type"));
}
if (!result->hasValidData())
{
ERROR_LOG(logger) << "Got message with invalid data part: " << message;
delete result;
result = new InvalidMessage(uuid, QStringLiteral("Has invalid data part"));
}
return result;
}
ClientMessage::ClientMessage(const QString &type, const QString &uuid, const QJsonObject &data)
: m_type(type)
, m_uuid(uuid)
, m_data(data)
{
}
// ####### ClientMessage end #######
InvalidMessage::InvalidMessage(const QString &uuid, const QString &errorText)
: ClientMessage(QString(), uuid)
, m_errorText(errorText)
{}
// ####### InvalidMessage begin #######
void InvalidMessage::process(DataManager *dataManager, MessageTransmitter *transmitter)
{
Q_UNUSED(dataManager);
ErrorMessage::ErrorData errorData;
errorData.errorClass = 1; // ??????????
errorData.errorMessage = m_errorText;
errorData.uuid = m_uuid;
transmitter->addMessage(ErrorMessage(errorData));
}
bool InvalidMessage::hasValidData() const
{
// always true because there is no data part
return true;
}
// ####### InvalidMessage end #######
// ####### GetBiteDataMessage begin #######
void GetBiteDataMessage::process(DataManager *dataManager, MessageTransmitter *transmitter)
{
using namespace Messages;
int equipNo = m_data.value(EQUIP_NO()).toInt();
int boxId = m_data.value(BOX()).toInt();
dataManager->setEquipNoBoxId(equipNo, boxId, transmitter, m_uuid);
OkMessage::OkData okMsg;
okMsg.messageType = GET_BITE_DATA();
okMsg.uuid = m_uuid;
okMsg.info = QStringLiteral("Received");
OkMessage msg(okMsg);
transmitter->addMessage(msg);
}
// ####### GetBiteDataMessage end #######
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,71 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id: client_message.h 58958 2023-02-13 11:26:20Z fritsche_h $
* $HeadURL: svn://slssvn/TDS/branches/product/stable/guez2/Lts/Services/WarehouseGuez/WsBiteInterface/Messages/client_message.h $
* $Author: fritsche_h $
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtCore/QJsonObject>
#include "logger.h"
namespace WarehouseGuez
{
namespace WsBiteInterface
{
class DataManager;
namespace Messages
{
class MessageTransmitter;
class ClientMessage
{
public:
static ClientMessage *create(const QString &message, NGL::INGLLogger &logger);
virtual void process(DataManager *dataManager, MessageTransmitter *transmitter) = 0;
virtual bool hasValidData() const = 0;
virtual ~ClientMessage() = default;
ClientMessage(const QString &type, const QString &uuid, const QJsonObject &data = QJsonObject());
protected: // attributes
const QString m_type;
const QString m_uuid;
const QJsonObject m_data;
}; // class ClientMessage
class GetBiteDataMessage final : public ClientMessage
{
public:
void process(DataManager *dataManager, MessageTransmitter *transmitter) override;
bool hasValidData() const override { return true; }
using ClientMessage::ClientMessage;
}; // class GetBiteDataMessage
class InvalidMessage final : public ClientMessage
{
friend class ClientMessage;
explicit InvalidMessage(const QString &uuid, const QString &errorText);
public:
void process(DataManager *dataManager, MessageTransmitter *transmitter) override;
bool hasValidData() const override;
using ClientMessage::ClientMessage;
private: // attributes
QString m_errorText;
}; // class InvalidMessage
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,79 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id: reply_message.cpp 58543 2022-12-05 13:40:10Z rathje $
* $HeadURL: svn://slssvn/TDS/branches/product/stable/guez2/Lts/Services/WarehouseGuez/WsBiteInterface/Messages/reply_message.cpp $
* $Author: rathje $
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "ws_reply_message.h"
#include "WsBiteInterface/json_keys.h"
#include <QtCore/QUuid>
#include <QtCore/QJsonDocument>
#include <QtGlobal>
namespace WarehouseGuez
{
namespace WsBiteInterface
{
using namespace JsonKeys;
namespace Messages
{
// ####### ReplyMessage begin #######
QString ReplyMessage::toJsonString() const
{
return QString::fromUtf8(QJsonDocument(m_jsonRepresentation).toJson(QJsonDocument::Compact));
}
ReplyMessage::ReplyMessage(const QString &type, const QString &uuid)
{
m_jsonRepresentation.insert(TYPE(), type);
Q_ASSERT(!uuid.isEmpty());
m_jsonRepresentation.insert(UUID_(), uuid);
}
// ####### ReplyMessage end #######
// ####### OkMessage begin #######
OkMessage::OkMessage(const OkMessage::OkData &data)
: ReplyMessage(OK(), data.uuid)
{
m_jsonRepresentation.insert(DATA(),
QJsonObject({
{MESSAGE_TYPE(), data.messageType},
{INFO(), data.info}
}));
}
// ####### OkMessage end #######
// ####### ErrorMessage begin #######
ErrorMessage::ErrorMessage(const ErrorMessage::ErrorData &data)
: ReplyMessage(ERROR_(), data.uuid)
{
m_jsonRepresentation.insert(DATA(),
QJsonObject({
{ERROR_CLASS(), data.errorClass},
{ERROR_MESSAGE(), data.errorMessage}
}));
}
// ####### ErrorMessage end #######
// ####### BiteDataMessage begin #######
BiteDataMessage::BiteDataMessage(const QJsonObject &data, const QString &uuid)
: ReplyMessage(BITE_DATA(), uuid)
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### BiteDataMessage end #######
} // namespace Messages
} // namespace WsBiteInterface
} // namespace CombatNetRadio
@@ -0,0 +1,103 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id: reply_message.h 58543 2022-12-05 13:40:10Z rathje $
* $HeadURL: svn://slssvn/TDS/branches/product/stable/guez2/Lts/Services/WarehouseGuez/WsBiteInterface/Messages/reply_message.h $
* $Author: rathje $
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtCore/QVector>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include <QtCore/QString>
namespace WarehouseGuez
{
namespace WsBiteInterface
{
namespace Messages
{
class ReplyMessage
{
public:
ReplyMessage() = default; //!< QVector requires this; don't use directly
virtual ~ReplyMessage() = default;
virtual QString toJsonString() const;
protected:
template<typename T>
static QJsonArray vecToPath(const QVector<T> &data)
{
QJsonArray result;
for (const T &p : data)
result.append(p.toJson());
return result;
}
protected: // methods
explicit ReplyMessage(const QString &type, const QString &uuid);
protected: // attributes
QJsonObject m_jsonRepresentation;
}; // class Reply Message
class ErrorMessage final
: public ReplyMessage
{
public:
struct ErrorData
{
int errorClass = 0;
QString uuid;
QString errorMessage;
ErrorData() = default;
explicit ErrorData(int errorClass_, const QString &uuid_, const QString &errorMessage_)
: errorClass(errorClass_)
, uuid(uuid_)
, errorMessage(errorMessage_)
{}
};
explicit ErrorMessage(const ErrorData &data);
explicit ErrorMessage(int errorClass, const QString &uuid, const QString &errorMessage)
: ErrorMessage(ErrorData(errorClass, uuid, errorMessage))
{}
}; // class ErrorMessage
class OkMessage final
: public ReplyMessage
{
public:
struct OkData
{
QString uuid;
QString messageType;
QString info;
};
explicit OkMessage(const OkData &data);
}; // class OkMessage
class BiteDataMessage final
: public ReplyMessage
{
public:
explicit BiteDataMessage(const QJsonObject &data, const QString &uuid);
}; // BiteDataMessage
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,294 @@
/**
* @RDE_HEADER@
* The DataManager class takes strucure element data from the HandlerStructureElement,
* offers an interface to get and set structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "bite_data_manager.h"
#include "dds_handler.h"
#include "WsBiteInterface/Messages/message_transmitter.h"
#include <WarehouseGuez/WsBiteInterface/json_keys.h>
#include <LtsUtilsCore/outdated_position.h>
#include <nglsettings/nglsettings_connections.h>
#include <QMap>
#include <QUuid>
#include <QJsonDocument>
namespace WarehouseGuez
{
namespace WsBiteInterface
{
using namespace JsonKeys;
DataManager::DataManager(std::shared_ptr<DdsHandler> ddsHandler)
: m_scmWebSocket(new QWebSocket)
, m_ddsHandler(ddsHandler)
, m_timer(new QTimer(this))
, m_logger(getLogger(QLatin1String("DataManager")))
{
QUrl scmUrl(QStringLiteral("ws://%1:%2").arg(NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET)).arg(NGL::Connections::port(NGL::Connections::eConnection::SCM_WEBSOCKET)));
connect(m_scmWebSocket, &QWebSocket::connected, this, &DataManager::onConnected);
connect(m_scmWebSocket, &QWebSocket::disconnected, this, &DataManager::closed);
m_scmWebSocket->open(scmUrl);
// DTC TrainingAreaBase
m_tabDtc = TrainingAreaBase::Interface::Dtc::create(m_ddsHandler->dds());
if (m_tabDtc == nullptr)
{
ERROR_LOG(m_logger) << "Could not create DTC TrainingAreaBase";
return;
}
#if 0
connectToTabWebSocket(8080, 42);
#endif
}
DataManager::~DataManager()
{
for ( TabData data : m_tabWebSocketMap.values())
delete data.websocket;
delete m_scmWebSocket;
}
void DataManager::onConnected()
{
DEBUG_LOG(m_logger) << "SCM websocket port connected";
connect(m_scmWebSocket, &QWebSocket::textMessageReceived, this, &DataManager::onTextMessageFromScm);
connect(m_timer, &QTimer::timeout, this, &DataManager::onTimerTimeout);
m_timer->setInterval(60000);
m_timer->start();
onTimerTimeout();
}
void DataManager::onTimerTimeout()
{
m_scmWebSocket->sendTextMessage(QStringLiteral("{\"type\":\"servicePortsInformationRequest\", \"uuid\": \"7777\"}"));
}
void DataManager::onTextMessageFromScm(const QString &message)
{
DEBUG_LOG(m_logger) << "onTextMessageFromScm from scm: " << message;
const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
const QJsonArray exercises = doc.object().value(DATA()).toArray();
for (const QJsonValue &valEx : exercises)
{
const QJsonObject &exercise = valEx.toObject();
const int exerciseId = exercise.value(EXERCISE_ID()).toInt();
DEBUG_LOG(m_logger) << "textMessageFromScm from scm - exerciseId " << exerciseId;
const QJsonArray services = exercise.value(SERVICES()).toArray();
for (const QJsonValue &valSvc : services)
{
const QJsonObject &service = valSvc.toObject();
const QString svcName = service.value(NAME_ABBREVATION()).toString().toUpper();
DEBUG_LOG(m_logger) << "textMessageFromScm from scm - svcName " << svcName;
if (svcName != QStringLiteral("TAB"))
continue;
DEBUG_LOG(m_logger) << "service TAB ";
const QJsonArray ports = service.value(PORTS()).toArray();
for (const QJsonValue &valPort : ports)
{
const QJsonObject &port = valPort.toObject();
int typeId = port.value(TYPE_ID()).toInt();
if (typeId == Core::PortType::WebSocketServer)
{
int tabPort = port.value(NUMBER()).toInt();
DEBUG_LOG(m_logger) << "connect to TAB - port: " << tabPort;
connectToTabWebSocket(tabPort, exerciseId);
}
}
}
}
}
void DataManager::onConnectedTab()
{
DEBUG_LOG(m_logger) << "TAB connected";
}
void DataManager::connectToTabWebSocket(int port, int exerciseId)
{
if (m_tabWebSocketMap.contains(exerciseId))
{
TabData tabData = m_tabWebSocketMap.value(exerciseId);
// Websocket connection already exists?
if (tabData.port == port)
{
INFO_LOG(m_logger) << "Connection to port " << port << " already exists.";
return;
}
else
{
tabData.websocket->close();
delete tabData.websocket;
m_tabWebSocketMap.remove(exerciseId);
}
}
INFO_LOG(m_logger) << "New connection: exercise: " << exerciseId << ", port: " << port;
const QUrl url(QStringLiteral("ws://%1:%2").arg(NGL::Connections::hostName(NGL::Connections::eConnection::SCM_WEBSOCKET)).arg(port));
QWebSocket *webSocketTab = new QWebSocket;
connect(webSocketTab, &QWebSocket::connected, this, &DataManager::onConnectedTab);
connect(webSocketTab, &QWebSocket::disconnected, this, &DataManager::closedTab);
connect(webSocketTab, &QWebSocket::textMessageReceived, this, &DataManager::onTextMessageFromTab);
DEBUG_LOG(m_logger) << "Trying to connect to TAB at " << url.toString();
webSocketTab->open(url);
TabData data;
data.exerciseId = exerciseId;
data.port = port;
data.websocket = webSocketTab;
m_tabWebSocketMap.insert(exerciseId, data);
}
void DataManager::onClientDisconnect(Messages::MessageTransmitter *transmitter)
{
for (EquipData &ed : m_equipDataMap)
{
if (ed.transmitter == transmitter)
{
DEBUG_LOG(m_logger) << "removed dead transmitter";
ed.transmitter = nullptr;
}
}
}
void DataManager::onTextMessageFromTab(const QString &message)
{
INFO_LOG(m_logger) << "onTextMessageFromTab : " << message;
const QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8());
if (doc.object().value(TYPE()) != BITE_DATA() && doc.object().value(TYPE()) != POSITION_DATA())
return;
INFO_LOG(m_logger) << "TYPE: " << doc.object().value(TYPE()).toString();
QJsonObject data = doc.object().value(DATA()).toObject();
data.insert(LAST_UPDATE(), QDateTime::currentSecsSinceEpoch());
int equipNo = data.value(EQUIP_NO()).toInt();
INFO_LOG(m_logger) << "equipNo: " << equipNo;
if (equipNo <= 0)
return;
if (doc.object().value(TYPE()) == BITE_DATA())
m_equipDataMap[equipNo].biteMessage = data;
if (doc.object().value(TYPE()) == POSITION_DATA())
{
m_equipDataMap[equipNo].lastPositionUpdate = data.value(LAST_POSITION_UPDATE()).toDouble();
INFO_LOG(m_logger) << "lastPosUpdate: " << m_equipDataMap[equipNo].lastPositionUpdate;
}
if (m_equipDataMap[equipNo].boxId >= 0)
{
Messages::MessageTransmitter* transmitter = m_equipDataMap[equipNo].transmitter;
if (transmitter == nullptr)
{
ERROR_LOG(m_logger) << "transmitter for eqId " << equipNo << " no longer available";
return;
}
int &boxId = m_equipDataMap[equipNo].boxId;
QJsonObject &biteData = m_equipDataMap[equipNo].biteMessage;
biteData.insert(BOX(), boxId);
biteData.insert(LAST_POSITION_UPDATE(), m_equipDataMap[equipNo].lastPositionUpdate);
transmitter->addMessage(Messages::BiteDataMessage(biteData, m_equipDataMap[equipNo].uuid));
INFO_LOG(m_logger) << "Send Message with boxId: " << boxId << ", equipNo: " << equipNo;
if (boxId == 0)
boxId = -1; // only single request
}
}
void DataManager::setEquipNoBoxId(int equipNo, int boxId, Messages::MessageTransmitter *transmitter, const QString &uuid)
{
INFO_LOG(m_logger) << "equipNo: " << equipNo << ", boxId = " << boxId;
// erase abonnement, no further biteData telegrams
if (equipNo == 0)
{
for ( int key : m_equipDataMap.keys())
{
if (m_equipDataMap[key].boxId == boxId)
{
m_equipDataMap.remove(key);
break;
}
}
return;
}
auto &equipData = m_equipDataMap[equipNo];
// create new equipData if equipNo is not present yet
equipData.equipNo = equipNo;
equipData.boxId = boxId;
equipData.transmitter = transmitter;
equipData.uuid = uuid;
// find biteMessage and send biteData telegram if not older than 1h
if (!equipData.biteMessage.isEmpty())
{
QJsonObject &data = equipData.biteMessage;
qint64 lastUpdate = data.value(LAST_UPDATE()).toDouble();
if ( lastUpdate + 3600 > QDateTime::currentSecsSinceEpoch())
{
data.insert(BOX(), boxId);
data.insert(LAST_POSITION_UPDATE(), equipData.lastPositionUpdate);
transmitter->addMessage(Messages::BiteDataMessage(data, equipData.uuid));
INFO_LOG(m_logger) << "Send Message with boxId: " << boxId << ", equipNo: " << equipNo;
}
}
else
{
QJsonObject data;
data.insert(EQUIP_NO(), equipNo);
data.insert(BOX(), boxId);
data.insert(LAST_UPDATE(), 0);
data.insert(LAST_POSITION_UPDATE(), 0);
data.insert(IS_LICENSED(), false);
equipData.biteMessage = data;
transmitter->addMessage(Messages::BiteDataMessage(data, equipData.uuid));
INFO_LOG(m_logger) << "Send Message with boxId: " << boxId << ", equipNo: " << equipNo << ", lastUpdate 0";
// send bite request
QJsonObject biteData;
biteData.insert(DATA(), data);
biteData.insert(TYPE(), SEND_BITE_REQUEST());
biteData.insert(UUID_(), QUuid::createUuid().toString());
QString message = QString::fromStdString(QJsonDocument(biteData).toJson(QJsonDocument::Compact).toStdString());
INFO_LOG(m_logger) << "message: " << message;
for (TabData dataTab : m_tabWebSocketMap)
dataTab.websocket->sendTextMessage(message);
}
}
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* The DataManager class is a convenience class for managing structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <memory>
#include <QHash>
#include <QSet>
#include <QTimer>
#include <WarehouseGuez/WsBiteInterface/Messages/ws_reply_message.h>
#include "logger.h"
#include <TrainingAreaBase/tab.hpp>
#include <TrainingAreaBase/tab_dtc.h>
#include <ParticipantManagement/ppm.hpp>
#include <QtWebSockets/QWebSocket>
class QTimer;
namespace WarehouseGuez
{
namespace Interface
{
class Dtc;
}
class DdsHandler;
namespace WsBiteInterface
{
namespace Messages
{
class MessageTransmitter;
}
class DataManager final : public QObject
{
Q_OBJECT
public:
explicit DataManager(std::shared_ptr<DdsHandler> ddsHandler);
~DataManager();
void setEquipNoBoxId( int equipNo, int boxId, Messages::MessageTransmitter* transmitter, const QString &uuid);
Q_SIGNALS:
void closed();
void closedTab();
public slots:
void onClientDisconnect(Messages::MessageTransmitter *transmitter);
private Q_SLOTS:
void onConnected();
void onConnectedTab();
void onTimerTimeout();
void onTextMessageFromScm(const QString &message);
void onTextMessageFromTab(const QString &message);
private: // methods
void connectToTabWebSocket(int port, int exerciseId);
private: // attributes
struct EquipData
{
int equipNo{0};
int boxId{-1} ;
Messages::MessageTransmitter* transmitter{nullptr};
QString uuid;
QJsonObject biteMessage;
qint64 lastPositionUpdate{0};
};
struct TabData
{
int exerciseId{0};
int port{0};
QWebSocket* websocket;
};
QWebSocket *m_scmWebSocket;
QMap<int,TabData> m_tabWebSocketMap;
QMap<int,EquipData > m_equipDataMap; // Key: equipNo, Value: <equipData>
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<TrainingAreaBase::Interface::Dtc> m_tabDtc; ///< Pointer to DTC Training Area Base interface object
QTimer *m_timer = nullptr;
LOGGER_T m_logger;
}; // class DataManager
} // namespace WsBiteInterface
} // namespace WarehouseGuez
+79
View File
@@ -0,0 +1,79 @@
/**
* @RDE_HEADER@
* Header file which holds all occuring JSON keys
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QtCore/QString>
namespace WarehouseGuez
{
namespace WsBiteInterface
{
/**
* @brief Namespace which holds all occuring JSON keys as const QString objects
*/
namespace JsonKeys
{
// root keys
inline QString DATA() { return QLatin1String("data"); }
inline QString UUID_() { return QStringLiteral("uuid"); } // Windows: typedef GUID UUUID; ...
inline QString TYPE() { return QStringLiteral("type"); }
// types for TAB client
inline QString SEND_BITE_REQUEST() { return QStringLiteral("sendBiteRequest"); }
// types for client --> server messages
inline QString GET_BITE_DATA() { return QStringLiteral("getBiteData"); }
// types for server --> client messages
inline QString BITE_DATA() { return QStringLiteral("biteData"); }
inline QString POSITION_DATA() { return QStringLiteral("positionData"); }
inline QString OK() { return QStringLiteral("ok");}
inline QString ERROR_() { return QLatin1String("error"); }
// data keys
inline QString EQUIP_NO() { return QLatin1String("equipNo"); }
inline QString BOX() { return QLatin1String("box"); }
inline QString LAST_UPDATE() { return QLatin1String("lastUpdate"); }
inline QString BITE_ERRORS() { return QLatin1String("biteErrors"); }
inline QString ERROR_TEXT() { return QLatin1String("errorText"); }
inline QString LANG() { return QLatin1String("lang"); }
inline QString _TEXT() { return QLatin1String("text"); }
inline QString DEVICE_ID() { return QLatin1String("deviceId"); }
inline QString SUBDEVICE() { return QLatin1String("subdevice"); }
inline QString UNIQUE_ID() { return QLatin1String("uniqueId"); }
inline QString ERROR_NO() { return QLatin1String("errorNo"); }
inline QString CONFIGURATION_ERRORS() { return QLatin1String("configurationErrors"); }
inline QString COMPONENT_NAME() { return QLatin1String("componentName"); }
inline QString LAST_POSITION_UPDATE() { return QLatin1String("lastPositionUpdate"); }
inline QString IS_LICENSED() { return QLatin1String("isLicensed"); }
inline QString POS_LAT() { return QLatin1String("posX"); }
inline QString POS_LON() { return QLatin1String("posY"); }
inline QString POS_Z() { return QLatin1String("posZ"); }
inline QString INFO() { return QLatin1String("info"); }
inline QString ERROR_CLASS() { return QLatin1String("errorClass"); }
inline QString ERROR_MESSAGE() { return QLatin1String("errorMessage"); }
inline QString MESSAGE_TYPE() { return QLatin1String("msgType"); }
inline QString EXERCISE_ID() { return QLatin1String("exerciseId"); }
inline QString SERVICES() { return QLatin1String("services"); }
inline QString NAME() { return QLatin1String("name"); }
inline QString NAME_ABBREVATION() { return QLatin1String("nameAbbrevation"); }
inline QString PORTS() { return QLatin1String("ports"); }
inline QString TYPE_ID() { return QLatin1String("typeId"); }
inline QString NUMBER() { return QLatin1String("number"); }
} // namespace JsonKeys
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#include "ws_bite_interface_handler.h"
#include "Connection/connection_handler.h"
#include "Messages/message_handler.h"
#include "Messages/message_transmitter.h"
#include "dds_handler.h"
#include "bite_data_manager.h"
namespace WarehouseGuez
{
namespace WsBiteInterface
{
WsBiteInterfaceHandler::WsBiteInterfaceHandler(DataManager *dataManager)
: m_dataManager(dataManager)
, m_connectionHandler(nullptr)
, m_messageHandler(nullptr)
, m_logger(getLogger("WsBiteInterfaceHandler"))
{
Q_ASSERT(dataManager);
}
WsBiteInterfaceHandler::~WsBiteInterfaceHandler()
{
delete m_connectionHandler;
delete m_messageHandler;
}
bool WsBiteInterfaceHandler::init()
{
Q_ASSERT(!m_connectionHandler);
m_connectionHandler = new Connection::ConnectionHandler;
if (!m_connectionHandler->init())
{
ERROR_LOG(m_logger) << "Failed to initialise ConnectionHandler";
return false;
}
m_messageHandler = new Messages::MessageHandler(m_dataManager);
QObject::connect(m_connectionHandler, &Connection::ConnectionHandler::clientDisconnected, m_dataManager, &DataManager::onClientDisconnect);
//QObject::connect(m_connectionHandler, &Connection::ConnectionHandler::lastConnectionClosed, m_dataManager, &ExerciseManager::stopPositionsUpdate);
QObject::connect(m_connectionHandler, &Connection::ConnectionHandler::newTextMessageFromClient, m_messageHandler, &Messages::MessageHandler::onNewTextMessage);
return true;
}
} // namespace WsBiteInterface
} // namespace CombatNetRadio
@@ -0,0 +1,46 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, SLSE, 2017-09-07
*/
#pragma once
#include <QtCore/QObject>
#include "logger.h"
namespace WarehouseGuez
{
namespace WsBiteInterface
{
class DataManager;
namespace Messages
{
class MessageHandler;
}
namespace Connection
{
class ConnectionHandler;
}
class WsBiteInterfaceHandler final
{
public:
explicit WsBiteInterfaceHandler(DataManager *dataManager);
~WsBiteInterfaceHandler();
bool init();
private:
DataManager *m_dataManager;
Connection::ConnectionHandler *m_connectionHandler;
Messages::MessageHandler *m_messageHandler;
LOGGER_T m_logger;
}; // class WsBiteInterfaceHandle
} // namespace WsBiteInterface
} // namespace WarehouseGuez