Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+413
View File
@@ -0,0 +1,413 @@
/**
* Handler for one indirect fire single object
* $Id$
* $HeadURL$
* $Author$
*/
#include "object_exercise.h"
#include "convert_db_to_dds.h"
#include "dds_handler.h"
#include "exercise_state_machine.h"
#include "object_exercise_phase.h"
#include "handler_exercise.h"
#include "settings.h"
#include <DBAdministration/copy_exercise.h>
#include <DBAdministration/exercise_table_generator.h>
#include <Common/ProjectConfig/project_defines.h>
#include <QDateTime>
namespace ExerciseManagement
{
ObjectExercise::ObjectExercise(const std::shared_ptr<DdsHandler> &ddsHandler,
const std::shared_ptr<DatabaseInterface> &db,
const std::shared_ptr<DBAdministration::ExerciseTableGenerator> &exTblGen,
HandlerExercise *exHandler)
: QObject(exHandler)
, m_ddsHandler(ddsHandler)
, m_db(db)
, m_exTblGen(exTblGen)
, m_exerciseHandler(exHandler)
, m_copyExercise(nullptr)
, m_logger(getLogger("ObjectExercise"))
{
m_stateMachine = new ExerciseStateMachine(this);
}
ObjectExercise::~ObjectExercise()
{
delete m_copyExercise;
delete m_stateMachine;
}
bool ObjectExercise::isValid() const
{
return m_data.id > 0;
}
bool ObjectExercise::isStopped() const
{
return ( isValid() && m_data.exerciseStateId == ExerciseManagement::ExerciseState::Stopped);
}
bool ObjectExercise::isRunning() const
{
return (m_data.exerciseStateId == ExerciseManagement::ExerciseState::Started);
}
Core::FunctionReplyObject ObjectExercise::createObject(const Database::ExmExercise &data, bool forceInsert)
{
m_data = data;
fixupTimeZone();
if (m_data.id <= 0 || forceInsert)
{
m_data.exerciseStateId = ExerciseManagement::ExerciseState::Created;
m_data.created = QDateTime::currentDateTimeUtc();
if (!m_db->insert(m_data))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Error inserting exercise into database: " << errText;
return Core::FunctionReplyObject(0, tr_lts("dds", "Error inserting ExmExercise-data into database: %1")).arg(errText);
}
// Id is serial -> will be generated by db => inside m_data after db-insert
DEBUG_LOG(m_logger) << "New exerciseId: " << m_data.id;
// Create exercise-schema
if (!m_exTblGen->createExerciseSchema(m_data.id))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error creating database schema for exercise with id %1")).arg(m_data.id);
// Create tables/views/indexes for each service
if (!m_exTblGen->executeSql(m_data.id))
{
// Remove already created schema & user
m_exTblGen->dropExerciseSchema(m_data.id, false);
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error creating database tables/indexes/views for exercise with id %1")).arg(m_data.id);
}
}
// Additional writing of sqlFiles
if (m_logger.isDebugEnabled())
m_exTblGen->writeExerciseServicesSql(m_data.id);
INFO_LOG(m_logger) << "Exercise created sucessfully.";
// Publish Data via DTS
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
void ObjectExercise::initStateMachine()
{
// Start ExerciseStateMachine
m_stateMachine->init();
INFO_LOG(m_logger) << "StateMachine: currentState" << m_stateMachine->getCurrentState();
}
Core::FunctionReplyObject ObjectExercise::updateObject(const Database::ExmExercise &data)
{
if ( !isValid() || !isEditable())
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Trying to update an already removed ExmExercise object"));
m_data.id = data.id;
m_data.name = data.name;
m_data.beginTime = data.beginTime;
m_data.endTime = data.endTime;
m_data.startTime = data.startTime;
m_data.stopTime = data.stopTime;
m_data.trainingAreaId = data.trainingAreaId;
m_data.radioGroupId = data.radioGroupId;
m_data.activePhaseId = data.activePhaseId;
m_data.symbolFillTypeId = data.symbolFillTypeId;
m_data.forces = data.forces;
m_data.description = data.description;
m_data.defaultTimeZone = data.defaultTimeZone;
m_data.linkedExerciseId = data.linkedExerciseId;
m_data.sendToWms = data.sendToWms;
m_data.transferred = data.transferred;
m_data.hasHlaInterface = data.hasHlaInterface;
m_data.operatorId = data.operatorId;
m_data.workstationId = data.workstationId;
fixupTimeZone();
INFO_LOG(m_logger) << "Trying to updateObject ExerciseData with id = " << data.id;
if (!m_db->update(m_data))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << data.id << ": " << m_db->getLastError().databaseText();
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error updating ExmExercise-data in database: %1")).arg(errText);
}
// Change state if different
if (m_data.exerciseStateId != data.exerciseStateId)
{
Core::FunctionReplyObject retOb = switchToState(data.exerciseStateId, data.operatorId, data.workstationId);
if (retOb.error())
return retOb;
}
INFO_LOG(m_logger) << "ExerciseData changed sucessfully.";
onPublishData();
return Core::FunctionReplyObject(m_data.id);
}
Core::FunctionReplyObject ObjectExercise::removeObject(bool force)
{
if (isRunning() && !force)
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise still running"));
// Change State to terminated
if (!setExerciseState(ExerciseManagement::ExerciseState::Terminated))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "ExerciseState cannot be changed to terminated"));
if (!m_exTblGen->dropExerciseSchema(m_data.id, false))
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise %1 (%2) can not be removed")).arg(m_data.name).arg(m_data.id);
if (!m_db->removeExmExercise(m_data.id))
{
QString errText = m_db->getLastError().databaseText();
ERROR_LOG(m_logger) << "Cannot remove exercise id = " << m_data.id << "from database: " << errText;
return Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Exercise %1 (%2) can not be removed: %3")).arg(m_data.name).arg(m_data.id).arg(errText);
}
m_ddsHandler->dts()->removeExerciseByKey(m_data.id);
sendStateToServiceManagement(m_data.exerciseStateId); // terminated
INFO_LOG(m_logger) << "Exercise dropped sucessfully.";
return Core::FunctionReplyObject(m_data.id);
}
bool ObjectExercise::setLinkedExerciseId(int newLinkedExerciseId)
{
m_data.linkedExerciseId = newLinkedExerciseId;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << m_data.id << " : " << m_db->getLastError().text();
return false;
}
onPublishData();
return true;
}
bool ObjectExercise::setSendToWms(bool bSend)
{
m_data.sendToWms = bSend;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Failed to update ExerciseData with id = " << m_data.id << " : " << m_db->getLastError().text();
return false;
}
onPublishData();
return true;
}
bool ObjectExercise::setExerciseState(const ExerciseState &exerciseState)
{
if (m_data.exerciseStateId == exerciseState)
return true;
if (!isValid() || !isEditable())
{
WARNING_LOG(m_logger) << "Trying to update an already removed ExmExercise object";
return false;
}
DEBUG_LOG(m_logger) << "## m_data.exerciseStateId = " << m_data.exerciseStateId;
DEBUG_LOG(m_logger) << "## exerciseState = " << exerciseState;
m_data.exerciseStateId = exerciseState;
if (m_data.exerciseStateId == ExerciseManagement::ExerciseState::Stopped ||
m_data.exerciseStateId == ExerciseManagement::ExerciseState::Terminated ||
m_data.exerciseStateId == ExerciseManagement::ExerciseState::Unknown)
m_data.radioGroupId.clear();
QDateTime changed = QDateTime::currentDateTimeUtc();
if (m_data.exerciseStateId == ExerciseState::Started)
m_data.startTime = changed;
if (m_data.exerciseStateId == ExerciseState::Stopped)
m_data.stopTime = changed;
if (!m_db->update(m_data))
{
ERROR_LOG(m_logger) << "Error updating ExmExercise-data in database (exerciseState) for exercise id = " << m_data.id << " : " << m_db->getLastError().text();;
return false;
}
Database::ExmExerciseStateChange stateChange;
stateChange.exerciseId = m_data.id;
stateChange.exerciseStateId = m_data.exerciseStateId;
stateChange.operatorId = m_data.operatorId;
stateChange.workstationId = m_data.workstationId;
stateChange.changedAt = changed.currentMSecsSinceEpoch();
// record exerciseStateChange
if (!m_db->insert(stateChange))
WARNING_LOG(m_logger) << "Recording change of exerciseState failed: " << m_db->getLastError().text();;
// Publish Data via DTS
onPublishData();
return true;
}
void ObjectExercise::onPublishData()
{
INFO_LOG(m_logger) << "Publish ExmExercise-data to dds (" << m_data.id << ", " << m_data.name << ") ...";
if (!m_ddsHandler->dts()->publish(convert(m_data)))
ERROR_LOG(m_logger) << "Error publishing ExmExercise-data to dds";
}
void ObjectExercise::fixupTimeZone()
{
if (m_data.defaultTimeZone.isEmpty())
m_data.defaultTimeZone = QLatin1Char('Z'); // zulu
const char tz = m_data.defaultTimeZone.at(0).toLatin1();
if (tz < 'A' || tz == 'J' || tz > 'Z')
m_data.defaultTimeZone = QLatin1Char('Z'); // zulu
}
Core::FunctionReplyObject ObjectExercise::switchToState(const ExerciseState &newExerciseState, int operatorId, int workstationId)
{
Core::FunctionReplyObject ret;
INFO_LOG(m_logger) << "-------------- switchToState: " << newExerciseState
<< ", currentState = " << m_stateMachine->getCurrentState();
m_data.operatorId = operatorId;
m_data.workstationId = workstationId;
bool ok = m_stateMachine->switchToState(newExerciseState);
if (!ok)
{
ret = Core::FunctionReplyObject(m_data.id, tr_lts("dds", "Error switching to state %1")).arg(newExerciseState.underlying());
}
NOTICE_LOG(m_logger) << "-------------- after switch: currentState" << m_stateMachine->getCurrentState();
NOTICE_LOG(m_logger) << "-> OperatorId: " << m_data.operatorId << ", workstationId: " << m_data.workstationId;
return ret;
}
static int s_requestId = 0;
bool ObjectExercise::sendStateToServiceManagement(ExerciseState state)
{
Core::ServiceGroupType sgt = Core::ServiceGroupType::Core; // invalid here
switch (state.underlying())
{
case ExerciseState::Created:
sgt = Core::ServiceGroupType::ExercisePreparation;
break;
case ExerciseState::Started:
sgt = Core::ServiceGroupType::ExerciseExecution;
break;
case ExerciseState::Paused:
sgt = Core::ServiceGroupType::ExercisePaused;
break;
case ExerciseState::Stopped:
sgt = Core::ServiceGroupType::ExerciseAfterActionReview;
break;
case ExerciseState::Terminated:
sgt = Core::ServiceGroupType::Master; // Master + ExerciseId != 0 -> stopp all groups for this exercise
break;
case ExerciseState::Unknown:
break;
}
if (sgt == Core::ServiceGroupType::Core)
return false;
if (sgt != Core::ServiceGroupType::Master)
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Trying to enable service group " << sgt;
else
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Trying to stop all services";
// if there is an old group, stop them
ServiceManagement::GroupRequest request(sgt,
m_ddsHandler->ddsConnector()->getDomainId(),
m_data.id,
0,
++s_requestId,
{});
#ifndef EXM_OBJECT_EXERCISE_NO_HANDLER
if (!m_exerciseHandler || !m_exerciseHandler->startGroupRequest(request))
{
ERROR_LOG(m_logger) << "Ex " << m_data.id << " - Failed to start group " << sgt;
return false;
}
#endif
if (sgt != Core::ServiceGroupType::Master)
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Sent start group " << sgt;
else
DEBUG_LOG(m_logger) << "Ex " << m_data.id << " - Sent stop all services";
return true;
}
bool ObjectExercise::transferExerciseToEvaluationDb()
{
if (m_data.transferred)
return true;
if (!m_copyExercise)
m_copyExercise = new DBAdministration::CopyExercise(m_logger, DBAdministration::CopyExercise::CopyForEvaluation, this);
connect(m_copyExercise, &DBAdministration::CopyExercise::copyDbFinished,
this, &ObjectExercise::onTransferDbFinished);
connect(m_copyExercise, &DBAdministration::CopyExercise::copyDbFailed,
this, &ObjectExercise::onErrorOccured);
if (!m_copyExercise->init(m_data.id,
Settings::serverDatabaseHost(),
Settings::serverDatabaseName(),
Settings::evaluationDatabaseHost(),
Settings::evaluationDatabaseName()) ||
!m_copyExercise->doCopy())
{
WARNING_LOG(m_logger) << "Could not backup exercise database to evaluation server";
return false;
}
NOTICE_LOG(m_logger) << "Ex " << m_data.id << " - Started copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
return true;
}
void ObjectExercise::onTransferDbFinished()
{
NOTICE_LOG(m_logger) << "Ex " << m_data.id << " - Finished copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
m_data.transferred = true;
if (!m_db->update(m_data))
ERROR_LOG(m_logger) << "Error updating ExmExercise-data (transferred) in database (exerciseState) for exercise id = " << m_data.id << " : " << m_db->getLastError().text();;
delete m_copyExercise;
m_copyExercise = nullptr;
//publish because we changed transferred
onPublishData();
}
void ObjectExercise::onErrorOccured()
{
WARNING_LOG(m_logger) << "Ex " << m_data.id << " - Copying exercise from "
<< Settings::serverDatabaseHost() << ":" << Settings::serverDatabaseName()
<< " to " << Settings::evaluationDatabaseHost() << ":" << Settings::evaluationDatabaseName();
delete m_copyExercise;
m_copyExercise = nullptr;
}
} // Namespace ExerciseManagement