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,192 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "client_message.h"
#include "WsBaseDataInterface/Messages/reply_message.h"
#include "WsBaseDataInterface/data_manager.h"
#include <WMS_Interface/json_keys.h>
#include <LtsWebSocket/json_keys.h>
#include <WMS_Interface/wms_ecc_exercises.h>
#include <WMS_Interface/wms_ecc_playerInfo.h>
#include <WMS_Interface/wms_ecc_registration.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QUuid>
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
namespace Messages
{
// ####### ClientMessageFactory begin #######
ClientMessageFactory::ClientMessageFactory(DataManager *dataManager)
: LtsWebSocket::AbstractClientMessageFactory(getLogger("BiteClientMessageFactory"))
, m_dataManager(dataManager)
{}
LtsWebSocket::AbstractClientMessage *ClientMessageFactory::createMessage(LtsWebSocket::ReplyHandler *client,
const QString &message)
{
LtsWebSocket::AbstractClientMessage *result = checkAttributes(client, message);
if (result)
return result;
if (m_type == LtsWebSocket::JsonKeys::ERROR_())
{
ERROR_LOG(m_logger) << "Error in message: " << message;
return nullptr;
}
if (m_type == ::WebSocketInterface::JsonKeys::EQUIPMENT_TYPES_REQUEST())
result = new EquipmentTypesRequest(m_dataManager, client, m_type, m_typeId, m_uuid);
if (m_type == ::WebSocketInterface::JsonKeys::EXERCISE_UPDATE())
result = new ExerciseUpdate(m_dataManager, client, m_type, m_typeId, m_uuid, m_data);
if (m_type == ::WebSocketInterface::JsonKeys::EXERCISES_REPLY())
result = new ExercisesReply(m_dataManager, client, m_type, m_typeId, m_uuid, m_data);
if (m_type == ::WebSocketInterface::JsonKeys::PARTICIPANTS_UPDATE())
result = new ParticipantsUpdate(m_dataManager, client, m_type, m_typeId, m_uuid, m_data);
if (m_type == ::WebSocketInterface::JsonKeys::PARTICIPANTS_REPLY())
result = new ParticipantsReply(m_dataManager, client, m_type, m_typeId, m_uuid, m_data);
if (m_type == ::WebSocketInterface::JsonKeys::PARTICIPANT_REGISTER_IN_EXCON_REPLY())
result = new ParticipantRegisterInExconReply(m_dataManager, client, m_type, m_typeId, m_uuid, m_data);
return checkResult(client, message, result);
}
// ####### ClientMessageFactory end #######
// ####### ClientMessage begin #######
ClientMessage::ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: LtsWebSocket::AbstractClientMessage(client, type, typeId, uuid, data)
, m_dataManager(dataManager)
{}
// ####### ClientMessage end #######
// ####### EquipmentTypesRequest begin #######
EquipmentTypesRequest::EquipmentTypesRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
: ClientMessage (dataManager,client, type, typeId, uuid, QJsonValue())
{}
void EquipmentTypesRequest::process()
{
m_client->addReply(m_dataManager->equipmentTypes(m_uuid));
}
bool EquipmentTypesRequest::hasValidData() const
{
return true;
}
// ####### EquipmentTypesRequest end #######
// ####### ExercisesReply begin #######
ExercisesReply::ExercisesReply(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: ClientMessage (dataManager,client, type, typeId, uuid, data)
{}
void ExercisesReply::process()
{
EXCON::Exercises ex = EXCON::Exercises::fromJsonAll(m_data.toObject());
m_dataManager->newExerciseInformationsFromEXCON(ex);
}
bool ExercisesReply::hasValidData() const
{
return true;
}
// ####### ExercisesReply end #######
// ####### ExercisesReply begin #######
ExerciseUpdate::ExerciseUpdate(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: ClientMessage (dataManager,client, type, typeId, uuid, data)
{}
void ExerciseUpdate::process()
{
const QJsonObject obj = m_data.toObject();
DataModification modType = (DataModification) obj[::WebSocketInterface::JsonKeys::DATA_MODIFICATION()].toInt();
IWMS2ECC::Exercise exeData = EXCON::Exercises::fromJson(obj);
m_dataManager->exerciseChanged(exeData, modType);
}
bool ExerciseUpdate::hasValidData() const
{
return true;
}
// ####### ExerciseUpdate end #######
// ####### ParticipantsReply begin #######
ParticipantsReply::ParticipantsReply(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: ClientMessage (dataManager,client, type, typeId, uuid, data)
{}
void ParticipantsReply::process()
{
PlayerInfo pl = PlayerInfo::fromJsonAll(m_data.toObject());
m_dataManager->participantsFromEXCON(pl);
}
bool ParticipantsReply::hasValidData() const
{
return true;
}
// ####### ParticipantsReply end #######
// ####### ParticipantsUpdate begin #######
ParticipantsUpdate::ParticipantsUpdate(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: ClientMessage (dataManager,client, type, typeId, uuid, data)
{}
void ParticipantsUpdate::process()
{
const QJsonObject obj = m_data.toObject();
int modType = obj[::WebSocketInterface::JsonKeys::DATA_MODIFICATION()].toInt();
IWMS2ECC::PlayerInfo pl = PlayerInfo::fromJson(obj);
m_dataManager->participantFromEXCON(pl, modType);
}
bool ParticipantsUpdate::hasValidData() const
{
return true;
}
// ####### ParticipantsUpdate end #######
// ####### ParticipantRegisterInExconReply begin #######
ParticipantRegisterInExconReply::ParticipantRegisterInExconReply(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data)
: ClientMessage (dataManager,client, type, typeId, uuid, data)
{}
void ParticipantRegisterInExconReply::process()
{
const QJsonObject obj = m_data.toObject();
int exerciseId = obj[::WebSocketInterface::JsonKeys::EXERCISE_ID()].toInt();
qint32 participantId = obj[::WebSocketInterface::JsonKeys::PARTICIPANT_ID()].toInt();
int returnValue = obj[::WebSocketInterface::JsonKeys::REGISTRATION_RETURN_VALUE()].toInt();
//qWarning() << "exerciseId: " << exerciseId << ", participantId: " << participantId << ", retValue: " << returnValue;
m_dataManager->registerReply(exerciseId, participantId, returnValue);
}
bool ParticipantRegisterInExconReply::hasValidData() const
{
return true;
}
// ####### ParticipantRegisterInExconReply end #######
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,130 @@
/**
* @RDE_HEADER@
* The ClientMessage class is an abstract base class for concrete ClientMessage objects
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QtCore/QJsonObject>
#include <LtsWebSocket/client_message_factory.h>
#include <LtsWebSocket/client_message.h>
#include "logger.h"
#include <WMS_Interface/json_keys.h>
// incoming messages
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
class DataManager;
namespace Messages
{
/**
* @brief The ClientMessageFactory class creates ClientMessages which will then be processed
*/
class ClientMessageFactory final : public LtsWebSocket::AbstractClientMessageFactory
{
public:
explicit ClientMessageFactory(DataManager* dataManager);
/**
* @brief create Only way to create a ClientMessage object
* @param message Text message which will be converted to a ClientMessage object
* @return A ClientMessage object, which represents a concrete ClientMessage
*/
LtsWebSocket::AbstractClientMessage *createMessage(LtsWebSocket::ReplyHandler *client,
const QString &message);
private:
DataManager *m_dataManager;
}; // class ClientMessageFactory
/**
* @brief The ClientMessage class is an abstract base class for concrete ClientMessage objects
*/
class ClientMessage : public LtsWebSocket::AbstractClientMessage
{
protected: // method
explicit ClientMessage(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
protected: // attributes
DataManager *m_dataManager;
}; // class ClientMessage
// ################## Message Implementations ##################
class EquipmentTypesRequest : public ClientMessage
{
public:
explicit EquipmentTypesRequest (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
void process();
bool hasValidData() const;
}; // class EquipmentTypesRequest
// Client -> Server - answer to ExercisesRequest or information from Client that a specific exercise changed
// -> then there is only one exercise in the message
class ExercisesReply : public ClientMessage
{
public:
explicit ExercisesReply (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
void process();
bool hasValidData() const;
}; // class ExercisesReply
class ExerciseUpdate : public ClientMessage
{
public:
explicit ExerciseUpdate (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
void process();
bool hasValidData() const;
}; // class ExerciseUpdate
class ParticipantsReply : public ClientMessage
{
public:
explicit ParticipantsReply (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
void process();
bool hasValidData() const;
}; // class ParticipantsReply
class ParticipantsUpdate : public ClientMessage
{
public:
explicit ParticipantsUpdate (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
void process();
bool hasValidData() const;
}; // class ParticipantsUpdate
class ParticipantRegisterInExconReply : public ClientMessage
{
public:
explicit ParticipantRegisterInExconReply (DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid, const QJsonValue &data);
void process();
bool hasValidData() const;
}; // class ParticipantRegisterInExconReply
} // namespace Messages
} // namespace WsBiteInterface
} // namespace WarehouseGuez
@@ -0,0 +1,55 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "reply_message.h"
#include <LtsWebSocket/json_keys.h>
#include <WMS_Interface/json_keys.h>
#include <QtCore/QUuid>
#include <QtCore/QJsonDocument>
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
namespace Messages
{
using namespace LtsWebSocket::JsonKeys;
ExerciseRequest::ExerciseRequest()
: LtsWebSocket::ReplyMessage(::WebSocketInterface::JsonKeys::EXERCISES_REQUEST(), 1, QStringLiteral("drei")) // Todo: Check whether typeId is correct
{}
ParticipantsRequest::ParticipantsRequest(int exerciseId)
: LtsWebSocket::ReplyMessage(::WebSocketInterface::JsonKeys::PARTICIPANTS_REQUEST(), 2, QStringLiteral("drei")) // Todo: Check whether typeId is correct
{
m_jsonRepresentation[::WebSocketInterface::JsonKeys::EXERCISE_ID()] = exerciseId;
}
// ####### EquipmentTypesMessage begin #######
EquipmentTypesMessage::EquipmentTypesMessage(const QJsonObject &data, const QString &uuid)
: ReplyMessage (::WebSocketInterface::JsonKeys::EQUIPMENT_TYPES_REPLY(), 3, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### EquipmentTypesMessage end #######
// ####### ParticipantRegisterInExconRequest begin #######
ParticipantRegisterInExconRequest::ParticipantRegisterInExconRequest(const QJsonObject &data, const QString &uuid)
: ReplyMessage (::WebSocketInterface::JsonKeys::PARTICIPANT_REGISTER_IN_EXCON_REQUEST(), 4, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### ParticipantRegisterInExconRequest end #######
} // namespace Messages
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <LtsWebSocket/client_message_factory.h>
#include <DDS/Core/core.hpp>
#include <QString>
#include <QMap>
#include <QtCore/QVector>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
// outgoing messages
// ReplyMessage -> MessageToSend
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
namespace Messages
{
// WHG -> WMS - requesting data for all exercises
class ExerciseRequest : public LtsWebSocket::ReplyMessage
{
public:
ExerciseRequest();
};
class ParticipantsRequest : public LtsWebSocket::ReplyMessage
{
public:
explicit ParticipantsRequest(int exerciseId);
};
/**
* @brief The EquipmentTypesMessage class represents the equipment types from the svcWarehouseGuez
*/
class EquipmentTypesMessage final : public LtsWebSocket::ReplyMessage
{
public:
explicit EquipmentTypesMessage (const QJsonObject &data, const QString &uuid);
}; // class EquipmentTypesMessage
// ReplyMessage -> message to send
class ParticipantRegisterInExconRequest : public LtsWebSocket::ReplyMessage
{
public:
explicit ParticipantRegisterInExconRequest(const QJsonObject &data, const QString &uuid);
void process();
bool hasValidData() const;
}; // class ParticipantRegisterRequest
} // namespace Messages
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez
@@ -0,0 +1,559 @@
/**
* @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 Philipp Dassow, SLST, 2017-12-05
*/
#include "WsBaseDataInterface/data_manager.h"
#include <WMS_Interface/IWMS2ECC.h>
#include <WMS_Interface/wms_ecc_equipmenttypes.h>
#include <WMS_Interface/wms_ecc_registration.h>
#include <ngltools/ngltools_filesystem.h>
#include <ngldb/nsqlquery.h>
#include <CodeGen/SqlGenerator.h>
#include "main_handler.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QMap>
#include <QSqlRecord>
#include <QTimer>
#include <QVector>
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
static constexpr int s_exerciseOffset = 1000000;
static inline QString tlnTable(int exerciseId)
{
return QStringLiteral("tln_") + QString::number(exerciseId);
}
static inline QString tlnZuordnungTable(int exerciseId)
{
return QStringLiteral("tln_zuordnung_") + QString::number(exerciseId);
}
static inline QString triggerName(int exerciseId)
{
if (exerciseId <= 0)
return QStringLiteral("whg_notifyInsertParticipantAssignment");
return QStringLiteral("whg_notifyInsertParticipantAssignment_") + QString::number(exerciseId);
}
//---------------------------------------------------------------------------------------------------------
DataManager::DataManager(MainHandler *mainHandler)
: m_mainHandler(mainHandler)
, m_webSocketServer(nullptr)
, m_logger(getLogger(QStringLiteral("DataManager")))
{
}
//---------------------------------------------------------------------------------------------------------
bool DataManager::init()
{
reconnectSignals();
return true;
}
//---------------------------------------------------------------------------------------------------------
LtsWebSocket::ReplyMessage *DataManager::equipmentTypes(const QString &uuid) const
{
using namespace Messages;
IWMS2ECC::equipTypeMap etMap;
auto cbEqTypeWh = [&etMap](const WarehouseGuez::Database::EquipmentTypeWh &data)
{
IWMS2ECC::EquipmentType et;
et.id = data.id;
et.name = data.name;
et.minEquipNo = data.ausstgNrMin;
et.maxEquipNo = data.ausstgNrMax;
et.specialType = 0; // not used in GUEZ
et.taVulnerabilityModelId = data.vbAgdus;
etMap.insert(et.id, et);
};
m_mainHandler->db()->select(cbEqTypeWh);
EquipmentTypes eTypes;
eTypes.setEquipmentTypes(etMap);
QJsonObject obj = eTypes.toJsonAll();
INFO_LOG(m_logger) << "equipTypeMap.size: " << etMap.size() << ", " << QJsonDocument(obj).toJson();
return new EquipmentTypesMessage(obj, uuid);
}
void DataManager::newExerciseInformationsFromEXCON(const EXCON::Exercises &ex)
{
const IWMS2ECC::exerciseMap exMap = ex.getExercises();
DEBUG_LOG(m_logger) << "Exercises map.size: " << exMap.size();
QVector<Database::Exercise> dataVec;
m_mainHandler->db()->select(dataVec);
for (const IWMS2ECC::Exercise &exercise : exMap)
{
WarehouseGuez::Database::Exercise ex;
ex.id = exercise.id;
ex.name = exercise.name;
ex.beginn = QDateTime::fromMSecsSinceEpoch(exercise.start);
ex.aktUebung = exercise.actExercise;
ex.tln = tlnTable(exercise.id);
ex.tlnZuordnung = tlnZuordnungTable(exercise.id);
bool ok;
if (findExercise(exercise.id, dataVec))
ok = m_mainHandler->db()->update(ex);
else
ok = m_mainHandler->db()->insert(ex);
if (!ok)
{
ERROR_LOG(m_logger) << "Error while insert/update in exercise";
break;
}
}
}
bool DataManager::findExercise(int exerciseId, const QVector<Database::Exercise> &dataVec)
{
for (const Database::Exercise &exercise : dataVec)
{
if (exercise.id == exerciseId)
return true;
}
return false;
}
static inline QString fixupString(const QString &in)
{
// Warehouse in GUEZ loves SQL-Injection
QString ret;
ret.reserve(in.size());
for (const QChar &c : in)
{
if (c == QLatin1Char('\"') ||
c == QLatin1Char('\'') ||
c.unicode() == 0x60 || // GRAVE ACCENT - `
c.unicode() == 0xb4 || // ACUTE ACCENT - ´
c == QLatin1Char(';'))
continue;
ret += c;
}
return ret;
}
static inline void bindPlayer(NSqlQuery &q, const IWMS2ECC::PlayerInfo &pl)
{
q.bindValue(QStringLiteral(":id"), pl.playerId);
q.bindValue(QStringLiteral(":hierarchie"), fixupString(pl.hierarchy));
q.bindValue(QStringLiteral(":typ_id"), pl.typeId);
q.bindValue(QStringLiteral(":ausstg_typ_id"), pl.equipTypeId);
q.bindValue(QStringLiteral(":ausstg_nr"), pl.equipNo);
q.bindValue(QStringLiteral(":zuordnung"), pl.assignState);
q.bindValue(QStringLiteral(":y_nr"), fixupString(pl.alias));
q.bindValue(QStringLiteral(":info"), fixupString(pl.info));
q.bindValue(QStringLiteral(":loeschkennung"), pl.isDeleted ? 1 : 0); // bool -> smallint :(
q.bindValue(QStringLiteral(":zeit"), NSqlQuery::qDateTimeToQVariant(QDateTime::currentDateTime()));
}
void DataManager::participantsFromEXCON(const PlayerInfo &pl)
{
const QMap<int,IWMS2ECC::PlayerInfo> plMap = pl.getAllPlayerInfo();
if (plMap.isEmpty())
{
DEBUG_LOG(m_logger) << "No valid exercise -> no participants";
return;
}
// first get exerciseId
int exerciseId = plMap.firstKey() / s_exerciseOffset;
DEBUG_LOG(m_logger) << "ExerciseId: " << exerciseId;
DEBUG_LOG(m_logger) << "plMap.size(): " << plMap.size();
// table is created at exercise creation
QString tlnOut = tlnTable(exerciseId);
NSqlQuery q(m_mainHandler->db()->database());
// If entries exist delete them
QString stmt = QStringLiteral("DELETE FROM ") + tlnOut;
DEBUG_LOG(m_logger) << "Executing delete statement: " << stmt;
q.exec(stmt);
stmt = QStringLiteral("INSERT INTO ") + tlnOut + QStringLiteral("( id, hierarchie, typ_id, ausstg_typ_id, ausstg_nr, zuordnung, y_nr, info, loeschkennung, zeit ) VALUES (" )
+ QStringLiteral(":id, :hierarchie, :typ_id, :ausstg_typ_id, :ausstg_nr, :zuordnung, :y_nr, :info, :loeschkennung, :zeit )");
DEBUG_LOG(m_logger) << "Preparing insert statement: " << stmt;
q.prepare(stmt);
for (const IWMS2ECC::PlayerInfo &pl : plMap)
{
bindPlayer(q, pl);
DEBUG_LOG(m_logger) << "Executing insert statement for participant: " << pl.playerId;
if (!q.exec())
ERROR_LOG(m_logger) << "Error inserting data for participantId: " << pl.playerId;
}
}
bool DataManager::participantFromEXCON(const IWMS2ECC::PlayerInfo &pl, int modType)
{
DEBUG_LOG(m_logger) << "IN participantFromEXCON: modType: " << modType << ", participantId: " << pl.playerId;
DataModification modificationType = static_cast<DataModification>(modType);
int exerciseId = pl.playerId / s_exerciseOffset;
QString tlnOut = tlnTable(exerciseId);
NSqlQuery q(m_mainHandler->db()->database());
DEBUG_LOG(m_logger) << "Executing delete statement: " << QStringLiteral("DELETE FROM %1 WHERE id = %2").arg(tlnOut).arg(pl.playerId);
q.exec(QStringLiteral("DELETE FROM %1 WHERE id = %2").arg(tlnOut).arg(pl.playerId));
switch(modificationType)
{
case DataModification::eModified:
case DataModification::eAdded:
{
QString stmt = QStringLiteral("INSERT INTO ") + tlnOut +
QStringLiteral("( id, hierarchie, typ_id, ausstg_typ_id, ausstg_nr, zuordnung, y_nr, info, loeschkennung, zeit ) VALUES (" ) +
QStringLiteral(":id, :hierarchie, :typ_id, :ausstg_typ_id, :ausstg_nr, :zuordnung, :y_nr, :info, :loeschkennung, :zeit )");
if (!q.prepare(stmt))
{
ERROR_LOG(m_logger) << "Error while preparing insert stmt: " << stmt;
return false;
}
bindPlayer(q, pl);
DEBUG_LOG(m_logger) << "Executing insert statement: " << stmt;
if (!q.exec())
{
ERROR_LOG(m_logger) << "Error inserting data for participantId: " << pl.playerId;
return false;
}
break;
}
case DataModification::eRemoved:
break;
} // end switch
return true;
}
bool DataManager::exerciseChanged(const IWMS2ECC::Exercise &data, DataModification modType)
{
DEBUG_LOG(m_logger) << "IN exerciseChanged: id = " << data.id << ", actExercise: " << data.actExercise;
Database::Exercise exData;
exData.id = data.id;
exData.name = data.name;
exData.beginn = QDateTime::fromMSecsSinceEpoch(data.start);
exData.tln = tlnTable(data.id);
exData.tlnZuordnung = tlnZuordnungTable(data.id);
exData.aktUebung = data.actExercise;
switch(modType)
{
case DataModification::eAdded:
{
Database::Exercise tmp;
if (m_mainHandler->db()->selectExercise(exData.id, tmp))
{
if (!m_mainHandler->db()->update(exData))
{
ERROR_LOG(m_logger) << "Error while updating exerciseData for exercise: " << data.id;
return false;
}
}
else
{
if (!m_mainHandler->db()->insert(exData))
{
ERROR_LOG(m_logger) << "Error while inserting exerciseData for exercise: " << data.id;
return false;
}
createTableTlnOut(exData.id);
createTableTlnZuordnung(exData.id);
}
return true;
}
case DataModification::eModified:
{
if (!m_mainHandler->db()->update(exData))
{
ERROR_LOG(m_logger) << "Error while updating exerciseData for exercise: " << data.id;
return false;
}
return true;
}
case DataModification::eRemoved:
{
if (!m_mainHandler->db()->removeExercise(data.id))
{
ERROR_LOG(m_logger) << "Error while removing exerciseData for exercise: " << data.id;
return false;
}
return true;
}
}
ERROR_LOG(m_logger) << "DataModification Type: " << modType << " not handled in switch";
return false;
}
void DataManager::createTableTlnOut(int exerciseId)
{
ERROR_LOG(m_logger) << "createTableTlnOut";
// xmlPath
const QString xmlPath = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("xml"));
QString xmlFile = xmlPath + QStringLiteral("/whg.xml");
CodeGen::SqlGenerator sqlGen(xmlFile);
QString tlnOut = tlnTable(exerciseId);
ERROR_LOG(m_logger) << "tlnOut: " << tlnOut;
sqlGen.createStatementsForTable(QStringLiteral("tln"), tlnOut);
const CodeGen::SqlGenerator::SqlStatements stmts = sqlGen.getStatements(CodeGen::Global);
QSqlRecord record = m_mainHandler->db()->database().record(tlnOut);
NSqlQuery q(m_mainHandler->db()->database());
for (const QString &stmt : stmts.createTables)
{
ERROR_LOG(m_logger) << "stmt: " << stmt;
if (record.isEmpty())
{
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error executing stmt: " << stmt;
}
}
}
void DataManager::createTableTlnZuordnung(int exerciseId)
{
ERROR_LOG(m_logger) << "in createTableTlnZuordnung";
// xmlPath
const QString xmlPath = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("xml"));
QString xmlFile = xmlPath + QStringLiteral("/whg.xml");
CodeGen::SqlGenerator sqlGen(xmlFile);
const QString tlnZuordnung = tlnZuordnungTable(exerciseId);
ERROR_LOG(m_logger) << "tlnZuordnung: " << tlnZuordnung;
sqlGen.createStatementsForTable(QStringLiteral("tln_zuordnung"), tlnZuordnung);
const CodeGen::SqlGenerator::SqlStatements stmts = sqlGen.getStatements(CodeGen::Global);
QSqlRecord record = m_mainHandler->db()->database().record(tlnZuordnung);
NSqlQuery q(m_mainHandler->db()->database());
for (const QString &stmt : stmts.createTables)
{
DEBUG_LOG(m_logger) << "stmt: " << stmt;
if (record.isEmpty())
{
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error executing stmt: " << stmt;
}
}
for (const QString &stmt : stmts.createSequences)
{
DEBUG_LOG(m_logger) << "stmt: " << stmt;
if (record.isEmpty())
{
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error executing stmt: " << stmt;
}
}
createTriggerForExercise(q, exerciseId);
}
void DataManager::createTriggerForExercise(NSqlQuery &q, int exerciseId)
{
QString stmt;
const QString triggerNameExercise(triggerName(exerciseId));
const QString tlnZuordnungTableExercise(tlnZuordnungTable(exerciseId));
// drop possible old trigger
stmt = QLatin1String("DROP TRIGGER IF EXISTS ") + triggerNameExercise +
QLatin1String(" ON ") + tlnZuordnungTableExercise;
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error dropping possible old trigger: " << stmt;
// Create trigger
stmt = QLatin1String("CREATE OR REPLACE FUNCTION public.") + triggerNameExercise +
QLatin1String("() RETURNS TRIGGER AS $$ "
" BEGIN "
" PERFORM pg_notify('") + triggerNameExercise + QLatin1String("', CAST(NEW.id AS text));"
" RETURN NEW;"
"END;"
"$$ LANGUAGE plpgsql;");
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error while creating function in statement: " << stmt;
stmt = QLatin1String("CREATE TRIGGER ") + triggerNameExercise +
QLatin1String(" AFTER INSERT ON ") + tlnZuordnungTableExercise +
QLatin1String(" FOR EACH ROW EXECUTE PROCEDURE public.") + triggerNameExercise + QLatin1String("();");
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error while creating trigger in statement: " << stmt;
QSqlDriver *driver = m_mainHandler->db()->database().driver();
// create subscription
connect(driver, QOverload<const QString &, QSqlDriver::NotificationSource, const QVariant &>::of(&QSqlDriver::notification),
this, &DataManager::onNotification, Qt::UniqueConnection);
if (!driver->subscribeToNotification(triggerNameExercise))
WARNING_LOG(m_logger) << "Error subscribing to DB notification " << triggerNameExercise;
else
DEBUG_LOG(m_logger) << "Sucessfully subscribed to DB notification " << triggerNameExercise;
}
void DataManager::onInsertParticipantAssignment(int participantId, int exerciseId)
{
INFO_LOG(m_logger) << "Reading ParticipantAssignment from database!";
NSqlQuery q(m_mainHandler->db()->database());
QString stmt = QLatin1String("SELECT id, ausstg_nr, zuordnung, y_nr, info FROM ") + m_mainHandler->db()->schemaName() +
QLatin1Char('.') + tlnZuordnungTable(exerciseId) +
QLatin1String(" WHERE gesendet = false");
if (participantId > 0)
stmt += QLatin1String(" AND id = ") + QString::number(participantId);
stmt += QLatin1String(" ORDER BY zeit");
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error selecting data for participantId: " << participantId << ", exerciseId: " << exerciseId;
return;
}
// we may get back more than one row, esp. in case participantId == 0
while (q.next())
{
// use the 'correct' participant id in case participantId == 0 was given (happens during startup)
participantId = q.value(0).toInt() - (exerciseId * s_exerciseOffset);
DEBUG_LOG(m_logger) << "participantId: " << participantId;
Registration reg;
reg.setPlayerId(participantId);
reg.setAssignment(IWMS2ECC::int2PlayerAssignState(q.value(2).toInt()));
if (!q.value(1).isNull())
reg.setEquipNo(q.value(1).toInt());
if (!q.value(3).isNull())
reg.setAlias(q.value(3).toString());
if (!q.value(4).isNull())
reg.setInfo(q.value(4).toString());
reg.setExerciseId(exerciseId);
reg.setOk(true);
Messages::ParticipantRegisterInExconRequest *msg = new Messages::ParticipantRegisterInExconRequest(reg.toJson(),
QLatin1String("blub"));
INFO_LOG(m_logger) << "registration: " << msg->toJsonString();
m_webSocketServer->sendToAllClients(msg);
}
}
void DataManager::onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload)
{
Q_UNUSED(source);
// search coresponding signal
INFO_LOG(m_logger) << "IN onNotification name: " << name;
const QString triggerNameEx0 = triggerName(0);
if (name.contains(triggerNameEx0))
{
QStringRef name_(&name);
name_ = name_.mid(triggerNameEx0.size() + 1);
int exerciseId = name_.toInt();
int participantId = payload.toInt();
INFO_LOG(m_logger) << "exerciseId: " << exerciseId << ", participantId: " << participantId;
onInsertParticipantAssignment(participantId, exerciseId);
}
}
void DataManager::reconnectSignals()
{
NSqlQuery q(m_mainHandler->db()->database());
QVector<Database::Exercise> vecExercises;
m_mainHandler->db()->select(vecExercises);
for (const auto &exercise : qAsConst(vecExercises))
{
createTriggerForExercise(q, exercise.id);
const QString tn = triggerName(exercise.id);
QTimer::singleShot(5000, this, [this, tn]() { onNotification(tn, QSqlDriver::UnknownSource, QLatin1String("0")); });
}
}
void DataManager::registerReply(int exerciseId, qint32 participantId, int returnValue)
{
DEBUG_LOG(m_logger) << "registerReply for exercise id = " << exerciseId << ", participant id = " << participantId << ", return value = " << returnValue;
NSqlQuery q(m_mainHandler->db()->database());
qint32 newParticipantId = participantId + (exerciseId * s_exerciseOffset);
QString stmt = QLatin1String("UPDATE ") + m_mainHandler->db()->schemaName() +
QLatin1Char('.') + tlnZuordnungTable(exerciseId) +
QLatin1String(" SET gesendet = true, error_code = ") + QString::number(returnValue) +
QLatin1String(" WHERE id = ") + QString::number(newParticipantId) +
QLatin1String(" AND gesendet = false");
DEBUG_LOG(m_logger) << "registerReply: stmt = " << stmt;
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Cannot update reply entry for participant: " << participantId
<< " in exercise: " << exerciseId << ", returnValue: " << returnValue;
}
else
{
INFO_LOG(m_logger) << "Update successful for participant " << participantId << " in exercise: " << exerciseId;
}
if (returnValue != 0)
{
QString stmt1 = QLatin1String("UPDATE ") + m_mainHandler->db()->schemaName() +
QLatin1String(".") + tlnTable(exerciseId) +
QLatin1String(" SET ausstg_nr = -1, zeit = ") + QString::number(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch()) +
QLatin1String(" WHERE id = ") + QString::number(newParticipantId);
DEBUG_LOG(m_logger) << "registerReply: stmt1 = " << stmt1;
if (!q.exec(stmt1))
{
ERROR_LOG(m_logger) << "Cannot update austtg_nr for participant: " << participantId
<< " in exercise: " << exerciseId << ", returnValue: " << returnValue;
}
else
{
INFO_LOG(m_logger) << "Update ausstg_nr successful for participant " << participantId << " in exercise: " << exerciseId;
}
}
}
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* The DataManager class is a convenience class for managing structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include "WsBaseDataInterface/Messages/reply_message.h"
#include "logger.h"
#include "ws_base_data_interface_handler.h"
#include <WarehouseGuez/whg_db.h>
#include <WMS_Interface/wms_ecc_exercises.h>
#include <WMS_Interface/wms_ecc_playerInfo.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <QObject>
#include <QSqlDriver>
class NSqlQuery;
namespace WarehouseGuez
{
class MainHandler;
class GroupHandler;
namespace WsBaseDataInterface
{
/**
* @brief The DataManager class is a convenience class for managing structure data
*/
class DataManager final : public QObject
{
Q_OBJECT
public:
/**
* @brief DataManager Constructor
* @param mainHandler Source for needed handlers
*/
explicit DataManager(MainHandler *mainHandler);
/**
* @brief init Initialises a DataManager object
* @return True on sucess, otherwise false
*/
bool init();
void setWebSocketServer( LtsWebSocket::LtsWebSocketServer *webSocketServer) { m_webSocketServer = webSocketServer;}
void reconnectSignals();
LtsWebSocket::ReplyMessage *equipmentTypes(const QString &uuid) const;
void newExerciseInformationsFromEXCON(const EXCON::Exercises &ex);
void participantsFromEXCON(const PlayerInfo &pl);
bool participantFromEXCON(const IWMS2ECC::PlayerInfo &pl, int modType);
bool exerciseChanged(const IWMS2ECC::Exercise &data, DataModification modType);
void registerReply(int exerciseId, qint32 participantId, int returnValue);
private:
static bool findExercise(int exerciseId, const QVector<Database::Exercise> &dataVec);
void createTableTlnOut(int exerciseId);
void createTableTlnZuordnung(int exerciseId);
void createTriggerForExercise(NSqlQuery &q, int exerciseId);
private slots:
void onInsertParticipantAssignment(int participantId, int exerciseId);
void onNotification(const QString &name, QSqlDriver::NotificationSource source, const QVariant &payload);
private: // attributes
MainHandler *m_mainHandler;
LtsWebSocket::LtsWebSocketServer *m_webSocketServer;
LOGGER_T m_logger;
}; // class DataManager
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez
@@ -0,0 +1,98 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "ws_base_data_interface_handler.h"
#include "main_handler.h"
#include "data_manager.h"
#include "WsBaseDataInterface/Messages/client_message.h"
#include <nglsettings/nglsettings_connections.h>
namespace WarehouseGuez
{
namespace WsBaseDataInterface
{
WsBaseDataInterfaceHandler::WsBaseDataInterfaceHandler(MainHandler *mainHandler, QObject *parent)
: QObject(parent)
, m_webSocketServer(nullptr)
, m_messageFactory(nullptr)
, m_dataManager(nullptr)
, m_mainHandler(mainHandler)
, m_logger(getLogger("WsBaseDataInterfaceHandler"))
{
}
WsBaseDataInterfaceHandler::~WsBaseDataInterfaceHandler()
{
cleanup();
}
bool WsBaseDataInterfaceHandler::init()
{
cleanup();
DEBUG_LOG(m_logger) << "... ok";
DEBUG_LOG(m_logger) << "Initialising DataManager";
m_dataManager = new DataManager(m_mainHandler);
if (!m_dataManager->init())
{
ERROR_LOG(m_logger) << "Failed to initialise DataManager";
return false;
}
DEBUG_LOG(m_logger) << "... ok";
m_messageFactory = new Messages::ClientMessageFactory(m_dataManager);
m_webSocketServer = new LtsWebSocket::LtsWebSocketServer(m_messageFactory, this);
DEBUG_LOG(m_logger) << "Initialising LtsWebSocketServer";
int port = NGL::Connections::port(NGL::Connections::eConnection::ZSLV);
ERROR_LOG(m_logger) << "Port: " << port;
if (!m_webSocketServer->init(QStringLiteral("localhost"), port))
{
ERROR_LOG(m_logger) << "Failed to initialise LtsWebSocketServer";
return false;
}
DEBUG_LOG(m_logger) << "... ok";
DEBUG_LOG(m_logger) << "Connection signals and slots";
m_dataManager->setWebSocketServer(m_webSocketServer);
return true;
}
void WsBaseDataInterfaceHandler::cleanup()
{
delete m_webSocketServer;
delete m_messageFactory;
delete m_dataManager;
}
void WsBaseDataInterfaceHandler::requestExercisesFromExcon()
{
m_webSocketServer->sendToAllClients(new Messages::ExerciseRequest);
}
void WsBaseDataInterfaceHandler::requestParticipantsFromExcon(int exerciseId)
{
m_webSocketServer->sendToAllClients(new Messages::ParticipantsRequest(exerciseId));
}
void WsBaseDataInterfaceHandler::sendEquipmentTypes()
{
m_webSocketServer->sendToAllClients(m_dataManager->equipmentTypes(QStringLiteral("spontaneous")));
}
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez
@@ -0,0 +1,75 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QtCore/QObject>
#include <LtsWebSocket/lts_web_socket_server.h>
#include "logger.h"
namespace WarehouseGuez
{
class MainHandler;
namespace WsBaseDataInterface
{
namespace Messages
{
class ClientMessageFactory;
}
class DataManager;
/**
* @brief The WsBaseDataInterfaceHandler class is the main class for the WsBaseDataInterface
*/
class WsBaseDataInterfaceHandler final : public QObject
{
public:
/**
* @brief WsBaseDataInterfaceHandler Constructor
* @param mainHandler MainHandler to get the HandlerStructureElement from
*/
explicit WsBaseDataInterfaceHandler(MainHandler *mainHandler, QObject *parent = nullptr);
/**
* @brief ~WsBaseDataInterfaceHandler Destructor
*/
~WsBaseDataInterfaceHandler();
/**
* @brief init Initialises all needed classes for the WsBaseDataInterface
* @return True on success, otherwise false
*/
bool init();
/**
* @brief webSocketPort returns the listen port
* @return listen port
*/
unsigned short webSocketPort() { return m_webSocketServer->webSocketPort(); }
void requestExercisesFromExcon();
void requestParticipantsFromExcon(int exerciseId);
void sendEquipmentTypes();
private:
void cleanup();
private:
LtsWebSocket::LtsWebSocketServer *m_webSocketServer;
Messages::ClientMessageFactory *m_messageFactory;
DataManager *m_dataManager;
MainHandler *m_mainHandler;
LOGGER_T m_logger;
}; // class WsBaseDataInterfaceHandle
} // namespace WsBaseDataInterface
} // namespace WarehouseGuez