Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Services/ExerciseManagement/handler_exercise.cpp
T
berrux-dev c3eda18234 Migrates remaining services and plugins to generated rtiddsgen API
Converts accessor calls to plain member access across the residual service set.
2026-09-21 02:47:48 +02:00

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