Converts accessor calls to plain member access across the residual service set.
824 lines
28 KiB
C++
Executable File
824 lines
28 KiB
C++
Executable File
/**
|
|
* 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 <QtGui/QPixmap>
|
|
#include <QtCore/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
|