Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
Executable
+79
@@ -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
|
||||
)
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ClientConnection class represents a connection to a single client.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#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 ;//<< 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);
|
||||
}
|
||||
|
||||
} // namespace LtsWebSocket
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ClientConnection class represents a connection to a single client.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QQueue>
|
||||
|
||||
#include "reply_message.h"
|
||||
#include "logger.h"
|
||||
#include <LtsWebSocket/ltswebsocketserver_export.h>
|
||||
|
||||
class QWebSocket;
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The ClientConnection class represents a connection to a single client
|
||||
*/
|
||||
class LTSWEBSOCKETSERVER_EXPORT ClientConnection
|
||||
: public QThread
|
||||
, public ReplyHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ClientConnection Constructor
|
||||
* @param socket Pointer to a QWebSocket Object. Should be QWebSocketServer::nextPendingConnection()
|
||||
* @param parent QObject parent
|
||||
*/
|
||||
explicit ClientConnection(QWebSocket *socket, QObject *parent = nullptr);
|
||||
|
||||
|
||||
/**
|
||||
* @brief ~ClientConnection Destructor
|
||||
*/
|
||||
~ClientConnection();
|
||||
|
||||
/**
|
||||
* @brief init Initialises the ClientConnection
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief writeTextMessage Add a message to a queue to be sent to the client
|
||||
* @param message Message which is queued
|
||||
*/
|
||||
void addReply(ReplyMessage *message) override;
|
||||
|
||||
QWebSocket *webSocket() { return m_socket; }
|
||||
signals:
|
||||
/**
|
||||
* @brief newTextMessage Emitted when a new text message arrives at the associated socket
|
||||
* @param message The new message
|
||||
*/
|
||||
void newTextMessage(ClientConnection *client, const QString &message);
|
||||
|
||||
/**
|
||||
* @brief disconnected Emitted when a client disconnects
|
||||
* @param client The client
|
||||
*/
|
||||
void disconnected(ClientConnection *client);
|
||||
|
||||
|
||||
private slots:
|
||||
void onNewTextMessageFromSocket(const QString &message);
|
||||
void sendMessagesFromQueue();
|
||||
void onSocketDisconnect();
|
||||
private: // methods
|
||||
/**
|
||||
* @brief sendMessages Runs in an own thread and constantly tries to send
|
||||
* queued messages
|
||||
*/
|
||||
void sendMessages();
|
||||
|
||||
private: // attributes
|
||||
QWebSocket *m_socket;
|
||||
QQueue<ReplyMessage *> m_messageQueue;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
|
||||
|
||||
}; // class ClientConnection
|
||||
} // namespace LtsWebSocket
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Client Message
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
#include "client_message.h"
|
||||
|
||||
#include "json_keys.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
// ######## AbstractClientMessage end ########
|
||||
|
||||
// ######## InvalidMessage begin ########
|
||||
InvalidMessage::InvalidMessage(ReplyHandler *client, const QString &uuid, const QString &errorText)
|
||||
: AbstractClientMessage(client, QString(), 1, uuid) // Check if 1 is feasible - or if it needs to be a part of InvalidMessage
|
||||
, 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
|
||||
{
|
||||
// always true because there is no data part
|
||||
return true;
|
||||
}
|
||||
// ######## InvalidMessage end ########
|
||||
|
||||
} // namespace LtsWebSocketServer
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Client Message
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LtsWebSocket/ltswebsocketcommon_export.h>
|
||||
#include "reply_message.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonValue>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief API for client message implementations that are needed by the LtsWebSocketServer
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief process Processes the messages data
|
||||
*/
|
||||
virtual void process() = 0;
|
||||
|
||||
/**
|
||||
* @brief hasValidData Validates the content of the message
|
||||
* @return True if the message is ICD conform, otherwise false
|
||||
*/
|
||||
virtual bool hasValidData() const = 0;
|
||||
|
||||
/**
|
||||
* @brief reply Send a Reply to the client that has send this message
|
||||
* @param reply
|
||||
*/
|
||||
void reply(ReplyMessage *reply)
|
||||
{
|
||||
m_client->addReply(reply);
|
||||
}
|
||||
|
||||
protected: // attributes
|
||||
const QString m_type;
|
||||
const int m_typeId;
|
||||
const QString m_uuid;
|
||||
const QJsonValue m_data;
|
||||
ReplyHandler *m_client;
|
||||
|
||||
}; // class AbstractClientMessage
|
||||
|
||||
// #################### basic error messages ####################
|
||||
|
||||
/**
|
||||
* @brief The InvalidMessage class represents any message from the client which either could
|
||||
* not be validated, is not JSON conform or has no type or uuid
|
||||
*/
|
||||
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: // attributes
|
||||
QString m_errorText;
|
||||
|
||||
}; // class InvalidMessage
|
||||
|
||||
} // namespace LtsWebSocket
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Client Message Factory
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
|
||||
#include "client_message_factory.h"
|
||||
|
||||
#include "client_connection.h"
|
||||
#include "json_keys.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
// ######## AbstractClientMessageFactory begin ########
|
||||
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;
|
||||
}
|
||||
|
||||
// ######## AbstractClientMessageFactory end ########
|
||||
|
||||
} // namespace LtsWebSocketServer
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Client Message Factory
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
#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
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief API for the client message factory that has to be provided to the LtsWebSocketServer
|
||||
* */
|
||||
class LTSWEBSOCKETCOMMON_EXPORT AbstractClientMessageFactory
|
||||
{
|
||||
public:
|
||||
explicit AbstractClientMessageFactory(NGL::INGLLogger &logger)
|
||||
: m_logger(logger)
|
||||
{}
|
||||
|
||||
virtual ~AbstractClientMessageFactory() {}
|
||||
|
||||
/**
|
||||
* @brief parseMessage parses the message for TYPE, UUID and DATA
|
||||
* @param message to be parsed
|
||||
* @return QJsonParseError
|
||||
*/
|
||||
QJsonParseError parseMessage(const QString &message);
|
||||
|
||||
/**
|
||||
* @brief createMessage Creates the ClientMessage which will process the message
|
||||
* Since the message is not created in the main thread, nothing should be done here!
|
||||
* @param client Handler for replys
|
||||
* @return new client message processor
|
||||
*/
|
||||
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: // attributes of currently processed message
|
||||
QString m_type;
|
||||
int m_typeId;
|
||||
QString m_uuid;
|
||||
QJsonValue m_data;
|
||||
NGL::INGLLogger &m_logger;
|
||||
|
||||
}; // class AbstractClientMessageFactory
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace LtsWebSocket
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ConnectionHandler class functions as a WebSocket server and manages incoming client connections.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#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;
|
||||
}
|
||||
// TODO: get the SslMode from config ?
|
||||
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();
|
||||
}
|
||||
}
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The ConnectionHandler class functions as a WebSocket server and manages incoming client connections.
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtWebSockets/qwebsocketprotocol.h>
|
||||
#include <QtWebSockets/QWebSocketServer>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
class MessageHandler;
|
||||
class ClientConnection;
|
||||
class ReplyMessage;
|
||||
|
||||
/**
|
||||
* @brief The ConnectionHandler class functions as a WebSocket server and manages incoming client connections
|
||||
*/
|
||||
class ConnectionHandler
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief ConnectionHandler
|
||||
* @param parent
|
||||
*/
|
||||
explicit ConnectionHandler(QObject *parent = nullptr);
|
||||
~ConnectionHandler();
|
||||
|
||||
/**
|
||||
* @brief init Starts listening for incoming connections and connects internal signals and slots
|
||||
* @param serviceName Is Used to name the server (RDE-"servicename")
|
||||
* @param port Port the server is listening, if 0 the port number will be random
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
bool init(const QString &serviceName, unsigned short port);
|
||||
|
||||
/**
|
||||
* @brief sendToAll Sends the given msg to all connected Clients
|
||||
* @param message Message to be send
|
||||
*/
|
||||
void sendToAllClients(ReplyMessage *message);
|
||||
|
||||
/**
|
||||
* @brief webSocketPort returns the listen port
|
||||
* @return listen port
|
||||
*/
|
||||
unsigned short webSocketPort() { return m_server->serverPort(); }
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief newTextMessageFromClient Emitted when a message from any client is received
|
||||
* @param client Client where replies are written to
|
||||
* @param message Message from the client
|
||||
*/
|
||||
void newTextMessageFromClient(ClientConnection *client, const QString &message);
|
||||
|
||||
/**
|
||||
* @brief clientDisconnected Emitted when a client disconnects
|
||||
* @param client The client
|
||||
*/
|
||||
void clientDisconnected(ClientConnection *client);
|
||||
|
||||
/**
|
||||
* @brief clientConnected Emitted when a client connects
|
||||
* @param client The client
|
||||
*/
|
||||
void clientConnected(ClientConnection *client);
|
||||
|
||||
private slots:
|
||||
void onServerError(QWebSocketProtocol::CloseCode closeCode);
|
||||
void onNewConnection();
|
||||
|
||||
private: // attributes
|
||||
QWebSocketServer *m_server;
|
||||
QSet<ClientConnection *> m_connections;
|
||||
LOGGER_T m_logger;
|
||||
|
||||
};
|
||||
} // namespace LtsWebSocket
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The DataManager class is a convenience class for managing structure data
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-12-12
|
||||
*/
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
|
||||
#include "reply_message.h"
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
/**
|
||||
* @brief The DataManager class is a convenience class for managing structure data from db
|
||||
* T needs to be of namspace::DatabaseInterface
|
||||
*/
|
||||
template<typename T>
|
||||
class DataManagerBaseDl
|
||||
{
|
||||
protected: // methods
|
||||
/**
|
||||
* @brief DataManager Constructor
|
||||
* @param dbInterface Interface for requesting db-structs data
|
||||
*/
|
||||
explicit DataManagerBaseDl(const std::shared_ptr<T> &dbInterface, NGL::INGLLogger &logger)
|
||||
: m_logger(logger)
|
||||
, m_dbInterface(dbInterface)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief toBaseDataMessageFromDb selects the DbType data from the db and converts it to JSON
|
||||
* @param uuid of the base DL request
|
||||
* @return message send to the requester
|
||||
*/
|
||||
template<typename DbType, typename MessageType>
|
||||
ReplyMessage *toBaseDataMessageFromDb(const QString &uuid) const
|
||||
{
|
||||
QVector<DbType> dataVec;
|
||||
// read all data from database
|
||||
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: // attributes
|
||||
NGL::INGLLogger &m_logger;
|
||||
std::shared_ptr<T> m_dbInterface;
|
||||
}; // class DataManagerBaseDl
|
||||
} // namespace LtsWebSocket
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Header file which holds basic JSON keys
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLSt, 2017-11-28
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QString>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
/**
|
||||
* @brief Namespace which holds all occuring JSON keys as const QString objects
|
||||
*/
|
||||
namespace JsonKeys
|
||||
{
|
||||
// root keys
|
||||
inline QString DATA() { return QStringLiteral("data"); }
|
||||
inline QString UUID_() { return QStringLiteral("uuid"); } // Windows: typedef GUID UUUID; ...
|
||||
inline QString TYPE() { return QStringLiteral("type"); }
|
||||
inline QString TYPEID() { return QStringLiteral("typeId"); }
|
||||
|
||||
// types for server --> client messages
|
||||
inline QString ERROR_() { return QStringLiteral("error"); }
|
||||
|
||||
// data keys
|
||||
inline QString ERROR_MESSAGE() { return QStringLiteral("errorMessage"); }
|
||||
inline QString ERROR_CLASS() { return QStringLiteral("errorClass"); }
|
||||
} // namespace JsonKeys
|
||||
} // namespace LtsWebSocket
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - logging facility
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace LtsWebSocketServer
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - logging facility
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
|
||||
#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());
|
||||
}
|
||||
} // namespace LtsWebSocket
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The LtsWebSocketClient handles a websocket connection to a server
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-03-19
|
||||
*/
|
||||
#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() // no parent, is moved to another thread
|
||||
, 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::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"
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The LtsWebSocketClient handles a websocket connection to a server
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, NLST, 2018-03-19
|
||||
*/
|
||||
#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;
|
||||
|
||||
/**
|
||||
* @brief The LtsWebSocketClient class processes messages from a client connection and starts the parsing of them
|
||||
*/
|
||||
class LTSWEBSOCKETCLIENT_EXPORT LtsWebSocketClient
|
||||
: public QObject
|
||||
, public ReplyHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief LtsWebSocketClient Constructor
|
||||
* @param messageFactory Convenience class for creating ClientMessage
|
||||
* @param parent QObject parent
|
||||
*/
|
||||
explicit LtsWebSocketClient(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr);
|
||||
~LtsWebSocketClient();
|
||||
|
||||
/**
|
||||
* @brief open Opens a websocket with the given hostname and port
|
||||
* @param hostname Hostname
|
||||
* @param port Port of the server
|
||||
*/
|
||||
void open(const QString &hostname, const unsigned short port);
|
||||
|
||||
/**
|
||||
* @brief close
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief addReply Sends the message to the server
|
||||
* @param message Message to be send
|
||||
*/
|
||||
void addReply(ReplyMessage *message) override;
|
||||
|
||||
signals:
|
||||
void connected();
|
||||
void disconnected();
|
||||
void dataReceived();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief called when the thread finsihed parsing the message so it can be processed by the main thread
|
||||
*/
|
||||
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;
|
||||
};
|
||||
} // namespace LtsWebSocketClient
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The LtsWebSocketServer class processes messages from the client
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#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) // "portRange.second > 65535" check is superfluous since 65535 ist max quint16
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* The LtsWebSocketServer class processes messages from the client
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
#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;
|
||||
|
||||
/**
|
||||
* @brief The LtsWebSocketServer class processes messages from a client connection and starts the parsing of them
|
||||
*/
|
||||
class LTSWEBSOCKETSERVER_EXPORT LtsWebSocketServer
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief LtsWebSocketServer Constructor
|
||||
* @param messageFactory Convenience class for creating ClientMessage
|
||||
* @param parent QObject parent
|
||||
*/
|
||||
explicit LtsWebSocketServer(AbstractClientMessageFactory *messageFactory, QObject *parent = nullptr);
|
||||
virtual ~LtsWebSocketServer();
|
||||
|
||||
/**
|
||||
* @brief init Initialises the LtsWebSocketServer to work properly
|
||||
* @param serviceName Is Used to name the server (RDE-servicename)
|
||||
* @param port Port the server is listening, if 0 the port number will be random
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
bool init(const QString &serviceName, const unsigned short port = 0);
|
||||
|
||||
/**
|
||||
* @brief init Initialises the LtsWebSocketServer to work properly
|
||||
* @param serviceName Is Used to name the server (RDE-servicename)
|
||||
* @param portRange Range where to look at for a port. If invalid (valid range is 1025-65535) a random port is chosen
|
||||
* @return True on success, otherwise false
|
||||
*/
|
||||
bool init(const QString &serviceName, const QPair<quint16, quint16> &portRange);
|
||||
|
||||
/**
|
||||
* @brief sendToAll Sends the given msg to all connected Clients
|
||||
* @param message Message to be send
|
||||
*/
|
||||
void sendToAllClients(ReplyMessage *message);
|
||||
|
||||
/**
|
||||
* @brief webSocketPort returns the listen port
|
||||
* @return listen port
|
||||
*/
|
||||
unsigned short webSocketPort() const;
|
||||
|
||||
/**
|
||||
* @brief setMessageDelay Sets the delay between two messages to be processed
|
||||
* @param delay Delay in milliseconds
|
||||
*/
|
||||
void setMessageDelay(qint32 delay);
|
||||
signals:
|
||||
/**
|
||||
* @brief disconnected Emitted when a client disconnects
|
||||
* @param client The client
|
||||
*/
|
||||
void clientDisconnected(ClientConnection *client);
|
||||
|
||||
/**
|
||||
* @brief clientConnected Emitted when a client connects
|
||||
* @param client The client
|
||||
*/
|
||||
void clientConnected(ClientConnection *client);
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief onNewTextMessage Slot to react to new messages from clients
|
||||
* @param client Client to handle a reply
|
||||
* @param message Message from a client
|
||||
*/
|
||||
void onNewTextMessage(ReplyHandler *client, const QString &message);
|
||||
|
||||
/**
|
||||
* @brief onQueueTimerTimeout Timeout for messageprocessing
|
||||
*/
|
||||
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;
|
||||
};
|
||||
} // namespace LtsWebSocket
|
||||
+26
@@ -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 Baeuerle, SLSE, 2016-08-25
|
||||
*/
|
||||
|
||||
namespace LtsWebSocketServer
|
||||
{
|
||||
class ReplyMessage;
|
||||
|
||||
/**
|
||||
* @brief The MessageHandler Interface offers an extremely lightweight API for a class to just write textmessages to
|
||||
*/
|
||||
class MessageHandler
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief writeTextMessage Method to write a textmessage via this MessageHandler
|
||||
* @param message Message to send
|
||||
*/
|
||||
virtual void addMessage(ReplyMessage *message) = 0;
|
||||
};
|
||||
} // namespace LtsWebSocket
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Reply Message and Reply Handler
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
|
||||
#include "reply_message.h"
|
||||
|
||||
#include "json_keys.h"
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
// ######## ReplyMessage begin ########
|
||||
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);
|
||||
}
|
||||
// ######## ReplyMessage end ########
|
||||
|
||||
// ######## ErrorMessage begin ########
|
||||
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}
|
||||
}));
|
||||
}
|
||||
// ######## ErrorMessage end ########
|
||||
|
||||
} // namespace LtsWebSocketServer
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* LTS web socket server - Abstract Reply Message and Reply Handler
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Philipp Dassow, SLST, 2017-11-28
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <LtsWebSocket/ltswebsocketcommon_export.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
namespace LtsWebSocket
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Base class for reply messages
|
||||
*/
|
||||
class LTSWEBSOCKETCOMMON_EXPORT ReplyMessage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief ~ReplyMessage Destructor
|
||||
*/
|
||||
virtual ~ReplyMessage() = default;
|
||||
|
||||
/**
|
||||
* @brief toJsonString Converts this message into a valid JSON string
|
||||
* @return QString JSON representation of this class
|
||||
*/
|
||||
virtual QString toJsonString() const;
|
||||
|
||||
QJsonObject json() const { return m_jsonRepresentation; }
|
||||
|
||||
protected: // methods
|
||||
ReplyMessage() = default;
|
||||
ReplyMessage(const QString &type, const int typeId, const QString &uuid);
|
||||
|
||||
protected: // attributes
|
||||
QJsonObject m_jsonRepresentation;
|
||||
}; // class ReplyMessage
|
||||
|
||||
/**
|
||||
* @brief The ReplyHandler Interface offers an extremely lightweight API for a class to just write textmessages to
|
||||
*/
|
||||
class LTSWEBSOCKETCOMMON_EXPORT ReplyHandler
|
||||
{
|
||||
public:
|
||||
virtual ~ReplyHandler() = default;
|
||||
/**
|
||||
* @brief addReply Method to write a textmessage via this MessageTransmitter
|
||||
* @param message Message to send
|
||||
*/
|
||||
virtual void addReply(ReplyMessage *message) = 0;
|
||||
}; // class ReplyHandler
|
||||
|
||||
// #################### basic error messages ####################
|
||||
|
||||
/**
|
||||
* @brief The ErrorMessage class is sent when any data from a client could not be processed
|
||||
*/
|
||||
class LTSWEBSOCKETCOMMON_EXPORT ErrorMessage final
|
||||
: public ReplyMessage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The ErrorData struct represents the data part of an ErrorMessage object
|
||||
*/
|
||||
struct ErrorData
|
||||
{
|
||||
int errorClass = 0;
|
||||
QString uuid;
|
||||
QString errorMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ErrorMessage Constructor
|
||||
* @param data Data part of an ErrorMessage object
|
||||
*/
|
||||
explicit ErrorMessage(const ErrorData &data);
|
||||
}; // class ErrorMessage
|
||||
|
||||
} // namespace LtsWebSocket
|
||||
Reference in new issue
Block a user