402 lines
11 KiB
C++
Executable File
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();
|
|
}
|