/** * Base class for all aes objects * $Id$ * $HeadURL$ * $Author$ */ #include "object_base.h" #include #include 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::max()) timeToTickMsec = std::numeric_limits::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::max()) timeUntilFinishedMSec = std::numeric_limits::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(); }