Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+186
@@ -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)
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
+93
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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:
|
||||
+90
@@ -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
|
||||
+87
@@ -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
|
||||
+79
@@ -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
|
||||
+78
@@ -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
|
||||
+69
@@ -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
|
||||
+69
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
Executable
+52
@@ -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
@@ -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<octet>"/>
|
||||
</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<octet>"/>
|
||||
</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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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:
|
||||
Executable
+36
@@ -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
|
||||
Executable
+23
@@ -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
|
||||
Executable
+43
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
Executable
+112
@@ -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
|
||||
Executable
+37
@@ -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
|
||||
Reference in new issue
Block a user