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

+186
View File
@@ -0,0 +1,186 @@
#
# $Id$
# $HeadURL$
# $Author$
#
INCLUDE(MacroRTIDDSGen)
INCLUDE(MacroCodeGenIdl)
SET(xml_FILE exm_service.xml)
#
# --- Library svcExmCommon --------------------------------------------------------------------------------
#
SET(SRCS_common
exm_export.h
logger.cpp
logger.h
)
# Generate exm_enums.idl / exm.idl
CODEGEN_IDL_SVC(SRCS_common ${xml_FILE})
# Generate exm_db_impl.cpp/.h, exm_db.h
CODEGEN_DATABASE(SRCS_common ${xml_FILE})
# Generate *.cpp/*.hpp from idl
RTIDDSGEN(SRCS_common exm_enums.idl)
RTIDDSGEN(SRCS_common exm.idl)
# Generate exm_dt[s,c]_types.h, exm_ft[s,c]_types.h
CODEGEN_TOPIC_TYPEDEFS(SRCS_common ${xml_FILE})
# generate exm_[d|f]tc.h
CODEGEN_CLIENT_TOPICS_IFC(SRCS_common ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS HLA svcExmCommon SHARED ${SRCS_common})
NGL_TARGET_INCLUDE_DIRECTORIES(svcExmCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcExmCommon
PRIVATE
RTI::nddscpp2
PUBLIC
Qt5::Core
ddscore
NGLLogger
ngldb
LtsUtilsCore
)
NGL_INSTALL_TARGET(svcExmCommon)
#
# --- Library svcExmClient --------------------------------------------------------------------------------
#
SET(SRCS_client
exm_export.h
)
# generate exm_ftc_impl.[cpp|h]
CODEGEN_CLIENT_TOPICS_IMPL(SRCS_client ${xml_FILE})
NGL_ADD_LIBRARY(AAR SV WS HLA svcExmClient SHARED ${SRCS_client})
NGL_TARGET_INCLUDE_DIRECTORIES(svcExmClient
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcExmClient
PRIVATE
RTI::nddscpp2
ddsconnector
PUBLIC
svcExmCommon
ddsftc
)
NGL_INSTALL_TARGET(svcExmClient)
#
# --- Library svcExmServer --------------------------------------------------------------------------------
#
SET(SRCS_server
exm_export.h
convert_db_to_dds.h
)
# generate exm_[d|f]ts.h, exm_[d|f]ts_impl.[cpp|h]
CODEGEN_SERVER_TOPICS(SRCS_server ${xml_FILE})
NGL_ADD_LIBRARY(WS AAR SV svcExmServer SHARED ${SRCS_server})
NGL_TARGET_INCLUDE_DIRECTORIES(svcExmServer
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(svcExmServer
PRIVATE
RTI::nddscpp2
ddsconnector
ddsfts
LtsUtilsCore
PUBLIC
svcExmCommon
ngldb
)
NGL_INSTALL_TARGET(svcExmServer)
#
# --- Topic Type Plugin --------------------------------------------------------------
#
CREATE_TOPIC_TYPE_PLUGIN(${xml_FILE})
#
# --- Executable svcExerciseManagement (sEXM) --------------------------------------------------------------
#
SET(SRCS_exe
dds_handler.cpp
dds_handler.h
exercise_state_base.cpp
exercise_state_base.h
exercise_state_machine.cpp
exercise_state_machine.h
exercise_state_manager.cpp
exercise_state_manager.h
handler_exercise.cpp
handler_exercise.h
handler_exercise_phase.cpp
handler_exercise_phase.h
# handler_service_tables_create.cpp
# handler_service_tables_create.h
main.cpp
main_handler.cpp
main_handler.h
object_exercise.cpp
object_exercise.h
object_exercise_phase.cpp
object_exercise_phase.h
settings.cpp
settings.h
WebSocketInterface/data_manager.cpp
WebSocketInterface/data_manager.h
WebSocketInterface/json_keys.h
WebSocketInterface/websocket_interface_handler.cpp
WebSocketInterface/websocket_interface_handler.h
WebSocketInterface/Messages/client_message.cpp
WebSocketInterface/Messages/client_message.h
WebSocketInterface/Messages/reply_message.cpp
WebSocketInterface/Messages/reply_message.h
log4cxx_exm.props
${xml_FILE}
)
# Generate exm.sql, exs.xml
CODEGEN_SQL(SRCS_exe ${CMAKE_CURRENT_SOURCE_DIR}/exm_service.xml)
NGL_ADD_SVC_ICON(SRCS_exe)
NGL_ADD_EXECUTABLE(AAR SV svcExerciseManagement ${SRCS_exe})
NGL_TARGET_LINK_LIBRARIES(svcExerciseManagement
dbAdminCopyExercise
ddsconnector
LtsCommandLineParser
LtsWebSocketServer
nglcoreapplication
NGLLogger
nglsettings
ngltools
ProjectConfig
Qt5::Core
svcCoreClient
svcExmCommon
svcExmServer
svcScmClient
svcPpmClient
svcAvmClient
LtsUtilsDds
)
NGL_INSTALL_TARGET(svcExerciseManagement)
NGL_INSTALL(AAR SV FILES log4cxx_exm.props DESTINATION bin)
NGL_ADD_TARGET_LINK(svcExerciseManagement sEXM)
# Uncomment if necessary
ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(ModuleTests)
+62
View File
@@ -0,0 +1,62 @@
#
# $Id$
# $HeadURL$
# $Author$
#
IF(BUILD_TESTING AND TARGET svcExmClient)
SET(SRCS_TEST
exm_tests.cpp
exm_tests.h
)
# Testing DataTopic and FunctionTopic Functionality (DTC+FTC)
ADD_EXECUTABLE(exmTests ${SRCS_TEST})
TARGET_LINK_LIBRARIES(exmTests
Qt5::Core
Qt5::Test
RTI::nddscpp2
svcExmClient
svcAsmClient
LtsCommandLineParser
ddsconnector
nglcoreapplication
)
IF(NOT BUILD_RPM)
NGL_INSTALL(AAR SV TARGETS exmTests DESTINATION bin/test)
NGL_INSTALL(AAR SV FILES ../log4cxx_exm.props DESTINATION bin/test)
ENDIF()
SET(STMT_TEST
exm_statemachine_test.cpp
exm_statemachine_test.h
../dds_handler.cpp
../dds_handler.h
../exercise_state_base.cpp
../exercise_state_base.h
../exercise_state_manager.cpp
../exercise_state_manager.h
../exercise_state_machine.cpp
../exercise_state_machine.h
../object_exercise.cpp
../object_exercise.h
../settings.cpp
../settings.h
)
ADD_EXECUTABLE(exmStateMachineTest ${STMT_TEST})
TARGET_COMPILE_DEFINITIONS(exmStateMachineTest PUBLIC EXM_OBJECT_EXERCISE_NO_HANDLER)
TARGET_LINK_LIBRARIES(exmStateMachineTest
dbAdminCopyExercise
ddsconnector
nglcoreapplication
NGLLogger
ngltools
Qt5::Core
Qt5::Test
svcCoreClient
svcExmCommon
svcExmServer
svcScmClient
svcPpmClient
svcAvmClient
)
ENDIF()
@@ -0,0 +1,175 @@
/**
* Unittest for testing the statemachine for aes objects
* $Id$
* $HeadURL$
* $Author$
*/
#include "exm_statemachine_test.h"
// Generated
#include <ExerciseManagement/exm_enums.hpp>
#include <ExerciseManagement/exm_db.h>
#include <DBAdministration/exercise_table_generator.h>
#include <ExerciseManagement/exercise_state_machine.h>
#include <ExerciseManagement/dds_handler.h>
#include <ExerciseManagement/object_exercise.h>
#include <ExerciseManagement/settings.h>
#include <ExerciseManagement/logger.h>
#include <QCoreApplication>
#include <QDebug>
#include <QSignalSpy>
#include <QTest>
#include <nglapplication/nglcoreapplication.h>
#include <ngltools/ngltools_filesystem.h>
using namespace ExerciseManagement;
namespace QTest
{
template<>
char *toString(const ExerciseState &s) { std::ostringstream str; str << s; return strdup(str.str().c_str()); }
}
StateMachineTest::StateMachineTest()
: m_exmObject(nullptr)
{
}
StateMachineTest::~StateMachineTest()
{
delete m_exmObject;
}
void StateMachineTest::initTestCase()
{
NGL::INGLLogger &logger = getLogger("StateMachineTest");
m_dbHandler = std::make_shared<DbHandler>();
QVERIFY2(m_dbHandler.get(), "Reset of dbHandler failed");
bool bOk = m_dbHandler->init(0, Settings::settings());
// Initialize database
QVERIFY2(bOk, "Init dbHandler/DbInitialisation failed");
// initialize DDS
int domainId = 0;
m_ddsHandler = std::make_shared<DdsHandler>(domainId);
QVERIFY2(m_ddsHandler.get(), "Reset of ddsInterface failed");
QVERIFY2(m_ddsHandler->init(), "Ininitialisation of ddsInterface failed");
// Initialise ExerciseTableGenerator
const QString xmlPath = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("xml"));
m_exTblGen = std::make_shared<DBAdministration::ExerciseTableGenerator>(logger, xmlPath);
QVERIFY2(m_exTblGen, "new object creation of ExerciseTableGenerator failed");
QVERIFY2(m_exTblGen->init(m_dbHandler->db()->database(), m_dbHandler->db()->schemaName(), false), "Initialisation of ExerciseTableGenerator failed");
HandlerExercise *exHandler = nullptr;
m_exmObject = new ObjectExercise(m_ddsHandler, m_dbHandler->db(), m_exTblGen, exHandler);
QVERIFY2(m_exmObject, "Initialisation of exerciseObject failed");
#if 1
Database::ExmExercise data;
data.name = QLatin1String("Test Statemachine");
data.trainingAreaId = 6; // Bremen
data.beginTime = QDateTime::currentDateTimeUtc();
data.endTime = data.beginTime.addDays(2);
#else
QVector<Database::ExmExercise> dataVec;
// read all exercises from database
QVERIFY2(m_dbHandler->db()->select(dataVec), "Error retrieving exercises from db");
DatabaseInterface::ExmExercise data;
if (dataVec.isEmpty() )
{
data.name = QLatin1String("Test Statemachine");
data.beginTime = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch();
data.endTime = QDateTime::currentDateTimeUtc().toMSecsSinceEpoch() + 2000;
data.created = QDateTime::currentDateTimeUtc();
}
else
{
data = dataVec.first();
qDebug() << "First existing exerciseId: " << data.id << ", name = " << data.name;
}
#endif
Core::FunctionReplyObject ret = m_exmObject->createObject(data);
QVERIFY2(!ret.error(), "Creation of new exerciseObject failed");
QTest::qWait(100);
}
void StateMachineTest::staticTests()
{
// newly created, nothing set -> Status should be ExerciseState::Created
QCOMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Created);
}
void StateMachineTest::dynamicTests()
{
QSignalSpy spy(m_exmObject->getStateMachine(), &ExerciseStateMachine::exerciseStateChanged);
// Switch to STARTED
m_exmObject->switchToState(ExerciseState::Started, 0, 0);
QTRY_COMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Started);
QCOMPARE(spy.count(), 1); // Started
spy.clear();
// => PAUSED
m_exmObject->switchToState(ExerciseState::Paused, 99, 10);
QTRY_COMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Paused);
QCOMPARE(spy.count(), 1); // PAUSED
spy.clear();
// Try to (re)start
m_exmObject->switchToState(ExerciseState::Started, 99, 10);
QTRY_COMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Started);
QCOMPARE(spy.count(), 1); // Started (Resumed)
spy.clear();
// => Stopped
m_exmObject->switchToState(ExerciseState::Stopped, 99, 10);
QTRY_COMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Stopped);
QCOMPARE(spy.count(), 1); // Stopped
spy.clear();
// => Terminated
m_exmObject->switchToState(ExerciseState::Terminated, 99, 10);
QTRY_COMPARE(m_exmObject->exerciseState().underlying(), ExerciseState::Terminated);
QCOMPARE(spy.count(), 1); // Terminated
spy.clear();
}
void StateMachineTest::cleanupTestCase()
{
delete m_exmObject;
m_exmObject = nullptr;
}
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("EXM", 0);
STARTUP_LOG(app.baseLogger());
ExerciseManagement::StateMachineTest *test = new ExerciseManagement::StateMachineTest;
int retCode = QTest::qExec(test, 0, argv);
delete test;
SHUTDOWN_LOG(app.baseLogger());
return retCode;
}
@@ -0,0 +1,42 @@
/**
* Unittest for testing the statemachine for aes objects
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include <memory>
#include <ExerciseManagement/exm_db.h>
#include <ngldb/db_service_handler.h>
#include <DBAdministration/exercise_table_generator.h>
namespace ExerciseManagement
{
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class ObjectExercise;
class DdsHandler;
class StateMachineTest : public QObject
{
Q_OBJECT
public:
StateMachineTest();
~StateMachineTest();
private Q_SLOTS:
void initTestCase();
void staticTests();
void dynamicTests();
void cleanupTestCase();
private:
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<DBAdministration::ExerciseTableGenerator> m_exTblGen;
ObjectExercise *m_exmObject;
};
} // namespace ExerciseManagement
+550
View File
@@ -0,0 +1,550 @@
/**
* EXM Function Topic test executable
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hanna E. Rathje, SLSE, 2015-09-10
*/
#include "exm_tests.h"
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <QTimer>
#include <QTest>
#include <ExerciseManagement/exm_ftc.h>
#include <AssetManagement/asm_ftc.h>
/* ----------------------------------------------------------------------------------
* HOW TO RUN THE TESTS (example)
* ----------------------------------------------------------------------------------
* 0. Preconditions:
* Start with an empty database (BUILD)
* make db
* Start services: (SV)
* ./bin/svcAssetManagement -d 74 &
* ./bin/svcExerciseManagement -d 74 &
* Test steps: see exm_tests.csv
*/
//--------------------------------------------------------------------------
namespace ExerciseManagement
{
ExmTests::ExmTests(QObject *parent)
: QObject(parent)
, m_exerciseId(0)
, m_trainingAreaId(0)
, m_phaseId(0)
, m_testId(0)
, m_bTestsSuccessful(true)
, m_logger(getLogger("Test"))
{
qRegisterMetaType<Core::FunctionReply>("Core::FunctionReply");
}
//--------------------------------------------------------------------------
ExmTests::~ExmTests()
{}
//--------------------------------------------------------------------------
bool ExmTests::init(int domainId, int exerciseId, int testId, int trainingAreaId, int phaseId)
{
INFO_LOG(m_logger) << "init begin";
m_testId = testId;
m_exerciseId = exerciseId; // overwritten when new exercise created
m_trainingAreaId = trainingAreaId; //overwritten
m_phaseId = phaseId;
// initialize DDS, always on ex_0
// exm ist immer auf ex_0 --> -e wird für die zu modifizierende/loeschende exercise missbraucht
m_ddsConnector.reset(new DDSConnector::DdsConnector(domainId, IDL::PartitionType::Global));
if (!m_ddsConnector->init())
return false;
m_ddsConnector->initPublisherSubscriber();
DEBUG_LOG(m_logger) << "Create dtc EXM";
m_dtc = ExerciseManagement::Interface::Dtc::create(m_ddsConnector);
DEBUG_LOG(m_logger) << "Init dtc";
bool bRet = m_dtc->enableAll();
DEBUG_LOG(m_logger) << "init DTC EXM end - bRet = " << bRet;
connect(m_dtc.get(), &ExerciseManagement::Interface::Dtc::exerciseChanged,
this, &ExmTests::onExerciseChanged);
DEBUG_LOG(m_logger) << "create ftc EXM";
m_ftc = ExerciseManagement::Interface::Ftc::create(m_ddsConnector);
DEBUG_LOG(m_logger) << "init ftc EXM";
bRet &= m_ftc->enableAll();
DEBUG_LOG(m_logger) << "init FTC EXM end - bRet = " << bRet;
DEBUG_LOG(m_logger) << "create ftc ASM";
m_ftc_asm = AssetManagement::Interface::Ftc::create(m_ddsConnector);
DEBUG_LOG(m_logger) << "init ftc ASM";
bRet &= m_ftc_asm->enableAll();
DEBUG_LOG(m_logger) << "init end EXM - bRet = " << bRet;
DEBUG_LOG(m_logger) << "Domain Id = " << domainId;
DEBUG_LOG(m_logger) << "Exercise Id = " << exerciseId;
DEBUG_LOG(m_logger) << "Test Id = " << testId;
DEBUG_LOG(m_logger) << "Training Area Id = " << trainingAreaId;
DEBUG_LOG(m_logger) << "Phase Id = " << phaseId;
//TODO: add connects
return bRet;
}
//--------------------------------------------------------------------------
void ExmTests::startTest()
{
INFO_LOG(m_logger) << "startTest";
if (m_testId <= 1)
QTimer::singleShot(0, this, &ExmTests::tryCreate);
else if (m_testId == 2)
QTimer::singleShot(0, this, &ExmTests::tryModify);
else if (m_testId == 3)
QTimer::singleShot(0, this, &ExmTests::tryDelete);
else if (m_testId == 4)
QTimer::singleShot(0, this, &ExmTests::tryCreateTrainingArea);
else if (m_testId == 10)
QTimer::singleShot(0, this, &ExmTests::tryGetAllExercises);
else if (m_testId == 11)
QTimer::singleShot(0, this, &ExmTests::tryGetExercise);
else if (m_testId == 20)
QTimer::singleShot(0, this, &ExmTests::tryStartExercise);
else if (m_testId == 30)
QTimer::singleShot(0, this, &ExmTests::tryCreateExercisePhase);
else if (m_testId == 31)
QTimer::singleShot(0, this, &ExmTests::tryModifyExercisePhase);
else if (m_testId == 32)
QTimer::singleShot(0, this, &ExmTests::tryDeleteExercisePhase);
else if (m_testId == 99)
// do nothing, return true, need this in squish to alter sequence to 42
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
else
{
ERROR_LOG(m_logger) << "Unknown test id: " << m_testId;
failExit();
}
}
//--------------------------------------------------------------------------
void ExmTests::tryCreateTrainingArea()
{
INFO_LOG(m_logger) << "trying to send modifyRequest TrainingArea (create)";
AssetManagement::TrainingAreaModifyRequest request;
request.id(0);
request.name("TestTrainingAreaCreate");
request.bottomLeft(Core::GeoPosition2D(11.61/180*M_PI, 53.07/180*M_PI));
request.height(1000000);
request.width(1000000);
if (!m_ftc_asm->trainingAreaModifyRequest(request, this))
{
ERROR_LOG(m_logger) << "Error requesting create data";
failExit();
}
INFO_LOG(m_logger) << "Training Area Id = " << m_trainingAreaId;
QTest::qWait(1000);
}
//--------------------------------------------------------------------------
void ExmTests::tryCreateExercisePhase()
{
INFO_LOG(m_logger) << "trying to send createrequest Exercise Phase";
if(!m_ftc->exercisePhaseCreateRequest(ExercisePhaseCreateRequest(std::string("Test Phase"), std::string("Test Phase description"), 10, 20, m_exerciseId), this))
{
ERROR_LOG(m_logger) << "Error requesting create Exercise Phase";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------
void ExmTests::tryCreate()
{
// TODO: add symbolFillType (PDM)
int symbolFillType = 1;
int radioGroupId = 1;
INFO_LOG(m_logger) << "Training Area Id = " << m_trainingAreaId;
INFO_LOG(m_logger) << "trying to send createRequest Exercise";
ExerciseCreateRequest req;
req.name(std::string("TestExerciseCreate"));
req.beginTime(10);
req.endTime(20);
req.trainingAreaId(m_trainingAreaId);
req.radioGroupId(radioGroupId);
req.symbolFillTypeId(symbolFillType);
req.forces(std::string("Forces"));
req.description(std::string("Description"));
if (!m_ftc->exerciseCreateRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting create data";
failExit();
}
QTest::qWait(10000);
}
//--------------------------------------------------------------------------
void ExmTests::tryModify()
{
INFO_LOG(m_logger) << "trying to send modify request for exercise Id = " << m_exerciseId;
ExerciseModifyRequest req;
req.id(m_exerciseId);
req.name(std::string("ExerciseModifyTest"));
req.beginTime(1000);
req.endTime(2000);
req.trainingAreaId(m_trainingAreaId);
req.radioGroupId(1);
req.symbolFillTypeId(1);
req.forces(std::string("ForcesUpdated"));
req.description(std::string("DescriptionUpdated"));
req.activePhaseId(m_phaseId);
req.newState(ExerciseState::Created);
if (!m_ftc->exerciseModifyRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------
void ExmTests::tryModifyExercisePhase()
{
INFO_LOG(m_logger) << "trying to send modify exercise phase request for exercise phase Id = " << m_phaseId;
if (!m_ftc->exercisePhaseModifyRequest(ExercisePhaseModifyRequest(std::string("Modified Exercise Phase"), std::string("Exercise Phase has been modified"), 20, 30, m_exerciseId, m_phaseId), this))
{
ERROR_LOG(m_logger) << "Error requesting modify exercise phase";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------
void ExmTests::tryDelete()
{
INFO_LOG(m_logger) << "trying to send delete request for exercise Id = " << m_exerciseId;
if (!m_ftc->exerciseRemoveRequest(ExerciseRemoveRequest(m_exerciseId, false), this))
{
ERROR_LOG(m_logger) << "Error requesting delete data";
failExit();
}
QTest::qWait(5000);
}
//--------------------------------------------------------------------------
void ExmTests::tryDeleteExercisePhase()
{
if (m_phaseId == 0)
{
ERROR_LOG(m_logger) << "No phase available for deleting";
failExit();
}
INFO_LOG(m_logger) << "trying to send delete request for exercise phase Id = " << m_phaseId;
m_ftc->enableExercisePhaseRemove();
if (!m_ftc->exercisePhaseRemoveRequest(ExercisePhaseRemoveRequest(m_phaseId), this))
{
ERROR_LOG(m_logger) << "Error requesting delete exercise phase";
failExit();
}
QTest::qWait(1000);
}
//--------------------------------------------------------------------------
void ExmTests::tryStartExercise()
{
INFO_LOG(m_logger) << "trying to start exercise Id = " << m_exerciseId;
m_dtc->waitForHistoricalDataExercise();
ExerciseManagement::Exercise ex = m_dtc->exercise(m_exerciseId);
DEBUG_LOG(m_logger) << "Training area id = " << ex.trainingAreaId();
ExerciseModifyRequest req;
req.id(m_exerciseId);
req.name(std::string("ExerciseStarted"));
req.beginTime(ex.beginTime());
req.endTime(ex.endTime());
req.trainingAreaId(ex.trainingAreaId());
req.radioGroupId(ex.radioGroupId());
req.symbolFillTypeId(ex.symbolFillTypeId());
req.activePhaseId(ex.activePhaseId());
req.forces(ex.forces());
req.description(ex.description());
req.newState(ExerciseState::Started);
if (!m_ftc->exerciseModifyRequest(req, this))
{
ERROR_LOG(m_logger) << "Error requesting modify data";
failExit();
}
// ExerciseChangeStateRequest req;
// req.id(m_exerciseId);
// req.newState(ExerciseState::Started);
// req.startTime(3000);
// if (!m_ftc->exerciseChangeStateRequest(req, this))
// {
// ERROR_LOG(m_logger) << "Error requesting change state";
// failExit();
// }
QTest::qWait(2000);
}
//-------------------------------------------------------------------------------
void ExmTests::tryGetAllExercises()
{
INFO_LOG(m_logger) << "trying to get all exercises";
m_dtc->waitForHistoricalDataExercise();
bool bAtLeastOneFound = false;
auto cb = [this, &bAtLeastOneFound](const ExerciseManagement::Exercise &ex)
{
INFO_LOG(m_logger) << "Exe(" << ex.id() << ") : " << ex.name();
bAtLeastOneFound = true;
};
m_dtc->exercises(cb);
if (!bAtLeastOneFound)
{
ERROR_LOG(m_logger) << "Error receiving data for exercises";
failExit();
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//-------------------------------------------------------------------------------
void ExmTests::tryGetExercise()
{
INFO_LOG(m_logger) << "trying get itemData for exercise " << m_exerciseId;
m_dtc->waitForHistoricalDataExercise();
ExerciseManagement::Exercise data = m_dtc->exercise(m_exerciseId);
if (data.id() <= 0)
{
ERROR_LOG(m_logger) << "Error receiving data for exercise " << m_exerciseId;
DEBUG_LOG(m_logger) << "data.id() = " << data.id();
failExit();
}
else
{
INFO_LOG(m_logger) << "## Exe(" << data.id() << ") : name = " << data.name()
<< ", begin = " << data.beginTime() << ", end = " << data.endTime()
<< ", state = " << data.state();
}
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//-------------------------------------------------------------------------------
void ExmTests::onExerciseChanged(const ExerciseManagement::Exercise &data,
DataModification modType)
{
const int id = data.id();
DEBUG_LOG(m_logger) << "onExerciseChanged for exercise " << id
<< " - modType: " << (int)modType;
if (m_exerciseId == id)
INFO_LOG(m_logger) << "##################### Data for id = " << id << " received";
}
//--------------------------------------------------------------------------
void ExmTests::onExerciseCreateReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExerciseCreateReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got createReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
m_exerciseId = replyObject.id();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------
void ExmTests::onExerciseModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExerciseModifyReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got modifyRequest with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
////--------------------------------------------------------------------------
//void ExmTests::onExerciseChangeStateReply(const Core::FunctionReplyObject &replyObject)
//{
// if (replyObject.error())
// {
// ERROR_LOG(m_logger) << "Got onExerciseChangeStateReply error: " << replyObject();
// failExit();
// return;
// }
// INFO_LOG(m_logger) << "Got changeStateRequest with id " << replyObject.id()
// << " and errorString " << replyObject.translated();
// QTimer::singleShot(0, qApp, &QCoreApplication::quit);
//}
//--------------------------------------------------------------------------
void ExmTests::onExerciseRemoveReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExerciseRemoveReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got removeReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------
void ExmTests::onExercisePhaseCreateReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExercisePhaseCreateReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got createExercisePhaseReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
m_phaseId = replyObject.id();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------
void ExmTests::onExercisePhaseModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExercisePhaseModifyReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got modifyExercisePhaseReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------
void ExmTests::onExercisePhaseRemoveReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onExercisePhaseRemoveReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got removeExercisePhaseReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
//--------------------------------------------------------------------------
void ExmTests::onTrainingAreaModifyReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
{
ERROR_LOG(m_logger) << "Got onTrainingAreaModifyReply error: " << replyObject();
failExit();
return;
}
INFO_LOG(m_logger) << "Got createReply with id " << replyObject.id()
<< " and errorString " << replyObject.translated();
m_trainingAreaId = replyObject.id();
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
// ------------------------------------------------------------------------------
void ExmTests::failExit()
{
ERROR_LOG(m_logger) << "FAIL EXIT !";
m_bTestsSuccessful = false;
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
}
} // end namespace
// ------------------------------------------------------------------
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser()
{
addOption( { QStringList() << QLatin1String("t") << QLatin1String("testId")
, QLatin1String("TestId.")
, QLatin1String("Default: 0 (all tests)")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("a") << QLatin1String("trainingAreaId")
, QLatin1String("TrainingAreaId.")
, QLatin1String("Default: 0 (Unknown)")
, QLatin1String("0")
} );
addOption( { QStringList() << QLatin1String("p") << QLatin1String("phaseId")
, QLatin1String("PhaseId.")
, QLatin1String("Default: 0 (Unknown)")
, QLatin1String("0")
} );
}
int testId() const { return isSet(QLatin1String("t")) ? value(QLatin1String("t")).toInt() : -1; }
int trainingAreaId() const { return isSet(QLatin1String("a")) ? value(QLatin1String("a")).toInt() : -1; }
int phaseId() const { return isSet(QLatin1String("p")) ? value(QLatin1String("p")).toInt() : -1; }
};
// ------------------------------------------------------------------
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
app.createLogger("EXM", 0 );
int iRet = -1;
CmdLineParser options;
if (!options.init())
return -1;
std::unique_ptr<ExerciseManagement::ExmTests> ExmTests(new ExerciseManagement::ExmTests);
if(!ExmTests->init(options.domainId(), options.exerciseId(), options.testId(), options.trainingAreaId(), options.phaseId()))
return iRet;
QTimer::singleShot(0, ExmTests.get(), &ExerciseManagement::ExmTests::startTest);
STARTUP_LOG(app.baseLogger());
fprintf(stdout, "%s\n", app.baseLogger().getLoggerName().data());
app.exec();
iRet = ExmTests->testsSuccessful() ? 0 : 1;
fprintf(stderr, "%d\n", iRet);
DEBUG_LOG(app.baseLogger()) << "** TEST result = " << iRet;
SHUTDOWN_LOG(app.baseLogger());
return iRet;
}
+12
View File
@@ -0,0 +1,12 @@
Parameters, Comment
-t 4,1. Create training area id 1
-t 1 -a 1,2. Create exercise with training area 1
-t 2 -e 1 -p 1,3. Modify Exercise id 1 with phase 1
-t 3 -e 1,4. Remove exercise id 1
-t 1 -1 1,5. Create new exercise
-t 10,6. Get all exercises
-t 11 -e 42,7. Get exercise 42
-t 20 -e 42,8. Start exercise 42
-t 30 -e 42,9. Create exercise phase 3 for ex 42
-t 31 -e 42 -p 3,10. Modify exercise phase 3
-t 32 -e 42 -p 3,11. Remove exercise phase 3
1 Parameters Comment
2 -t 4 1. Create training area id 1
3 -t 1 -a 1 2. Create exercise with training area 1
4 -t 2 -e 1 -p 1 3. Modify Exercise id 1 with phase 1
5 -t 3 -e 1 4. Remove exercise id 1
6 -t 1 -1 1 5. Create new exercise
7 -t 10 6. Get all exercises
8 -t 11 -e 42 7. Get exercise 42
9 -t 20 -e 42 8. Start exercise 42
10 -t 30 -e 42 9. Create exercise phase 3 for ex 42
11 -t 31 -e 42 -p 3 10. Modify exercise phase 3
12 -t 32 -e 42 -p 3 11. Remove exercise phase 3
+93
View File
@@ -0,0 +1,93 @@
/**
* @RDE_HEADER@
*
* EXM Function Topic test executable
*
* ModuleTests for create/modify/remove exercise
* ---------------------------------------------------
* Client FTC is implemented
*
* => (!) Preconditon:
* - Service sExeMgmt running
*
* -------------------------------------------------
*
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hanna E. Rathje, SLSE, 2015-09-10
*/
#pragma once
#include <ExerciseManagement/exm_ftc.h>
#include <ExerciseManagement/exm_ftc_types.h>
#include <AssetManagement/asm_ftc.h>
#include <AssetManagement/asm_ftc_types.h>
#include <ExerciseManagement/exm_dtc.h>
#include <ExerciseManagement/exm_dtc_types.h>
#include <ExerciseManagement/logger.h>
#include <DDS/DDSConnector/dds_connector.h>
//--------------------------------------------------------------------------------------------
namespace ExerciseManagement
{
class ExmTests : public QObject
, protected ExerciseManagement::Interface::Ftc::ExerciseCreateReplyHandler
, protected ExerciseManagement::Interface::Ftc::ExerciseModifyReplyHandler
// , protected ExerciseManagement::Interface::Ftc::ExerciseChangeStateReplyHandler
, protected ExerciseManagement::Interface::Ftc::ExerciseRemoveReplyHandler
, protected ExerciseManagement::Interface::Ftc::ExercisePhaseCreateReplyHandler
, protected ExerciseManagement::Interface::Ftc::ExercisePhaseModifyReplyHandler
, protected ExerciseManagement::Interface::Ftc::ExercisePhaseRemoveReplyHandler
, protected AssetManagement::Interface::Ftc::TrainingAreaModifyReplyHandler
{
Q_OBJECT
public:
explicit ExmTests(QObject *parent = nullptr);
~ExmTests();
bool init(int domainId, int exerciseId, int testId, int trainingAreaId, int phaseId);
bool testsSuccessful() const { return m_bTestsSuccessful; }
public Q_SLOTS:
void startTest();
protected Q_SLOTS:
void tryCreate();
void tryCreateTrainingArea();
void tryCreateExercisePhase();
void tryModify();
void tryModifyExercisePhase();
void tryDelete();
void tryDeleteExercisePhase();
void tryStartExercise();
void tryGetAllExercises();
void tryGetExercise();
void onExerciseChanged(const ExerciseManagement::Exercise &data, DataModification modType);
protected:
void onExerciseCreateReply(const Core::FunctionReplyObject &replyObject) override final;
void onExerciseModifyReply(const Core::FunctionReplyObject &replyObject) override final;
// void onExerciseChangeStateReply(const Core::FunctionReplyObject &replyObject) override final;
void onExerciseRemoveReply(const Core::FunctionReplyObject &replyObject) override final;
void onExercisePhaseCreateReply(const Core::FunctionReplyObject &replyObject) override final;
void onExercisePhaseModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void onExercisePhaseRemoveReply(const Core::FunctionReplyObject &replyObject) override final;
void onTrainingAreaModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void failExit();
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<ExerciseManagement::Interface::Ftc> m_ftc;
std::shared_ptr<AssetManagement::Interface::Ftc> m_ftc_asm;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_dtc;
qint32 m_exerciseId;
qint32 m_trainingAreaId;
qint32 m_phaseId;
int m_testId;
bool m_bTestsSuccessful;
LOGGER_T m_logger;
};
} // namespace ExerciseManagement
+40
View File
@@ -0,0 +1,40 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(SRCS_DT_TEST
data_topic_tests_main.cpp
data_topic_tests.cpp
)
SET(HDRS_DT_TEST
data_topic_tests_main.h
data_topic_tests.h
)
IF(BUILD_TESTING AND TARGET svcExmCommon)
# Testing DataTopic Functionality (DTS/DTC)
ADD_EXECUTABLE(exmDtUnitTest ${SRCS_DT_TEST} ${HDRS_DT_TEST})
TARGET_LINK_LIBRARIES(exmDtUnitTest
nglcoreapplication
NGLLogger
RTI::nddscpp2
svcExmCommon
ddscore
ddsgen
ddsdts
ddsdtc
ddsQoS
ProjectConfig
Qt5::Core
Qt5::Test
)
ADD_TEST(NAME exmDtUnitTest COMMAND $<TARGET_FILE:exmDtUnitTest>)
IF(NOT BUILD_RPM)
NGL_INSTALL(AAR SV TARGETS exmDtUnitTest DESTINATION bin/test)
NGL_INSTALL(AAR SV FILES log4cxx_exm_unit_tests.props DESTINATION bin/test)
ENDIF()
ENDIF()
@@ -0,0 +1,159 @@
/**
* @RDE_HEADER@
* Unit test for ExerciseManagement (EXM)
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-20
*/
#include "data_topic_tests.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <DDS/QosProfile/DDS_DefaultQos.h>
#include <Common/ProjectConfig/project_defines.h>
#include <DDS/DTS/generic_dts_impl.h>
#include <DDS/DTC/generic_dtc_impl.h>
#include <QtTest/QTest>
#include <QVariant>
#include <QThread>
DataTopicTests::DataTopicTests()
: m_domainParticipantPub(nullptr)
, m_domainParticipantSub(nullptr)
, m_domainId(0)
, m_ddsPublisher(nullptr)
, m_dtsExercise(nullptr)
, m_ddsSubscriber(nullptr)
, m_subscriberListener(nullptr)
, m_subscriberListenerBinder(nullptr)
, m_dtcExercise(nullptr)
, m_mainThreadId(QThread::currentThreadId())
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.DataTopicTests"))
{
if (!DDS::QosProfile::setDefault())
ERROR_LOG(m_logger) << "Error setting default QoS from xml-file ";
DEBUG_LOG(m_logger) << "Main thread ID is " << m_mainThreadId;
}
DataTopicTests::~DataTopicTests()
{
// Publisher
delete m_dtsExercise;
// Subscriber
delete m_dtcExercise;
m_subscriberListener->prepareShutdown();
m_subscriberListenerBinder = nullptr;
m_ddsSubscriber = nullptr;
m_ddsPublisher = nullptr;
m_domainParticipantSub = nullptr;
m_domainParticipantPub = nullptr;
}
/**
* Publisher
**/
bool DataTopicTests::initPublisher()
{
m_domainParticipantPub =
dds::domain::DomainParticipant(m_domainId,
DDS::QosProfile::domainParticipantShm());
dds::pub::qos::PublisherQos publisherQos = m_domainParticipantPub.default_publisher_qos();
// ExerciseData will be published to Partition 0
// use only selected partition
const std::string partition = IDL::exercisePartition(0);
dds::core::policy::Partition partitionQos;
partitionQos.name( { partition } );
publisherQos << partitionQos;
DEBUG_LOG(m_logger) << "Publish on partition " << partition;
m_ddsPublisher = dds::pub::Publisher(m_domainParticipantPub, publisherQos);
// Create DTS Exercise
m_dtsExercise = new DTS::ExerciseImpl(m_domainParticipantPub, m_ddsPublisher);
return true;
}
bool DataTopicTests::write(const ExerciseManagement::Exercise &data)
{
INFO_LOG(m_logger) << "Publish topic Exercise ";
DEBUG_LOG(m_logger) << "ExeId: " << data.id() << ", state: " << data.state()
<< ", partitionName: " << data.partitionName();
return m_dtsExercise->write(data);
}
/**
* Subscriber
**/
bool DataTopicTests::initSubscriber()
{
DEBUG_LOG(m_logger) << "Initialisation DDS (Subscriber/Reader)";
m_domainParticipantSub =
dds::domain::DomainParticipant(m_domainId,
DDS::QosProfile::domainParticipantShm());
dds::sub::qos::SubscriberQos subscriberQos = m_domainParticipantSub.default_subscriber_qos();
// Initial Partition for listening on topic Exercise => partition 0
// + Listening for Topics in partition 1
dds::core::StringSeq partitions;
for(int partitionId : { 0, 1 } )
{
QString partition= QString::fromStdString(IDL::exercisePartition(partitionId));
partitions.push_back(partition.toStdString());
}
dds::core::policy::Partition partitionQos;
partitionQos.name( partitions );
subscriberQos << partitionQos;
DEBUG_LOG(m_logger) << "Initialise DTC::SubscriberListener";
m_subscriberListener = new DDSGeneric::SubscriberListener();
m_ddsSubscriber = dds::sub::Subscriber(m_domainParticipantSub,
subscriberQos);
m_subscriberListenerBinder = rti::core::bind_and_manage_listener(m_ddsSubscriber,
m_subscriberListener,
DDSGeneric::SubscriberListener::mask());
// Initialise DTC for Topic Exercise
m_dtcExercise = new DTC::ExerciseImpl(m_domainParticipantSub, m_ddsSubscriber, m_subscriberListener);
m_dtcExercise->setSubscriberCallback([this](const ExerciseManagement::Exercise &data, DataModification modType) { processData(data, modType); } );
return true;
}
void DataTopicTests::processData(const ExerciseManagement::Exercise &instance, DataModification modType)
{
Q_UNUSED(modType);
INFO_LOG(m_logger) << "process Lts_Exercise";
DEBUG_LOG(m_logger) << "exerciseId: " << instance.id();
DEBUG_LOG(m_logger) << "name: " << instance.name();
DEBUG_LOG(m_logger) << "state: " << instance.state();
m_receivedExerciseData = instance;
QCOMPARE(m_mainThreadId, QThread::currentThreadId());
emit exerciseDataArrived();
}
@@ -0,0 +1,75 @@
/**
* @RDE_HEADER@
* Unit test for ExerciseManagement (EXM)
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-20
*/
#pragma once
#include <QObject>
#include <DDS/Generic/subscriber_listener.h>
// For Topic Exercise
#include <ExerciseManagement/exm_dtc_types.h>
#include <ExerciseManagement/exm_dts_types.h>
#include <LtsUtilsCore/data_modification_enum.h>
#include <ExerciseManagement/exm.hpp>
#include <rti/core/ListenerBinder.hpp>
namespace NGL {
class INGLLogger;
}
class DataTopicTests : public QObject
{
Q_OBJECT
public:
DataTopicTests();
virtual ~DataTopicTests();
// Initialisation
bool initPublisher();
bool initSubscriber();
// Publisher
bool write(const ExerciseManagement::Exercise &data);
// Subscriber
ExerciseManagement::Exercise getReceivedExerciseData() const { return m_receivedExerciseData; }
protected:
// Subscriber Methods
void processData(const ExerciseManagement::Exercise& instance, DataModification modType);
Q_SIGNALS:
void exerciseDataArrived();
private:
dds::domain::DomainParticipant m_domainParticipantPub;
dds::domain::DomainParticipant m_domainParticipantSub;
int m_domainId;
// Publisher
dds::pub::Publisher m_ddsPublisher;
DTS::Exercise *m_dtsExercise;
// Subscriber
dds::sub::Subscriber m_ddsSubscriber;
DDSGeneric::SubscriberListener *m_subscriberListener;
rti::core::ListenerBinder< dds::sub::Subscriber, dds::sub::SubscriberListener> m_subscriberListenerBinder;
DTC::Exercise *m_dtcExercise;
Qt::HANDLE m_mainThreadId;
// For returning TestData
ExerciseManagement::Exercise m_receivedExerciseData;
NGL::INGLLogger& m_logger;
};
@@ -0,0 +1,89 @@
/**
* @RDE_HEADER@
* UnitTests for ExerciseManagement (EXM)
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-20
*/
#include "data_topic_tests_main.h"
#include "data_topic_tests.h"
#include <QtTest/QTest>
#include <Common/ProjectConfig/project_defines.h>
#include <nglapplication/nglcoreapplication.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
//-------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
NGLCoreApplication app(argc, argv);
app.createLogger("UnitTests", "log4cxx_exm_unit_tests.props");
DataTopicTestsMain *tc = new DataTopicTestsMain;
int retCode = QTest::qExec(tc, argc, argv);
delete tc;
return retCode;
}
DataTopicTestsMain::DataTopicTestsMain()
: m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.DataTopicTestsMain"))
{
}
//-------------------------------------------------------------------------------------------------------
void DataTopicTestsMain::initTestCase()
{
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: initTestCase";
m_dtTest = new DataTopicTests();
QVERIFY2(m_dtTest, "Initialisation of DataTopicTest failed");
QVERIFY2(m_dtTest->initPublisher(), "Initialisation Publisher failed");
QVERIFY2(m_dtTest->initSubscriber(), "Initialisation Subscriber failed");
m_exerciseDataArrivedSpy = new QSignalSpy(m_dtTest, &DataTopicTests::exerciseDataArrived);
}
//-------------------------------------------------------------------------------------------------------
void DataTopicTestsMain::publishTestCase()
{
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: publish data";
// Topic Exercise
m_sentExerciseData.id(222);
m_sentExerciseData.state(ExerciseManagement::ExerciseState::Started);
m_sentExerciseData.name("UnitTest Exe 222");
m_sentExerciseData.isUsed(true);
m_sentExerciseData.partitionName(IDL::exercisePartition(222));
QVERIFY2(m_dtTest->write(m_sentExerciseData), "Publishing Topic Exercise failed");
}
void DataTopicTestsMain::subscribeTestCase()
{
DEBUG_LOG(m_logger) << "IN subscribeTestCase";
QTRY_COMPARE(m_exerciseDataArrivedSpy->count(), 1);
ExerciseManagement::Exercise recvExercise = m_dtTest->getReceivedExerciseData();
QCOMPARE(recvExercise.id(), m_sentExerciseData.id());
QCOMPARE(recvExercise.state(), m_sentExerciseData.state());
}
//-------------------------------------------------------------------------------------------------------
void DataTopicTestsMain::cleanupTestCase()
{
DEBUG_LOG(m_logger) <<"DataTopicTestsMain: cleanupTestCase";
delete m_dtTest; m_dtTest = nullptr;
delete m_exerciseDataArrivedSpy; m_exerciseDataArrivedSpy = nullptr;
}
@@ -0,0 +1,47 @@
/**
* @RDE_HEADER@
* UnitTests for ExerciseManagement (EXM)
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-20
*/
#pragma once
#include <QObject>
#include <QSignalSpy>
#include <ExerciseManagement/exm_dts_types.h>
namespace NGL
{
class INGLLogger;
}
class DataTopicTests;
class DataTopicTestsMain : public QObject
{
Q_OBJECT
public:
DataTopicTestsMain();
private Q_SLOTS:
void initTestCase();
void publishTestCase();
void subscribeTestCase();
void cleanupTestCase();
protected:
private:
ExerciseManagement::Exercise m_sentExerciseData;
QSignalSpy *m_exerciseDataArrivedSpy = nullptr;
DataTopicTests *m_dtTest = nullptr;
NGL::INGLLogger &m_logger;
};
@@ -0,0 +1,111 @@
/**
* @RDE_HEADER@
* DataTopic Unit test for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-07-24
*/
#include "Initialisation.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <DDS/QosProfile/DDS_DefaultQos.h>
#include <Common/ProjectConfig/project_defines.h>
Initialisation::Initialisation()
: m_domainParticipant(nullptr)
, m_domainParticipantSub(nullptr)
, m_domainId(0)
, m_partitionId(-1)
, m_ddsPublisher(nullptr)
, m_ddsSubscriber(nullptr)
, m_subListener(new DTC::SubscriberListener)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("UnitTests.Initialisation"))
{
DDS::DefaultQos defaultQos;
//if (!defaultQos.setDefaultQos(QStringList("/shd/CTC/SV/.default_dds_profile.xml")))
if (!defaultQos.setDefaultQos())
ERROR_LOG(m_logger) << "Error setting default QoS from xml-file ";
}
Initialisation::~Initialisation()
{
cleanupSubscriberListener();
m_ddsSubscriber = nullptr;
m_ddsPublisher = nullptr;
}
void Initialisation::cleanupSubscriberListener()
{
if (m_ddsSubscriber != nullptr && m_subListener != nullptr)
{
m_ddsSubscriber.listener(nullptr, dds::core::status::StatusMask::none());
delete m_subListener;
m_subListener = nullptr;
}
}
bool Initialisation::initDDS(int domainId, int partitionId, PubSub pubsub)
{
if (m_domainParticipant == nullptr)
m_domainParticipant = dds::domain::DomainParticipant(domainId);
// SpatialData will be published to Partition $partitionId
if (pubsub == USE_PUBLISHER || pubsub == USE_PUB_SUB)
{
dds::pub::qos::PublisherQos publisherQos = m_domainParticipant.default_publisher_qos();
DEBUG_LOG(m_logger) << "Publish on partition " << IDL::getPartitionString(partitionId);
// use only selected partition
dds::core::policy::Partition partitionQos;
partitionQos.name( { IDL::getPartitionString(partitionId) } );
publisherQos << partitionQos;
m_ddsPublisher = dds::pub::Publisher(m_domainParticipant,
publisherQos);
}
if (pubsub == USE_SUBSCRIBER || pubsub == USE_PUB_SUB)
{
dds::domain::DomainParticipant domainPart = m_domainParticipant;
if ( domainPart == nullptr || m_domainParticipantSub == nullptr)
{
m_domainParticipantSub = dds::domain::DomainParticipant(domainId);
domainPart = m_domainParticipantSub;
}
dds::sub::qos::SubscriberQos subscriberQos = domainPart.default_subscriber_qos();
DEBUG_LOG(m_logger) << "Subsribing on partition " << IDL::getPartitionString(partitionId);
// use only selected partition
dds::core::policy::Partition partitionQos;
partitionQos.name( { IDL::getPartitionString(partitionId) } );
subscriberQos << partitionQos;
dds::core::status::StatusMask mask(DDS_REQUESTED_DEADLINE_MISSED_STATUS |
DDS_DATA_AVAILABLE_STATUS |
DDS_DATA_ON_READERS_STATUS);
m_ddsSubscriber = dds::sub::Subscriber(domainPart,
subscriberQos,
m_subListener,
mask);
}
if (m_domainId == 0)
m_domainId = domainId;
if (m_partitionId < 0)
m_partitionId = partitionId;
return true;
}
+54
View File
@@ -0,0 +1,54 @@
/**
* @RDE_HEADER@
* Unit test for DataTopic Server & Client (DTS & DTC)
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-07-24
*/
#include <DDS/Generic/generic_base.h>
#include <DDS/DTC/dtc_subscriber_listener.h>
namespace NGL {
class INGLLogger;
}
class Initialisation
{
public:
Initialisation();
virtual ~Initialisation();
enum PubSub
{
USE_PUBLISHER = 1,
USE_SUBSCRIBER = 2,
USE_PUB_SUB = 4
};
// Initialisation
bool initDDS(int domainId, int partitionId, PubSub pubsub = USE_PUBLISHER);
void cleanupSubscriberListener();
dds::domain::DomainParticipant getDomainParticipant() { return m_domainParticipant; }
dds::domain::DomainParticipant getDomainParticipantSub() { return m_domainParticipantSub != nullptr ? m_domainParticipantSub : m_domainParticipant; }
dds::pub::Publisher getDDSPublisher() { return m_ddsPublisher; }
dds::sub::Subscriber getDDSSubscriber() { return m_ddsSubscriber; }
DTC::SubscriberListener *getSubscriberListener() { return m_subListener; }
private:
dds::domain::DomainParticipant m_domainParticipant;
dds::domain::DomainParticipant m_domainParticipantSub;
int m_domainId;
int m_partitionId;
dds::pub::Publisher m_ddsPublisher;
dds::sub::Subscriber m_ddsSubscriber;
DTC::SubscriberListener *m_subListener;
NGL::INGLLogger &m_logger;
};
@@ -0,0 +1,109 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: PlayerManagement
###########################################################
log4j.logger.UnitTests.main=WARN
# GenericDataTests
log4j.logger.UnitTests.Initialisation=WARN
log4j.logger.UnitTests.GenericDataTests=WARN
# DataTopicTests
log4j.logger.UnitTests.DataTopicTestsMain=WARN
log4j.logger.UnitTests.DataTopicTests=WARN
# FunctionTopicTests
log4j.logger.UnitTests.FtTest=INFO
# Generic
log4j.logger.GenericBase=WARN
log4j.logger.Generic.DataWriter=WARN
log4j.logger.Generic.DataReader=WARN
log4j.logger.Generic.DroTypeInfo=WARN
# DTCs
log4j.logger.Generic.DTC=WARN
log4j.logger.DTC.SubscriberListener=WARN
log4j.logger.DTC.Topic=WARN
log4j.logger.DTC=WARN
# FTSs / FTCs
log4j.logger.FTS=WARN
log4j.logger.FTC=INFO
log4j.logger.Generic.FTS=WARN
log4j.logger.Generic.FTC=INFO
# connext (request/reply feature)
log4j.logger.connext=WARN
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_dds_unittests.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
@@ -0,0 +1,90 @@
/**
* @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 "WebSocketInterface/Messages/reply_message.h"
#include "WebSocketInterface/json_keys.h"
#include "WebSocketInterface/data_manager.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QUuid>
namespace ExerciseManagement
{
namespace WebSocketInterface
{
namespace Messages
{
using namespace JsonKeys;
// ####### ClientMessageFactory begin #######
ClientMessageFactory::ClientMessageFactory(DataManager *dataManager)
: LtsWebSocket::AbstractClientMessageFactory(getLogger("ClientMessageFactory"))
, 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 == OcBaseDownload::WebSocketInterface::JsonKeys::BASE_DL_REQUEST())
result = new BaseDlRequest(m_dataManager, client, m_type, m_typeId, m_uuid);
if (m_type == JsonKeys::EXERCISE_INFORMATION_REQUEST())
result = new ExerciseDlRequest(m_dataManager, client, m_type, m_typeId, m_uuid);
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 #######
// ####### BaseDlRequest begin #######
BaseDlRequest::BaseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
: ClientMessage(dataManager, client, type, typeId, uuid)
{}
void BaseDlRequest::process()
{
m_client->addReply(m_dataManager->exercisePhaseBaseData(m_uuid));
m_client->addReply(m_dataManager->exerciseStateChangeBaseData(m_uuid));
m_client->addReply(m_dataManager->exerciseBaseData(m_uuid));
}
bool BaseDlRequest::hasValidData() const
{
return true;
}
ExerciseDlRequest::ExerciseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid)
: ClientMessage(dataManager, client, type, typeId, uuid)
{}
void ExerciseDlRequest::process()
{
m_client->addReply(m_dataManager->exerciseInformation(m_uuid));
}
bool ExerciseDlRequest::hasValidData() const
{
return true;
}
// ####### BaseDlRequest end #######
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,87 @@
/**
* @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"
namespace ExerciseManagement
{
namespace WebSocketInterface
{
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);
DataManager *dataManager() { return m_dataManager; }
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 = QJsonValue());
protected: // attributes
DataManager *m_dataManager;
}; // class ClientMessage
/**
* @brief The BaseDlRequest class handles the base DL request and sends multiple replys depending on the data requested
*/
class BaseDlRequest : public ClientMessage
{
public:
explicit BaseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
void process();
bool hasValidData() const;
}; // class BaseDlRequest
/**
* @brief The ExerciseDlRequest class handles the exercise DL request
*/
class ExerciseDlRequest : public ClientMessage
{
public:
explicit ExerciseDlRequest(DataManager *dataManager, LtsWebSocket::ReplyHandler *client, const QString &type, const int typeId, const QString &uuid);
void process();
bool hasValidData() const;
}; // class BaseDlRequest
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,79 @@
/**
* @RDE_HEADER@
* The ReplyMessage class is an abstract base class for all possible messages which are sent to the client
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "reply_message.h"
#include <LtsWebSocket/json_keys.h>
#include "WebSocketInterface/json_keys.h"
#include <QtCore/QUuid>
namespace ExerciseManagement
{
namespace WebSocketInterface
{
namespace Messages
{
using namespace JsonKeys;
using namespace LtsWebSocket::JsonKeys;
// ####### ExercisePhaseBaseData begin #######
ExercisePhaseBaseData::ExercisePhaseBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (EXERCISE_PHASE_BASE_DATA(), 1, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### ExercisePhaseBaseData begin #######
// ####### ExerciseStateChangeBaseData begin #######
ExerciseStateChangeBaseData::ExerciseStateChangeBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (EXERCISE_STATE_CHANGE_BASE_DATA(), 2, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
// ####### ExerciseStateChangeBaseData begin #######
// ####### ExerciseBaseData begin #######
ExerciseBaseData::ExerciseBaseData(const QJsonArray &data, const QString &uuid)
: LtsWebSocket::ReplyMessage (EXERCISE_BASE_DATA(), 3, uuid) // Todo: Check whether typeId is correct
{
m_jsonRepresentation.insert(DATA(), data);
}
/**************************************************************
* Attention: 6004 is negotiated with ESG for use with ADLER! *
**************************************************************/
ExerciseInformationMessage::ExerciseInformationMessage(const QVector<ExInformation> &data, const QString &uuid)
: ReplyMessage (EXERCISE_INFORMATION(), 6004, uuid) // Todo: Check whether typeId is correct
{
QJsonArray exercises;
for (const auto &exercise : data)
{
QJsonObject ex;
ex.insert(QStringLiteral("id"), exercise.exerciseId);
ex.insert(QStringLiteral("name"), exercise.name);
ex.insert(QStringLiteral("beginTime"), exercise.beginTime);
ex.insert(QStringLiteral("endTime"), exercise.endTime);
ex.insert(QStringLiteral("startTime"), exercise.startTime);
ex.insert(QStringLiteral("stopTime"), exercise.stopTime);
ex.insert(QStringLiteral("timeZone"), exercise.timeZone);
ex.insert(QStringLiteral("status"), exercise.status);
ex.insert(QStringLiteral("description"), exercise.description);
ex.insert(QStringLiteral("trainingAreaId"), exercise.trainingAreaId);
exercises.append(ex);
}
m_jsonRepresentation.insert(DATA(), exercises);
}
// ####### ExerciseBaseData begin #######
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,78 @@
/**
* @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/reply_message.h>
#include <QJsonArray>
#include <QString>
#include <QtCore/QVector>
namespace ExerciseManagement
{
namespace WebSocketInterface
{
namespace Messages
{
/**
* @brief The ExercisePhaseBaseData class exercise phase data for base DL
*/
class ExercisePhaseBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ExercisePhaseBaseData(const QJsonArray &data, const QString &uuid);
}; // class ExercisePhaseBaseData
/**
* @brief The ExerciseStateChangeBaseData class exercise state change data for base DL
*/
class ExerciseStateChangeBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ExerciseStateChangeBaseData(const QJsonArray &data, const QString &uuid);
}; // class ExerciseStateChangeBaseData
/**
* @brief The ExerciseBaseData class exercise data for base DL
*/
class ExerciseBaseData : public LtsWebSocket::ReplyMessage
{
public:
explicit ExerciseBaseData(const QJsonArray &data, const QString &uuid);
}; // class ExerciseBaseData
class ExerciseInformationMessage final : public LtsWebSocket::ReplyMessage
{
public:
struct ExInformation
{
int32_t exerciseId;
QString name;
qint64 beginTime;
qint64 endTime;
qint64 startTime;
qint64 stopTime;
QString timeZone;
int status;
int trainingAreaId;
QString description;
};
explicit ExerciseInformationMessage (const QVector<ExInformation> &data, const QString &uuid);
};
} // namespace Messages
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,68 @@
/**
* @RDE_HEADER@
* The DataManager class handles data to be send to the websocket
* offers an interface to get and set structure data
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "data_manager.h"
#include "Messages/reply_message.h"
#include "object_exercise.h"
namespace ExerciseManagement
{
namespace WebSocketInterface
{
//---------------------------------------------------------------------------------------------------------
DataManager::DataManager(const std::shared_ptr<DatabaseInterface> &dbInterface, const std::shared_ptr<HandlerExercise> &exHandler)
: LtsWebSocket::DataManagerBaseDl<DatabaseInterface>(dbInterface, getLogger("DataManager"))
, m_exerciseHandler(exHandler)
{}
LtsWebSocket::ReplyMessage *DataManager::exercisePhaseBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::ExmExercisePhase, Messages::ExercisePhaseBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::exerciseStateChangeBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::ExmExerciseStateChange, Messages::ExerciseStateChangeBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::exerciseBaseData(const QString &uuid) const
{
return toBaseDataMessageFromDb<Database::ExmExercise, Messages::ExerciseBaseData>(uuid);
}
LtsWebSocket::ReplyMessage *DataManager::exerciseInformation(const QString &uuid) const
{
using namespace Messages;
ExerciseInformationMessage::ExInformation exInfo;
QVector<ExerciseInformationMessage::ExInformation> data;
const auto &exerciseMap = m_exerciseHandler->getExerciseMap();
data.reserve(exerciseMap.size());
for (const ObjectExercise *ex : exerciseMap)
{
const auto &exData = ex->data();
exInfo.exerciseId = exData.id;
exInfo.name = exData.name;
exInfo.beginTime = exData.beginTime.toMSecsSinceEpoch();
exInfo.endTime = exData.endTime.toMSecsSinceEpoch();
exInfo.startTime = exData.startTime.toMSecsSinceEpoch();
exInfo.stopTime = exData.stopTime.toMSecsSinceEpoch();
exInfo.timeZone = exData.defaultTimeZone;
exInfo.status = exData.exerciseStateId.underlying();
exInfo.description = exData.description;
data.append(exInfo);
}
return new ExerciseInformationMessage(data, uuid);
}
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,44 @@
/**
* @RDE_HEADER@
* The DataManager class handles data to be send to the websocket
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <LtsWebSocket/data_manager_base_dl.h>
#include "handler_exercise.h"
#include "logger.h"
namespace ExerciseManagement
{
namespace WebSocketInterface
{
/**
* @brief The DataManager class is a convenience class for managing structure data
*/
class DataManager final : public LtsWebSocket::DataManagerBaseDl<DatabaseInterface>
{
public:
/**
* @brief DataManager Constructor
* @param mainHandler Source for needed handlers
*/
explicit DataManager(const std::shared_ptr<DatabaseInterface> &dbInterface, const std::shared_ptr<HandlerExercise> &exHandler);
LtsWebSocket::ReplyMessage *exercisePhaseBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *exerciseStateChangeBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *exerciseBaseData(const QString &uuid) const;
LtsWebSocket::ReplyMessage *exerciseInformation(const QString &uuid) const;
private:
std::shared_ptr <HandlerExercise> m_exerciseHandler;
}; // class DataManager
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* Header file which holds all occuring JSON keys
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#pragma once
#include <QString>
#include <OcBaseDownload/WebSocketInterface/json_keys.h>
namespace ExerciseManagement
{
namespace WebSocketInterface
{
/**
* @brief Namespace which holds all occuring JSON keys as const QString objects
*/
namespace JsonKeys
{
// types for client --> server messages
inline QString EXERCISE_INFORMATION_REQUEST() { return QStringLiteral("exerciseInformationRequest"); }
// types for server --> client messages
inline QString EXERCISE_INFORMATION() { return QStringLiteral("exerciseInformation"); }
inline QString EXERCISE_PHASE_BASE_DATA() { return QStringLiteral("exercisePhaseBaseData"); }
inline QString EXERCISE_STATE_CHANGE_BASE_DATA() { return QStringLiteral("exerciseStateChangeBaseData"); }
inline QString EXERCISE_BASE_DATA() { return QStringLiteral("exerciseBaseData"); }
} // namespace JsonKeys
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* Handler for the WebSocket Interface
* $Id$
* $HeadURL$
* $Author$
* Created by Philipp Dassow, SLST, 2017-12-05
*/
#include "websocket_interface_handler.h"
#include "main_handler.h"
#include "data_manager.h"
#include "WebSocketInterface/Messages/client_message.h"
#include "settings.h"
namespace ExerciseManagement
{
namespace WebSocketInterface
{
WebSocketInterfaceHandler::WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent)
: QObject(parent)
, m_webSocketServer(nullptr)
, m_messageFactory(nullptr)
, m_dataManager(nullptr)
, m_mainHandler(mainHandler)
, m_logger(getLogger("WebSocketInterfaceHandler"))
{
}
WebSocketInterfaceHandler::~WebSocketInterfaceHandler()
{
cleanup();
}
bool WebSocketInterfaceHandler::init()
{
cleanup();
DEBUG_LOG(m_logger) << "... ok";
m_dataManager = new DataManager(m_mainHandler->db(), m_mainHandler->exerciseHandler());
m_messageFactory = new Messages::ClientMessageFactory(m_dataManager);
m_webSocketServer = new LtsWebSocket::LtsWebSocketServer(m_messageFactory, this);
DEBUG_LOG(m_logger) << "Initialising LtsWebSocketServer";
if (!m_webSocketServer->init(QStringLiteral("EXM"), Settings::getWebSocketServerPortRange(1)))
{
ERROR_LOG(m_logger) << "Failed to initialise LtsWebSocketServer";
return false;
}
DEBUG_LOG(m_logger) << "... ok";
DEBUG_LOG(m_logger) << "Connection signals and slots";
return true;
}
void WebSocketInterfaceHandler::cleanup()
{
delete m_webSocketServer;
delete m_messageFactory;
delete m_dataManager;
}
} // namespace WebSocketInterface
} // namespace ExerciseManagement
@@ -0,0 +1,69 @@
/**
* @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 ExerciseManagement
{
class MainHandler;
namespace WebSocketInterface
{
namespace Messages
{
class ClientMessageFactory;
}
class DataManager;
/**
* @brief The WebSocketInterfaceHandler class is the main class for the WebSocketInterface
*/
class WebSocketInterfaceHandler final : public QObject
{
public:
/**
* @brief WebSocketInterfaceHandler Constructor
* @param mainHandler MainHandler to get the HandlerStructureElement from
*/
explicit WebSocketInterfaceHandler(MainHandler *mainHandler, QObject *parent = nullptr);
/**
* @brief ~WebSocketInterfaceHandler Destructor
*/
~WebSocketInterfaceHandler();
/**
* @brief init Initialises all needed classes for the WebSocketInterface
* @return True on success, otherwise false
*/
bool init();
/**
* @brief webSocketPort returns the listen port
* @return listen port
*/
unsigned short webSocketPort() { return m_webSocketServer->webSocketPort(); }
private:
void cleanup();
private:
LtsWebSocket::LtsWebSocketServer *m_webSocketServer;
Messages::ClientMessageFactory *m_messageFactory;
DataManager *m_dataManager;
MainHandler *m_mainHandler;
LOGGER_T m_logger;
}; // class WebSocketInterfaceHandle
} // namespace WebSocketInterface
} // namespace ExerciseManagement
+75
View File
@@ -0,0 +1,75 @@
/**
* Convert db structures to dds structures
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2017-07-20
*/
#pragma once
#include <ExerciseManagement/exm_db.h>
#include <ExerciseManagement/exm.hpp>
#include <Common/ProjectConfig/project_defines.h>
#include <LtsUtilsCore/dds_vector.h>
namespace ExerciseManagement
{
namespace Database
{
inline Exercise convert(const ExmExercise &dbData)
{
ExerciseManagement::Exercise ddsData;
ddsData.id(dbData.id);
ddsData.name(dbData.name.toStdString());
// behaviour of QDateTime::toMSecsSinceEpoch() is undefinded if not valid:
if (dbData.beginTime.isValid())
ddsData.beginTime(dbData.beginTime.toMSecsSinceEpoch());
if (dbData.endTime.isValid())
ddsData.endTime(dbData.endTime.toMSecsSinceEpoch());
if (dbData.created.isValid())
ddsData.created(dbData.created.toMSecsSinceEpoch());
if (dbData.startTime.isValid())
ddsData.startTime(dbData.startTime.toMSecsSinceEpoch());
if (dbData.stopTime.isValid())
ddsData.stopTime(dbData.stopTime.toMSecsSinceEpoch());
ddsData.trainingAreaId(dbData.trainingAreaId);
if (!dbData.radioGroupId.isNull() && dbData.radioGroupId.canConvert<unsigned int>())
ddsData.radioGroupId(dbData.radioGroupId.toUInt());
ddsData.activePhaseId(dbData.activePhaseId);
ddsData.symbolFillTypeId(dbData.symbolFillTypeId);
ddsData.linkedExerciseId(dbData.linkedExerciseId);
if (!dbData.defaultTimeZone.isEmpty())
ddsData.defaultTimeZone(dbData.defaultTimeZone.at(0).toLatin1());
else
ddsData.defaultTimeZone('Z');
ddsData.sendToWMS(dbData.sendToWms);
ddsData.transferred(dbData.transferred);
ddsData.hasHlaInterface(dbData.hasHlaInterface);
ddsData.state(dbData.exerciseStateId);
ddsData.forces(dbData.forces.toStdString());
ddsData.description(dbData.description.toStdString());
ddsData.partitionName(IDL::exercisePartition(dbData.id));
return ddsData;
}
inline ExercisePhase convert(const ExmExercisePhase &dbData)
{
ExerciseManagement::ExercisePhase ddsData;
ddsData.id(dbData.id);
ddsData.name(dbData.name.toStdString());
ddsData.description(dbData.description.toStdString());
ddsData.beginTime(dbData.beginTime.toMSecsSinceEpoch());
ddsData.endTime(dbData.endTime.toMSecsSinceEpoch());
ddsData.exerciseId(dbData.exerciseId);
return ddsData;
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#include "dds_handler.h"
#include <DDS/Core/pacemaker.h>
namespace ExerciseManagement
{
DdsHandler::DdsHandler(int domainId)
: m_ddsConnector(std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Any))
, m_logger(getLogger("DdsHandler"))
{
DEBUG_LOG(m_logger) << "DdsHandler(" << domainId << ") for partition: " << m_ddsConnector->getDefaultPartition();
}
bool DdsHandler::init(const QStringList &qosFileList)
{
if(!m_ddsConnector->init(qosFileList))
{
ERROR_LOG(m_logger) << "Error initializing dds connections - aborting";
return false;
}
// default initialize Publisher and Subscriber
m_ddsConnector->initPublisherSubscriber();
// Pacemaker
m_pacemaker = std::make_shared<Core::Pacemaker>(m_ddsConnector);
m_topicLockMaster = std::make_shared<Core::TopicLockMaster>(m_ddsConnector);
m_ddsDts = ExerciseManagement::Interface::Dts::create(m_ddsConnector);
m_ddsFts = ExerciseManagement::Interface::Fts::create(m_ddsConnector);
bool bRet = m_ddsDts->enableAll();
INFO_LOG(m_logger) << "Dts initialised";
bRet &= m_ddsFts->enableAll();
INFO_LOG(m_logger) << "Fts initialised";
m_scmFtc = ServiceManagement::Interface::Ftc::create(m_ddsConnector);
bRet &= m_scmFtc->enableAll();
INFO_LOG(m_logger) << "ScmFtc Initialised";
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_ppmDtc->enableEquipmentExerciseMapping();
INFO_LOG(m_logger) << "m_ppmDtc Initialised";
m_avmDtc = AudioVideoManagement::Interface::Dtc::create(m_ddsConnector);
bRet &= m_avmDtc->enableCnrConfig();
bRet &= m_avmDtc->enableCnrChannel();
INFO_LOG(m_logger) << "m_avmDtc Initialised";
return bRet;
}
} // Namespace ExerciseManagement
+58
View File
@@ -0,0 +1,58 @@
/**
* DdsHandler for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-02
*/
#pragma once
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/topic_lock_master.h>
#include <ExerciseManagement/exm_dts.h>
#include <ExerciseManagement/exm_fts.h>
#include <ServiceManagement/scm_ftc.h>
#include <ParticipantManagement/ppm_dtc.h> // for checkEquipmentNumbersOnAllExercises
#include <AudioVideoManagement/avm_dtc.h> // for checking double cnrconfigs in linked exercise
#include "logger.h"
namespace Core
{
class Pacemaker;
}
namespace ExerciseManagement
{
class DdsHandler final
{
public:
explicit DdsHandler(int domainId);
~DdsHandler() = default;
bool init(const QStringList &qosFileList = QStringList());
std::shared_ptr<Core::Pacemaker> pacemaker() const {return m_pacemaker; }
std::shared_ptr<Core::TopicLockMaster> topicLockMaster() const { return m_topicLockMaster; }
std::shared_ptr<ExerciseManagement::Interface::Dts> dts() const { return m_ddsDts; }
std::shared_ptr<ExerciseManagement::Interface::Fts> fts() const { return m_ddsFts; }
std::shared_ptr<ServiceManagement::Interface::Ftc> scmFtc() const { return m_scmFtc; }
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtc() const { return m_ppmDtc; }
std::shared_ptr<AudioVideoManagement::Interface::Dtc> avmDtc() const { return m_avmDtc; }
std::shared_ptr<DDSConnector::DdsConnector> ddsConnector() const { return m_ddsConnector; }
private:
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
std::shared_ptr<Core::Pacemaker> m_pacemaker;
std::shared_ptr<Core::TopicLockMaster> m_topicLockMaster;
std::shared_ptr<ExerciseManagement::Interface::Dts> m_ddsDts;
std::shared_ptr<ExerciseManagement::Interface::Fts> m_ddsFts;
std::shared_ptr<ServiceManagement::Interface::Ftc> m_scmFtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_avmDtc;
NGL::INGLLogger &m_logger;
};
}
+122
View File
@@ -0,0 +1,122 @@
/**
* @RDE_HEADER@
* Base for ExerciseState
* $Id$
* $HeadURL$
* $Author$
*/
#include "exercise_state_base.h"
#include "logger.h"
#include <time.h>
namespace ExerciseManagement
{
//----------------------------------------
ExerciseStateBase::ExerciseStateBase(const QString &objectName, const ExerciseState &exerciseState, ObjectExercise *objExe)
: QState()
, m_exercise(objExe)
, m_logger(getLogger(QLatin1String("State.") + objectName))
, m_exerciseState(exerciseState)
{
setObjectName(QLatin1String("State.") + objectName);
}
void ExerciseStateBase::onEntry(QEvent *event)
{
QState::onEntry(event);
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": onEntry Base: write exerciseState: " << m_exerciseState;
// write exerciseState to db
m_exercise->setExerciseState(m_exerciseState);
m_exercise->sendStateToServiceManagement(m_exerciseState);
}
//----------------------------------------
Unknown::Unknown(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Unknown"), ExerciseState::Unknown, objExe)
{}
/*
//----------------------------------------
Free::Free(ObjectExercise *objExe)
: ExerciseStateBase("Free", SYSTEM_FREI, objExe)
{}
*/
//----------------------------------------
Creation::Creation(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Creation"), ExerciseState::Created, objExe)
{}
void Creation::onEntry(QEvent *event)
{
// TODO: still needed?
// fill durchgef_uebungen, before base because
// we need a filled durchgef_uebungen there
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": onEntry Creation ";
ExerciseStateBase::onEntry(event);
// TODO
/*
// initialize shm
m_db->initializeSharedMemory();
// add first entry to aufz_zeiten (if not already there)
m_db->insertInitialRecordingTime();
*/
}
//----------------------------------------
Start::Start(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Start"), ExerciseState::Started, objExe)
{}
void Start::onEntry(QEvent *event)
{
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": onEntry Start: write exerciseState: started";
ExerciseStateBase::onEntry(event);
// TODO:
//m_db->updateRecordingTimes(true);
}
void Start::onExit(QEvent *event)
{
ExerciseStateBase::onExit(event);
//TODO
//m_db->updateRecordingTimes(false);
}
//----------------------------------------
Pause::Pause(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Pause"), ExerciseState::Paused, objExe)
{}
//-----------------------------------------------------------------------------------------------------
Stop::Stop(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Stop"), ExerciseState::Stopped, objExe)
{}
void Stop::onEntry(QEvent *event)
{
ExerciseStateBase::onEntry(event);
m_exercise->transferExerciseToEvaluationDb();
}
//-----------------------------------------------------------------------------------------------------
Terminate::Terminate(ObjectExercise *objExe)
: ExerciseStateBase(QLatin1String("Resume"), ExerciseState::Terminated, objExe)
{}
} // namespace ExerciseManagement
+91
View File
@@ -0,0 +1,91 @@
/**
* @RDE_HEADER@
* Base for ExerciseState
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QState>
#include <ExerciseManagement/exm_enums.hpp>
#include "object_exercise.h"
namespace ExerciseManagement
{
class ExerciseStateBase : public QState
{
Q_OBJECT
public:
ExerciseManagement::ExerciseState exerciseState() const { return m_exerciseState; }
protected:
ExerciseStateBase(const QString &objectName, const ExerciseState &exerciseState, ObjectExercise* objExe);
void onEntry(QEvent *event);
int exerciseId() const { return m_exercise ? m_exercise->id() : -1; }
protected:
ObjectExercise *m_exercise;
NGL::INGLLogger &m_logger;
private:
const ExerciseManagement::ExerciseState m_exerciseState;
};
class Unknown : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Unknown(ObjectExercise *objExe);
};
class Creation : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Creation(ObjectExercise *objExe);
protected:
void onEntry(QEvent *event);
};
class Start : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Start(ObjectExercise *objExe);
protected:
void onEntry(QEvent *event);
void onExit(QEvent *event);
};
class Pause : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Pause(ObjectExercise *objExe);
};
class Stop : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Stop(ObjectExercise *objExe);
protected:
void onEntry(QEvent *event);
};
class Terminate : public ExerciseStateBase
{
Q_OBJECT
public:
explicit Terminate(ObjectExercise *objExe);
};
} // namespace ExerciseManagement
+162
View File
@@ -0,0 +1,162 @@
/**
* @RDE_HEADER@
* State Machine handler for Exercise Control
* $Id$
* $HeadURL$
* $Author$
*/
#include "exercise_state_machine.h"
#include "exercise_state_manager.h"
#include "exercise_state_base.h"
#include "object_exercise.h"
#include "settings.h"
#include "logger.h"
namespace ExerciseManagement
{
ExerciseStateMachine::ExerciseStateMachine(ObjectExercise* objExe, QObject *parent)
: QObject(parent)
, m_stateManager(new ExerciseStateManager(objExe, this))
, m_stateMachine(new QStateMachine(this))
, m_exercise(objExe)
, m_logger(getLogger("ExerciseStateMachine"))
{
qRegisterMetaType<ExerciseManagement::ExerciseState>("ExerciseManagement::ExerciseState");
m_exerciseStates.insert(ExerciseState::Unknown, new Unknown(m_exercise));
m_exerciseStates.insert(ExerciseState::Created, new Creation(m_exercise));
m_exerciseStates.insert(ExerciseState::Started, new Start(m_exercise));
m_exerciseStates.insert(ExerciseState::Stopped, new Stop(m_exercise));
m_exerciseStates.insert(ExerciseState::Paused, new Pause(m_exercise));
m_exerciseStates.insert(ExerciseState::Terminated, new Terminate(m_exercise));
// Define transitions from one state to the other
// ----------------------------------------------------------------------------------------------------------
// ** Unknown
// => Created
m_exerciseStates[ExerciseState::Unknown]->addTransition(m_stateManager, &ExerciseStateManager::stateCreated,
m_exerciseStates[ExerciseState::Created]);
// ** Created
// => Started
m_exerciseStates[ExerciseState::Created]->addTransition(m_stateManager, &ExerciseStateManager::stateStarted,
m_exerciseStates[ExerciseState::Started]);
// => Terminated
m_exerciseStates[ExerciseState::Created]->addTransition(m_stateManager, &ExerciseStateManager::stateTerminated,
m_exerciseStates[ExerciseState::Terminated]);
// ** Started
// => Paused
m_exerciseStates[ExerciseState::Started]->addTransition(m_stateManager, &ExerciseStateManager::statePaused,
m_exerciseStates[ExerciseState::Paused]);
// => Stopped
m_exerciseStates[ExerciseState::Started]->addTransition(m_stateManager, &ExerciseStateManager::stateStopped,
m_exerciseStates[ExerciseState::Stopped]);
// ** Paused
// => Stopped
m_exerciseStates[ExerciseState::Paused]->addTransition(m_stateManager, &ExerciseStateManager::stateStopped,
m_exerciseStates[ExerciseState::Stopped]);
// => Resumed (= Started)
m_exerciseStates[ExerciseState::Paused]->addTransition(m_stateManager, &ExerciseStateManager::stateStarted,
m_exerciseStates[ExerciseState::Started]);
// ** Stopped
// => Terminated
m_exerciseStates[ExerciseState::Stopped]->addTransition(m_stateManager, &ExerciseStateManager::stateTerminated,
m_exerciseStates[ExerciseState::Terminated]);
// ** Terminated
// => Stopped
if ( ExerciseManagement::Settings::enableExerciseTerminatedToStopped())
{
m_exerciseStates[ExerciseState::Terminated]->addTransition(m_stateManager, &ExerciseStateManager::stateStopped,
m_exerciseStates[ExerciseState::Stopped]);
}
// ----------------------------------------------------------------------------------------------------------
//States zur StateMachine hinzufügen
for (ExerciseStateBase *state : qAsConst(m_exerciseStates))
{
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": state: " << state->exerciseState();
m_stateMachine->addState(state);
connect(state, &ExerciseStateBase::entered, this, &ExerciseStateMachine::onEnteredState);
}
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": Constructor ExerciseStateMachine: size m_exerciseStates = " << m_exerciseStates.size();
}
ExerciseStateMachine::~ExerciseStateMachine()
{
qDeleteAll(m_exerciseStates);
delete m_stateManager;
delete m_stateMachine;
}
bool ExerciseStateMachine::init()
{
if (m_stateMachine->isRunning())
return false;
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": Init ExerciseStateMachine ";
// initialise current exerciseState
ExerciseStateBase *currentExerciseState = getStateFromId(m_exercise->exerciseState());
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": State = "
<< ExerciseStateManager::toString(m_exercise->exerciseState());
m_stateMachine->setInitialState(currentExerciseState);
m_stateMachine->start();
m_stateManager->setInitial(m_exercise->exerciseState());
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": ==> Done Init ExerciseStateMachine ";
return true;
}
ExerciseManagement::ExerciseState ExerciseStateMachine::getCurrentState()
{
return m_exercise->exerciseState();
}
bool ExerciseStateMachine::switchToState(ExerciseState newExerciseState)
{
return m_stateManager->triggerSwitchState(newExerciseState);
}
void ExerciseStateMachine::onEnteredState()
{
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": ExerciseStateMachine - onEnteredState";
ExerciseStateBase *s = qobject_cast<ExerciseStateBase*>(sender());
if (!s)
return;
DEBUG_LOG(m_logger) << "Ex " << exerciseId() << ": ExerciseStateBase : " << s->exerciseState();
m_stateManager->setCurrent(s->exerciseState());
emit exerciseStateChanged(s->exerciseState());
}
//----------------------------------------
ExerciseStateBase* ExerciseStateMachine::getStateFromId(ExerciseState exerciseState)
{
ExerciseStateBase *s = m_exerciseStates.value(exerciseState);
if (!s)
s = m_exerciseStates.value(ExerciseState::Unknown);
return s;
}
//----------------------------------------
int ExerciseStateMachine::exerciseId() const
{
return m_exercise ? m_exercise->id() : -1;
}
} // namespace ExerciseManagement
+60
View File
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* State Machine handler for Exercise Object
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hanna E. Rathje, SLSE, 2015-10-12
*/
#pragma once
#include <QObject>
#include <QStateMachine>
#include <ExerciseManagement/exm_enums.hpp>
namespace NGL
{
class INGLLogger;
}
namespace ExerciseManagement
{
class ExerciseStateManager;
class ExerciseStateBase;
class ObjectExercise;
class ExerciseStateMachine : public QObject
{
Q_OBJECT
public:
explicit ExerciseStateMachine(ExerciseManagement::ObjectExercise *objExe, QObject *parent = nullptr);
~ExerciseStateMachine();
bool init();
bool switchToState(ExerciseManagement::ExerciseState newExerciseState);
ExerciseManagement::ExerciseState getCurrentState();
Q_SIGNALS:
void finished();
void exerciseStateChanged(ExerciseManagement::ExerciseState exerciseState);
public Q_SLOTS:
void onEnteredState();
private:
ExerciseStateBase *getStateFromId(ExerciseManagement::ExerciseState exerciseState);
int exerciseId() const;
ExerciseStateManager *m_stateManager;
QStateMachine *m_stateMachine;
QMap<ExerciseManagement::ExerciseState, ExerciseStateBase*> m_exerciseStates;
ObjectExercise *m_exercise;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseManagement
+109
View File
@@ -0,0 +1,109 @@
/**
* @RDE_HEADER@
* ExerciseStateManager / Manages the state of an exercise
* $Id$
* $HeadURL$
* $Author$
*
* Created by Hanna E. Rathje, SLSE, 2015-10-12
*/
#include "exercise_state_manager.h"
#include "object_exercise.h"
#include "logger.h"
namespace ExerciseManagement
{
ExerciseStateManager::ExerciseStateManager(ObjectExercise *objExe, QObject *parent)
: QObject(parent)
, m_exerciseState(ExerciseState::Unknown)
, m_exercise(objExe)
, m_logger(getLogger("ExerciseStateManager"))
{
m_stateSignals[ExerciseState::Unknown] = [&]() { emit stateUnknown(); };
m_stateSignals[ExerciseState::Created] = [&]() { emit stateCreated(); };
m_stateSignals[ExerciseState::Started] = [&]() { emit stateStarted(); };
m_stateSignals[ExerciseState::Stopped] = [&]() { emit stateStopped(); };
m_stateSignals[ExerciseState::Paused] = [&]() { emit statePaused(); };
m_stateSignals[ExerciseState::Terminated] = [&]() { emit stateTerminated(); };
}
bool ExerciseStateManager::triggerSwitchState(ExerciseManagement::ExerciseState state)
{
INFO_LOG(m_logger) << "Ex " << exerciseId() << ": Trying to switch to state " << toString(state);
std::function<void()> f = m_stateSignals.value(state);
if (f)
{
//DEBUG_LOG(m_logger) << "function: emit signal";
emit f();
}
return (f != nullptr);
}
bool ExerciseStateManager::isValid(ExerciseManagement::ExerciseState state) const
{
return m_stateSignals.contains(state);
}
void ExerciseStateManager::setCurrent(ExerciseManagement::ExerciseState state)
{
setStateInternal(state);
}
void ExerciseStateManager::setInitial(ExerciseManagement::ExerciseState state)
{
setStateInternal(state);
}
QString ExerciseStateManager::toString(ExerciseManagement::ExerciseState state)
{
switch (state.underlying())
{
case ExerciseState::Unknown:
return tr("UNKNOWN");
case ExerciseState::Created:
return tr("CREATED");
case ExerciseState::Started:
return tr("STARTED");
case ExerciseState::Stopped:
return tr("STOPPED");
case ExerciseState::Paused:
return tr("PAUSED");
case ExerciseState::Terminated:
return tr("TERMINATED");
}
return tr("Unknown State: (%1)").arg(state.underlying());
}
void ExerciseStateManager::setStateInternal(ExerciseManagement::ExerciseState state)
{
INFO_LOG(m_logger) << "Ex " << exerciseId() << ": In setStateInternal: new state " << toString(state);
if(m_exerciseState == state)
return;
m_exerciseState = state;
// TODO:
/*
// inform CTA
betriebszustandsmeldung_rt tele;
memset(&tele, 0, sizeof(tele));
tele.zustand = state;
NGLSettings::sendToZsksTX(tele);
// inform A/V - todo: here? or in the dedicated states?
// or separate object (because we've to check the a/v state periodically etc)
*/
INFO_LOG(m_logger) << "Ex " << exerciseId() << ": Changed exercise state to " << toString(state);
}
int ExerciseStateManager::exerciseId() const
{
return m_exercise ? m_exercise->id() : -1;
}
} // namespace ExerciseManagement
+59
View File
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* ExerciseStateManager / Manages the state of an exercise
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include <functional>
#include <QMap>
#include <ExerciseManagement/exm_enums.hpp>
namespace NGL
{
class INGLLogger;
}
namespace ExerciseManagement
{
class ObjectExercise;
class ExerciseStateManager : public QObject
{
Q_OBJECT
public:
explicit ExerciseStateManager(ObjectExercise* objExe, QObject *parent = nullptr);
bool triggerSwitchState(ExerciseManagement::ExerciseState state);
bool isValid(ExerciseManagement::ExerciseState state) const;
void setCurrent(ExerciseManagement::ExerciseState state);
void setInitial(ExerciseManagement::ExerciseState state);
static QString toString(ExerciseManagement::ExerciseState state);
Q_SIGNALS:
void stateUnknown();
void stateCreated();
void stateStarted();
void statePaused();
void stateStopped();
void stateTerminated();
void stateFalse();
private:
void setStateInternal(ExerciseManagement::ExerciseState state);
int exerciseId() const;
QMap<ExerciseManagement::ExerciseState, std::function<void()> > m_stateSignals;
ExerciseManagement::ExerciseState m_exerciseState;
ObjectExercise *m_exercise;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseManagement
+52
View File
@@ -0,0 +1,52 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-08-04
*/
#pragma once
#include <QtCore/qglobal.h>
#ifndef EXM_COMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EXM_COMMON_EXPORT
# else
# if defined(MAKE_SVCEXMCOMMON_LIB)
# define EXM_COMMON_EXPORT Q_DECL_EXPORT
# ifndef WIN32
# ifdef NDDSUSERDllExport
# undef NDDSUSERDllExport
# endif
# define NDDSUSERDllExport Q_DECL_EXPORT
# endif
# else
# define EXM_COMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef EXM_CLIENT_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EXM_CLIENT_EXPORT
# else
# if defined(MAKE_SVCEXMCLIENT_LIB)
# define EXM_CLIENT_EXPORT Q_DECL_EXPORT
# else
# define EXM_CLIENT_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
#ifndef EXM_SERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define EXM_SERVER_EXPORT
# else
# if defined(MAKE_SVCEXMSERVER_LIB)
# define EXM_SERVER_EXPORT Q_DECL_EXPORT
# else
# define EXM_SERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
+226
View File
@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<service namespace="ExerciseManagement" abbreviation="EXM" procNames="ExerciseManagement" exerciseDependency="Global" groups="Core" prio="4">
<rcs>
<Id Id="$Id$"/>
<HeadURL HeadURL="$HeadURL$"/>
<Author Author="$Author$"/>
</rcs>
<operatorRights offset="12">
<guiPlugin name="ExerciseManagement" offset="1">
<operatorRightGroup name="Exercise Management" description="Create/Modify/Delete Exercises">
<operatorRight right="ExercisesShow" value="1" description="Show Exercises"/>
<operatorRight right="ExercisesEdit" value="2" description="Edit Exercises"/>
</operatorRightGroup>
<operatorRightGroup name="Exercise Management - Edit Operators" description="Edit Operator Assignment">
<operatorRight right="ExerciseOperatorEdit" value="6" description="Edit Operator Assignment"/>
</operatorRightGroup>
<operatorRightGroup name="Exercise Management - Edit Phases" description="Add/Remove exercise phases">
<operatorRight right="ExercisePhaseEdit" value="3" description="Edit Phases"/>
</operatorRightGroup>
<operatorRightGroup name="Exercise Management - Edit Purpose" description="Edit purpose of exercise">
<operatorRight right="ExercisePurposeEdit" value="4" description="Edit Purpose"/>
</operatorRightGroup>
<operatorRightGroup name="Exercise Management - Edit State" description="Change exercise state">
<operatorRight right="ExerciseStateEdit" value="5" description="Change exercise state"/>
</operatorRightGroup>
</guiPlugin>
</operatorRights>
<tables>
<table name="exercise">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="name" datatype="text"/>
<column name="exercise_state_id" datatype="integer" isForeignKey="1"/>
<column name="operator_id" datatype="integer"/>
<column name="workstation_id" datatype="integer"/>
<column name="begin_time" datatype="timestamp"/>
<column name="end_time" datatype="timestamp"/>
<column name="created" datatype="timestamp" default="now()"/>
<column name="start_time" datatype="timestamp"/>
<column name="stop_time" datatype="timestamp"/>
<column name="training_area_id" datatype="integer" isForeignKey="1"/>
<column name="radio_group_id" datatype="integer" isOptional="1" isForeignKey="1"/>
<column name="active_phase_id" datatype="integer" comment="no FK, because of cross reference"/>
<column name="symbol_fill_type_id" datatype="integer"/>
<column name="linked_exercise_id" datatype="integer"/>
<column name="default_time_zone" datatype="text"/>
<column name="send_to_wms" datatype="boolean" default="false"/>
<column name="forces" datatype="text"/>
<column name="description" datatype="text"/>
<column name="transferred" datatype="boolean" default="false"/>
<column name="has_hla_interface" datatype="boolean" default="false"/>
<constraint name="UQ_exercise_name" type="unique">
<column name="name"/>
</constraint>
<constraint name="UQ_exercise_radio_group_id" type="unique">
<column name="radio_group_id"/>
</constraint>
<foreignKey name="FK_ea_exercise_exercise_state" referential_integrity="DeleteRestrict">
<source table="exercise" column="exercise_state_id"/>
<destination table="exercise_state" column="id"/>
</foreignKey>
<foreignKey name="FK_exercise_asm_training_area" referential_integrity="DeleteRestrict">
<source table="exercise" column="training_area_id"/>
<destination service="AssetManagement" serviceAbbr="asm" schema="asm" table="asm_training_area" column="id"/>
</foreignKey>
<foreignKey name="FK_exercise_pdm_radio_group" referential_integrity="DeleteRestrict">
<source table="exercise" column="radio_group_id"/>
<destination service="ProjectDataManagement" serviceAbbr="pdm" schema="pdm" table="pdm_radio_group" column="id"/>
</foreignKey>
</table>
<table name="exercise_image">
<column name="exercise_id" datatype="integer" isPrimaryKey="1" isForeignKey="1"/>
<column name="image" datatype="bytea"/>
<foreignKey name="FK_exercise_image_exercise">
<source table="exercise_image" column="exercise_id"/>
<destination table="exercise" column="id"/>
</foreignKey>
</table>
<table name="exercise_phase">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="name" datatype="text"/>
<column name="description" datatype="text"/>
<column name="begin_time" datatype="timestamp"/>
<column name="end_time" datatype="timestamp"/>
<column name="exercise_id" datatype="integer" isForeignKey="1"/>
<foreignKey name="FK_exercise_phase_exercise">
<source table="exercise_phase" column="exercise_id"/>
<destination table="exercise" column="id"/>
</foreignKey>
</table>
<table name="exercise_state">
<column name="id" datatype="integer" isPrimaryKey="1"/>
<column name="name" datatype="text"/>
</table>
<table name="exercise_state_change" comment="The table shows the time of change of the exercise state.">
<column name="id" datatype="integer" isPrimaryKey="1">
<sequence start="1" increment="1"/>
</column>
<column name="exercise_id" datatype="integer" isForeignKey="1"/>
<column name="exercise_state_id" datatype="integer" isForeignKey="1"/>
<column name="changed_at" datatype="bigint"/>
<column name="operator_id" datatype="integer"/>
<column name="workstation_id" datatype="integer"/>
<foreignKey name="FK_exercise_state_change_exercise">
<source table="exercise_state_change" column="exercise_id"/>
<destination table="exercise" column="id"/>
</foreignKey>
<foreignKey name="FK_exercise_state_change_exercise_state">
<source table="exercise_state_change" column="exercise_state_id"/>
<destination table="exercise_state" column="id"/>
</foreignKey>
</table>
</tables>
<views/>
<dbServiceStructs>
<serviceStruct name="ExmExercise" table="exercise"/>
<serviceStruct name="ExmExerciseImage" table="exercise_image"/>
<serviceStruct name="ExmExercisePhase" table="exercise_phase"/>
<serviceStruct name="ExmExerciseStateChange" table="exercise_state_change"/>
<table name="exercise"/>
<table name="exercise_image"/>
<table name="exercise_phase"/>
<table name="exercise_state_change"/>
</dbServiceStructs>
<topicTypes>
<topicType name="GetExerciseImageReply" baseValueType="Core::FunctionReply">
<column name="imageData" idlType="sequence&lt;octet&gt;"/>
</topicType>
<topicType name="GetExerciseImageRequest">
<column name="exerciseId" idlType="long"/>
<column name="maxHeight" idlType="long"/>
<column name="maxWidth" idlType="long"/>
<column name="lowBandwidth" idlType="boolean"/>
</topicType>
<topicType name="Exercise" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="1" baseValueType="Core::BaseEntity">
<column name="beginTime" idlType="long long"/>
<column name="endTime" idlType="long long"/>
<column name="created" idlType="long long"/>
<column name="startTime" idlType="long long"/>
<column name="stopTime" idlType="long long"/>
<column name="trainingAreaId" idlType="long"/>
<column name="radioGroupId" idlType="long" isOptional="1"/>
<column name="activePhaseId" idlType="long"/>
<column name="symbolFillTypeId" idlType="long"/>
<column name="linkedExerciseId" idlType="long"/>
<column name="defaultTimeZone" idlType="char"/>
<column name="sendToWMS" idlType="boolean"/>
<column name="transferred" idlType="boolean"/>
<column name="hasHlaInterface" idlType="boolean"/>
<column name="state" idlType="ExerciseState"/>
<column name="forces" idlType="string"/>
<column name="description" idlType="string"/>
<column name="partitionName" idlType="string"/>
</topicType>
<topicType name="ExercisePhase" isDataObject="1" autosubscribe="1" routeToClient="1" routeToServer="0" routeInReplay="1" baseValueType="Core::BaseEntity">
<column name="description" idlType="string"/>
<column name="beginTime" idlType="long long"/>
<column name="endTime" idlType="long long"/>
<column name="exerciseId" idlType="long"/>
</topicType>
<topicType name="ExerciseCreateRequest">
<column name="id" idlType="long"/>
<column name="name" idlType="string"/>
<column name="beginTime" idlType="long long"/>
<column name="endTime" idlType="long long"/>
<column name="startTime" idlType="long long"/>
<column name="stopTime" idlType="long long"/>
<column name="trainingAreaId" idlType="long"/>
<column name="radioGroupId" idlType="long" isOptional="1"/>
<column name="symbolFillTypeId" idlType="long"/>
<column name="linkedExerciseId" idlType="long"/>
<column name="defaultTimeZone" idlType="char"/>
<column name="sendToWMS" idlType="boolean"/>
<column name="transferred" idlType="boolean"/>
<column name="hasHlaInterface" idlType="boolean"/>
<column name="forces" idlType="string"/>
<column name="description" idlType="string"/>
</topicType>
<topicType name="ExerciseImageModifyRequest">
<column name="id" idlType="long"/>
<column name="image" idlType="sequence&lt;octet&gt;"/>
</topicType>
<topicType name="ExerciseModifyRequest" baseValueType="ExerciseCreateRequest">
<column name="newState" idlType="ExerciseState"/>
<column name="activePhaseId" idlType="long"/>
<column name="operatorId" idlType="long"/>
<column name="workstationId" idlType="long"/>
</topicType>
<topicType name="ExerciseRemoveRequest" baseValueType="Core::RemoveElementRequest">
<column name="force" idlType="boolean"/>
</topicType>
<topicType name="ExercisePhaseCreateRequest">
<column name="name" idlType="string"/>
<column name="description" idlType="string"/>
<column name="beginTime" idlType="long long"/>
<column name="endTime" idlType="long long"/>
<column name="exerciseId" idlType="long"/>
</topicType>
<topicType name="ExercisePhaseModifyRequest" baseValueType="ExercisePhaseCreateRequest">
<column name="id" idlType="long"/>
</topicType>
</topicTypes>
<functionTopics>
<functionTopic name="GetExerciseImage" requestTopicType="GetExerciseImageRequest" replyTopicType="GetExerciseImageReply" requestTopicName="GetExerciseImageRequest" replyTopicName="GetExerciseImageReply" routeInReplay="0"/>
<functionTopic name="ExerciseCreate" requestTopicType="ExerciseCreateRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExerciseCreateRequest" replyTopicName="ExerciseCreateReply" routeInReplay="0"/>
<functionTopic name="ExerciseImageModify" requestTopicType="ExerciseImageModifyRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExerciseImageModifyRequest" replyTopicName="ExerciseImageModifyReply" routeInReplay="0"/>
<functionTopic name="ExerciseModify" requestTopicType="ExerciseModifyRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExerciseModifyRequest" replyTopicName="ExerciseModifyReply" routeInReplay="0"/>
<functionTopic name="ExerciseRemove" requestTopicType="ExerciseRemoveRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExerciseRemoveRequest" replyTopicName="ExerciseRemoveReply" routeInReplay="0"/>
<functionTopic name="ExercisePhaseCreate" requestTopicType="ExercisePhaseCreateRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExercisePhaseCreateRequest" replyTopicName="ExercisePhaseCreateReply" routeInReplay="0"/>
<functionTopic name="ExercisePhaseModify" requestTopicType="ExercisePhaseModifyRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExercisePhaseModifyRequest" replyTopicName="ExercisePhaseModifyReply" routeInReplay="0"/>
<functionTopic name="ExercisePhaseRemove" requestTopicType="Core::RemoveElementRequest" replyTopicType="Core::FunctionReply" requestTopicName="ExercisePhaseRemoveRequest" replyTopicName="ExercisePhaseRemoveReply" routeInReplay="0"/>
</functionTopics>
<enumerations>
<enumeration name="ExerciseState" table="exercise_state">
<attribute key="Unknown" value="0"/>
<attribute key="Created" value="1"/>
<attribute key="Started" value="2"/>
<attribute key="Paused" value="3"/>
<attribute key="Stopped" value="5"/>
<attribute key="Terminated" value="6"/>
</enumeration>
</enumerations>
</service>
+823
View File
@@ -0,0 +1,823 @@
/**
* Handler for all exercise objects
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-03
*/
#include "handler_exercise.h"
#include "exm_db.h"
#include "handler_exercise_phase.h"
#include "main_handler.h"
#include "object_exercise.h"
#include "settings.h"
//#include <QPixmap>
#include <QTimer>
#include <vector>
#include <LtsUtilsCore/dds_vector_to_pixmap.h>
#include <DBAdministration/exercise_table_generator.h>
#include <ngldb/dbtransaction.h>
#include <LtsUtilsCore/dds_vector.h>
namespace ExerciseManagement
{
HandlerExercise::HandlerExercise(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> &exTableGenerator,
QObject *parent)
: QObject(parent)
, Interface::Fts::ExerciseCreateRequestHandler(ddsHandler->fts())
, Interface::Fts::ExerciseModifyRequestHandler(ddsHandler->fts())
, Interface::Fts::ExerciseRemoveRequestHandler(ddsHandler->fts())
, Interface::Fts::ExerciseImageModifyRequestHandler(ddsHandler->fts())
, Interface::Fts::GetExerciseImageRequestHandler(ddsHandler->fts())
, m_mainHandler(mainHandler)
, m_ddsHandler(ddsHandler)
, m_db(db)
, m_exTblGen(exTableGenerator)
, m_startupExerciseTimer(new QTimer(this))
, m_logger(getLogger("HandlerExercise"))
{
//m_imageCache.setMaxCost(15);
m_startupExerciseTimer->setSingleShot(false);
m_startupExerciseTimer->setInterval(2000);
connect(m_startupExerciseTimer, &QTimer::timeout, this, &HandlerExercise::onStartNextExercise);
}
HandlerExercise::~HandlerExercise()
{
qDeleteAll(m_exeObjMap);
}
bool HandlerExercise::init()
{
DEBUG_LOG(m_logger) << "Finish Initialise DbAdministration (dba) ";
INFO_LOG(m_logger) << "Data available for services: " << m_exTblGen->getAvailableServices().join(QLatin1Char(','));
QVector<Database::ExmExercise> dataVec;
// read all exercises from database
if (!m_db->select(dataVec))
{
ERROR_LOG(m_logger) << "Error reading data from database!";
return true;
}
QVector<QPair<int, ExerciseState>> exerciseStates;
exerciseStates.reserve(dataVec.size());
// create exercise objects
for (const Database::ExmExercise &data : qAsConst(dataVec))
{
ObjectExercise *ifs(new ObjectExercise(m_ddsHandler, m_db, m_exTblGen, this));
Core::FunctionReplyObject ret = ifs->createObject(data);
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger) << ret();
return false;
}
m_exeObjMap.insert(ifs->id(), ifs);
exerciseStates.push_back({ifs->id(), ifs->data().exerciseStateId});
}
DEBUG_LOG(m_logger) << "m_exeObjMap.size() = " << m_exeObjMap.size();
// make sure to start the important exercises first
std::sort(exerciseStates.begin(), exerciseStates.end(),
[](const QPair<int, ExerciseState> &a, const QPair<int, ExerciseState> &b)
{
if (a.second == b.second)
return a.first < b.first;
if (a.second == ExerciseState::Started)
return true;
if (b.second == ExerciseState::Started)
return false;
if (a.second == ExerciseState::Paused)
return true;
if (b.second == ExerciseState::Paused)
return false;
if (a.second == ExerciseState::Created)
return true;
if (b.second == ExerciseState::Created)
return false;
return a.first < b.first;
});
for (const auto &p : qAsConst(exerciseStates))
m_exercisesToStart.enqueue(p.first);
m_startupExerciseTimer->start();
return true;
}
ObjectExercise *HandlerExercise::getExerciseById(qint32 id) const
{
return m_exeObjMap.value(id);
}
bool HandlerExercise::startGroupRequest(const ServiceManagement::GroupRequest &req)
{
return m_ddsHandler->scmFtc()->startGroupRequest(req, this);
}
void HandlerExercise::onStartNextExercise()
{
if (m_exercisesToStart.isEmpty())
{
NOTICE_LOG(m_logger) << "onStartNextExercise no more exercises to start";
m_startupExerciseTimer->stop();
delete m_startupExerciseTimer;
m_startupExerciseTimer = nullptr;
return;
}
m_startupExerciseTimer->setInterval(m_startupExerciseTimer->interval() + 1000);
while (true)
{
int exId = m_exercisesToStart.dequeue();
auto *objEx = m_exeObjMap.value(exId);
if (!objEx)
{
WARNING_LOG(m_logger) << "Trying to start exercise with id " << exId << " but could not find a handler !?";
continue;
}
NOTICE_LOG(m_logger) << "Init state machine for exercise " << exId << " (" << objEx->data().name << ")";
objEx->initStateMachine();
break;
}
m_startupExerciseTimer->start();
}
bool HandlerExercise::hasError(const Core::FunctionReplyObject &replyObject, ObjectExercise *ifs) const
{
if (replyObject.error())
{
delete ifs;
ERROR_LOG(m_logger) << replyObject();
return true;
}
return false;
}
template<typename T>
static Database::ExmExercise copyBaseData(const T &in)
{
Database::ExmExercise newData;
// exerciseState defined in ObjectExercise
newData.id = in.id();
newData.name = QString::fromStdString(in.name());
newData.beginTime = QDateTime::fromMSecsSinceEpoch(in.beginTime());
newData.endTime = QDateTime::fromMSecsSinceEpoch(in.endTime());
newData.startTime = QDateTime::fromMSecsSinceEpoch(in.startTime());
newData.stopTime = QDateTime::fromMSecsSinceEpoch(in.stopTime());
newData.trainingAreaId = in.trainingAreaId();
if (in.radioGroupId().is_set())
newData.radioGroupId = in.radioGroupId().get();
newData.symbolFillTypeId = in.symbolFillTypeId();
newData.linkedExerciseId = in.linkedExerciseId();
newData.defaultTimeZone = QLatin1Char(in.defaultTimeZone());
newData.sendToWms = in.sendToWMS();
newData.transferred = in.transferred();
newData.hasHlaInterface = in.hasHlaInterface();
newData.forces = QString::fromStdString(in.forces());
newData.description = QString::fromStdString(in.description());
return newData;
}
void HandlerExercise::onExerciseCreateRequest(const rti::core::SampleIdentity &cookie,
const ExerciseManagement::ExerciseCreateRequest &request)
{
auto ret = exerciseCreateRequest(request);
if (ret.error())
{
WARNING_LOG(m_logger) << ret();
}
m_ddsHandler->fts()->exerciseCreateReply(cookie, ret);
}
Core::FunctionReplyObject HandlerExercise::exerciseCreateRequest(const ExerciseCreateRequest &request)
{
DEBUG_LOG(m_logger) << "onExerciseCreateRequest: " << request;
Database::ExmExercise newData(copyBaseData(request));
newData.linkedExerciseId = 0;
DBTransaction trans(m_db->database());
int iCnt = 0;
QSet<int> linkedExerciseIds;
for (const ObjectExercise *ex : qAsConst(m_exeObjMap))
{
const auto state = ex->exerciseState();
// ignore linked exercises
if (linkedExerciseIds.contains(ex->id()))
continue;
if (ex->linkedExerciseId() != 0)
linkedExerciseIds += ex->linkedExerciseId();
if (state == ExerciseState::Created)
++iCnt;
}
if (iCnt >= Settings::maxPreparedExercises())
return Core::FunctionReplyObject(tr_lts("dds", "Can not create exercise - only %1 prepared exercise(s) allowed")).arg(Settings::maxPreparedExercises());
ObjectExercise *ifs(new ObjectExercise(m_ddsHandler, m_db, m_exTblGen, this));
Core::FunctionReplyObject ret = ifs->createObject(newData, true);
if (hasError(ret, ifs))
{
// remove failed create exercise request from DDS bus (id = 0)
ERROR_LOG(m_logger) << "CreateExercise failed, removing request from dds";
m_ddsHandler->dts()->removeExerciseByKey(0);
return ret;
}
// Create default phase for exercise
ExercisePhaseCreateRequest phaseData;
phaseData.name(tr("Phase 1").toStdString());
phaseData.description(tr("Default exercise phase").toStdString());
phaseData.beginTime(request.beginTime());
phaseData.endTime(request.endTime());
phaseData.exerciseId(ifs->id());
ret = m_mainHandler->exercisePhaseHandler()->exercisePhaseCreateRequest(phaseData);
if (hasError(ret, ifs))
{
ERROR_LOG(m_logger) << "CreateExercisePhase failed, removing requests from dds";
m_ddsHandler->dts()->removeExercisePhaseByKey(0);
m_ddsHandler->dts()->removeExerciseByKey(0);
return ret;
}
// Set default phase as active in exercise
newData = ifs->data();
newData.activePhaseId = ret.id();
ret = ifs->updateObject(newData);
if (hasError(ret, ifs))
{
ERROR_LOG(m_logger) << "Set active phase failed, removing requests from dds";
m_ddsHandler->dts()->removeExercisePhaseByKey(0);
m_ddsHandler->dts()->removeExerciseByKey(0);
return ret;
}
ifs->initStateMachine();
m_exeObjMap.insert(ifs->id(), ifs);
ret.setId(ifs->id()); // set the returned id to the new exercise id
trans.commit();
return Core::FunctionReplyObject(ifs->id());
}
void HandlerExercise::onExerciseModifyRequest(const rti::core::SampleIdentity &cookie,
const ExerciseManagement::ExerciseModifyRequest &request)
{
auto ret = exerciseModifyRequest(request);
if (ret.error())
WARNING_LOG(m_logger) << ret();
m_ddsHandler->fts()->exerciseModifyReply(cookie, ret);
}
Core::FunctionReplyObject HandlerExercise::exerciseModifyRequest(const ExerciseModifyRequest &request)
{
INFO_LOG(m_logger) << "onExerciseModifyRequest: " << request;
ObjectExercise *ifs = m_exeObjMap.value(request.id());
if (ifs == nullptr)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Exercise with id %1 not found")).arg(request.id());
if (ifs->exerciseState() == ExerciseState::Created &&
request.newState() == ExerciseState::Started)
{
// check if the running exercise has CnrChannels, which are already included in the global CnrConfigs
QStringList doubleChannelNames = doubleChannels(request.id(), 0);
if (!doubleChannelNames.isEmpty())
{
std::sort(doubleChannelNames.begin(), doubleChannelNames.end());
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "The following channel(s) are already planned in the global CnrConfig: %1")).arg(doubleChannelNames.join(QLatin1String(", ")));
}
// check if another exercise is already running, only allow N active exercises in parallel
// check if another running exercise has HLA interface on.
int iCntRunningExercises = 0;
QSet<int> linkedExerciseIds;
for (const ObjectExercise *ex : qAsConst(m_exeObjMap))
{
const auto state = ex->exerciseState();
if (ex->linkedExerciseId() == request.id())
{
doubleChannelNames = doubleChannels(ex->id(), 0);
if (!doubleChannelNames.isEmpty())
{
std::sort(doubleChannelNames.begin(), doubleChannelNames.end());
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "The following channel(s) are planned in the global CnrConfig and the linked exercise %1: %2")).arg(ex->name()).arg(doubleChannelNames.join(QLatin1String(", ")));
}
}
// ignore linked exercises
if (linkedExerciseIds.contains(ex->id()))
continue;
if (ex->linkedExerciseId() != 0)
{
linkedExerciseIds += ex->linkedExerciseId();
}
if (state == ExerciseState::Started ||
state == ExerciseState::Paused)
++iCntRunningExercises;
}
if (iCntRunningExercises >= Settings::maxStartedExercises())
return Core::FunctionReplyObject(request.id(),
tr_lts("dds", "Can not start exercise - only %1 started exercise(s) allowed")).arg(Settings::maxStartedExercises());
}
if (request.newState() == ExerciseState::Stopped)
{
int iCnt = 0;
for (const ObjectExercise *ex : qAsConst(m_exeObjMap))
{
const auto state = ex->exerciseState();
if (ex->id() != request.id() && state == ExerciseState::Stopped)
++iCnt;
}
if (Settings::maxStoppedExercises() > 0 && iCnt >= Settings::maxStoppedExercises())
return Core::FunctionReplyObject(request.id(),
tr_lts("dds", "Can not stop exercise - there are %1 stopped exercise(s) from maximal %2 allowed")).arg(iCnt).arg(Settings::maxStoppedExercises());
}
if (request.hasHlaInterface() && (request.newState() == ExerciseState::Started ||
request.newState() == ExerciseState::Paused))
{
int iCnt = 0;
for (const ObjectExercise *ex : qAsConst(m_exeObjMap))
{
if (ex->id() != request.id() && ex->data().hasHlaInterface && (ex->exerciseState() == ExerciseState::Started ||
ex->exerciseState() == ExerciseState::Paused))
++iCnt;
}
if (iCnt >= Settings::maxHlaExercises())
return Core::FunctionReplyObject(request.id(),
tr_lts("dds", "Can not modify exercise - only %1 running exercise(s) with HLA allowed")).arg(Settings::maxHlaExercises());
}
if (ifs->exerciseState() != ExerciseManagement::ExerciseState::Created)
{
if (request.linkedExerciseId() != ifs->linkedExerciseId())
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not change linked exercise when exercise is already started"));
if (request.sendToWMS() == false && ifs->data().sendToWms == true)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Exercise is already visible in WMS - can not reset this option"));
}
if (request.linkedExerciseId() == 0)
{
if (ifs->linkedExerciseId() != 0)
{
ObjectExercise *linked = m_exeObjMap.value(ifs->linkedExerciseId());
if (linked != nullptr)
{
if (!linked->setLinkedExerciseId(0))
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not update unlinked exercise"));
}
}
}
else // request.linkedExercise() != 0
{
ObjectExercise *linked = m_exeObjMap.value(request.linkedExerciseId());
if (linked == nullptr)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not find linked exercise with id %1")).arg(request.linkedExerciseId());
if (linked->linkedExerciseId() != request.id())
{
if (linked->linkedExerciseId() != 0)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "The exercise to link already has another linked exercise"));
// own state is checked above
if (linked->exerciseState() != ExerciseManagement::ExerciseState::Created)
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not link to an already running exercise"));
QStringList doubleChannelNames = doubleChannels(request.id(), request.linkedExerciseId());
if (!doubleChannelNames.isEmpty())
{
std::sort(doubleChannelNames.begin(), doubleChannelNames.end());
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Linked exercise has overlapping CnrConfig(s) for channel(s): %1")).arg(doubleChannelNames.join(QLatin1String(", ")));
}
if (!linked->setLinkedExerciseId(request.id()))
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not update linked exercise"));
}
if (linked->sendToWms() != request.sendToWMS())
{
if (!linked->setSendToWms(request.sendToWMS()))
return Core::FunctionReplyObject(request.id(), tr_lts("dds", "Can not update linked exercise"));
}
}
DEBUG_LOG(m_logger) << "Linked exercise: " << request.linkedExerciseId()
<< ", state: " << ifs->exerciseState()
<< ", new: " << request.newState();
if (request.linkedExerciseId() != 0 && ifs->exerciseState() != request.newState())
{
if (request.newState() == ExerciseManagement::ExerciseState::Started ||
request.newState() == ExerciseManagement::ExerciseState::Stopped)
{
ObjectExercise *ifsLinked = m_exeObjMap.value(request.linkedExerciseId());
if (!ifsLinked)
WARNING_LOG(m_logger) << "Linked exercise with id " << request.linkedExerciseId() << " not found!";
else
{
if (ifsLinked->exerciseState() != ExerciseManagement::ExerciseState::Paused ||
request.newState() == ExerciseManagement::ExerciseState::Stopped)
{
auto retLinked = ifsLinked->switchToState(request.newState(), request.operatorId(), request.workstationId());
if (retLinked.error())
return retLinked;
}
}
}
}
// state change to started requested? (case 'start' or 'resume')
if ((request.newState() != ifs->exerciseState() ) &&
(request.newState() == ExerciseManagement::ExerciseState::Started))
{
QString mesg;
if (!checkEquipmentNumbersOnAllExercises(request.id(), mesg))
return Core::FunctionReplyObject(request.id(),
tr_lts("dds", "Can not start exercise - equipment numbers already in use: %1 ")).arg(mesg);
}
Database::ExmExercise data(copyBaseData(request));
data.exerciseStateId = request.newState();
data.activePhaseId = request.activePhaseId();
data.operatorId = request.operatorId();
data.workstationId = request.workstationId();
return ifs->updateObject(data);
}
//void HandlerExercise::onExerciseChangeStateRequest(const rti::core::SampleIdentity &cookie,
// const ExerciseManagement::ExerciseChangeStateRequest &request)
//{
// Core::FunctionReplyObject ret;
// INFO_LOG(m_logger) << "onExerciseChangeStateRequest id = " << request.id();
// ObjectExercise *ifs = m_exeObjMap.value(request.id());
// if (ifs == nullptr)
// {
// ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "Exercise with id %1 not found")).arg(request.id());
// m_ddsHandler->fts()->exerciseChangeStateReply(cookie, ret);
// return;
// }
// DEBUG_LOG(m_logger) << "request.newState() = " << request.newState();
// ret = ifs->switchToState(request.newState());
// if (!ret.error() && ifs->linkedExerciseId() != 0)
// {
// if (request.newState() == ExerciseManagement::ExerciseState::Started ||
// request.newState() == ExerciseManagement::ExerciseState::Stopped)
// {
// ObjectExercise *ifsLinked = m_exeObjMap.value(ifs->linkedExerciseId());
// if (!ifsLinked)
// WARNING_LOG(m_logger) << "Linked exercise with id " << ifs->linkedExerciseId() << " not found!";
// else
// {
// if (ifsLinked->exerciseState() != ExerciseManagement::ExerciseState::Paused ||
// request.newState() == ExerciseManagement::ExerciseState::Stopped)
// ret = ifsLinked->switchToState(request.newState());
// }
// }
// }
// if (ret.error())
// ERROR_LOG(m_logger) << ret();
// m_ddsHandler->fts()->exerciseChangeStateReply(cookie, ret);
//}
//uint qHash(const HandlerExercise::ImageKey &key)
//{
// return ::qHash(key.m_size.height()) ^ (::qHash(key.m_size.width()) >> 2) ^ ::qHash(key.m_lowBandwidth);
//}
void HandlerExercise::onGetExerciseImageRequest(const rti::core::SampleIdentity &cookie,
const GetExerciseImageRequest &request)
{
Core::FunctionReplyObject fro;
GetExerciseImageReply reply;
int32_t exerciseId = request.exerciseId();
reply.id(exerciseId);
DEBUG_LOG(m_logger) << "onGetExerciseImageRequest id = " << exerciseId;
Database::ExmExerciseImage exmImg;
if (!m_db->selectExmExerciseImage(exerciseId, exmImg))
fro = Core::FunctionReplyObject(exerciseId, tr_lts("dds", "Exercise image with id %1 not found")).arg(exerciseId);
if (fro.error())
{
reply.id(fro.id());
reply.errorString(fro.str());
reply.errorParams(fro.args());
}
else
{
reply.imageData(LtsUtils::convert<std::vector<uint8_t>>(exmImg.image));
// TODO: reimpl optimization routines (e.g. use ImageMagick instead of GuiApplication, which won't work without an X server)
// QPixmap excPx(LtsUtils::getPixmap(exmImg.image));
// if (!excPx.isNull())
// {
// QSize newImgSize(request.maxWidth(), request.maxHeight());
// QRect requestedRect(QPoint(0, 0), newImgSize);
// QRect pictureRect(QPoint(0, 0), excPx.size());
// if (pictureRect.contains(requestedRect)) // only shrink, don't grow (which would make the image unnecessarily bigger)
// {
// if (request.lowBandwidth()) // make the image even smaller on limited bandwidth (e.g. Tablet on WiFi)
// newImgSize /= 2.5;
// excPx = excPx.scaled(newImgSize, Qt::KeepAspectRatio);
// }
// reply.imageData(LtsUtils::fromPixmap(excPx));
// }
// else
// ERROR_LOG(m_logger) << "image isNull for exercise " << exerciseId;
}
/* test behaviour on bad network speeds
QTimer::singleShot(5000, this, [this, cookie, reply]()
{*/
m_ddsHandler->fts()->getExerciseImageReply(cookie, reply); /*
});*/
}
void HandlerExercise::onExerciseImageModifyRequest(const rti::core::SampleIdentity &cookie,
const ExerciseImageModifyRequest &request)
{
qint32 exerciseId = request.id();
DEBUG_LOG(m_logger) << "onExerciseImageModifyRequest id = " << exerciseId << ", image size = " << request.image().size();
Database::ExmExerciseImage exImg;
exImg.exerciseId = exerciseId;
bool result = m_db->selectExmExerciseImage(exerciseId, exImg);
exImg.image = LtsUtils::toQByteArray(request.image());
if (result)
{
// exists => update
if (!m_db->update(exImg))
{
m_ddsHandler->fts()->exerciseImageModifyReply(cookie, Core::FunctionReplyObject(exerciseId, tr_lts("dds", "Error updating Exercise image for exercise id %1")).arg(exerciseId));
return;
}
}
else
{
// doesn't exist yet => insert
if (!m_db->insert(exImg))
{
m_ddsHandler->fts()->exerciseImageModifyReply(cookie, Core::FunctionReplyObject(exerciseId, tr_lts("dds", "Error inserting Exercise image for exercise id %1")).arg(exerciseId));
return;
}
}
m_ddsHandler->fts()->exerciseImageModifyReply(cookie, Core::FunctionReplyObject(exerciseId));
}
void HandlerExercise::onExerciseRemoveRequest(const rti::core::SampleIdentity &cookie,
const ExerciseManagement::ExerciseRemoveRequest &request)
{
Core::FunctionReplyObject ret;
ObjectExercise *ifs = m_exeObjMap.value(request.id());
if (ifs == nullptr)
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "Exercise with id %1 not found")).arg(request.id());
m_ddsHandler->fts()->exerciseRemoveReply(cookie, ret);
return;
}
ExerciseManagement::Exercise ddsData;
ddsData.id(request.id());
if (m_mainHandler->dds()->topicLockMaster()->isLocked(ddsData, 0))
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "Exercise '%1' is used by another user!")).arg(ifs->data().name);
m_mainHandler->dds()->fts()->exerciseRemoveReply(cookie, ret);
return;
}
ret = ifs->removeObject(request.force());
if (ret.error())
{
ERROR_LOG(m_logger) << ret();
}
else
{
// delete ObjectExercise (including ExerciseStateMachine)
delete ifs;
m_exeObjMap.remove(request.id());
}
m_ddsHandler->fts()->exerciseRemoveReply(cookie, ret);
return;
}
void HandlerExercise::onStartGroupReply(const Core::FunctionReplyObject &replyObject)
{
if (replyObject.error())
WARNING_LOG(m_logger) << "Error starting group: " << replyObject();
}
bool HandlerExercise::checkEquipmentNumbersOnAllExercises(int exerciseIdToBeStarted, QString &mesgOut)
{
INFO_LOG(m_logger) << LOGFUNCTION <<" START, exerciseIdTobeStarted: " << exerciseIdToBeStarted;
// only one exercise, about to start
if (m_exeObjMap.size() <= 1)
return true;
// first
// get all eqIds of exercise to be started
QSet<int> eqNoForEx;
{
std::shared_ptr<DDSConnector::DdsConnector> ddsConnectorForEx;
int domainId = m_ddsHandler->ddsConnector()->getDomainId();
ddsConnectorForEx = std::make_shared<DDSConnector::DdsConnector>(domainId, IDL::PartitionType::Exercise, exerciseIdToBeStarted);
ddsConnectorForEx->init();
ddsConnectorForEx->initSubscriber();
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtcForEx;
ppmDtcForEx = ParticipantManagement::Interface::Dtc::create(ddsConnectorForEx);
if (!ppmDtcForEx)
{
WARNING_LOG(m_logger) << LOGFUNCTION << " no ppmDtcForEx (" << exerciseIdToBeStarted << ")";
return true;
}
ppmDtcForEx->enableParticipant();
ppmDtcForEx->waitForHistoricalDataParticipant(); // <---------- waitForHistoricalDataParticipant may take a while :-(
const auto cb = [&eqNoForEx](const ParticipantManagement::Participant &data) -> void
{
if (data.equipmentNumber().is_set())
eqNoForEx.insert(data.equipmentNumber().get());
};
ppmDtcForEx->participants(cb);
}
// second:
// get ParticipantManagement::EquipmentExerciseMapping
// all eqIds of running/started exercises
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtc = m_ddsHandler->ppmDtc();
if (!ppmDtc)
{
WARNING_LOG(m_logger) << LOGFUNCTION << " no ppmDtc";
return true;
}
QMap<int, QSet<int>> usedEqNumbers;
const auto cb = [this, &usedEqNumbers, exerciseIdToBeStarted, eqNoForEx](const ParticipantManagement::EquipmentExerciseMapping &equipmentExerciseMapping) -> void
{
const int exerciseId = equipmentExerciseMapping.exerciseId();
// in case of resume exercise:
if (exerciseId == exerciseIdToBeStarted)
return;
// typically just exercises with state == Started should be published.
// but to be sure:
ObjectExercise *ifs = m_exeObjMap.value(exerciseId);
if (!ifs)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Exercise not found, exId = " << exerciseId;
return;
}
if ((ifs->exerciseState() == ExerciseState::Started) ||
(ifs->exerciseState() == ExerciseState::Paused))
{
const std::vector<ParticipantManagement::Equipment2ParticipantId> &mapping = equipmentExerciseMapping.mapping();
for (const auto &eq2p : qAsConst(mapping))
if (eqNoForEx.contains(eq2p.equipmentNumber()))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " !!! eq = " << eq2p.equipmentNumber() << " is already used in exercise =" << exerciseId;
usedEqNumbers[exerciseId].insert(eq2p.equipmentNumber());
}
}
};
ppmDtc->equipmentExerciseMappings(cb);
if (!usedEqNumbers.isEmpty())
{
// convert to msg string:
QMapIterator<int, QSet<int>> mapIt(usedEqNumbers);
while (mapIt.hasNext())
{
mapIt.next();
int exerciseId = mapIt.key();
const QSet<int> &usedEqNumbersPerExercise = mapIt.value();
if (!usedEqNumbersPerExercise.isEmpty())
{
mesgOut += QString(QStringLiteral("'%1' -> (")).arg(m_exeObjMap[exerciseId]->data().name);
QSetIterator<int> setIt(usedEqNumbersPerExercise);
while (setIt.hasNext())
{
int eqNo = setIt.next();
mesgOut += QString::number(eqNo);
if (setIt.hasNext())
mesgOut += QStringLiteral(",");
} // while
mesgOut += QStringLiteral("); ");
}
} // while
WARNING_LOG(m_logger) << LOGFUNCTION << " state change request not allowed: mesgOut:" << mesgOut;
// limit length:
if (mesgOut.size()>200)
{
mesgOut.resize(200);
mesgOut += QStringLiteral("...");
}
return false;
}
return true;
}
QStringList HandlerExercise::doubleChannels(int exerciseAId, int exerciseBId) const
{
auto cnrConfigs = m_ddsHandler->avmDtc()->cnrConfigs();
QVector<int> exerciseAChannelIds;
QVector<int> exerciseBChannelIds;
exerciseAChannelIds.reserve(cnrConfigs.size());
exerciseBChannelIds.reserve(cnrConfigs.size());
for (auto it = cnrConfigs.constBegin(); it != cnrConfigs.constEnd(); ++it)
{
int cnrExerciseId = it.value().exerciseId().is_set() ? it.value().exerciseId().get() : 0;
if (cnrExerciseId == exerciseAId)
{
exerciseAChannelIds.append(it.value().cnrChannelId());
continue;
}
if (cnrExerciseId == exerciseBId)
{
exerciseBChannelIds.append(it.value().cnrChannelId());
continue;
}
}
QStringList doubleChannelNames;
doubleChannelNames.reserve(exerciseAChannelIds.size());
for (int exerciseAChannelId : qAsConst(exerciseAChannelIds))
{
if (exerciseBChannelIds.contains(exerciseAChannelId))
doubleChannelNames.append(QString::fromStdString(m_ddsHandler->avmDtc()->cnrChannel(exerciseAChannelId).name()));
}
return doubleChannelNames;
}
} // Namespace ExerciseManagement
+121
View File
@@ -0,0 +1,121 @@
/**
* @RDE_HEADER@
* Handler for all exercise objects
* $Id$
* $HeadURL$
* $Author$
* Created by Hanna E. Rathje, SLSE, 2015-09-03
*/
#pragma once
#include <QObject>
#include <QQueue>
#include <QHash>
#include <QCache>
#include <QSize>
#include <DBAdministration/exercise_table_generator.h>
#include "dds_handler.h"
#include <ExerciseManagement/exm_db.h>
#include "logger.h"
class QTimer;
namespace ExerciseManagement
{
class MainHandler;
class ObjectExercise;
class HandlerExercise final : public QObject
, protected Interface::Fts::ExerciseCreateRequestHandler
, protected Interface::Fts::ExerciseModifyRequestHandler
, protected Interface::Fts::ExerciseRemoveRequestHandler
, protected Interface::Fts::ExerciseImageModifyRequestHandler
, protected Interface::Fts::GetExerciseImageRequestHandler
, protected ServiceManagement::Interface::Ftc::StartGroupReplyHandler
{
Q_OBJECT
public:
explicit HandlerExercise(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> &exTableGenerator,
QObject *parent = nullptr);
~HandlerExercise();
bool init();
ObjectExercise *getExerciseById(qint32 id) const;
const QHash<int, ObjectExercise*> &getExerciseMap() const { return m_exeObjMap; }
bool startGroupRequest(const ServiceManagement::GroupRequest &req);
// class ImageKey
// {
// public:
// ImageKey(const QSize &size = QSize(0, 0), bool lowBandwith = false)
// : m_lowBandwidth(lowBandwith)
// , m_size(size)
// {}
// bool m_lowBandwidth { false };
// QSize m_size;
// bool operator<(const ImageKey &o) const
// {
// if (m_size.height() != o.m_size.height())
// return m_size.height() < o.m_size.height();
// if (m_size.width() != o.m_size.width())
// return m_size.width() < o.m_size.width();
// return m_lowBandwidth < o.m_lowBandwidth;
// }
// bool operator==(const ImageKey &o) const
// {
// return (m_size == o.m_size && m_lowBandwidth == o.m_lowBandwidth);
// }
// };
private:
void onStartNextExercise();
private:
/**
* @brief HandlerExercise::hasError
* checks if there is an error in the replyObject and, if given, deletes
* the ifs pointer and logs the error
* @param replyObject
* @param ifs
* @return
*/
bool hasError(const Core::FunctionReplyObject &replyObject, ObjectExercise *ifs) const;
void onExerciseCreateRequest(const rti::core::SampleIdentity &cookie,
const ExerciseCreateRequest &request) override final;
Core::FunctionReplyObject exerciseCreateRequest(const ExerciseCreateRequest &request);
void onExerciseModifyRequest(const rti::core::SampleIdentity &cookie,
const ExerciseModifyRequest &request) override final;
Core::FunctionReplyObject exerciseModifyRequest(const ExerciseModifyRequest &request);
// void onExerciseChangeStateRequest(const rti::core::SampleIdentity &cookie,
// const ExerciseChangeStateRequest &request) override final;
void onExerciseRemoveRequest(const rti::core::SampleIdentity &cookie,
const ExerciseRemoveRequest &request) override final;
void onExerciseImageModifyRequest(const rti::core::SampleIdentity &cookie,
const ExerciseImageModifyRequest &request) override final;
void onGetExerciseImageRequest(const rti::core::SampleIdentity &cookie,
const GetExerciseImageRequest &request) override final;
void onStartGroupReply(const Core::FunctionReplyObject &replyObject) override final;
bool checkEquipmentNumbersOnAllExercises(int exerciseIdToBeStarted, QString &mesgOut);
QStringList doubleChannels(int exerciseAId, int exerciseBId) const;
private:
QHash<int, ObjectExercise*> m_exeObjMap;
MainHandler *m_mainHandler;
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DatabaseInterface>m_db;
std::shared_ptr<DBAdministration::ExerciseTableGenerator> m_exTblGen;
QTimer *m_startupExerciseTimer;
QQueue<int> m_exercisesToStart;
//QCache<ImageKey, QByteArray> m_imageCache;
NGL::INGLLogger &m_logger;
};
}
+193
View File
@@ -0,0 +1,193 @@
/**
* Handler for all exercise phase objects
* $Id$
* $HeadURL$
* $Author$
*/
#include "handler_exercise_phase.h"
#include <QDateTime>
#include <QString>
#include "dds_handler.h"
#include "exm_db.h"
#include "handler_exercise.h"
#include "object_exercise_phase.h"
#include "object_exercise.h"
#include "main_handler.h"
namespace ExerciseManagement
{
HandlerExercisePhase::HandlerExercisePhase(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db)
: Interface::Fts::ExercisePhaseCreateRequestHandler(ddsHandler->fts())
, Interface::Fts::ExercisePhaseModifyRequestHandler(ddsHandler->fts())
, Interface::Fts::ExercisePhaseRemoveRequestHandler(ddsHandler->fts())
, m_mainHandler(mainHandler)
, m_ddsHandler(ddsHandler)
, m_db(db)
, m_logger(getLogger("HandlerExercisePhase"))
{}
HandlerExercisePhase::~HandlerExercisePhase()
{
qDeleteAll(m_exercisePhaseObjMap);
}
bool HandlerExercisePhase::init()
{
bool bRet = true;
// create exercise objects
auto cbPhase = [this, &bRet](const Database::ExmExercisePhase &data)
{
ObjectExercisePhase *ifs(new ObjectExercisePhase(m_ddsHandler, m_db));
Core::FunctionReplyObject ret = ifs->createObject(data);
if (ret.error())
{
delete ifs;
ERROR_LOG(m_logger) << ret();
bRet = false;
return;
}
m_exercisePhaseObjMap.insert(ifs->getId(), ifs);
};
if (!m_db->select(cbPhase) || !bRet)
{
ERROR_LOG(m_logger) << "Error reading data from database!";
return true;
}
DEBUG_LOG(m_logger) << "m_exercisePhaseObjMap.size() = " << m_exercisePhaseObjMap.size();
return true;
}
Core::FunctionReplyObject HandlerExercisePhase::exercisePhaseCreateRequest(const ExercisePhaseCreateRequest &request)
{
Database::ExmExercisePhase newData;
newData.name = QString::fromStdString(request.name());
newData.description = QString::fromStdString(request.description());
newData.beginTime = QDateTime::fromMSecsSinceEpoch(request.beginTime());
newData.endTime = QDateTime::fromMSecsSinceEpoch(request.endTime());
newData.exerciseId = request.exerciseId();
ObjectExercisePhase *ifs(new ObjectExercisePhase(m_ddsHandler, m_db));
Core::FunctionReplyObject ret = ifs->createObject(newData);
if (ret.error())
delete ifs;
else
{
m_exercisePhaseObjMap.insert(ifs->getId(), ifs);
ret.setId(ifs->getId()); // set the returned id to the new phase id
}
return ret;
}
Core::FunctionReplyObject HandlerExercisePhase::removeAllPhasesByExerciseId(qint64 exerciseId)
{
QMutableMapIterator<int, ObjectExercisePhase *> it(m_exercisePhaseObjMap);
while (it.hasNext())
{
auto phase = it.next().value()->getData();
if (exerciseId == phase.exerciseId)
{
if (!m_ddsHandler->dts()->removeExercisePhaseByKey(phase.id))
return Core::FunctionReplyObject(phase.id, tr_lts("dds", "ExercisePhase with id %1 can not be removed from DDS")).arg(phase.id);
it.remove();
}
}
return Core::FunctionReplyObject();
}
void HandlerExercisePhase::onExercisePhaseCreateRequest(const rti::core::SampleIdentity &cookie,
const ExercisePhaseCreateRequest &request)
{
DEBUG_LOG(m_logger) << "onExercisePhaseCreateRequest " << request;
Core::FunctionReplyObject ret = exercisePhaseCreateRequest(request);
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_ddsHandler->fts()->exercisePhaseCreateReply(cookie, ret);
}
void HandlerExercisePhase::onExercisePhaseModifyRequest(const rti::core::SampleIdentity &cookie, const ExercisePhaseModifyRequest &request)
{
DEBUG_LOG(m_logger) << "onExercisePhaseModifyRequest " << request;
Core::FunctionReplyObject ret;
ObjectExercisePhase *ifs = m_exercisePhaseObjMap.value(request.id());
if (ifs == nullptr)
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "ExercisePhase with id %1 not found")).arg(request.id());
m_ddsHandler->fts()->exercisePhaseModifyReply(cookie, ret);
return;
}
Database::ExmExercisePhase data;
data.id = request.id();
data.name = QString::fromStdString(request.name());
data.description = QString::fromStdString(request.description());
data.beginTime = QDateTime::fromMSecsSinceEpoch(request.beginTime());
data.endTime = QDateTime::fromMSecsSinceEpoch(request.endTime());
data.exerciseId = request.exerciseId();
ret = ifs->updateObject(data);
if (ret.error())
ERROR_LOG(m_logger) << ret();
m_ddsHandler->fts()->exercisePhaseModifyReply(cookie, ret);
}
void HandlerExercisePhase::onExercisePhaseRemoveRequest(const rti::core::SampleIdentity &cookie, const Core::RemoveElementRequest &request)
{
DEBUG_LOG(m_logger) << "onExercisePhaseRemoveRequest " << request;
Core::FunctionReplyObject ret;
ObjectExercisePhase *ifs = m_exercisePhaseObjMap.value(request.id());
if (ifs == nullptr)
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "ExercisePhase with id %1 not found")).arg(request.id());
m_ddsHandler->fts()->exercisePhaseRemoveReply(cookie, ret);
return;
}
// check if phase is active phase in corresponding exercise
ObjectExercise *exercise = m_mainHandler->exerciseHandler()->getExerciseById(ifs->getData().exerciseId);
if (exercise->data().activePhaseId == request.id())
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "ExercisePhase with id %1 is current phase in exercise")).arg(request.id());
m_ddsHandler->fts()->exercisePhaseRemoveReply(cookie, ret);
return;
}
ExerciseManagement::ExercisePhase ddsData;
ddsData.id(request.id());
if (m_mainHandler->dds()->topicLockMaster()->isLocked(ddsData, 0))
{
ret = Core::FunctionReplyObject(request.id(), tr_lts("dds", "ExercisePhase '%1' is used by another user!")).arg(ifs->getData().name);
m_mainHandler->dds()->fts()->exercisePhaseRemoveReply(cookie, ret);
return;
}
ret = ifs->removeObject();
if (ret.error())
ERROR_LOG(m_logger) << ret();
else
{
// delete ObjectExercise (including ExerciseStateMachine)
delete ifs;
m_exercisePhaseObjMap.remove(request.id());
}
m_ddsHandler->fts()->exercisePhaseRemoveReply(cookie, ret);
}
} // namespace ExerciseManagement
+54
View File
@@ -0,0 +1,54 @@
/**
* @RDE_HEADER@
* Handler for all exercise phase objects
* $Id$
* $HeadURL$
* $Author$
* created by Philipp Dassow, SLEA, 2017-04-06
*/
#pragma once
#include <QMap>
#include "logger.h"
#include "exm_fts.h"
namespace ExerciseManagement
{
class DdsHandler;
class DatabaseInterface;
class MainHandler;
class ObjectExercisePhase;
class HandlerExercisePhase final
: protected Interface::Fts::ExercisePhaseCreateRequestHandler
, protected Interface::Fts::ExercisePhaseModifyRequestHandler
, protected Interface::Fts::ExercisePhaseRemoveRequestHandler
{
public:
HandlerExercisePhase(MainHandler *mainHandler,
const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db);
~HandlerExercisePhase();
bool init();
Core::FunctionReplyObject exercisePhaseCreateRequest(const ExercisePhaseCreateRequest &request);
Core::FunctionReplyObject removeAllPhasesByExerciseId(qint64 exerciseId);
private:
void onExercisePhaseCreateRequest(const rti::core::SampleIdentity &cookie,
const ExercisePhaseCreateRequest &request) override final;
void onExercisePhaseModifyRequest(const rti::core::SampleIdentity &cookie,
const ExercisePhaseModifyRequest &request) override final;
void onExercisePhaseRemoveRequest(const rti::core::SampleIdentity &cookie,
const Core::RemoveElementRequest &request) override final;
private:
QMap<int, ObjectExercisePhase *> m_exercisePhaseObjMap;
MainHandler *m_mainHandler;
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DatabaseInterface> m_db;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseManagement
+87
View File
@@ -0,0 +1,87 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# Service: ExerciseManagement
###########################################################
#log4j.logger.EXM=DEBUG
#log4j.logger.EXM.main=DEBUG
#log4j.logger.EXM.DbAdministration=DEBUG
#log4j.logger.EXM.DdsHandler=DEBUG
#log4j.logger.EXM.ExerciseStateMachine=DEBUG
#log4j.logger.EXM.ExerciseStateManager=DEBUG
#log4j.logger.EXM.HandlerExercise=DEBUG
#log4j.logger.EXM.HandlerExercisePhase=DEBUG
#log4j.logger.EXM.ObjectExercise=DEBUG
#log4j.logger.EXM.ObjectExercisePhase=DEBUG
#log4j.logger.EXM.State=DEBUG
# File
###########################################################
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
+36
View File
@@ -0,0 +1,36 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace ExerciseManagement
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "EXM.";
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 ExerciseManagement
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for ExerciseManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "exm_export.h"
namespace ExerciseManagement
{
using LOGGER_T = NGL::INGLLogger &;
EXM_COMMON_EXPORT LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace ExerciseManagement
+43
View File
@@ -0,0 +1,43 @@
/**
* @RDE_HEADER@
* main for ExerciseManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglcoreapplication.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include "main_handler.h"
#include "logger.h"
#include "settings.h"
int main(int argc, char **argv)
{
NGLCoreApplication app(argc, argv);
LtsCommandLineParser::LtsCommandLineParser options;
if (!options.init())
return -1;
app.createLogger("EXM", options.exerciseId());
ExerciseManagement::Settings::setIniFile(options.iniFile());
int iRet = -3;
{
std::unique_ptr<ExerciseManagement::MainHandler> mainHandler(new ExerciseManagement::MainHandler);
if (!mainHandler->init(options.domainId()))
return iRet;
STARTUP_LOG(app.baseLogger());
iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
}
return iRet;
}
+148
View File
@@ -0,0 +1,148 @@
/**
* MainHandler for ExerciseManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "main_handler.h"
#include <DBAdministration/exercise_table_generator.h>
#include "handler_exercise.h"
#include "handler_exercise_phase.h"
#include "settings.h"
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/Core/pacemaker.h>
#include <ngltools/ngltools_filesystem.h>
namespace ExerciseManagement
{
MainHandler::MainHandler(QObject *parent)
: QObject(parent)
, m_logger(getLogger("MainHandler"))
{}
MainHandler::~MainHandler()
{
m_handlerExercisePhases = nullptr;
m_handlerExercise = nullptr;
m_websocketHandler = nullptr;
m_exTblGen = nullptr;
m_dbHandler = nullptr;
m_ddsHandler = nullptr;
}
bool MainHandler::init(int domainId)
{
// initialize DDS
m_ddsHandler = std::make_shared<DdsHandler>(domainId);
if (!m_ddsHandler->init())
{
ERROR_LOG(m_logger) << "Initialisation ddsHandler failed.";
return false;
}
// initialize database
m_dbHandler = std::make_shared<DbHandler>();
if (!m_dbHandler->init(0, Settings::settings()))
{
ERROR_LOG(m_logger) << "Initialisation dbHandler failed.";
return false;
}
// initialize DbAdministration
if (!initDbAdminstration())
{
ERROR_LOG(m_logger) << "Initialisation dbAdministration failed.";
return false;
}
//create all global schemas + tables
m_exTblGen->executeSql();
// read project specific data from database
// publish project specific data
// initialize the specific handler
if (!initExerciseHandler())
return false;
if (!initExercisePhaseHandler())
return false;
if (!initWebSocketHandler())
return false;
m_ddsHandler->pacemaker()->setNewState(Core::RunningState::Running);
return true;
}
bool MainHandler::initDbAdminstration()
{
// xmlPath
const QString xmlPath = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("xml"));
m_exTblGen = std::make_shared<DBAdministration::ExerciseTableGenerator>(getLogger("ExerciseTableGenerator"), xmlPath);
connect(m_exTblGen.get(), &DBAdministration::ExerciseTableGenerator::aliveSignal,
this, &MainHandler::sendKeepAlive);
DEBUG_LOG(m_logger) << "Initialise DbAdmin ";
if (!m_exTblGen->init(m_dbHandler->db()->database(),
m_dbHandler->db()->schemaName()))
{
ERROR_LOG(m_logger) << "Initialisation dbAdminHandler failed.";
return false;
}
return true;
}
bool MainHandler::initExerciseHandler()
{
m_handlerExercise.reset(new HandlerExercise(this, m_ddsHandler, m_dbHandler->db(), m_exTblGen));
DEBUG_LOG(m_logger) << "Initialise ExerciseHandler ";
if (!m_handlerExercise->init())
{
ERROR_LOG(m_logger) << "Initialisation ExerciseHandler failed.";
return false;
}
return true;
}
bool MainHandler::initExercisePhaseHandler()
{
m_handlerExercisePhases.reset(new HandlerExercisePhase(this, m_ddsHandler, m_dbHandler->db()));
DEBUG_LOG(m_logger) << "Initialise ExercisePhaseHandler ";
if (!m_handlerExercisePhases->init())
{
ERROR_LOG(m_logger) << "Initialisation ExercisePhaseHandler failed.";
return false;
}
return true;
}
bool MainHandler::initWebSocketHandler()
{
m_websocketHandler = std::make_shared<WebSocketInterface::WebSocketInterfaceHandler>(this);
DEBUG_LOG(m_logger) << "Initialise WebSocketInterfaceHandler";
if (!m_websocketHandler->init())
{
ERROR_LOG(m_logger) << "Initialisation WebSocketInterfaceHandler failed.";
return false;
}
m_ddsHandler->pacemaker()->addPort(m_websocketHandler->webSocketPort(), Core::PortType::WebSocketServer);
return true;
}
void MainHandler::sendKeepAlive()
{
m_ddsHandler->pacemaker()->publishState();
}
} // namespace ExerciseManagement
+59
View File
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include <memory>
#include "dds_handler.h"
#include "exm_db.h"
#include "logger.h"
#include "WebSocketInterface/websocket_interface_handler.h"
#include <ngldb/db_service_handler.h>
namespace DBAdministration
{
class ExerciseTableGenerator;
}
namespace ExerciseManagement
{
using DbHandler = NGLDB::DbServiceHandler<DatabaseInterface>;
class HandlerExercise;
class HandlerExercisePhase;
class MainHandler : public QObject
{
Q_OBJECT
public:
explicit MainHandler(QObject *parent = nullptr);
~MainHandler();
bool init(int domainId);
std::shared_ptr<HandlerExercise> exerciseHandler () const { return m_handlerExercise; }
std::shared_ptr<HandlerExercisePhase> exercisePhaseHandler () const{ return m_handlerExercisePhases; }
std::shared_ptr<DatabaseInterface> db() const { return m_dbHandler->db(); }
std::shared_ptr<DdsHandler> dds() const { return m_ddsHandler; }
private:
bool initDbAdminstration();
bool initExerciseHandler();
bool initExercisePhaseHandler();
bool initWebSocketHandler();
void sendKeepAlive();
private:
std::shared_ptr<HandlerExercise> m_handlerExercise;
std::shared_ptr<HandlerExercisePhase> m_handlerExercisePhases;
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DbHandler> m_dbHandler;
std::shared_ptr<DBAdministration::ExerciseTableGenerator> m_exTblGen;
std::shared_ptr<WebSocketInterface::WebSocketInterfaceHandler> m_websocketHandler;
NGL::INGLLogger &m_logger;
};
} // namespace ExerciseManagement
+413
View File
@@ -0,0 +1,413 @@
/**
* Handler for one indirect fire single object
* $Id$
* $HeadURL$
* $Author$
*/
#include "object_exercise.h"
#include "convert_db_to_dds.h"
#include "dds_handler.h"
#include "exercise_state_machine.h"
#include "object_exercise_phase.h"
#include "handler_exercise.h"
#include "settings.h"
#include <DBAdministration/copy_exercise.h>
#include <DBAdministration/exercise_table_generator.h>
#include <Common/ProjectConfig/project_defines.h>
#include <QDateTime>
namespace ExerciseManagement
{
ObjectExercise::ObjectExercise(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> &exTblGen,
HandlerExercise *exHandler)
: QObject(exHandler)
, m_ddsHandler(ddsHandler)
, m_db(db)
, m_exTblGen(exTblGen)
, m_exerciseHandler(exHandler)
, m_copyExercise(nullptr)
, m_logger(getLogger("ObjectExercise"))
{
m_stateMachine = new ExerciseStateMachine(this);
}
ObjectExercise::~ObjectExercise()
{
delete m_copyExercise;
delete m_stateMachine;
}
bool ObjectExercise::isValid() const
{
return m_data.id > 0;
}
bool ObjectExercise::isStopped() const
{
return ( isValid() && m_data.exerciseStateId == ExerciseManagement::ExerciseState::Stopped);
}
bool ObjectExercise::isRunning() const
{
return (m_data.exerciseStateId == ExerciseManagement::ExerciseState::Started);
}
Core::FunctionReplyObject ObjectExercise::createObject(const Database::ExmExercise &data, bool forceInsert)
{
m_data = data;
fixupTimeZone();
if (m_data.id <= 0 || forceInsert)
{
m_data.exerciseStateId = ExerciseManagement::ExerciseState::Created;
m_data.created = QDateTime::currentDateTimeUtc();
if (!m_db->insert(m_data))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Error inserting exercise into database: " << errText;
return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting ExmExercise-data into database: %1")).arg(errText);
}
// Id is serial -> will be generated by db => inside m_data after db-insert
DEBUG_LOG(m_logger) << "New exerciseId: " << m_data.id;
// Create exercise-schema
if (!m_exTblGen->createExerciseSchema(m_data.id))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error creating database schema for exercise with id %1")).arg(m_data.id);
// Create tables/views/indexes for each service
if (!m_exTblGen->executeSql(m_data.id))
{
// Remove already created schema & user
m_exTblGen->dropExerciseSchema(m_data.id, false);
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error creating database tables/indexes/views for exercise with id %1")).arg(m_data.id);
}
}
// Additional writing of sqlFiles
if (m_logger.isDebugEnabled())
m_exTblGen->writeExerciseServicesSql(m_data.id);
INFO_LOG(m_logger) << "Exercise created sucessfully.";
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
void ObjectExercise::initStateMachine()
{
// Start ExerciseStateMachine
m_stateMachine->init();
INFO_LOG(m_logger) << "StateMachine: currentState" << m_stateMachine->getCurrentState();
}
Core::FunctionReplyObject ObjectExercise::updateObject(const Database::ExmExercise &data)
{
if ( !isValid() || !isEditable())
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Trying to update an already removed ExmExercise object"));
m_data.id = data.id;
m_data.name = data.name;
m_data.beginTime = data.beginTime;
m_data.endTime = data.endTime;
m_data.startTime = data.startTime;
m_data.stopTime = data.stopTime;
m_data.trainingAreaId = data.trainingAreaId;
m_data.radioGroupId = data.radioGroupId;
m_data.activePhaseId = data.activePhaseId;
m_data.symbolFillTypeId = data.symbolFillTypeId;
m_data.forces = data.forces;
m_data.description = data.description;
m_data.defaultTimeZone = data.defaultTimeZone;
m_data.linkedExerciseId = data.linkedExerciseId;
m_data.sendToWms = data.sendToWms;
m_data.transferred = data.transferred;
m_data.hasHlaInterface = data.hasHlaInterface;
m_data.operatorId = data.operatorId;
m_data.workstationId = data.workstationId;
fixupTimeZone();
INFO_LOG(m_logger) << "Trying to updateObject ExerciseData with id = " << data.id;
if (!m_db->update(m_data))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << data.id << ": " << m_db->getLastError().databaseText();
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating ExmExercise-data in database: %1")).arg(errText);
}
// Change state if different
if (m_data.exerciseStateId != data.exerciseStateId)
{
Core::FunctionReplyObject retOb = switchToState(data.exerciseStateId, data.operatorId, data.workstationId);
if (retOb.error())
return retOb;
}
INFO_LOG(m_logger) << "ExerciseData changed sucessfully.";
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectExercise::removeObject(bool force)
{
if (isRunning() && !force)
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise still running"));
// Change State to terminated
if (!setExerciseState(ExerciseManagement::ExerciseState::Terminated))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "ExerciseState cannot be changed to terminated"));
if (!m_exTblGen->dropExerciseSchema(m_data.id, false))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise %1 (%2) can not be removed")).arg(m_data.name).arg(m_data.id);
if (!m_db->removeExmExercise(m_data.id))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Cannot remove exercise id = " << m_data.id << "from database: " << errText;
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise %1 (%2) can not be removed: %3")).arg(m_data.name).arg(m_data.id).arg(errText);
}
m_ddsHandler->dts()->removeExerciseByKey(m_data.id);
sendStateToServiceManagement(m_data.exerciseStateId); // terminated
INFO_LOG(m_logger) << "Exercise dropped sucessfully.";
return Core::FunctionReplyObject(m_data.id);
}
bool ObjectExercise::setLinkedExerciseId(int newLinkedExerciseId)
{
m_data.linkedExerciseId = newLinkedExerciseId;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << m_data.id << " : " << m_db->getLastError().text();
return false;
}
onPublishData();
return true;
}
bool ObjectExercise::setSendToWms(bool bSend)
{
m_data.sendToWms = bSend;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << m_data.id << " : " << m_db->getLastError().text();
return false;
}
onPublishData();
return true;
}
bool ObjectExercise::setExerciseState(const ExerciseState &exerciseState)
{
if (m_data.exerciseStateId == exerciseState)
return true;
if (!isValid() || !isEditable())
{
WARNING_LOG(m_logger) << "Trying to update an already removed ExmExercise object";
return false;
}
DEBUG_LOG(m_logger) << "## m_data.exerciseStateId = " << m_data.exerciseStateId;
DEBUG_LOG(m_logger) << "## exerciseState = " << exerciseState;
m_data.exerciseStateId = exerciseState;
if (m_data.exerciseStateId == ExerciseManagement::ExerciseState::Stopped ||
m_data.exerciseStateId == ExerciseManagement::ExerciseState::Terminated ||
m_data.exerciseStateId == ExerciseManagement::ExerciseState::Unknown)
m_data.radioGroupId.clear();
QDateTime changed = QDateTime::currentDateTimeUtc();
if (m_data.exerciseStateId == ExerciseState::Started)
m_data.startTime = changed;
if (m_data.exerciseStateId == ExerciseState::Stopped)
m_data.stopTime = changed;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Error updating ExmExercise-data in database (exerciseState) for exercise id = " << m_data.id << " : " << m_db->getLastError().text();;
return false;
}
Database::ExmExerciseStateChange stateChange;
stateChange.exerciseId = m_data.id;
stateChange.exerciseStateId = m_data.exerciseStateId;
stateChange.operatorId = m_data.operatorId;
stateChange.workstationId = m_data.workstationId;
stateChange.changedAt = changed.currentMSecsSinceEpoch();
// record exerciseStateChange
if (!m_db->insert(stateChange))
WARNING_LOG(m_logger) << "Recording change of exerciseState failed: " << m_db->getLastError().text();;
// Publish Data via DTS
onPublishData();
return true;
}
void ObjectExercise::onPublishData()
{
INFO_LOG(m_logger) << "Publish ExmExercise-data to dds (" << m_data.id << ", " << m_data.name << ") ...";
if (!m_ddsHandler->dts()->publish(convert(m_data)))
ERROR_LOG(m_logger) << "Error publishing ExmExercise-data to dds";
}
void ObjectExercise::fixupTimeZone()
{
if (m_data.defaultTimeZone.isEmpty())
m_data.defaultTimeZone = QLatin1Char('Z'); // zulu
const char tz = m_data.defaultTimeZone.at(0).toLatin1();
if (tz < 'A' || tz == 'J' || tz > 'Z')
m_data.defaultTimeZone = QLatin1Char('Z'); // zulu
}
Core::FunctionReplyObject ObjectExercise::switchToState(const ExerciseState &newExerciseState, int operatorId, int workstationId)
{
Core::FunctionReplyObject ret;
INFO_LOG(m_logger) << "-------------- switchToState: " << newExerciseState
<< ", currentState = " << m_stateMachine->getCurrentState();
m_data.operatorId = operatorId;
m_data.workstationId = workstationId;
bool ok = m_stateMachine->switchToState(newExerciseState);
if (!ok)
{
ret = Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error switching to state %1")).arg(newExerciseState.underlying());
}
NOTICE_LOG(m_logger) << "-------------- after switch: currentState" << m_stateMachine->getCurrentState();
NOTICE_LOG(m_logger) << "-> OperatorId: " << m_data.operatorId << ", workstationId: " << m_data.workstationId;
return ret;
}
static int s_requestId = 0;
bool ObjectExercise::sendStateToServiceManagement(ExerciseState state)
{
Core::ServiceGroupType sgt = Core::ServiceGroupType::Core; // invalid here
switch (state.underlying())
{
case ExerciseState::Created:
sgt = Core::ServiceGroupType::ExercisePreparation;
break;
case ExerciseState::Started:
sgt = Core::ServiceGroupType::ExerciseExecution;
break;
case ExerciseState::Paused:
sgt = Core::ServiceGroupType::ExercisePaused;
break;
case ExerciseState::Stopped:
sgt = Core::ServiceGroupType::ExerciseAfterActionReview;
break;
case ExerciseState::Terminated:
sgt = Core::ServiceGroupType::Master; // Master + ExerciseId != 0 -> stopp all groups for this exercise
break;
case ExerciseState::Unknown:
break;
}
if (sgt == Core::ServiceGroupType::Core)
return false;
if (sgt != Core::ServiceGroupType::Master)
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Trying to enable service group " << sgt;
else
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Trying to stop all services";
// if there is an old group, stop them
ServiceManagement::GroupRequest request(sgt,
m_ddsHandler->ddsConnector()->getDomainId(),
m_data.id,
0,
++s_requestId,
{});
#ifndef EXM_OBJECT_EXERCISE_NO_HANDLER
if (!m_exerciseHandler || !m_exerciseHandler->startGroupRequest(request))
{
ERROR_LOG(m_logger) << "Ex " << m_data.id << " - Failed to start group " << sgt;
return false;
}
#endif
if (sgt != Core::ServiceGroupType::Master)
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Sent start group " << sgt;
else
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Sent stop all services";
return true;
}
bool ObjectExercise::transferExerciseToEvaluationDb()
{
if (m_data.transferred)
return true;
if (!m_copyExercise)
m_copyExercise = new DBAdministration::CopyExercise(m_logger, DBAdministration::CopyExercise::CopyForEvaluation, this);
connect(m_copyExercise, &DBAdministration::CopyExercise::copyDbFinished,
this, &ObjectExercise::onTransferDbFinished);
connect(m_copyExercise, &DBAdministration::CopyExercise::copyDbFailed,
this, &ObjectExercise::onErrorOccured);
if (!m_copyExercise->init(m_data.id,
Settings::serverDatabaseHost(),
Settings::serverDatabaseName(),
Settings::evaluationDatabaseHost(),
Settings::evaluationDatabaseName()) ||
!m_copyExercise->doCopy())
{
WARNING_LOG(m_logger) << "Could not backup exercise database to evaluation server";
return false;
}
NOTICE_LOG(m_logger) << "Ex " << m_data.id << " - Started copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
return true;
}
void ObjectExercise::onTransferDbFinished()
{
NOTICE_LOG(m_logger) << "Ex " << m_data.id << " - Finished copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
m_data.transferred = true;
if (!m_db->update(m_data))
ERROR_LOG(m_logger) << "Error updating ExmExercise-data (transferred) in database (exerciseState) for exercise id = " << m_data.id << " : " << m_db->getLastError().text();;
delete m_copyExercise;
m_copyExercise = nullptr;
//publish because we changed transferred
onPublishData();
}
void ObjectExercise::onErrorOccured()
{
WARNING_LOG(m_logger) << "Ex " << m_data.id << " - Copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
delete m_copyExercise;
m_copyExercise = nullptr;
}
} // Namespace ExerciseManagement
+87
View File
@@ -0,0 +1,87 @@
/**
* Exercise object
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include "dds_handler.h"
#include <DDS/Core/function_reply_object.h>
// Generated
#include <ExerciseManagement/exm_db.h>
namespace DBAdministration
{
class CopyExercise;
class ExerciseTableGenerator;
}
namespace ExerciseManagement
{
class DatabaseInterface;
class ExerciseStateMachine;
class HandlerExercise;
class ObjectExercise final : public QObject
{
Q_OBJECT
public:
explicit ObjectExercise(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> &exTblGen,
HandlerExercise *exHandler);
~ObjectExercise();
Core::FunctionReplyObject createObject(const Database::ExmExercise &data, bool forceInsert = false);
Core::FunctionReplyObject updateObject(const Database::ExmExercise &data);
Core::FunctionReplyObject removeObject(bool force);
bool setLinkedExerciseId(int newLinkedExerciseId);
bool setSendToWms(bool bSend);
void initStateMachine();
qint32 id() const { return m_data.id; }
ExerciseState exerciseState() const { return m_data.exerciseStateId; }
int linkedExerciseId() const { return m_data.linkedExerciseId; }
QString name() const { return m_data.name; }
bool sendToWms() const { return m_data.sendToWms; }
// for updating (when part of the data is changed)
const Database::ExmExercise &data() const { return m_data; }
// Used for Unittest (getting the signals)
ExerciseStateMachine* getStateMachine() { return m_stateMachine; }
// Used by ExerciseStateManager (only internal)
bool setExerciseState(const ExerciseState &exerciseState);
// Used by Client
Core::FunctionReplyObject switchToState(const ExerciseState &newExerciseState, int operatorId, int workstationId);
// TODO: implement
bool isEditable() const{ return true; }
bool isValid() const;
bool isRunning() const;
bool isStopped() const;
bool sendStateToServiceManagement(ExerciseState state);
bool transferExerciseToEvaluationDb();
private:
void onPublishData();
void fixupTimeZone();
void onErrorOccured();
void onTransferDbFinished();
private:
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DatabaseInterface> m_db;
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> m_exTblGen;
Database::ExmExercise m_data;
ExerciseStateMachine *m_stateMachine;
HandlerExercise *m_exerciseHandler;
DBAdministration::CopyExercise *m_copyExercise;
NGL::INGLLogger &m_logger;
};
}
+99
View File
@@ -0,0 +1,99 @@
/**
* Exercise phase object
* $Id$
* $HeadURL$
* $Author$
* created by Philipp Dassow, SLEA, 2017-04-05
*/
#include "object_exercise_phase.h"
#include "convert_db_to_dds.h"
#include "dds_handler.h"
#include "logger.h"
namespace ExerciseManagement
{
ObjectExercisePhase::ObjectExercisePhase(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db)
: m_ddsHandler(ddsHandler)
, m_db(db)
, m_logger(getLogger("ObjectExercisePhase"))
{}
ObjectExercisePhase::~ObjectExercisePhase ()
{}
Core::FunctionReplyObject ObjectExercisePhase::createObject(const Database::ExmExercisePhase &data)
{
m_data = data;
if (m_data.id <= 0)
{
if (!m_db->insert(m_data))
{
return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting ExmExercisePhase-data into database"));
}
// Id is serial -> will be generated by db => inside m_data after db-insert
DEBUG_LOG(m_logger) << "New exercisePhaseId: " << m_data.id;
}
INFO_LOG(m_logger) << "ExercisePhase created sucessfully.";
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectExercisePhase::updateObject(const Database::ExmExercisePhase &data)
{
if (!isValid())
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Trying to update an already removed Exercise Phase object"));
m_data = data;
INFO_LOG(m_logger) << "Trying to updateObject ExercisePhaseData with id = " << data.id;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Failed to update ExercisePhaseData with id = " << data.id;
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating ExmExercisePhase-data with id %1 in database")).arg(data.id);
}
INFO_LOG(m_logger) << "ExercisePhaseData changed sucessfully.";
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectExercisePhase::removeObject()
{
if (!m_db->removeExmExercisePhase(m_data.id))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "ExercisePhase with id %1 can not be removed from DB")).arg(m_data.id);
if (!m_ddsHandler->dts()->removeExercisePhaseByKey(m_data.id))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "ExercisePhase %1 can not be removed from DDS")).arg(m_data.id);
return Core::FunctionReplyObject(m_data.id);
}
bool ObjectExercisePhase::isValid() const
{
return m_data.id > 0;
}
void ObjectExercisePhase::onPublishData()
{
INFO_LOG(m_logger) << "Publish ExmExercisePhase-data to dds (" << m_data.id << ", " << m_data.name << ") ...";
if (!m_ddsHandler->dts()->publish(convert(m_data)))
ERROR_LOG(m_logger) << "Error publishing ExmExercisePhase-data to dds";
}
} // namespace ExerciseManagement
+45
View File
@@ -0,0 +1,45 @@
/**
* @RDE_HEADER@
* Exercise phase object
* $Id$
* $HeadURL$
* $Author$
* created by Philipp Dassow, SLEA, 2017-04-05
*/
#pragma once
#include <DDS/Core/function_reply_object.h>
// Generated
#include <ExerciseManagement/exm_db.h>
namespace ExerciseManagement
{
class DdsHandler;
class ObjectExercisePhase
{
public:
ObjectExercisePhase(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db);
~ObjectExercisePhase();
Core::FunctionReplyObject createObject(const Database::ExmExercisePhase &data);
Core::FunctionReplyObject updateObject(const Database::ExmExercisePhase &data);
Core::FunctionReplyObject removeObject();
qint32 getId() const { return m_data.id; }
const Database::ExmExercisePhase &getData() const { return m_data; }
bool isValid() const;
protected:
void onPublishData();
private:
std::shared_ptr<DdsHandler> m_ddsHandler;
std::shared_ptr<DatabaseInterface> m_db;
Database::ExmExercisePhase m_data;
NGL::INGLLogger &m_logger;
};
} // namepace ExerciseManagement
+112
View File
@@ -0,0 +1,112 @@
/**
* Settings for ExerciseManagement
* $Id$
* $HeadURL$
* $Author$
*/
#include "settings.h"
#include <QtCore/QSettings>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
#include "LtsUtilsCore/lts_portrange.h"
namespace ExerciseManagement
{
class NGLSettingsClass : public NGL::Settings
{
public:
NGLSettingsClass()
: NGL::Settings(s_iniFile.isEmpty() ? NGL::findProjectConfigFile() : s_iniFile)
{}
static void setIniFile(const QString &fn)
{
s_iniFile = fn;
}
static QString s_iniFile;
};
QString NGLSettingsClass::s_iniFile;
Q_GLOBAL_STATIC(NGLSettingsClass, s)
//-----------------------------------------------------------------------------
NGL::Settings *Settings::settings()
{
return s();
}
//-----------------------------------------------------------------------------
void Settings::setIniFile(const QString &ini)
{
NGLSettingsClass::setIniFile(ini);
}
int Settings::maxStartedExercises()
{
int ret = s()->settings()->value(QStringLiteral("ExerciseManagement/maxStartedExercises"), -1).toInt();
if (ret < 1)
ret = 1;
return ret;
}
int Settings::maxPreparedExercises()
{
int ret = s()->settings()->value(QStringLiteral("ExerciseManagement/maxPreparedExercises"), -1).toInt();
if (ret < 1)
ret = 4;
return ret;
}
int Settings::maxStoppedExercises()
{
int ret = s()->settings()->value(QStringLiteral("ExerciseManagement/maxStoppedExercises"), -1).toInt();
if (ret < 1)
ret = 5;
return ret;
}
int Settings::maxHlaExercises()
{
int ret = s()->settings()->value(QStringLiteral("ExerciseManagement/maxHlaExercises"), -1).toInt();
if (ret < 1)
ret = 1;
return ret;
}
bool Settings::enableExerciseTerminatedToStopped()
{
bool ret = s()->settings()->value(QStringLiteral("ExerciseManagement/enableExerciseTerminatedToStopped"), 0).toBool();
return ret;
}
//-----------------------------------------------------------------------------
QString Settings::serverDatabaseHost()
{
return s()->databaseHostname();
}
//-----------------------------------------------------------------------------
QString Settings::serverDatabaseName()
{
return s()->databaseName();
}
//-----------------------------------------------------------------------------
QString Settings::evaluationDatabaseHost()
{
return s()->settings()->value(QStringLiteral("EvaluationDb/DatabaseHostname"), QStringLiteral("EXCADB01")).toString();
}
//-----------------------------------------------------------------------------
QString Settings::evaluationDatabaseName()
{
return s()->settings()->value(QStringLiteral("EvaluationDb/DatabaseName"), QStringLiteral("dbAdmin")).toString();
}
//-----------------------------------------------------------------------------
QPair<quint16, quint16> Settings::getWebSocketServerPortRange(int type)
{
return LtsUtils::fromQPoint(settings()->settings()->value(QString(QStringLiteral("ExerciseManagement/webSocketServerPortRange/%1")).arg(type), QPoint()).toPoint());
}
} // namespace ExerciseManagement
+37
View File
@@ -0,0 +1,37 @@
/**
* Settings for ExerciseManagement
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QString>
#include <QPair>
namespace NGL
{
class Settings;
}
namespace ExerciseManagement
{
namespace Settings
{
//!< The real NGL::Settings - object
NGL::Settings *settings();
void setIniFile(const QString &ini);
int maxStartedExercises();
int maxPreparedExercises();
int maxStoppedExercises();
int maxHlaExercises();
bool enableExerciseTerminatedToStopped();
QString serverDatabaseHost();
QString serverDatabaseName();
QString evaluationDatabaseHost();
QString evaluationDatabaseName();
QPair<quint16, quint16> getWebSocketServerPortRange(int type);
}
} // namespace ExerciseManagement