Files
qt5-legatus-legacy/Lts/Services/AreaEffectSimulation/object_base.cpp
T
2026-08-13 18:33:10 +02:00

402 lines
11 KiB
C++
Executable File

/**
* Base class for all aes objects
* $Id$
* $HeadURL$
* $Author$
*/
#include "object_base.h"
#include <QTimer>
#include <QDateTime>
using namespace AreaEffectSimulation;
//
// ObjectBase
//
ObjectBase::ObjectBase(MainHandler *mainHandler, const QString &typeName, QObject *parent)
: QObject(parent)
, m_aesTriggerTimer(new QTimer(this))
, m_publishDataTimer(new QTimer(this))
, m_mainHandler(mainHandler)
, m_typeName(typeName)
, m_logger(getLogger(QStringLiteral("Object") + typeName))
{
m_aesTriggerTimer->setSingleShot(true);
connect(m_aesTriggerTimer, &QTimer::timeout,
this, &ObjectBase::checkEnterNewState);
m_publishDataTimer->setSingleShot(true);
connect(m_publishDataTimer, &QTimer::timeout,
this, &ObjectBase::onPublishData);
m_publishDataTimer->setInterval(1);
}
ObjectBase::~ObjectBase()
{
m_aesTriggerTimer->stop();
m_publishDataTimer->stop();
}
void ObjectBase::setEffectState(AesEffectState newState)
{
Q_UNUSED(newState)
m_publishDataTimer->start();
AesEffectState es = checkEnterNewState(); // can create loop because checkEnterNewState is calling trySwitchFoo() but it's ok
DEBUG_LOG(m_logger) << id() << " New effect state is " << es;
emit effectStateChanged();
}
qint64 ObjectBase::partitionTimeMSec() const
{
return m_mainHandler->dds()->currentPartitionTimeMSec();
}
QDateTime ObjectBase::partitionTime() const
{
return m_mainHandler->dds()->currentPartitionTime();
}
qint64 ObjectBase::toMSec(const QDateTime &dt)
{
if (!dt.isValid())
return 0;
return dt.toMSecsSinceEpoch();
}
bool ObjectBase::isOnlyObjectNameModifiable() const
{
return (effectState() == AesEffectState::EsActivated ||
effectState() == AesEffectState::EsPaused);
}
bool ObjectBase::isObjectModifiable() const
{
return (effectState() == AesEffectState::EsCreated ||
effectState() == AesEffectState::EsPrepared ||
effectState() == AesEffectState::EsActivated ||
effectState() == AesEffectState::EsPaused ||
effectState() == AesEffectState::EsReceived);
}
bool ObjectBase::isObjectInTrainingArea() const
{
return (effectState() == AesEffectState::EsPrepared ||
effectState() == AesEffectState::EsActivated ||
effectState() == AesEffectState::EsFired ||
effectState() == AesEffectState::EsPaused);
}
AesEffectState ObjectBase::checkEnterNewState()
{
DEBUG_LOG(m_logger) << id() << " checkEnterNewState - cur effect state is " << effectState();
bool bNeedToPublishNewData = false;
switch (effectState().underlying())
{
case AesEffectState::EsUnknown:
// initial state, switch to Received or Created
if (trySwitchToReceived() || trySwitchToCreated())
{
bNeedToPublishNewData = true;
break;
}
break;
case AesEffectState::EsCreated:
// maybe we can switch to prepared directly
bNeedToPublishNewData = trySwitchToPrepared();
break;
case AesEffectState::EsReceived:
if (trySwitchToPrepared() || trySwitchToCreated())
{
bNeedToPublishNewData = true;
break;
}
break;
case AesEffectState::EsPrepared:
case AesEffectState::EsFired:
// first check if the user changed something so we have to go back to 'Created'
if (trySwitchToCreated())
{
bNeedToPublishNewData = true;
break;
}
if (trySwitchToFired())
{
bNeedToPublishNewData = true;
break;
}
// now check if we can go directly to 'Activated'
if (trySwitchToActivated())
{
bNeedToPublishNewData = true;
break;
}
if (isActivationTimeSet())
{
qint64 timeToTickMsec = 0;
if (aesType() == AesType::AtExplosionArea && effectState() != AesEffectState::EsFired) // only Artillery with state != EsFired can enter State EsFired
{
timeToTickMsec = activationTime().toMSecsSinceEpoch() - partitionTimeMSec() - flightDurationMsec();
DEBUG_LOG(m_logger) << id() << " timeUntilFiredMSec: " << timeToTickMsec;
}
else // setup EsAvtivated
{
timeToTickMsec = activationTime().toMSecsSinceEpoch() - partitionTimeMSec(); // time until activation
DEBUG_LOG(m_logger) << id() << " timeUntilActivationMSec: " << timeToTickMsec
<< " cur: " << partitionTimeMSec()
<< " activation: " << activationTime().toMSecsSinceEpoch();
}
m_aesTriggerTimer->stop(); // if it is called directly by someone
if (timeToTickMsec > 0)
{
// attn: QTimer::start(int) --> 2.100.000.000msec = 2.100.000s = 35.000min = 583h = 24days !
// --> we need to check if we're at the correct time
if (timeToTickMsec > std::numeric_limits<int>::max())
timeToTickMsec = std::numeric_limits<int>::max();
m_aesTriggerTimer->start(timeToTickMsec);
}
else
{
if (!trySwitchToActivated())
{
WARNING_LOG(m_logger) << id() << " Error switching to active ...";
QTimer::singleShot(100, this, &ObjectBase::checkEnterNewState); // maybe some timing
}
}
bNeedToPublishNewData = true;
}
break;
case AesEffectState::EsActivated:
// check if we are done
if (trySwitchToExpired())
{
bNeedToPublishNewData = true;
break;
}
if (isEndTimeSet())
{
// start timer for done
m_aesTriggerTimer->stop(); // if it is called directly by someone
qint64 timeUntilFinishedMSec = endTime().toMSecsSinceEpoch() - partitionTimeMSec();
DEBUG_LOG(m_logger) << id() << " timeUntilFinishedMSec: " << timeUntilFinishedMSec
<< " cur: " << partitionTimeMSec()
<< " activation: " << activationTime().toMSecsSinceEpoch();
if (timeUntilFinishedMSec > 0)
{
// attn: QTimer::start(int) --> 2.100.000.000msec = 2.100.000s = 35.000min = 583h = 24days !
// --> we need to check if we're at the correct time
if (timeUntilFinishedMSec > std::numeric_limits<int>::max())
timeUntilFinishedMSec = std::numeric_limits<int>::max();
m_aesTriggerTimer->start(timeUntilFinishedMSec);
}
else
{
if (!trySwitchToExpired())
{
WARNING_LOG(m_logger) << id() << " Error switching from active to expired ...";
QTimer::singleShot(100, this, &ObjectBase::checkEnterNewState); // maybe some timing
}
}
bNeedToPublishNewData = true;
}
break;
case AesEffectState::EsPaused:
case AesEffectState::EsExpired:
case AesEffectState::EsRemoved:
break;
default:
ERROR_LOG(m_logger) << id() << " Unknown new state " << effectState();
break;
}
if (bNeedToPublishNewData)
m_publishDataTimer->start();
return effectState();
}
// Possible transitions
// Unknown -> Received
bool ObjectBase::trySwitchToReceived()
{
if (effectState() != AesEffectState::EsUnknown)
return false;
if (!isUsingEffectStateReceived())
return false;
DEBUG_LOG(m_logger) << id() << " trySwitchToReceived succeeded";
setEffectState(AesEffectState::EsReceived);
return true;
}
// Prepared, Received, Unknown -> Created
bool ObjectBase::trySwitchToCreated()
{
if (effectState() != AesEffectState::EsPrepared &&
effectState() != AesEffectState::EsUnknown &&
effectState() != AesEffectState::EsReceived)
return false;
if ((effectState() == AesEffectState::EsPrepared ||
effectState() == AesEffectState::EsReceived) &&
isActivationTimeSet())
return false;
if (effectState() == AesEffectState::EsReceived &&
isUsingEffectStateReceived())
return false;
DEBUG_LOG(m_logger) << id() << " trySwitchToCreated succeeded";
setEffectState(AesEffectState::EsCreated);
return true;
}
// Created, Received -> Prepared
bool ObjectBase::trySwitchToPrepared()
{
if (effectState() != AesEffectState::EsCreated &&
effectState() != AesEffectState::EsReceived)
return false;
if (effectState() == AesEffectState::EsReceived &&
!isUsingEffectStateReceived())
return false;
if (!isActivationTimeSet())
return false;
DEBUG_LOG(m_logger) << id() << " trySwitchToPrepared succeeded";
setEffectState(AesEffectState::EsPrepared);
return true;
}
// Prepared -> Fired (only Artillery)
bool ObjectBase::trySwitchToFired()
{
if (effectState() != AesEffectState::EsPrepared)
{
DEBUG_LOG(m_logger) << id() << " trySwitchToFired failed - not prepared: " << effectState();
return false;
}
if (aesType() != AesType::AtExplosionArea && aesType() != AesType::AtEffectCluster)
{
DEBUG_LOG(m_logger) << id() << " trySwitchToFired failed - not AtExplosionArea: " << aesType();
return false;
}
if (!isFiringTimeReached())
{
DEBUG_LOG(m_logger) << id() << " trySwitchToFired failed - firing time is not reached";
return false;
}
if (isActivationTimeReached())
{
DEBUG_LOG(m_logger) << id() << " trySwitchToFired failed - activation time already reached!";
return false;
}
DEBUG_LOG(m_logger) << id() << " trySwitchToFired succeeded";
setEffectState(AesEffectState::EsFired);
return true;
}
// Prepared, Fired, Paused -> Activated
bool ObjectBase::trySwitchToActivated()
{
if (effectState() != AesEffectState::EsPrepared &&
effectState() != AesEffectState::EsPaused &&
effectState() != AesEffectState::EsFired)
{
DEBUG_LOG(m_logger) << id() << " trySwitchToActivated failed - not prepared/fired/paused: " << effectState();
return false;
}
if (!isActivationTimeReached())
{
DEBUG_LOG(m_logger) << id() << " trySwitchToActivated failed - activation time not reached: "
<< isActivationTimeSet() << " - " << time(nullptr) << " - " << activationTime().toSecsSinceEpoch();
return false;
}
DEBUG_LOG(m_logger) << id() << " trySwitchToActivated succeeded";
setEffectState(AesEffectState::EsActivated);
return true;
}
// Activated -> Paused
bool ObjectBase::trySwitchToPaused()
{
if (effectState() != AesEffectState::EsActivated)
return false;
if (!isPausingSupported())
return false;
DEBUG_LOG(m_logger) << id() << " trySwitchToPaused succeeded";
setEffectState(AesEffectState::EsPaused);
return true;
}
// Activated -> Done
bool ObjectBase::trySwitchToExpired()
{
if (effectState() != AesEffectState::EsActivated &&
effectState() != AesEffectState::EsPaused)
{
DEBUG_LOG(m_logger) << id() << " trySwitchToExpired failed - not active or paused: " << effectState();
return false;
}
if (!isEndTimeReached())
{
DEBUG_LOG(m_logger) << id() << " trySwitchToExpired failed - end time not reached: "
<< isEndTimeSet() << " - " << time(nullptr) << " - " << endTime().toSecsSinceEpoch();
return false;
}
DEBUG_LOG(m_logger) << id() << " trySwitchToExpired succeeded";
setEffectState(AesEffectState::EsExpired);
return true;
}
bool ObjectBase::trySwitchToRemoved()
{
DEBUG_LOG(m_logger) << id() << " trySwitchToRemoved: " << effectState();
switch (effectState().underlying())
{
case AesEffectState::EsReceived:
setUseEffectStateReceived(false);
Q_FALLTHROUGH();
case AesEffectState::EsCreated:
case AesEffectState::EsPrepared:
setEffectState(AesEffectState::EsRemoved);
return true;
case AesEffectState::EsActivated:
case AesEffectState::EsPaused:
setEndTimeToNow();
return trySwitchToExpired();
default:
break;
}
return false;
}
void ObjectBase::onPublishData()
{
m_publishDataTimer->stop();
}