Continues migration with DDS, Common

This commit is contained in:
torsten committed 2026-07-17 12:41:42 +02:00
1 parent 9b15cee841
commit fa48db111e
668 files changed
+111841 -2

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Philipp Dassow, SLST, 2017-11-28
#
# --- Library LtsWebSocketCommon --------------------------------------------------------------------------------
#
SET(LtsWebSocketCommon_SRCS
client_message_factory.cpp
client_message_factory.h
client_message.cpp
client_message.h
reply_message.cpp
reply_message.h
data_manager_base_dl.h
json_keys.h
logger.cpp
logger.h
)
NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketCommon SHARED ${LtsWebSocketCommon_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsWebSocketCommon
Qt5::Core
Qt5::WebSockets
NGLLogger
)
#
# --- Library ltsWebSocketServer --------------------------------------------------------------------------------
#
SET(LtsWebSocketServer_SRCS
client_connection.cpp
client_connection.h
connection_handler.cpp
connection_handler.h
lts_web_socket_server.cpp
lts_web_socket_server.h
)
NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketServer SHARED ${LtsWebSocketServer_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsWebSocketServer
Qt5::Core
LtsWebSocketCommon
)
#
# --- Library ltsWebSocketClient --------------------------------------------------------------------------------
#
SET(LtsWebSocketClient_SRCS
lts_web_socket_client.cpp
lts_web_socket_client.h
)
NGL_ADD_LIBRARY2(WS AAR SV LtsWebSocketClient SHARED ${LtsWebSocketClient_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(LtsWebSocketClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(LtsWebSocketClient
Qt5::Core
LtsWebSocketCommon
)
+84
View File
@@ -0,0 +1,84 @@
#include "client_connection.h"
#include <QWebSocket>
namespace LtsWebSocket
{
ClientConnection::ClientConnection(QWebSocket *socket, QObject *parent)
: QThread(parent)
, m_socket(socket ? socket : new QWebSocket)
, m_logger(getLogger("ClientConnection"))
{
}
ClientConnection::~ClientConnection()
{
DEBUG_LOG(m_logger) << "Thread closed" << this;
delete m_socket;
qDeleteAll(m_messageQueue);
}
bool ClientConnection::init()
{
if (m_socket == nullptr)
{
ERROR_LOG(m_logger) << "Initialisation failed due to an invalid socket";
return false;
}
DEBUG_LOG(m_logger) << "Started new thread" << this;
connect(m_socket, &QWebSocket::textMessageReceived, this, &ClientConnection::onNewTextMessageFromSocket, Qt::DirectConnection);
connect(m_socket, &QWebSocket::connected, this, &ClientConnection::sendMessagesFromQueue);
connect(m_socket, &QWebSocket::disconnected, this, &ClientConnection::onSocketDisconnect);
return true;
}
void ClientConnection::addReply(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());
DEBUG_LOG(m_logger) << "Sending message: " << msg ;
m_socket->sendTextMessage(msg);
delete message;
}
void ClientConnection::onNewTextMessageFromSocket(const QString &message)
{
DEBUG_LOG(m_logger) << "Recv msg: " << message << this;
emit newTextMessage(this, message);
}
void ClientConnection::sendMessagesFromQueue()
{
INFO_LOG(m_logger) << "Client connected ip: " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort();
if (m_socket->state() != QAbstractSocket::ConnectedState)
return;
while (!m_messageQueue.isEmpty())
{
ReplyMessage *message = m_messageQueue.dequeue();
const QString msg(message->toJsonString());
TRACE_LOG(m_logger) << "Send msg: " << msg;
m_socket->sendTextMessage(msg);
delete message;
DEBUG_LOG(m_logger) << "Sent message from queue, new size: " << m_messageQueue.size();
}
}
void ClientConnection::onSocketDisconnect()
{
DEBUG_LOG(m_logger) << "Client disconnected ip: " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort();
exit(0);
emit disconnected(this);
}
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <QtCore/QThread>
#include <QtCore/QQueue>
#include "reply_message.h"
#include "logger.h"
#include <LtsWebSocket/ltswebsocketserver_export.h>
class QWebSocket;
namespace LtsWebSocket
{
class LTSWEBSOCKETSERVER_EXPORT ClientConnection
: public QThread
, public ReplyHandler
{
Q_OBJECT
public:
explicit ClientConnection(QWebSocket *socket, QObject *parent = nullptr);
~ClientConnection();
bool init();
void addReply(ReplyMessage *message) override;
QWebSocket *webSocket() { return m_socket; }
signals:
void newTextMessage(ClientConnection *client, const QString &message);
void disconnected(ClientConnection *client);
private slots:
void onNewTextMessageFromSocket(const QString &message);
void sendMessagesFromQueue();
void onSocketDisconnect();
private:
void sendMessages();
private:
QWebSocket *m_socket;
QQueue<ReplyMessage *> m_messageQueue;
LOGGER_T m_logger;
};
}
+38
View File
@@ -0,0 +1,38 @@
#include "client_message.h"
#include "json_keys.h"
#include <QJsonDocument>
namespace LtsWebSocket
{
InvalidMessage::InvalidMessage(ReplyHandler *client, const QString &uuid, const QString &errorText)
: AbstractClientMessage(client, QString(), 1, uuid)
, m_errorText(errorText)
{}
void InvalidMessage::process()
{
ErrorMessage::ErrorData errorData;
errorData.errorClass = 1;
errorData.errorMessage = m_errorText;
errorData.uuid = m_uuid;
m_client->addReply(new ErrorMessage(errorData));
}
bool InvalidMessage::hasValidData() const
{
return true;
}
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <LtsWebSocket/ltswebsocketcommon_export.h>
#include "reply_message.h"
#include <QString>
#include <QJsonValue>
namespace LtsWebSocket
{
class LTSWEBSOCKETCOMMON_EXPORT AbstractClientMessage
{
public:
AbstractClientMessage(ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data = QJsonValue())
: m_type(type)
, m_typeId(typeId)
, m_uuid(uuid)
, m_data(data)
, m_client(client)
{}
virtual ~AbstractClientMessage() = default;
virtual void process() = 0;
virtual bool hasValidData() const = 0;
void reply(ReplyMessage *reply)
{
m_client->addReply(reply);
}
protected:
const QString m_type;
const int m_typeId;
const QString m_uuid;
const QJsonValue m_data;
ReplyHandler *m_client;
};
class LTSWEBSOCKETCOMMON_EXPORT InvalidMessage final : public AbstractClientMessage
{
public:
explicit InvalidMessage(ReplyHandler *client, const QString &uuid, const QString &errorText);
void process() override;
bool hasValidData() const override;
private:
QString m_errorText;
};
}
+69
View File
@@ -0,0 +1,69 @@
#include "client_message_factory.h"
#include "client_connection.h"
#include "json_keys.h"
#include <QJsonDocument>
namespace LtsWebSocket
{
QJsonParseError AbstractClientMessageFactory::parseMessage(const QString &message)
{
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error);
if (error.error != QJsonParseError::NoError)
return error;
QJsonObject obj = doc.object();
m_data = obj.value(JsonKeys::DATA());
m_type = obj.value(JsonKeys::TYPE()).toString();
m_typeId = obj.value(JsonKeys::TYPEID()).toInt();
m_uuid = obj.value(JsonKeys::UUID_()).toString();
return error;
}
AbstractClientMessage *AbstractClientMessageFactory::checkAttributes(ReplyHandler *client,
const QString &message) const
{
if (!m_uuid.isEmpty() && !m_type.isEmpty())
return nullptr;
if (m_uuid.isEmpty())
{
WARNING_LOG(m_logger) << "Got message without uuid: " << message << "'";
return new LtsWebSocket::InvalidMessage(client, QString(), QStringLiteral("Got message without uuid"));
}
WARNING_LOG(m_logger) << "Got message no type: " << message << "'";
return new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Message has no type"));
}
AbstractClientMessage *AbstractClientMessageFactory::checkResult(ReplyHandler *client,
const QString &message,
AbstractClientMessage *result) const
{
if (result == nullptr)
{
WARNING_LOG(m_logger) << "Could not parse message: " << message;
result = new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Invalid type"));
}
if (!result->hasValidData())
{
WARNING_LOG(m_logger) << "Resulting JSon message has invalid data... " << message;
delete result;
result = new LtsWebSocket::InvalidMessage(client, m_uuid, QStringLiteral("Has invalid data part"));
}
return result;
}
}
+71
View File
@@ -0,0 +1,71 @@
#pragma once
#include "client_message.h"
#include "reply_message.h"
#include <LtsWebSocket/ltswebsocketcommon_export.h>
#include <QString>
#include <QJsonParseError>
namespace NGL
{
class INGLLogger;
}
namespace LtsWebSocket
{
class LTSWEBSOCKETCOMMON_EXPORT AbstractClientMessageFactory
{
public:
explicit AbstractClientMessageFactory(NGL::INGLLogger &logger)
: m_logger(logger)
{}
virtual ~AbstractClientMessageFactory() {}
QJsonParseError parseMessage(const QString &message);
virtual AbstractClientMessage *createMessage(ReplyHandler *client, const QString &message) = 0;
protected:
AbstractClientMessage *checkAttributes(ReplyHandler *client,
const QString &message) const;
AbstractClientMessage *checkResult(ReplyHandler *client,
const QString &message,
AbstractClientMessage *result) const;
protected:
QString m_type;
int m_typeId;
QString m_uuid;
QJsonValue m_data;
NGL::INGLLogger &m_logger;
};
class PongMessage final : public ReplyMessage
{
public:
static QString strPong() { return QStringLiteral("pong"); }
static int idPong() { return 2; }
explicit PongMessage(const QString &payload)
: ReplyMessage(strPong(), idPong(), QString())
, m_payload(payload)
{}
QString toJsonString() const override
{
return m_payload.isEmpty() ? strPong() : QStringLiteral("%1 %2").arg(strPong(), m_payload);
}
private:
QString m_payload;
};
}
+89
View File
@@ -0,0 +1,89 @@
#include "connection_handler.h"
#include "client_connection.h"
#include <QtWebSockets/QWebSocket>
using namespace LtsWebSocket;
ConnectionHandler::ConnectionHandler(QObject *parent)
: QObject(parent)
, m_server(nullptr)
, m_logger(getLogger("ConnectionHandler"))
{}
ConnectionHandler::~ConnectionHandler()
{
for (auto connection : qAsConst(m_connections))
connection->disconnect(this);
}
bool ConnectionHandler::init(const QString &serviceName, unsigned short port)
{
if (m_server != nullptr)
{
m_server->disconnect();
delete m_server;
}
m_server = new QWebSocketServer(QStringLiteral("RME-") + serviceName, QWebSocketServer::NonSecureMode, this);
connect(m_server, &QWebSocketServer::newConnection, this, &ConnectionHandler::onNewConnection);
connect(m_server, &QWebSocketServer::serverError, this, &ConnectionHandler::onServerError);
DEBUG_LOG(m_logger) << "Trying to listen ";
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 " << m_server->serverPort();
return true;
}
void ConnectionHandler::sendToAllClients(ReplyMessage *message)
{
for (auto connection : qAsConst(m_connections))
connection->addReply(new ReplyMessage(*message));
delete 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);
connect(newConnection, &ClientConnection::destroyed, this, [this](QObject* o)
{
m_connections.remove(static_cast<ClientConnection*>(o));
});
if (!newConnection->init())
{
ERROR_LOG(m_logger) << "Failed to initialise new client";
delete newConnection;
newConnection = nullptr;
continue;
}
newConnection->start();
connect(newConnection, &ClientConnection::newTextMessage, this, &ConnectionHandler::newTextMessageFromClient);
connect(newConnection, &ClientConnection::disconnected, this, &ConnectionHandler::clientDisconnected);
connect(newConnection, &ClientConnection::finished, newConnection, &ClientConnection::deleteLater);
m_connections.insert(newConnection);
emit clientConnected(newConnection);
INFO_LOG(m_logger) << "New client connected ip: " << socket->localAddress().toString() << ":" << socket->localPort();
}
}
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include <QtNetwork/QHostAddress>
#include <QtWebSockets/qwebsocketprotocol.h>
#include <QtWebSockets/QWebSocketServer>
#include "logger.h"
namespace LtsWebSocket
{
class MessageHandler;
class ClientConnection;
class ReplyMessage;
class ConnectionHandler
: public QObject
{
Q_OBJECT
public:
explicit ConnectionHandler(QObject *parent = nullptr);
~ConnectionHandler();
bool init(const QString &serviceName, unsigned short port);
void sendToAllClients(ReplyMessage *message);
unsigned short webSocketPort() { return m_server->serverPort(); }
signals:
void newTextMessageFromClient(ClientConnection *client, const QString &message);
void clientDisconnected(ClientConnection *client);
void clientConnected(ClientConnection *client);
private slots:
void onServerError(QWebSocketProtocol::CloseCode closeCode);
void onNewConnection();
private:
QWebSocketServer *m_server;
QSet<ClientConnection *> m_connections;
LOGGER_T m_logger;
};
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <memory>
#include <NGLLogger/INGLLogger.h>
#include "reply_message.h"
namespace LtsWebSocket
{
template<typename T>
class DataManagerBaseDl
{
protected:
explicit DataManagerBaseDl(const std::shared_ptr<T> &dbInterface, NGL::INGLLogger &logger)
: m_logger(logger)
, m_dbInterface(dbInterface)
{}
template<typename DbType, typename MessageType>
ReplyMessage *toBaseDataMessageFromDb(const QString &uuid) const
{
QVector<DbType> dataVec;
if (!m_dbInterface->select(dataVec))
{
const QString errorMessage = QStringLiteral("Error reading data from database!");
ERROR_LOG(m_logger) << errorMessage;
ErrorMessage::ErrorData errData;
errData.errorMessage = errorMessage;
errData.uuid = uuid;
return new ErrorMessage(errData);
}
return new MessageType (m_dbInterface->template toJson<DbType>(dataVec), uuid);
}
protected:
NGL::INGLLogger &m_logger;
std::shared_ptr<T> m_dbInterface;
};
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <QtCore/QString>
namespace LtsWebSocket
{
namespace JsonKeys
{
inline QString DATA() { return QStringLiteral("data"); }
inline QString UUID_() { return QStringLiteral("uuid"); }
inline QString TYPE() { return QStringLiteral("type"); }
inline QString TYPEID() { return QStringLiteral("typeId"); }
inline QString ERROR_() { return QStringLiteral("error"); }
inline QString ERROR_MESSAGE() { return QStringLiteral("errorMessage"); }
inline QString ERROR_CLASS() { return QStringLiteral("errorClass"); }
}
}
+32
View File
@@ -0,0 +1,32 @@
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace LtsWebSocket
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "Lts.WebSocket.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <LtsWebSocket/ltswebsocketcommon_export.h>
#include <NGLLogger/INGLLogger.h>
namespace LtsWebSocket
{
typedef NGL::INGLLogger &LOGGER_T;
LTSWEBSOCKETCOMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
}
+202
View File
@@ -0,0 +1,202 @@
#include "lts_web_socket_client.h"
#include <QThread>
#include <QUrl>
#include <QWebSocket>
#include "client_connection.h"
namespace LtsWebSocket
{
class WebSocketHelper : public QObject
{
Q_OBJECT
public:
WebSocketHelper(AbstractClientMessageFactory *messageFactory,
LtsWebSocketClient *webSocketClient,
NGL::INGLLogger &logger);
~WebSocketHelper();
public Q_SLOTS:
void doOpen(const QString &hostname, const unsigned short port);
void doClose();
void doSendTextMessage(const QString &msg);
void onConnected();
void onDisconnected();
void onError(QAbstractSocket::SocketError error);
void parseMessage(const QString &message);
Q_SIGNALS:
void messageParsed(AbstractClientMessage *clientMessage);
void connected();
void disconnected();
void dataReceived();
void error(QAbstractSocket::SocketError);
private:
AbstractClientMessageFactory *m_messageFactory;
LtsWebSocketClient *m_webSocketClient;
QWebSocket *m_socket;
NGL::INGLLogger &m_logger;
};
WebSocketHelper::WebSocketHelper(AbstractClientMessageFactory *messageFactory,
LtsWebSocketClient *webSocketClient,
NGL::INGLLogger &logger)
: QObject()
, m_messageFactory(messageFactory)
, m_webSocketClient(webSocketClient)
, m_socket(new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this))
, m_logger(logger)
{
connect(m_socket, &QWebSocket::textMessageReceived, this, &WebSocketHelper::parseMessage);
connect(m_socket, &QWebSocket::textFrameReceived, this, &WebSocketHelper::dataReceived);
connect(m_socket, &QWebSocket::connected, this, &WebSocketHelper::onConnected);
connect(m_socket, &QWebSocket::disconnected, this, &WebSocketHelper::onDisconnected);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &WebSocketHelper::error);
}
WebSocketHelper::~WebSocketHelper()
{
m_socket->close();
delete m_socket;
}
void WebSocketHelper::doOpen(const QString &hostname, const unsigned short port)
{
const QUrl url(QStringLiteral("ws://%1:%2/").arg(hostname).arg(port));
if (m_socket->state() == QAbstractSocket::SocketState::ConnectedState)
{
if (m_socket->requestUrl() != url)
doClose();
else
{
INFO_LOG(m_logger) << "Web socket to url: " << url.toDisplayString() << " is already open.";
return;
}
}
INFO_LOG(m_logger) << "Opening web socket to url: " << url.toDisplayString();
m_socket->open(url);
}
void WebSocketHelper::doClose()
{
INFO_LOG(m_logger) << "Closing web socket to url:" << m_socket->requestUrl().toDisplayString();
m_socket->close();
}
void WebSocketHelper::doSendTextMessage(const QString &msg)
{
if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState)
{
ERROR_LOG(m_logger) << "Message could not be send, socket is not connected.";
return;
}
DEBUG_LOG(m_logger) << "Sending message: " << msg;
m_socket->sendTextMessage(msg);
}
void WebSocketHelper::onConnected()
{
INFO_LOG(m_logger) << "Connected to server url: " << m_socket->requestUrl().toDisplayString()
<< " using local port: " << m_socket->localPort();
emit connected();
}
void WebSocketHelper::onDisconnected()
{
INFO_LOG(m_logger) << "Disconnected from server url: " << m_socket->requestUrl().toDisplayString();
emit disconnected();
}
void WebSocketHelper::onError(QAbstractSocket::SocketError error)
{
ERROR_LOG(m_logger) << "Error occured: " << error << " closing socket.";
m_socket->close();
}
void WebSocketHelper::parseMessage(const QString &message)
{
const QString pingMsg(QStringLiteral("ping"));
if (message.startsWith(pingMsg))
{
doSendTextMessage(PongMessage(message.mid(pingMsg.size())).toJsonString());
return;
}
AbstractClientMessage *clientMessage = nullptr;
auto result = m_messageFactory->parseMessage(message);
if (QJsonParseError::NoError == result.error)
clientMessage = m_messageFactory->createMessage(m_webSocketClient, message);
else
{
WARNING_LOG(m_logger) << "Could not parse incoming message: '" << message
<< "', error: " << result.errorString();
clientMessage = new InvalidMessage(m_webSocketClient, QString(),
QStringLiteral("Got message which is not JSON conform (%1)").arg(result.errorString()));
}
if (clientMessage)
emit messageParsed(clientMessage);
}
LtsWebSocketClient::LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent)
: QObject(parent)
, m_messageFactory(messageFactory)
, m_webSocketHelperThread(new QThread(this))
, m_logger(getLogger("LtsWebSocketClient"))
{
m_webSocketHelper = new WebSocketHelper(m_messageFactory, this, m_logger);
m_webSocketHelper->moveToThread(m_webSocketHelperThread);
m_webSocketHelperThread->start();
connect(m_webSocketHelper, &WebSocketHelper::connected, this, &LtsWebSocketClient::connected);
connect(m_webSocketHelper, &WebSocketHelper::disconnected, this, &LtsWebSocketClient::disconnected);
connect(m_webSocketHelper, &WebSocketHelper::dataReceived, this, &LtsWebSocketClient::dataReceived);
connect(m_webSocketHelper, &WebSocketHelper::messageParsed, this, &LtsWebSocketClient::onNewClientMessage);
connect(this, &LtsWebSocketClient::onOpen, m_webSocketHelper, &WebSocketHelper::doOpen);
connect(this, &LtsWebSocketClient::onClose, m_webSocketHelper, &WebSocketHelper::doClose);
connect(this, &LtsWebSocketClient::onSendTextMessage, m_webSocketHelper, &WebSocketHelper::doSendTextMessage);
}
LtsWebSocketClient::~LtsWebSocketClient()
{
m_webSocketHelper->disconnect(this);
m_webSocketHelperThread->quit();
m_webSocketHelperThread->wait();
delete m_webSocketHelper;
}
void LtsWebSocketClient::open(const QString &hostname, const unsigned short port)
{
emit onOpen(hostname, port);
}
void LtsWebSocketClient::close()
{
emit onClose();
}
void LtsWebSocketClient::addReply(ReplyMessage *message)
{
const QString msg(message->toJsonString());
emit onSendTextMessage(msg);
delete message;
}
void LtsWebSocketClient::onNewClientMessage(AbstractClientMessage *clientMessage)
{
if (!clientMessage)
return;
clientMessage->process();
delete clientMessage;
}
}
#include "lts_web_socket_client.moc"
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <QObject>
#include <QAbstractSocket>
#include <LtsWebSocket/ltswebsocketclient_export.h>
#include "client_message_factory.h"
#include "logger.h"
class QThread;
namespace LtsWebSocket
{
class WebSocketHelper;
class LTSWEBSOCKETCLIENT_EXPORT LtsWebSocketClient
: public QObject
, public ReplyHandler
{
Q_OBJECT
public:
explicit LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr);
~LtsWebSocketClient();
void open(const QString &hostname, const unsigned short port);
void close();
void addReply(ReplyMessage *message) override;
signals:
void connected();
void disconnected();
void dataReceived();
private slots:
void onNewClientMessage(AbstractClientMessage *clientMessage);
signals:
void onOpen(const QString &hostname, const unsigned short port);
void onClose();
void onSendTextMessage(const QString &msg);
private:
AbstractClientMessageFactory *m_messageFactory;
QThread *m_webSocketHelperThread;
WebSocketHelper *m_webSocketHelper;
LOGGER_T m_logger;
};
}
+119
View File
@@ -0,0 +1,119 @@
#include "lts_web_socket_server.h"
#include "connection_handler.h"
#include "client_connection.h"
#include <QTimer>
namespace LtsWebSocket
{
LtsWebSocketServer::LtsWebSocketServer(AbstractClientMessageFactory *messageFactory, QObject *parent)
: QObject(parent)
, m_queueTimer(new QTimer(this))
, m_messageFactory(messageFactory)
, m_connectionHandler(nullptr)
, m_logger(getLogger("LtsWebSocketServer"))
{
connect(m_queueTimer, &QTimer::timeout,
this, &LtsWebSocketServer::onQueueTimerTimeout);
m_queueTimer->setSingleShot(true);
}
LtsWebSocketServer::~LtsWebSocketServer() = default;
bool LtsWebSocketServer::init(const QString &serviceName, const unsigned short port)
{
m_connectionHandler.reset(new ConnectionHandler(this));
if (!m_connectionHandler->init(serviceName, port))
{
ERROR_LOG(m_logger) << "Could not init m_connectionHandler with servicename: " << serviceName << ((port == 0) ? QStringLiteral(" random port") : QStringLiteral(" port: ") + QString::number(port));
m_connectionHandler.reset();
return false;
}
connect(m_connectionHandler.get(), &ConnectionHandler::newTextMessageFromClient, this, &LtsWebSocketServer::onNewTextMessage);
connect(m_connectionHandler.get(), &ConnectionHandler::clientDisconnected, this, &LtsWebSocketServer::clientDisconnected);
connect(m_connectionHandler.get(), &ConnectionHandler::clientConnected, this, &LtsWebSocketServer::clientConnected);
m_queueTimer->start(m_messageDelay);
return true;
}
bool LtsWebSocketServer::init(const QString &serviceName, const QPair<quint16, quint16> &portRange)
{
if (portRange.first < 1025 || portRange.first > portRange.second)
return init(serviceName);
for (quint16 currentPort = portRange.first; currentPort <= portRange.second; ++ currentPort)
{
if (init(serviceName, currentPort))
return true;
}
return init(serviceName);
}
void LtsWebSocketServer::sendToAllClients(ReplyMessage *message)
{
DEBUG_LOG(m_logger) << "Sending message to all clients: " << message->toJsonString();
m_connectionHandler->sendToAllClients(message);
}
unsigned short LtsWebSocketServer::webSocketPort() const
{
return m_connectionHandler->webSocketPort();
}
void LtsWebSocketServer::setMessageDelay(qint32 delay)
{
m_messageDelay = delay;
}
void LtsWebSocketServer::onNewTextMessage(ReplyHandler *client, const QString &message)
{
const QString pingMsg(QStringLiteral("ping"));
if (message.startsWith(pingMsg))
{
client->addReply(new PongMessage(message.mid(pingMsg.size())));
return;
}
DEBUG_LOG(m_logger) << "Got new message: " << message;
m_messageQueue.append({client, message});
if (!m_queueTimer->isActive())
m_queueTimer->start(m_messageDelay);
}
void LtsWebSocketServer::onQueueTimerTimeout()
{
if (m_messageQueue.isEmpty())
return;
MessageCredentials credentials = m_messageQueue.takeFirst();
AbstractClientMessage *clientMessage = nullptr;
auto result = m_messageFactory->parseMessage(credentials.message);
if (QJsonParseError::NoError == result.error)
clientMessage = m_messageFactory->createMessage(credentials.client, credentials.message);
else
{
WARNING_LOG(m_logger) << "Could not parse incoming message: '" << credentials.message << "', error: " << result.errorString();
clientMessage = new InvalidMessage(credentials.client, QString(), QStringLiteral("Got message which is not JSON conform"));
}
if (clientMessage)
clientMessage->process();
delete clientMessage;
m_queueTimer->start(m_messageDelay);
}
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include <QtCore/QObject>
#include <LtsWebSocket/ltswebsocketserver_export.h>
#include "client_message_factory.h"
#include "logger.h"
#include <memory>
#include <QQueue>
class QTimer;
namespace LtsWebSocket
{
class ConnectionHandler;
class ClientConnection;
class LTSWEBSOCKETSERVER_EXPORT LtsWebSocketServer
: public QObject
{
Q_OBJECT
public:
explicit LtsWebSocketServer(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr);
virtual ~LtsWebSocketServer();
bool init(const QString &serviceName, const unsigned short port = 0);
bool init(const QString &serviceName, const QPair<quint16, quint16> &portRange);
void sendToAllClients(ReplyMessage *message);
unsigned short webSocketPort() const;
void setMessageDelay(qint32 delay);
signals:
void clientDisconnected(ClientConnection *client);
void clientConnected(ClientConnection *client);
private slots:
void onNewTextMessage(ReplyHandler *client, const QString &message);
void onQueueTimerTimeout();
private:
struct MessageCredentials
{
ReplyHandler *client {nullptr};
QString message;
MessageCredentials(ReplyHandler *client, const QString &message)
: client(client)
, message(message)
{}
};
QQueue<MessageCredentials> m_messageQueue;
QTimer *m_queueTimer { nullptr };
AbstractClientMessageFactory *m_messageFactory { nullptr };
std::unique_ptr<ConnectionHandler> m_connectionHandler;
qint32 m_messageDelay { 0 };
LOGGER_T m_logger;
};
}
+17
View File
@@ -0,0 +1,17 @@
namespace LtsWebSocketServer
{
class ReplyMessage;
class MessageHandler
{
public:
virtual void addMessage(ReplyMessage *message) = 0;
};
}
+41
View File
@@ -0,0 +1,41 @@
#include "reply_message.h"
#include "json_keys.h"
#include <QUuid>
namespace LtsWebSocket
{
QString ReplyMessage::toJsonString() const
{
return QString::fromUtf8(QJsonDocument(m_jsonRepresentation).toJson(QJsonDocument::Compact));
}
ReplyMessage::ReplyMessage(const QString &type, const int typeId, const QString &uuid)
{
m_jsonRepresentation.insert(JsonKeys::TYPE(), type);
m_jsonRepresentation.insert(JsonKeys::TYPEID(), typeId);
if (!uuid.isEmpty())
m_jsonRepresentation.insert(JsonKeys::UUID_(), uuid);
}
ErrorMessage::ErrorMessage(const ErrorMessage::ErrorData &data)
: ReplyMessage(JsonKeys::ERROR_(), -1, QUuid::createUuid().toString())
{
m_jsonRepresentation.insert(JsonKeys::DATA(),
QJsonObject({{JsonKeys::UUID_(), data.uuid},
{JsonKeys::ERROR_CLASS(), data.errorClass},
{JsonKeys::ERROR_MESSAGE(), data.errorMessage}
}));
}
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <LtsWebSocket/ltswebsocketcommon_export.h>
#include <QString>
#include <QJsonObject>
#include <QJsonParseError>
namespace LtsWebSocket
{
class LTSWEBSOCKETCOMMON_EXPORT ReplyMessage
{
public:
virtual ~ReplyMessage() = default;
virtual QString toJsonString() const;
QJsonObject json() const { return m_jsonRepresentation; }
protected:
ReplyMessage() = default;
ReplyMessage(const QString &type, const int typeId, const QString &uuid);
protected:
QJsonObject m_jsonRepresentation;
};
class LTSWEBSOCKETCOMMON_EXPORT ReplyHandler
{
public:
virtual ~ReplyHandler() = default;
virtual void addReply(ReplyMessage *message) = 0;
};
class LTSWEBSOCKETCOMMON_EXPORT ErrorMessage final
: public ReplyMessage
{
public:
struct ErrorData
{
int errorClass = 0;
QString uuid;
QString errorMessage;
};
explicit ErrorMessage(const ErrorData &data);
};
}