Initial commit
This commit is contained in:
commit
b7d90b9afe
16245 files changed
+3396666
No files matched your search
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Some helper classes / functions for AES
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "aes_helper.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
//
|
||||
// class HandledImpactsStorage::Private
|
||||
//
|
||||
class HandledImpactsStorage::Private
|
||||
{
|
||||
public:
|
||||
Private() = default;
|
||||
~Private() = default;
|
||||
|
||||
// data:
|
||||
QHash<int, QSet<int>> m_aesItemId2Index; // Key = aesItemId ; processed/calculated index
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// class HandledImpactsStorage
|
||||
//
|
||||
HandledImpactsStorage::HandledImpactsStorage()
|
||||
: d(new Private)
|
||||
{}
|
||||
|
||||
HandledImpactsStorage::~HandledImpactsStorage()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void HandledImpactsStorage::setIndexProcessed(int aesItemId, int index)
|
||||
{
|
||||
d->m_aesItemId2Index[aesItemId].insert(index);
|
||||
}
|
||||
|
||||
bool HandledImpactsStorage::isIndexProcessed(int aesItemId, int index)
|
||||
{
|
||||
return d->m_aesItemId2Index[aesItemId].contains(index);
|
||||
}
|
||||
|
||||
void HandledImpactsStorage::removeAesItem(int aesItemId)
|
||||
{
|
||||
d->m_aesItemId2Index.remove(aesItemId);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Some helper classes / functions for AES
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_export.h>
|
||||
#include <math.h>
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
#include <QPointF>
|
||||
#include <QString>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <LtsUtilsCore/dds_bitarray.h>
|
||||
#include <LtsUtilsCore/qpoint3d.h>
|
||||
#include "aes_db.h"
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
inline bool chargeTypeIdIsVulcano(int chargeTypeId) { return /*(chargeTypeId == 376) ||*/ (chargeTypeId == 393) ; } // 376=Artillery ; 393=Rocket
|
||||
inline Core::GeoPosition toCoreGeoPosition(const LtsUtils::QPoint3D &point)
|
||||
{
|
||||
return Core::GeoPosition(point.x(), point.y(), point.z());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper namespace TrajectoryRelative (ADLER / SAA_Trajectory)
|
||||
//------------------------------------------------------------------------------
|
||||
namespace TrajectoryRelative
|
||||
{
|
||||
|
||||
// keyword used for encoding/decoding, or in other words: for toString/fromString
|
||||
static const QString TRAJECTORY_RELATIVE_KEYWORD = QStringLiteral("trajectoryRelative");
|
||||
|
||||
inline std::string toString(const std::vector<Core::RelativePositionRelativeTime> &trajectoryRelative)
|
||||
{
|
||||
if (trajectoryRelative.empty())
|
||||
return {};
|
||||
|
||||
QJsonObject obj;
|
||||
QJsonArray arr;
|
||||
|
||||
for (const auto &coordinate : trajectoryRelative)
|
||||
{
|
||||
QJsonObject coordObj;
|
||||
coordObj[QStringLiteral("offsetX")] = coordinate.offsetX();
|
||||
coordObj[QStringLiteral("offsetY")] = coordinate.offsetY();
|
||||
coordObj[QStringLiteral("offsetZ")] = coordinate.offsetZ();
|
||||
coordObj[QStringLiteral("relativeTime")] = coordinate.relativeTime();
|
||||
arr.append(coordObj);
|
||||
}
|
||||
obj[TRAJECTORY_RELATIVE_KEYWORD] = arr;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::JsonFormat::Compact).toStdString();
|
||||
}
|
||||
|
||||
inline std::vector<Core::RelativePositionRelativeTime> fromString(const QString &trajectoryRelativeJson)
|
||||
{
|
||||
if (trajectoryRelativeJson.isEmpty())
|
||||
return {};
|
||||
|
||||
QJsonObject obj = QJsonDocument::fromJson(trajectoryRelativeJson.toUtf8()).object();
|
||||
if (!obj.contains(TRAJECTORY_RELATIVE_KEYWORD) || !obj[TRAJECTORY_RELATIVE_KEYWORD].isArray() )
|
||||
return {};
|
||||
|
||||
std::vector<Core::RelativePositionRelativeTime> ret;
|
||||
|
||||
QJsonArray arr = obj[TRAJECTORY_RELATIVE_KEYWORD].toArray();
|
||||
for (const auto arrRef: arr)
|
||||
{
|
||||
QJsonObject coordinate = arrRef.toObject();
|
||||
|
||||
if (!coordinate.contains(QStringLiteral("offsetX")) ||
|
||||
!coordinate.contains(QStringLiteral("offsetY")) ||
|
||||
!coordinate.contains(QStringLiteral("offsetZ")) ||
|
||||
!coordinate.contains(QStringLiteral("relativeTime"))
|
||||
)
|
||||
return {};
|
||||
|
||||
Core::RelativePositionRelativeTime coord;
|
||||
coord.offsetX() = coordinate[QStringLiteral("offsetX")].toDouble();
|
||||
coord.offsetY() = coordinate[QStringLiteral("offsetY")].toDouble();
|
||||
coord.offsetZ() = coordinate[QStringLiteral("offsetZ")].toDouble();
|
||||
coord.relativeTime() = coordinate[QStringLiteral("relativeTime")].toInt();
|
||||
|
||||
ret.push_back(coord);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace TrajectoryRelative
|
||||
|
||||
|
||||
|
||||
inline double getAngleDegreeNorth(const QPointF &p1, const QPointF &p2)
|
||||
{
|
||||
double x = p2.x() - p1.x();
|
||||
double y = p2.y() - p1.y();
|
||||
|
||||
// Winkel der Geraden bestimmen
|
||||
double alphaRad = atan2(y, x);
|
||||
|
||||
// // Fuer Winkel > PI liefert atan2 math. rechts drehende (also negative) Werte.
|
||||
// // Um Werte von 0-2PI zu bekommen muss deshalb hier von 2Pi abgezogen werden.
|
||||
|
||||
alphaRad -= M_PI / 2.0;
|
||||
alphaRad *= -1.0;
|
||||
|
||||
if (alphaRad < 0.0)
|
||||
alphaRad = 2 * M_PI + alphaRad;
|
||||
|
||||
return (alphaRad * 180.0 / M_PI);
|
||||
}
|
||||
|
||||
inline bool isPointSimilar(AreaEffectSimulation::AesExplosionGeometry geometry)
|
||||
{
|
||||
return (geometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgAimPoint) ||
|
||||
(geometry.underlying() == AreaEffectSimulation::AesExplosionGeometry_def::EgPoint);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// helper class HandledImpactsStorage
|
||||
//------------------------------------------------------------------------------
|
||||
class AES_COMMON_EXPORT HandledImpactsStorage final
|
||||
{
|
||||
public:
|
||||
Q_DISABLE_COPY(HandledImpactsStorage)
|
||||
|
||||
HandledImpactsStorage();
|
||||
~HandledImpactsStorage();
|
||||
|
||||
void setIndexProcessed(int aesItemId, int index);
|
||||
bool isIndexProcessed(int aesItemId, int index);
|
||||
void removeAesItem(int aesItemId);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
/**
|
||||
* The private d->pointer to not break BC when changing internals of this class
|
||||
*/
|
||||
Private *d = nullptr;
|
||||
};
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Wandelt DDS-Strukturen in Database-Strukturen
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "ddsdbconvert.h"
|
||||
#include <aes.hpp>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
namespace DdsDbConvert
|
||||
{
|
||||
|
||||
Database::AesNContamination nContamination(const AreaEffectSimulation::NContamination &src)
|
||||
{
|
||||
Database::AesNContamination dst;
|
||||
dst.id = src.id();
|
||||
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
|
||||
dst.effectStateId = src.effectStateId();
|
||||
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
|
||||
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
|
||||
dst.effectDuration = src.effectDuration();
|
||||
dst.effectStart = src.effectStart().is_set() ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
|
||||
dst.name = QString::fromStdString(src.name());
|
||||
dst.impactPosition.rx() = src.impactPosition().lon();
|
||||
dst.impactPosition.ry() = src.impactPosition().lat();
|
||||
dst.borderLengthInner = src.borderLengthInner();
|
||||
dst.borderLengthOuter = src.borderLengthOuter();
|
||||
dst.impactRadius = src.impactRadius();
|
||||
dst.spanAngle = src.spanAngle();
|
||||
dst.rotationAngle = src.rotationAngle();
|
||||
dst.detonationValue = src.detonationValue();
|
||||
// dst.priv_entityId;
|
||||
// dst.privNContaminationId;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Database::AesBCContamination bcContamination(const BCContamination &src)
|
||||
{
|
||||
Database::AesBCContamination dst;
|
||||
dst.id = src.id();
|
||||
dst.effectStateId = src.effectStateId();
|
||||
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
|
||||
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
|
||||
dst.effectDuration = src.effectDuration();
|
||||
dst.effectStart = src.effectStart().is_set() ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
|
||||
dst.name = QString::fromStdString(src.name());
|
||||
dst.impactPosition.rx() = src.impactPosition().lon();
|
||||
dst.impactPosition.ry() = src.impactPosition().lat();
|
||||
dst.impactRadius = src.impactRadius();
|
||||
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
|
||||
dst.windSpeed = src.windSpeed();
|
||||
dst.weatherTimestamp = QDateTime::fromMSecsSinceEpoch(src.weatherTimestamp());
|
||||
dst.propagationDirection = src.propagationDirection();
|
||||
dst.weatherPosition = dst.impactPosition; // initial, is not used during update therefore it's ok here
|
||||
dst.upperOffset = src.upperOffset();
|
||||
dst.lowerOffset = src.lowerOffset();
|
||||
// dst.expansionFactor;
|
||||
// dst.isDynamicWeather;
|
||||
// dst.priv_entityId;
|
||||
// dst.privBCContaminationId;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Database::AesExplosionArea explosionArea(const ExplosionArea &src)
|
||||
{
|
||||
Database::AesExplosionArea dst;
|
||||
dst.id = src.id();
|
||||
dst.effectStateId = src.effectStateId();
|
||||
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
|
||||
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
|
||||
dst.effectDuration = src.effectDuration();
|
||||
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
|
||||
dst.name = QString::fromStdString(src.name());
|
||||
dst.impactPosition.rx() = src.area().cornerBottomLeft().lon();
|
||||
dst.impactPosition.ry() = src.area().cornerBottomLeft().lat();
|
||||
dst.impactLength = src.area().height();
|
||||
dst.impactWidth = src.area().width();
|
||||
dst.rotationAngle = src.area().rotation();
|
||||
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
|
||||
dst.cannonCount = src.cannonCount();
|
||||
dst.cannonRounds = src.cannonRounds();
|
||||
dst.originalEffectStart = src.originalEffectStart() > 0 ? QDateTime::fromMSecsSinceEpoch(src.originalEffectStart()) : QDateTime();
|
||||
dst.originalEffectDuration = src.originalEffectDuration();
|
||||
dst.remainExplosionNumber = src.remainExplosionNumber();
|
||||
// dst.firePurpose;
|
||||
// dst.fireRequestTime;
|
||||
// dst.priv_entityId;
|
||||
// dst.priv_explosionId;
|
||||
// dst.privExplosionAreaId;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Database::AesExplosionSingle explosionSingle(const ExplosionSingle &src)
|
||||
{
|
||||
Database::AesExplosionSingle dst;
|
||||
dst.id = src.id();
|
||||
dst.effectStateId = src.effectStateId();
|
||||
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
|
||||
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
|
||||
dst.effectDuration = src.effectDuration();
|
||||
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
|
||||
dst.name = QString::fromStdString(src.name());
|
||||
dst.impactPosition.rx() = src.impactPosition().lon();
|
||||
dst.impactPosition.ry() = src.impactPosition().lat();
|
||||
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
|
||||
// dst.priv_entityId;
|
||||
// dst.priv_explosionId;
|
||||
// dst.privExplosionSingleId;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Database::AesMineArea mineArea(const MineArea &src)
|
||||
{
|
||||
Database::AesMineArea dst;
|
||||
dst.id = src.id();
|
||||
dst.name = QString::fromStdString(src.name());
|
||||
dst.effectStart = src.effectStart().is_set() && src.effectStart().get() > 0 ? QDateTime::fromMSecsSinceEpoch(src.effectStart().get()) : QDateTime();
|
||||
dst.effectDuration = src.effectDuration();
|
||||
dst.partyId = src.partyId().is_set() ? src.partyId().get() : 0;
|
||||
dst.chargeTypeId = src.chargeStruct().chargeTypeId();
|
||||
dst.participantId = src.participantId().is_set() ? src.participantId().get() : 0;
|
||||
dst.density = src.density();
|
||||
dst.width = src.width();
|
||||
dst.layingProcedureTypeId = src.layingProcedureType();
|
||||
dst.isLaidHidden = src.isLaidHidden();
|
||||
dst.effectStateId = src.effectStateId();
|
||||
dst.isCenterLine = src.isCenterLine();
|
||||
// qint32 priv_entityId;
|
||||
// qint32 priv_mineId;
|
||||
// qint32 privMineAreaId;
|
||||
return dst;
|
||||
}
|
||||
|
||||
QVector<Database::AesMineAreaSegment> mineAreaSegments(const MineArea &src)
|
||||
{
|
||||
const std::vector<AreaEffectSimulation::MineSegment> &segments = src.segments();
|
||||
QVector<Database::AesMineAreaSegment> dbSegments;
|
||||
dbSegments.reserve(segments.size());
|
||||
for (size_t i = 0; i < segments.size(); ++i)
|
||||
{
|
||||
Database::AesMineAreaSegment newSegment;
|
||||
newSegment.elementNumber = i;
|
||||
newSegment.mineAreaId = src.id(); // Todo: korrekt?
|
||||
newSegment.id = 0;
|
||||
newSegment.startPosition = QPointF(segments[i].positionStart().lon(), segments[i].positionStart().lat());
|
||||
newSegment.endPosition = QPointF(segments[i].positionEnd().lon(), segments[i].positionEnd().lat());
|
||||
dbSegments << newSegment;
|
||||
}
|
||||
return dbSegments;
|
||||
}
|
||||
|
||||
} // namespace DdsDbConvert
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Wandelt DDS-Strukturen in Database-Strukturen
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_db_structs.h>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
class BCContamination;
|
||||
class ExplosionArea;
|
||||
class ExplosionSingle;
|
||||
class MineArea;
|
||||
class NContamination;
|
||||
|
||||
namespace DdsDbConvert
|
||||
{
|
||||
AES_COMMON_EXPORT Database::AesBCContamination bcContamination(const BCContamination &src);
|
||||
AES_COMMON_EXPORT Database::AesExplosionArea explosionArea(const ExplosionArea &src);
|
||||
AES_COMMON_EXPORT Database::AesExplosionSingle explosionSingle(const ExplosionSingle &src);
|
||||
AES_COMMON_EXPORT Database::AesMineArea mineArea(const MineArea &src);
|
||||
AES_COMMON_EXPORT QVector<Database::AesMineAreaSegment> mineAreaSegments(const MineArea &src);
|
||||
AES_COMMON_EXPORT Database::AesNContamination nContamination(const NContamination &src);
|
||||
} // namespace DdsDbConvert
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
/**
|
||||
* AES fire automaton - scheduling fire patterns for multiple effectors
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QTimer>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
|
||||
#include <AreaEffectSimulation/logger.h>
|
||||
#include <AreaEffectSimulation/settings.h>
|
||||
|
||||
#include "fire_automaton.h"
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
FireAutomaton::FireAutomaton(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_nextIdx(0)
|
||||
, m_tick(0)
|
||||
, m_durationMSecs(0)
|
||||
, m_intervalMSecs(0)
|
||||
, m_remainingTickMSecs(0)
|
||||
, m_state(State::Uninitialized)
|
||||
, m_logger(getLogger("Utils.FireAutomaton"))
|
||||
{
|
||||
m_timer.setSingleShot(true);
|
||||
connect(&m_timer, &QTimer::timeout, this, &FireAutomaton::nextTick, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initializes the fire automaton and derives a fire sequence from given parameters
|
||||
*
|
||||
* @param time Time used for the fire sequence calculation in milliseconds
|
||||
* @param timeType Type of the given time
|
||||
* @param rounds Number of rounds to be fired
|
||||
* @param effectorIds Ids of the effector that shall be used to fire rounds
|
||||
* @param pattern Fire pattern to be used
|
||||
* @return true when the automaton is succesfully initialized
|
||||
*/
|
||||
bool FireAutomaton::init(int32_t time, TimeType type, int32_t rounds, const QVector<int32_t>& effectorIds, const QString& pattern)
|
||||
{
|
||||
if(m_state != State::Stopped && m_state != State::Uninitialized)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - fire automaton must be in state unitialized or stopped.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(time < 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - time invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(effectorIds.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - effector Ids invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(rounds <= 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - number of rounds invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(pattern.isEmpty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - invalid fire pattern.";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_effectorIds = effectorIds;
|
||||
m_tick = 0;
|
||||
m_remainingTickMSecs = 0;
|
||||
m_nextIdx = 0;
|
||||
|
||||
if(!readPattern(pattern))
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - firing pattern invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!calculateSchedule(rounds))
|
||||
{
|
||||
ERROR_LOG(m_logger) << " init failed - firing schedule invalid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t maxTick = m_schedule.size();
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case TimeType::Cadence:
|
||||
m_intervalMSecs = time;
|
||||
m_durationMSecs = (maxTick - 1) * time;
|
||||
|
||||
break;
|
||||
|
||||
case TimeType::Duration:
|
||||
m_durationMSecs = time;
|
||||
|
||||
if(maxTick == 1)
|
||||
{
|
||||
m_intervalMSecs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_intervalMSecs = m_durationMSecs / (maxTick - 1);
|
||||
|
||||
// ensure atleast 1 ms spacing between ticks
|
||||
if(m_intervalMSecs <= 0)
|
||||
{
|
||||
m_intervalMSecs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
setState(State::Stopped);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FireAutomaton::setState(State state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
void FireAutomaton::nextTick()
|
||||
{
|
||||
QVector<int32_t> fireIds = getEffectorIds(m_tick);
|
||||
|
||||
for(auto id : fireIds)
|
||||
{
|
||||
emit fire(id);
|
||||
}
|
||||
|
||||
++m_tick;
|
||||
|
||||
if(m_tick >= m_schedule.size())
|
||||
{
|
||||
stop();
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_state != State::Stepping)
|
||||
{
|
||||
m_timer.start(m_intervalMSecs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the fire automaton's sequence derived from the fire pattern and parameters
|
||||
*
|
||||
* The sequence can only be started in state stopped and sets the state to running. After the sequence is finished, the state will be set to stopped.
|
||||
*
|
||||
* @return True when the automaton is succesfully resumed
|
||||
*/
|
||||
bool FireAutomaton::start()
|
||||
{
|
||||
if(m_state != State::Stopped)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " start failed - fire automaton must be in state stopped.";
|
||||
return false;
|
||||
}
|
||||
|
||||
setState(State::Running);
|
||||
m_timer.start(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resumes a previously stopped fire automaton
|
||||
*
|
||||
* In the state paused the fire automaton sequence execution will be resumed and the state set to running.
|
||||
*
|
||||
* @return True when the automaton is succesfully resumed
|
||||
*/
|
||||
bool FireAutomaton::resume()
|
||||
{
|
||||
if(m_state != State::Paused)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " resume failed - fire automaton must be in state paused.";
|
||||
return false;
|
||||
}
|
||||
|
||||
setState(State::Running);
|
||||
m_timer.start(m_remainingTickMSecs);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pauses a running fire automaton sequence
|
||||
*
|
||||
* In the state running the fire automaton sequence execution will be halted and the state set to paused.
|
||||
*
|
||||
* @return True when the automaton is succesfully paused
|
||||
*/
|
||||
bool FireAutomaton::pause()
|
||||
{
|
||||
if(m_state != State::Running)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " pause failed - fire automaton must be in state running.";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_remainingTickMSecs = m_timer.remainingTime();
|
||||
m_timer.stop();
|
||||
setState(State::Paused);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops and resets the fire automaton
|
||||
*
|
||||
* Forces the state stopped and resets the automaton sequence execution.
|
||||
*/
|
||||
void FireAutomaton::stop()
|
||||
{
|
||||
m_tick = 0;
|
||||
m_remainingTickMSecs = 0;
|
||||
m_timer.stop();
|
||||
setState(State::Stopped);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Steps through the derived fire automaton sequence
|
||||
*
|
||||
* Stepping can only be started from the state stopped and sets the state to stepping. After stepping through the whole sequence, the state will be set to stopped.
|
||||
*
|
||||
* @return True when step is succesfully performed
|
||||
*/
|
||||
bool FireAutomaton::performStep()
|
||||
{
|
||||
if(m_state != State::Stopped && m_state != State::Stepping)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " performStep failed - fire automaton must be in state stepping or stopped.";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_state = State::Stepping;
|
||||
nextTick();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current state of the fire automaton
|
||||
*
|
||||
* @return Fire automaton state
|
||||
*/
|
||||
FireAutomaton::State FireAutomaton::getState() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
bool FireAutomaton::readPattern(const QString& pattern)
|
||||
{
|
||||
bool fill = false;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
QStringList elements = pattern.split(QStringLiteral(","), Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList elements = pattern.split(QStringLiteral(","), QString::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
for(const QString& element : elements)
|
||||
{
|
||||
FireAutomaton::PatternElement patternElement;
|
||||
|
||||
if(!patternElementFromString(element, patternElement))
|
||||
{
|
||||
WARNING_LOG(m_logger) << " skipped invalid firing pattern elements.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if(patternElement == FireAutomaton::PatternElement::NextFill || patternElement == FireAutomaton::PatternElement::AllFill)
|
||||
{
|
||||
if(!fill)
|
||||
{
|
||||
fill = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(m_logger) << " a pattern may contain at most one fill element.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pattern.append(patternElement);
|
||||
}
|
||||
|
||||
if(m_pattern.empty())
|
||||
{
|
||||
ERROR_LOG(m_logger) << " error occured reading firing pattern.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FireAutomaton::patternElementFromString(const QString& str, FireAutomaton::PatternElement& out)
|
||||
{
|
||||
const QString key = str.trimmed().toLower();
|
||||
|
||||
if(key == QStringLiteral("all"))
|
||||
out = FireAutomaton::PatternElement::All;
|
||||
else if(key == QStringLiteral("all*"))
|
||||
out = FireAutomaton::PatternElement::AllFill;
|
||||
else if(key == QStringLiteral("next"))
|
||||
out = FireAutomaton::PatternElement::Next;
|
||||
else if(key == QStringLiteral("next*"))
|
||||
out = FireAutomaton::PatternElement::NextFill;
|
||||
else if(key == QStringLiteral("idle"))
|
||||
out = FireAutomaton::PatternElement::Idle;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVector<int32_t> FireAutomaton::getEffectorIds(int32_t tick) const
|
||||
{
|
||||
return m_schedule[tick];
|
||||
}
|
||||
|
||||
int32_t FireAutomaton::getMaxTick() const
|
||||
{
|
||||
return m_schedule.size();
|
||||
}
|
||||
|
||||
bool FireAutomaton::calculateSchedule(int32_t rounds)
|
||||
{
|
||||
|
||||
int32_t minCounter = 0;
|
||||
|
||||
// count needed rounds
|
||||
for(const FireAutomaton::PatternElement& element : m_pattern)
|
||||
{
|
||||
if(element == FireAutomaton::PatternElement::All)
|
||||
minCounter += m_effectorIds.size();
|
||||
else if(element == FireAutomaton::PatternElement::Next)
|
||||
++minCounter;
|
||||
}
|
||||
|
||||
int32_t fillCounter = rounds - minCounter;
|
||||
m_schedule.clear();
|
||||
|
||||
for(const FireAutomaton::PatternElement& element : m_pattern)
|
||||
{
|
||||
// stop when all rounds used
|
||||
if(rounds <= 0)
|
||||
break;
|
||||
|
||||
if(element == FireAutomaton::PatternElement::All)
|
||||
{
|
||||
if(rounds >= m_effectorIds.size())
|
||||
{
|
||||
m_schedule.append(m_effectorIds);
|
||||
rounds -= m_effectorIds.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_schedule.append(m_effectorIds.mid(0,rounds));
|
||||
rounds = 0;
|
||||
}
|
||||
}
|
||||
else if(element == FireAutomaton::PatternElement::AllFill && fillCounter > 0)
|
||||
{
|
||||
const int32_t times = fillCounter / m_effectorIds.size();
|
||||
const int32_t remainder = fillCounter % m_effectorIds.size();
|
||||
|
||||
for(int32_t i=0; i < times; i++)
|
||||
{
|
||||
m_schedule.append(m_effectorIds);
|
||||
rounds -= m_effectorIds.size();
|
||||
}
|
||||
|
||||
if(remainder > 0)
|
||||
{
|
||||
m_schedule.append(m_effectorIds.mid(0, remainder));
|
||||
rounds -= remainder;
|
||||
}
|
||||
}
|
||||
else if(element == FireAutomaton::PatternElement::Idle)
|
||||
{
|
||||
m_schedule.append(QVector<int32_t>{});
|
||||
}
|
||||
else if(element == FireAutomaton::PatternElement::Next)
|
||||
{
|
||||
m_schedule.append(QVector<int32_t>{m_effectorIds[m_nextIdx]});
|
||||
m_nextIdx = (m_nextIdx + 1) % m_effectorIds.size();
|
||||
rounds--;
|
||||
}
|
||||
else if(element == FireAutomaton::PatternElement::NextFill && fillCounter > 0)
|
||||
{
|
||||
for(int32_t i = 0; i < fillCounter; i++)
|
||||
{
|
||||
m_schedule.append(QVector<int32_t>{m_effectorIds[i % m_effectorIds.size()]});
|
||||
rounds--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(rounds > 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << " scheduling error - fire pattern does not provide sufficient slots to schedule all rounds. " << rounds;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* AES fire automaton - scheduling fire patterns for multiple effectors
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include <QtCore>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "../logger.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace NGL {
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace AreaEffectSimulation {
|
||||
class AES_COMMON_EXPORT FireAutomaton : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FireAutomaton(QObject *parent = nullptr);
|
||||
|
||||
enum class State {
|
||||
Uninitialized,
|
||||
Stopped,
|
||||
Running,
|
||||
Paused,
|
||||
Stepping
|
||||
};
|
||||
/*
|
||||
Allowed state transitions:
|
||||
|
||||
Uninitialized -> Stopped
|
||||
Running -> Stopped
|
||||
Stepping -> Stopped
|
||||
Paused -> Stopped
|
||||
|
||||
Stopped -> Running
|
||||
Running -> Paused
|
||||
Paused -> Running
|
||||
|
||||
Stopped -> Stepping
|
||||
*/
|
||||
|
||||
enum class TimeType {
|
||||
Duration,
|
||||
Cadence
|
||||
};
|
||||
|
||||
bool init(int32_t time, TimeType type, int32_t rounds, const QVector<int32_t>& effectorIds, const QString& pattern);
|
||||
State getState() const;
|
||||
|
||||
signals:
|
||||
void fire(int32_t effectorId);
|
||||
void finished();
|
||||
|
||||
public slots:
|
||||
bool start();
|
||||
void stop();
|
||||
bool pause();
|
||||
bool resume();
|
||||
bool performStep();
|
||||
|
||||
private slots:
|
||||
void nextTick();
|
||||
|
||||
private:
|
||||
enum class PatternElement {
|
||||
All,
|
||||
AllFill,
|
||||
Next,
|
||||
NextFill,
|
||||
Idle
|
||||
};
|
||||
|
||||
static bool patternElementFromString(const QString& str, FireAutomaton::PatternElement& out);
|
||||
|
||||
bool readPattern(const QString& pattern);
|
||||
bool calculateSchedule(int32_t rounds);
|
||||
void setState(State state);
|
||||
int32_t getMaxTick() const;
|
||||
QVector<int32_t> getEffectorIds(int32_t tick) const;
|
||||
|
||||
private:
|
||||
QVector<int32_t> m_effectorIds;
|
||||
QVector<FireAutomaton::PatternElement> m_pattern;
|
||||
QVector<QVector<int32_t>> m_schedule;
|
||||
QTimer m_timer;
|
||||
int32_t m_nextIdx;
|
||||
int32_t m_tick;
|
||||
int32_t m_durationMSecs;
|
||||
int32_t m_intervalMSecs;
|
||||
int32_t m_remainingTickMSecs;
|
||||
State m_state;
|
||||
|
||||
LOGGER_T m_logger;
|
||||
};
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Firing Timer, a helperclass for ExplosionCluster
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Norbert Thoden, ESVG, 2024-09-03
|
||||
*/
|
||||
|
||||
#include "firing_timer.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <LtsUtilsCore/qpoint3d.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QElapsedTimer>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
|
||||
// To be able to aesily differentiate between different instances:
|
||||
#define MY_LOGFUNCTION "debugInfo='" << d->m_debugInfo << "' " << Q_FUNC_INFO
|
||||
|
||||
|
||||
static long s_counter{0};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class FiringTimer::Private
|
||||
//------------------------------------------------------------------------------
|
||||
class FiringTimer::Private
|
||||
{
|
||||
public:
|
||||
Q_DISABLE_COPY(Private);
|
||||
|
||||
Private()
|
||||
{
|
||||
m_etActive = new QElapsedTimer();
|
||||
m_etPaused = new QElapsedTimer();
|
||||
m_firingTimer = new QTimer();
|
||||
reset();
|
||||
}
|
||||
~Private()
|
||||
{
|
||||
delete m_etActive;
|
||||
delete m_etPaused;
|
||||
}
|
||||
|
||||
QString m_debugInfo;
|
||||
|
||||
|
||||
double m_effectDurationMsec{-1};
|
||||
int m_ammoCount;
|
||||
long m_intervallMsec{-1};
|
||||
long m_ammoFlightDurationMsec{-1};
|
||||
|
||||
FiringMode m_currentFiringMode {FiringMode::FM_Undefined};
|
||||
|
||||
QElapsedTimer *m_etActive{nullptr};
|
||||
qint64 m_activeMsec{0};
|
||||
QElapsedTimer *m_etPaused{nullptr};
|
||||
qint64 m_pausedMsec{0};
|
||||
|
||||
QTimer *m_firingTimer{nullptr};
|
||||
int m_firedAmmoCount{0};
|
||||
|
||||
FiringTimer::CMD m_lastDelayedStateCmd {CMD::CMD_UNDEFINED};
|
||||
|
||||
QStringList m_logHistorie;
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_effectDurationMsec = -1;
|
||||
m_ammoCount = -1;
|
||||
m_intervallMsec = -1;
|
||||
m_ammoFlightDurationMsec = -1;
|
||||
|
||||
m_currentFiringMode = FiringMode::FM_Undefined;
|
||||
|
||||
m_etActive->invalidate();
|
||||
m_activeMsec = 0;
|
||||
m_etPaused->invalidate();
|
||||
m_pausedMsec = 0;
|
||||
|
||||
m_firingTimer->stop();
|
||||
m_firedAmmoCount = 0;
|
||||
|
||||
m_lastDelayedStateCmd = CMD::CMD_UNDEFINED;
|
||||
m_logHistorie.clear();
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
static LOGGER_T logger = getLogger("Utils.FiringTimer");
|
||||
|
||||
INFO_LOG(logger) << " DUMP m_effectDurationMsec=" << m_effectDurationMsec
|
||||
<< ", m_ammoCount=" << m_ammoCount
|
||||
<< "-> m_intervallMsec=" << m_intervallMsec
|
||||
<< ", m_ammoFlightDurationMsec=" << m_ammoFlightDurationMsec
|
||||
<< ", m_currentFiringMode=" << (int)m_currentFiringMode
|
||||
<< ", m_lastDelayedStateCmd=" << m_lastDelayedStateCmd
|
||||
<< ", m_etActive=" << m_etActive
|
||||
<< ", m_activeMsec=" << m_activeMsec
|
||||
<< ", m_etPaused=" << m_etPaused
|
||||
<< ", m_pausedMsec=" << m_pausedMsec
|
||||
<< ", m_firingTimer=" << m_firingTimer
|
||||
<< ", m_firedAmmoCount=" << m_firedAmmoCount;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// class FiringTimer
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
FiringTimer::FiringTimer(const QString &debugInfo)
|
||||
: BaseClass(nullptr)
|
||||
, d(new Private)
|
||||
, m_logger(getLogger("Utils.FiringTimer"))
|
||||
{
|
||||
++s_counter;
|
||||
d->m_debugInfo = debugInfo;
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
|
||||
}
|
||||
|
||||
FiringTimer::~FiringTimer()
|
||||
{
|
||||
--s_counter;
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
|
||||
QString debugInfoSave = d->m_debugInfo;
|
||||
delete d;
|
||||
//DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
|
||||
DEBUG_LOG(m_logger) << "debugInfo='" << debugInfoSave << "'" << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
|
||||
}
|
||||
|
||||
void FiringTimer::setDebugInfo(const QString &debugInfo)
|
||||
{
|
||||
d->m_debugInfo = debugInfo;
|
||||
}
|
||||
|
||||
void FiringTimer::reset()
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
|
||||
d->reset();
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
|
||||
}
|
||||
|
||||
bool FiringTimer::init(long effectDurationMsec, int ammoCount, long ammoFlightDurationMsec)
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START,"
|
||||
<< ", effectDurationMsec=" << effectDurationMsec
|
||||
<< ", ammoCount=" << ammoCount
|
||||
<< ", ammoFlightDurationMsec=" << ammoFlightDurationMsec;
|
||||
reset();
|
||||
|
||||
d->m_effectDurationMsec = effectDurationMsec;
|
||||
d->m_ammoCount = ammoCount;
|
||||
d->m_ammoFlightDurationMsec = ammoFlightDurationMsec;
|
||||
d->m_intervallMsec = static_cast<double>(d->m_effectDurationMsec) / d->m_ammoCount; // ensure float calculation
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " m_intervallMsec " << d->m_intervallMsec;
|
||||
|
||||
d->m_firingTimer->stop();
|
||||
d->m_firingTimer->setSingleShot(false);
|
||||
d->m_firingTimer->setInterval(d->m_intervallMsec);
|
||||
connect(d->m_firingTimer, &QTimer::timeout,
|
||||
this, &FiringTimer::onFiringTimout, Qt::UniqueConnection);
|
||||
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FiringTimer::startFiring()
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
|
||||
|
||||
if ((d->m_currentFiringMode != FiringMode::FM_Undefined) &&
|
||||
(d->m_currentFiringMode != FiringMode::FM_Paused))
|
||||
{
|
||||
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " wrong firingMode, cur = " << (int)d->m_currentFiringMode;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remainingAmmoCount() <= 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left"
|
||||
<< " (total ammoCount was: " << d->m_ammoCount
|
||||
<< " cur firingMode = " << (int)d->m_currentFiringMode;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
if (d->m_currentFiringMode == FiringMode::FM_Paused)
|
||||
{
|
||||
int firedAmmoCountCopy = d->m_firedAmmoCount;
|
||||
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, firedAmmoCountCopy](){
|
||||
emitStateChangeDelayed(CMD::CMD_RESUME, firedAmmoCountCopy);
|
||||
});
|
||||
}
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " start firing with m_firedAmmoCount = " << d->m_firedAmmoCount;
|
||||
|
||||
d->m_firingTimer->start();
|
||||
d->m_currentFiringMode = FiringMode::FM_Active;
|
||||
d->m_etActive->start();
|
||||
|
||||
// maybe m_etPaused is active, maybe not:
|
||||
qint64 pausedElapsedMsec = 0;
|
||||
if (d->m_etPaused->isValid())
|
||||
{
|
||||
pausedElapsedMsec = d->m_etPaused->elapsed();
|
||||
d->m_pausedMsec += pausedElapsedMsec;
|
||||
d->m_etPaused->invalidate();
|
||||
}
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " start firing after [ms]: " << pausedElapsedMsec
|
||||
<< ", m_activeMsec in total = " << d->m_activeMsec
|
||||
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
|
||||
|
||||
QString cmd = QString(QStringLiteral("StartFiring_at_%1")).arg(QDateTime::currentMSecsSinceEpoch());
|
||||
d->m_logHistorie.push_back(cmd);
|
||||
|
||||
// ammoCount : 2 ammo
|
||||
// effect Duration : 10000 msec
|
||||
// => interval : 5000 msec
|
||||
// _____________
|
||||
// | | |
|
||||
// _____________
|
||||
// ^ ^
|
||||
// | |_ fire 2
|
||||
// |_ fire 1
|
||||
//
|
||||
// ^ ^
|
||||
// |_ signal firing(1, 2)
|
||||
// |_ signal firing(2,2)
|
||||
//
|
||||
// ^ ^
|
||||
// |_ signal firingFinishedPlus
|
||||
// |_ signal firingFinished
|
||||
|
||||
|
||||
// to fire at the beginning of the interval:
|
||||
onFiringTimout();
|
||||
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FiringTimer::startFiringDelay(qint64 delayMsec)
|
||||
{
|
||||
if (delayMsec <= 0)
|
||||
return false;
|
||||
QTimer::singleShot(delayMsec, this, &FiringTimer::startFiring);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FiringTimer::pauseFiring()
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
|
||||
|
||||
if (d->m_currentFiringMode != FiringMode::FM_Active)
|
||||
{
|
||||
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " wrong firingMode";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remainingAmmoCount() <= 0)
|
||||
{
|
||||
ERROR_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left"
|
||||
<< " (total ammoCount was: " << d->m_ammoCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
int curAmmoCountCopy = d->m_firedAmmoCount;
|
||||
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, curAmmoCountCopy](){
|
||||
emitStateChangeDelayed(CMD::CMD_PAUSE, curAmmoCountCopy);
|
||||
});
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing at m_firedAmmoCount = " << d->m_firedAmmoCount;
|
||||
d->m_firingTimer->stop();
|
||||
d->m_currentFiringMode = FiringMode::FM_Paused;
|
||||
d->m_etPaused->start();
|
||||
|
||||
|
||||
Q_ASSERT_X(d->m_etActive->isValid(), "FiringTimer", "d->m_etActive not valid");
|
||||
|
||||
|
||||
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
|
||||
d->m_activeMsec += activeElapsedMsec;
|
||||
d->m_etActive->invalidate();
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing after [ms]: " << activeElapsedMsec
|
||||
<< ", m_activeMsec in total = " << d->m_activeMsec
|
||||
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
|
||||
|
||||
QString log = QString(QStringLiteral("pauseFiring_at_%1")).arg(QDateTime::currentMSecsSinceEpoch()) ;
|
||||
d->m_logHistorie.append(log);
|
||||
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
|
||||
return true;
|
||||
}
|
||||
|
||||
long FiringTimer::ammoFlightDurationMsec() const
|
||||
{
|
||||
return d->m_ammoFlightDurationMsec;
|
||||
}
|
||||
|
||||
int FiringTimer::firedAmmoCount() const
|
||||
{
|
||||
return d->m_firedAmmoCount;
|
||||
}
|
||||
|
||||
int FiringTimer::nextAmmoToFire() const
|
||||
{
|
||||
if (remainingAmmoCount() <= 0)
|
||||
return -1;
|
||||
return firedAmmoCount() + 1;
|
||||
}
|
||||
|
||||
int FiringTimer::remainingAmmoCount() const
|
||||
{
|
||||
return d->m_ammoCount - d->m_firedAmmoCount;
|
||||
}
|
||||
|
||||
long FiringTimer::intervallMsec() const
|
||||
{
|
||||
return d->m_intervallMsec;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
void FiringTimer::emitStateChangeDelayed(AreaEffectSimulation::FiringTimer::CMD cmd, int firedAmmoCount)
|
||||
{
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " SUPPORT_STATE_CHANGE_DELAYED: " << cmd
|
||||
<< ", firedAmmoCount = " << firedAmmoCount;
|
||||
emit stateChangeDelayedAsInt(static_cast<int>(cmd), firedAmmoCount);
|
||||
}
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
QStringList FiringTimer::getLogHistorie() const
|
||||
{
|
||||
return d->m_logHistorie;
|
||||
}
|
||||
|
||||
QString FiringTimer::toString(const CMD cmd)
|
||||
{
|
||||
switch(cmd)
|
||||
{
|
||||
case CMD::CMD_UNDEFINED:
|
||||
return QStringLiteral("CMD_UNDEFINED");
|
||||
case CMD::CMD_START:
|
||||
return QStringLiteral("CMD_START");
|
||||
case CMD::CMD_RESUME:
|
||||
return QStringLiteral("CMD_RESUME");
|
||||
case CMD::CMD_PAUSE:
|
||||
return QStringLiteral("CMD_PAUSE");
|
||||
case CMD::CMD_EXPIRED:
|
||||
return QStringLiteral("CMD_EXPIRED");
|
||||
case CMD::CMD_EXPIRED_PLUS:
|
||||
return QStringLiteral("CMD_EXPIRED_PLUS");
|
||||
default:
|
||||
return QStringLiteral("CMD_Error");
|
||||
}
|
||||
}
|
||||
|
||||
void FiringTimer::onFiringTimout()
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
|
||||
|
||||
++d->m_firedAmmoCount;
|
||||
|
||||
INFO_LOG(m_logger) << MY_LOGFUNCTION << " emit firing with: " << d->m_firedAmmoCount
|
||||
<< " of total ammoCount = " << d->m_ammoCount;
|
||||
emit firing(d->m_firedAmmoCount, d->m_ammoCount);
|
||||
|
||||
// prepared signal for impact, not interruptable
|
||||
if (d->m_ammoFlightDurationMsec >= 0)
|
||||
{
|
||||
// copy values:
|
||||
// i am not sure, if '=' would work here, so copy to local vars:
|
||||
int currentFiringNumber = d->m_firedAmmoCount;
|
||||
int totalFiringNumber = d->m_ammoCount;
|
||||
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " XXXXXXXXX schedule 'emit impact' " << currentFiringNumber;
|
||||
|
||||
QTimer::singleShot(d->m_ammoFlightDurationMsec, this, [this, currentFiringNumber, totalFiringNumber]()
|
||||
{
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
if (currentFiringNumber == 1) // Number -> start counting at '1'
|
||||
{
|
||||
emitStateChangeDelayed(CMD::CMD_START, currentFiringNumber);
|
||||
}
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
INFO_LOG(m_logger) << MY_LOGFUNCTION << " emit impact " << currentFiringNumber
|
||||
<< " of total = " << totalFiringNumber;
|
||||
emit impact(currentFiringNumber, totalFiringNumber);
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
if (currentFiringNumber >= totalFiringNumber)
|
||||
{
|
||||
emitStateChangeDelayed(CMD::CMD_EXPIRED, currentFiringNumber);
|
||||
|
||||
// for ..PLUS we need a delay:
|
||||
QTimer::singleShot(d->m_intervallMsec, this, [this, currentFiringNumber]()
|
||||
{
|
||||
emitStateChangeDelayed(CMD::CMD_EXPIRED_PLUS, currentFiringNumber);
|
||||
});
|
||||
}
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (d->m_firedAmmoCount >= d->m_ammoCount)
|
||||
{
|
||||
INFO_LOG(m_logger) << MY_LOGFUNCTION << " no ammo left, finishing firing"
|
||||
<< ", " << d->m_firedAmmoCount << " of total ammoCount = " << d->m_ammoCount << " are fired";
|
||||
d->m_firingTimer->stop();
|
||||
|
||||
d->m_currentFiringMode = FiringMode::FM_Expired;
|
||||
|
||||
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
|
||||
|
||||
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
|
||||
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing after [ms]: " << activeElapsedMsec
|
||||
<< ", m_activeMsec in total = " << d->m_activeMsec
|
||||
<< ", m_pausedMsec in total = " << d->m_pausedMsec
|
||||
<< " now FiringTimoutExpiredPlus ...";
|
||||
|
||||
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
|
||||
|
||||
emit firingFinished();
|
||||
|
||||
d->dump();
|
||||
|
||||
// FM_ExpiredPlus
|
||||
// maybe use disconnect via QMetaObject :
|
||||
delete d->m_firingTimer;
|
||||
d->m_firingTimer = new QTimer();
|
||||
d->m_firingTimer->setInterval(d->m_intervallMsec);
|
||||
// : maybe use disconnect via QMetaObject
|
||||
|
||||
connect(d->m_firingTimer, &QTimer::timeout,
|
||||
this, &FiringTimer::onFiringTimoutExpiredPlus, Qt::UniqueConnection);
|
||||
|
||||
d->m_firingTimer->setSingleShot(true);
|
||||
d->m_firingTimer->start();
|
||||
}
|
||||
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END" << ", m_firedAmmoCount " << d->m_firedAmmoCount;
|
||||
}
|
||||
|
||||
void FiringTimer::onFiringTimoutExpiredPlus()
|
||||
{
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " START";
|
||||
|
||||
d->m_currentFiringMode = FiringMode::FM_ExpiredPlus;
|
||||
|
||||
const qint64 activeElapsedMsec = d->m_etActive->elapsed();
|
||||
d->m_activeMsec += activeElapsedMsec;
|
||||
d->m_etActive->invalidate();
|
||||
|
||||
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
|
||||
|
||||
DEBUG_LOG(m_logger) << MY_LOGFUNCTION << " stop firing2 after [ms]: " << activeElapsedMsec
|
||||
<< ", m_activeMsec in total = " << d->m_activeMsec
|
||||
<< ", m_pausedMsec in total = " << d->m_pausedMsec;
|
||||
|
||||
// !! Attention: do pauseFiring() WITHIN the intervall will reduce amount of time the firing is active !!
|
||||
|
||||
emit firingFinishedPlus();
|
||||
|
||||
TRACE_LOG(m_logger) << MY_LOGFUNCTION << " END";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
AES_COMMON_EXPORT std::ostream &operator<< (std::ostream &stream, const FiringTimer::CMD value)
|
||||
{
|
||||
stream << FiringTimer::toString(value);
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Firing Timer, a helperclass for ExplosionCluster
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Norbert Thoden, ESVG, 2024-09-03
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "aes_export.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
#define SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
class AES_COMMON_EXPORT FiringTimer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
using BaseClass = QObject;
|
||||
|
||||
public:
|
||||
|
||||
enum class CMD
|
||||
{
|
||||
CMD_UNDEFINED,
|
||||
CMD_START,
|
||||
CMD_RESUME,
|
||||
CMD_PAUSE,
|
||||
CMD_EXPIRED,
|
||||
CMD_EXPIRED_PLUS,
|
||||
};
|
||||
Q_ENUM(CMD);
|
||||
|
||||
|
||||
enum class FiringMode
|
||||
{
|
||||
FM_Undefined = 0, //
|
||||
FM_Prepared = 1, //
|
||||
FM_Active = 2, //
|
||||
FM_Paused = 3, //
|
||||
FM_Expired = 4, //
|
||||
FM_ExpiredPlus = 6 //
|
||||
};
|
||||
|
||||
// ctor/dtor
|
||||
explicit FiringTimer(const QString &debugInfo = QStringLiteral("FiringTimer"));
|
||||
~FiringTimer();
|
||||
|
||||
void setDebugInfo(const QString &debugInfo);
|
||||
|
||||
// initialisation
|
||||
bool init(long effectDurationMsec, int ammoCount, long ammoFlightDurationMsec);
|
||||
void reset();
|
||||
|
||||
//
|
||||
bool startFiring();
|
||||
bool pauseFiring();
|
||||
bool resumeFiring();
|
||||
|
||||
|
||||
FiringMode currentFiringMode() const;
|
||||
long ammoFlightDurationMsec() const;
|
||||
int firedAmmoCount() const;
|
||||
int nextAmmoToFire() const;
|
||||
int remainingAmmoCount() const;
|
||||
long intervallMsec() const;
|
||||
|
||||
|
||||
QStringList getLogHistorie() const;
|
||||
|
||||
static QString toString(const CMD cmd);
|
||||
|
||||
public Q_SLOTS:
|
||||
bool startFiringDelay(qint64 delayMsec);
|
||||
|
||||
Q_SIGNALS:
|
||||
void firing(int currentFiringNumber, int totalAmmoCount);
|
||||
void impact(int currentImpactNumber, int totalAmmoCount);
|
||||
|
||||
void firingFinished();
|
||||
void firingFinishedPlus();
|
||||
|
||||
#ifdef SUPPORT_STATE_CHANGE_DELAYED
|
||||
// Q_SIGNALS:
|
||||
// void stateChangeDelayed(AreaEffectSimulation::FiringTimer::CMD cmd); // problems: Q_DECLARE_METATYPE and/or qRegisterMetatType... - grrrr
|
||||
void stateChangeDelayedAsInt(int cmdAsInt, int firedAmmoCount);
|
||||
public Q_SLOTS:
|
||||
void emitStateChangeDelayed(FiringTimer::CMD cmd, int firedAmmoCount);
|
||||
#endif // SUPPORT_STATE_CHANGE_DELAYED
|
||||
|
||||
protected Q_SLOTS:
|
||||
void onFiringTimout();
|
||||
void onFiringTimoutExpiredPlus();
|
||||
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
||||
Private *d;
|
||||
LOGGER_T m_logger; ///< Interface of the Logging-Facility
|
||||
};
|
||||
|
||||
AES_COMMON_EXPORT std::ostream &operator<< (std::ostream &stream, const FiringTimer::CMD value);
|
||||
|
||||
} // namespace AreaEffectSimulation
|
||||
|
||||
// at global scope
|
||||
Q_DECLARE_METATYPE(AreaEffectSimulation::FiringTimer::CMD);
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "icons.h"
|
||||
#include "polygons.h"
|
||||
|
||||
#include <AreaEffectSimulation/aes.hpp>
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
#include <QGuiApplication>
|
||||
#include <QIcon>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QTransform>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
namespace Icons
|
||||
{
|
||||
|
||||
// Todo: Remove
|
||||
const int NEBEL = 1;
|
||||
|
||||
struct PolygonData
|
||||
{
|
||||
QPolygonF poly;
|
||||
QColor color;
|
||||
Qt::BrushStyle fillPattern = Qt::NoBrush;
|
||||
|
||||
PolygonData() = default;
|
||||
PolygonData(const QPolygonF &_poly,
|
||||
const QColor &_color,
|
||||
const Qt::BrushStyle &_fillPattern)
|
||||
: poly(_poly)
|
||||
, color(_color)
|
||||
, fillPattern(_fillPattern)
|
||||
{}
|
||||
};
|
||||
|
||||
static QPixmap pixmapFromPolygons(const QVector<PolygonData> &datas,
|
||||
int width,
|
||||
const QColor &bgcolor = Qt::transparent)
|
||||
{
|
||||
if (datas.isEmpty() || datas.first().poly.isEmpty())
|
||||
return {};
|
||||
|
||||
QPolygonF polyUtm;
|
||||
QVector<QPolygonF> polysUtm;
|
||||
polysUtm.reserve(datas.size() * 10);
|
||||
int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(datas.first().poly.first()).zone;
|
||||
for (const PolygonData &data : datas)
|
||||
{
|
||||
polyUtm.clear();
|
||||
for (const QPointF &point : qAsConst(data.poly))
|
||||
{
|
||||
LtsCoordTrans::UtmCoordinate pointUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(point, zone);
|
||||
polyUtm << QPointF(pointUtm.x, pointUtm.y);
|
||||
}
|
||||
polysUtm << polyUtm;
|
||||
}
|
||||
|
||||
QPolygonF bounding;
|
||||
bounding.reserve(4 * datas.size());
|
||||
// Does not matter, how the total polygon looks as we just need the bounding rect.
|
||||
// << is cheaper than .united()
|
||||
for (const QPolygonF &polyUtmLocal : qAsConst(polysUtm))
|
||||
bounding << polyUtmLocal;
|
||||
|
||||
// center to center / Todo:: Does it always work?
|
||||
const QTransform mTrans(QTransform::fromTranslate(-bounding.boundingRect().bottomLeft().x(), -bounding.boundingRect().bottomLeft().y()));
|
||||
|
||||
const auto dpr = qApp->devicePixelRatio();
|
||||
width *= dpr;
|
||||
qreal w = bounding.boundingRect().width();
|
||||
qreal h = bounding.boundingRect().height();
|
||||
double scale = w > h ? width / w : width / h;
|
||||
w *= scale;
|
||||
h *= scale;
|
||||
scale *= 0.96; // Leave 4% border (i.e. 2% each side)
|
||||
const QTransform mScale(QTransform::fromScale(scale, -scale));
|
||||
const QTransform mTransSmall(QTransform::fromTranslate(0.02 * width + (width - w) / 2, 0.02 * width + (width - h) / 2));
|
||||
|
||||
QPixmap pix(width, width);
|
||||
pix.setDevicePixelRatio(dpr);
|
||||
pix.fill(bgcolor);
|
||||
|
||||
QPainter p(&pix);
|
||||
int i = 0;
|
||||
for (const PolygonData &d : qAsConst(datas))
|
||||
{
|
||||
QPolygonF mPoly = mTrans.map(polysUtm.at(i++));
|
||||
mPoly = mScale.map(mPoly);
|
||||
mPoly = mTransSmall.map(mPoly);
|
||||
|
||||
|
||||
p.setBrush(QBrush(d.color, d.fillPattern));
|
||||
p.setPen(QPen(p.brush(), 2));
|
||||
p.drawPolygon(mPoly);
|
||||
}
|
||||
p.end();
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
static QPixmap pixmapFromPolygon(const QPolygonF &poly,
|
||||
const QColor &color,
|
||||
const Qt::BrushStyle &fillPattern,
|
||||
const int width,
|
||||
const QColor &bgcolor = Qt::transparent)
|
||||
{
|
||||
return pixmapFromPolygons({PolygonData(poly, color, fillPattern)}, width, bgcolor);
|
||||
}
|
||||
|
||||
QIcon nContamination(const Database::AesNContamination &awf, int size)
|
||||
{
|
||||
const QPolygonF poly = Polygons::nContamination(awf);
|
||||
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
|
||||
}
|
||||
|
||||
QIcon bcContamination(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
double expansionFactor,
|
||||
const QDateTime ¤tTime,
|
||||
int size)
|
||||
{
|
||||
const QPolygonF poly = Polygons::bcContamination(cwf, volatilityType, expansionFactor, currentTime);
|
||||
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
|
||||
}
|
||||
|
||||
QIcon explosionSingle(const Database::AesExplosionSingle &es, double chargeRadius, int size)
|
||||
{
|
||||
QPolygonF poly = Polygons::explosionSingle(es, chargeRadius);
|
||||
|
||||
// Todo!
|
||||
QColor color = (es.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
|
||||
QColor bgrnd = Qt::transparent;
|
||||
if (es.chargeTypeId != NEBEL) // Nebel / Smoke
|
||||
{
|
||||
color = Qt::red; // Todo: Teamcolor! teams->color(ef.partei_id);
|
||||
bgrnd = QColor(0, 0, 0, 128);
|
||||
}
|
||||
return QIcon(pixmapFromPolygon(poly, color, Qt::Dense4Pattern, size, bgrnd));
|
||||
}
|
||||
|
||||
QIcon explosionArea(const Database::AesExplosionArea &ea, int size)
|
||||
{
|
||||
// Todo!
|
||||
QColor color = (ea.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
|
||||
QColor bgrnd = Qt::transparent;
|
||||
if (ea.chargeTypeId != NEBEL) // Nebel / Smoke
|
||||
{
|
||||
color = Qt::red; // Todo: Teamcolor! teams->color(ff.partei_id);
|
||||
bgrnd = QColor(0, 0, 0, 128);
|
||||
}
|
||||
QPolygonF area = Polygons::explosionArea(ea);
|
||||
return QIcon(pixmapFromPolygon(area, color, Qt::Dense4Pattern, size, bgrnd));
|
||||
}
|
||||
|
||||
QIcon explosionAreaSingleExplosions(const Database::AesExplosionArea &ea, int size)
|
||||
{
|
||||
// Todo!
|
||||
QColor color = (ea.chargeTypeId == NEBEL) ? Qt::lightGray : Qt::yellow;
|
||||
if (ea.chargeTypeId != NEBEL) // Nebel / Smoke
|
||||
color = Qt::red; // Todo: Teamcolor! teams->color(ea.partei_id);
|
||||
|
||||
const QVector<QPointF> singleExplosions = Polygons::explosionAreaSingleExplosions(ea);
|
||||
QVector<PolygonData> datas;
|
||||
datas.reserve(singleExplosions.size() + 1);
|
||||
datas << PolygonData(Polygons::explosionArea(ea), color, Qt::Dense4Pattern);
|
||||
for (const QPointF &explosion : singleExplosions)
|
||||
{
|
||||
/* TODO
|
||||
if (explosion.time() < QDateTime::currentMSecsSinceEpoch())
|
||||
datas << PolygonData(Polygons::circle(explosion.position(), 5), Qt::yellow, Qt::Dense4Pattern);
|
||||
else
|
||||
*/
|
||||
datas << PolygonData(Polygons::circle(explosion, 5), Qt::green, Qt::Dense4Pattern);
|
||||
}
|
||||
|
||||
return QIcon(pixmapFromPolygons(datas, size));
|
||||
}
|
||||
|
||||
QIcon mineArea(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments, int size)
|
||||
{
|
||||
QPolygonF gesamtPolygon = Polygons::mineArea(segments, area.width, area.density, area.isCenterLine);
|
||||
|
||||
const QVector<QPointF> minePositions = Polygons::minePositions(area, segments);
|
||||
QVector<PolygonData> datas;
|
||||
datas.reserve(minePositions.size() + 1);
|
||||
datas << PolygonData(gesamtPolygon, Qt::green, Qt::Dense4Pattern);
|
||||
for (const QPointF minePos : minePositions)
|
||||
datas << PolygonData(Polygons::circle(minePos, 5), Qt::red, Qt::Dense4Pattern);
|
||||
|
||||
return QIcon(pixmapFromPolygons(datas, size));
|
||||
}
|
||||
|
||||
|
||||
QIcon mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, int width, int size)
|
||||
{
|
||||
const QPolygonF poly = Polygons::mineCorridor(segments, width);
|
||||
return QIcon(pixmapFromPolygon(poly, Qt::yellow, Qt::Dense4Pattern, size, QColor(0, 0, 0, 128)));
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
} // Namespace
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_db_structs.h>
|
||||
#include <ProjectDataManagement/pdm_enums.hpp>
|
||||
|
||||
class QIcon;
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
namespace Icons
|
||||
{
|
||||
AES_COMMON_EXPORT QIcon nContamination(const Database::AesNContamination &awf, int size = 256);
|
||||
AES_COMMON_EXPORT QIcon bcContamination(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
double expansionFactor,
|
||||
const QDateTime ¤tTime,
|
||||
int size = 256);
|
||||
AES_COMMON_EXPORT QIcon explosionSingle(const Database::AesExplosionSingle &es, double chargeRadius, int size = 256);
|
||||
AES_COMMON_EXPORT QIcon explosionArea(const Database::AesExplosionArea &ea, int size = 256);
|
||||
AES_COMMON_EXPORT QIcon explosionAreaSingleExplosions(const Database::AesExplosionArea &ea, int size = 256);
|
||||
AES_COMMON_EXPORT QIcon mineArea(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments, int size = 256);
|
||||
AES_COMMON_EXPORT QIcon mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, int width, int size = 256);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Test ob sich ein Rechteck mit Wirkfeldformen überschneidet
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "intersections.h"
|
||||
#include "polygons.h"
|
||||
|
||||
#include <QtGui/QPolygonF>
|
||||
#include <QtGui/QPainterPath>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
namespace Intersections
|
||||
{
|
||||
|
||||
static inline bool intersects(const QPolygonF &p1, const QPolygonF &p2)
|
||||
{
|
||||
QPainterPath pp1;
|
||||
pp1.addPolygon(p1);
|
||||
QPainterPath pp2;
|
||||
pp2.addPolygon(p2);
|
||||
return pp1.intersects(pp2);
|
||||
}
|
||||
|
||||
bool nContamination(const Database::AesNContamination &awf,
|
||||
const QPolygonF &poly)
|
||||
{
|
||||
return (intersects(Polygons::nContamination(awf), poly));
|
||||
}
|
||||
|
||||
bool bcContamination(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
double expansionFactor,
|
||||
const QDateTime ¤tTime,
|
||||
const QPolygonF &poly)
|
||||
{
|
||||
return (intersects(Polygons::bcContamination(cwf, volatilityType, expansionFactor, currentTime), poly));
|
||||
}
|
||||
|
||||
bool explosionSingle(const Database::AesExplosionSingle &es,
|
||||
double chargeRadius,
|
||||
const QPolygonF &poly)
|
||||
{
|
||||
return (intersects(Polygons::explosionSingle(es, chargeRadius), poly));
|
||||
}
|
||||
|
||||
bool explosionArea(const Database::AesExplosionArea &ea,
|
||||
const QPolygonF &poly)
|
||||
{
|
||||
return (intersects(Polygons::explosionArea(ea), poly));
|
||||
}
|
||||
|
||||
bool mineArea(const QVector<Database::AesMineAreaSegment> &segments,
|
||||
double width,
|
||||
double density,
|
||||
bool bIsCenterLine,
|
||||
const QPolygonF &poly)
|
||||
{
|
||||
// if(!area.boundingRect.isEmpty() && !area.boundingRect.intersects(rect))
|
||||
// return false;
|
||||
|
||||
return (intersects(Polygons::mineArea(segments, width, density, bIsCenterLine), poly));
|
||||
}
|
||||
|
||||
QVector<int> mineIdsInCorridor(const Database::AesMineArea &area,
|
||||
const QVector<Database::AesMineAreaSegment> &segments,
|
||||
const QVector<QPolygonF> &corridorSegments)
|
||||
{
|
||||
QVector<int> minesIds;
|
||||
const QVector<QPointF> singleMines = Polygons::minePositions(area, segments);
|
||||
|
||||
// Other possibility: Create total polygon by uniting corridor segments. But that may be less efficient.
|
||||
for (const QPolygonF &corridorSegment : corridorSegments)
|
||||
{
|
||||
for (int i = 0; i < singleMines.size(); ++i)
|
||||
{
|
||||
if (corridorSegment.contains(singleMines[i]))
|
||||
minesIds << i;
|
||||
}
|
||||
}
|
||||
return minesIds;
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* Test ob sich ein Rechteck mit Wirkfeldformen überschneidet
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_db_structs.h>
|
||||
#include <ProjectDataManagement/pdm_enums.hpp>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
namespace Intersections
|
||||
{
|
||||
AES_COMMON_EXPORT bool nContamination(const Database::AesNContamination &awf,
|
||||
const QPolygonF &poly);
|
||||
AES_COMMON_EXPORT bool bcContamination(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
double expansionFactor,
|
||||
const QDateTime ¤tTime,
|
||||
const QPolygonF &poly);
|
||||
AES_COMMON_EXPORT bool explosionSingle(const Database::AesExplosionSingle &es,
|
||||
double chargeRadius,
|
||||
const QPolygonF &poly);
|
||||
AES_COMMON_EXPORT bool explosionArea(const Database::AesExplosionArea &ea,
|
||||
const QPolygonF &poly);
|
||||
AES_COMMON_EXPORT bool mineArea(const QVector<Database::AesMineAreaSegment> &segments,
|
||||
double width,
|
||||
double density,
|
||||
bool bIsCenterLine,
|
||||
const QPolygonF &poly);
|
||||
|
||||
|
||||
// keep area (instead of width/density) as also random number is needed!
|
||||
AES_COMMON_EXPORT QVector<int> mineIdsInCorridor(const Database::AesMineArea &area,
|
||||
const QVector<Database::AesMineAreaSegment> &segments,
|
||||
const QVector<QPolygonF> &corridorSegments);
|
||||
}
|
||||
}
|
||||
+1769
File diff suppressed because it is too large.
Load diff
+176
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_db_structs.h>
|
||||
#include <ProjectDataManagement/pdm_enums.hpp>
|
||||
|
||||
#include <QColor>
|
||||
#include <QPolygonF>
|
||||
#include <QRectF>
|
||||
|
||||
class QPointF;
|
||||
|
||||
namespace LtsCoordTrans
|
||||
{
|
||||
struct UtmCoordinate;
|
||||
}
|
||||
|
||||
namespace SituationMap
|
||||
{
|
||||
class ElevationMap;
|
||||
}
|
||||
|
||||
namespace ParticipantManagement
|
||||
{
|
||||
namespace Interface
|
||||
{
|
||||
class Dtc;
|
||||
}
|
||||
} // namespace ParticipantManagement
|
||||
|
||||
namespace NGL
|
||||
{
|
||||
class INGLLogger;
|
||||
}
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
// N. Thoden, 18. jan 2024:
|
||||
// I can´t find a file like aes_types.h, aes_constants.h.
|
||||
// So i use polygons.h to provide following for service as well as gui:
|
||||
static const int s_maxTrajectoriesPerExplosionCluster = 900;
|
||||
|
||||
namespace Polygons
|
||||
{
|
||||
using Trajectory = QPair<QPolygonF, QVector<double>>;
|
||||
|
||||
struct AES_COMMON_EXPORT AreaBottomLeft
|
||||
{
|
||||
explicit AreaBottomLeft(const QPointF &cornerBottomLeftMeter, double widthMeter, double heightMeter, double rotation_deg);
|
||||
explicit AreaBottomLeft(const LtsCoordTrans::UtmCoordinate &cornerBottomLeftMeter, double widthMeter, double heightMeter, double rotation_deg);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
QPointF m_cornerBottomLeft; // meter
|
||||
double m_width{0}; // x; meter,
|
||||
double m_height{0}; // y; meter, aka length
|
||||
double m_rotation_deg{0};
|
||||
};
|
||||
|
||||
AES_COMMON_EXPORT QPixmap pixmapFromPolygon(const QPolygonF &poly,
|
||||
const QColor &color,
|
||||
const int &width,
|
||||
const Qt::BrushStyle &fillPattern = Qt::BrushStyle::NoBrush,
|
||||
bool isLine = false,
|
||||
const QColor &bgcolor = Qt::transparent);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF circle(const QPointF ¢er, qreal radius, int steps = 72);
|
||||
AES_COMMON_EXPORT QVector<double> trajectory(const double &distance, const double &height, double angle, int steps = 72);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF nContamination(const Database::AesNContamination &awf);
|
||||
AES_COMMON_EXPORT void bcCurrentCenterPoint(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
const QDateTime ¤tTime,
|
||||
QPointF &newCenterPointGeographic,
|
||||
double &distance);
|
||||
AES_COMMON_EXPORT QPolygonF bcContamination(const Database::AesBCContamination &cwf,
|
||||
ProjectDataManagement::VolatilityType volatilityType,
|
||||
double expansionFactor,
|
||||
const QDateTime ¤tTime);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF airSupportImpact(const Database::AesAirSupportImpact &as, double chargeRadius);
|
||||
AES_COMMON_EXPORT QPolygonF explosionSingle(const Database::AesExplosionSingle &ef, double chargeRadius);
|
||||
AES_COMMON_EXPORT QPolygonF explosionArea(const Database::AesExplosionArea &ea);
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionAreaSingleExplosions(const Database::AesExplosionArea &explosionArea);
|
||||
|
||||
// ExplosionCluster (3D-Wirksim)
|
||||
AES_COMMON_EXPORT AreaBottomLeft explosionClusterAddTla(const AreaBottomLeft &areaBottomLeftIn, int tla);
|
||||
AES_COMMON_EXPORT QPolygonF explosionClusterArea(const Database::AesExplosionCluster &explosionCluster); // geometryType-aware
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterSingleExplosionsGAUSS(const Database::AesExplosionCluster &explosionCluster);
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterSingleExplosionsGAUSS_Mtp(const Database::AesExplosionCluster &explosionCluster, double mtpRotationNorthDegree);
|
||||
|
||||
AES_COMMON_EXPORT LtsUtils::QPoint3D calculateFiringUnitCenterNonRecursive(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
|
||||
int unitParticipantId,
|
||||
std::vector<Core::GeoPosition> &singlePositionsOut,
|
||||
NGL::INGLLogger &logger);
|
||||
|
||||
// MTP = Mittlerer TrefferPunkt
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterAreaToMtps(const AreaBottomLeft &boundary,
|
||||
int numberOfMTPs,
|
||||
double *singleWidthOut = nullptr,
|
||||
double *singleHeightOut = nullptr);
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterRadialToMtps(const AreaBottomLeft &boundary,
|
||||
int numberOfMTPs,
|
||||
const LtsCoordTrans::UtmCoordinate &firingPos,
|
||||
const LtsCoordTrans::UtmCoordinate &circleCenter,
|
||||
double *singleWidthOut = nullptr,
|
||||
double *singleHeightOut = nullptr);
|
||||
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterAreaToMtpsRelativeToBottomLeft(const AreaBottomLeft &boundary,
|
||||
int numberOfMTPs,
|
||||
double *singleWidthOut = nullptr,
|
||||
double *singleHeightOut = nullptr);
|
||||
AES_COMMON_EXPORT QVector<QPointF> explosionClusterRadialToMtpsRelativeToBottomLeft(const AreaBottomLeft &boundary,
|
||||
int numberOfMTPs,
|
||||
const LtsCoordTrans::UtmCoordinate &firingPos,
|
||||
const LtsCoordTrans::UtmCoordinate &circleCenter,
|
||||
double *singleWidthOut = nullptr,
|
||||
double *singleHeightOut = nullptr);
|
||||
|
||||
AES_COMMON_EXPORT qint64 explosionClusterCalculatedFlightDuration2DMSec(const Database::AesExplosionCluster &explosionCluster, int timeOffsetMSec = 2000);
|
||||
AES_COMMON_EXPORT qint64 explosionClusterEstimatedMaxFlightDuration2DMSec(qint64 calculatedFlightDuration, int calculationOffsetMSec = 10000);
|
||||
|
||||
// EffectCluster (3D-Wirksim/Adler)
|
||||
AES_COMMON_EXPORT QPolygonF effectClusterArea(const Database::AesEffectCluster &effectCluster); // geometryType-aware
|
||||
AES_COMMON_EXPORT QVector<QPointF> effectClusterSingleExplosionsGAUSS(const Database::AesEffectCluster &effectCluster);
|
||||
AES_COMMON_EXPORT QVector<QPointF> effectClusterSingleExplosionsGAUSS_Mtp(const Database::AesEffectCluster &effectCluster,
|
||||
const QHash<int, Database::AesEffectorData> &effectorData);
|
||||
|
||||
// offRouteMine
|
||||
AES_COMMON_EXPORT QPolygonF offRouteMineArea(const Database::AesOffRouteMine &ea);
|
||||
AES_COMMON_EXPORT QPolygonF offRouteMineAreaImpact(const Database::AesOffRouteMine &ea, int steps = 72);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF boobyTrapCake(const Database::AesBoobyTrap &boobyTrap, qreal radius);
|
||||
|
||||
AES_COMMON_EXPORT QPointF bottomLeft2CenterPoint(QPointF bottomLeft, double width, double height, double rotation);
|
||||
AES_COMMON_EXPORT QPointF centerPoint2BottomLeft(QPointF centerPoint, double width, double height, double rotation);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF functionalArea(const Database::AesFunctionalArea &fa);
|
||||
AES_COMMON_EXPORT QPolygonF technicalArea(const Database::AesTechnicalArea &ta);
|
||||
AES_COMMON_EXPORT QPolygonF reconArea(const Database::AesReconArea &ra);
|
||||
AES_COMMON_EXPORT QPolygonF dangerArea(const QVector<QPointF> &segments);
|
||||
AES_COMMON_EXPORT std::vector<Core::GeoPosition2D> dangerAreaDefaultSegments(const QPointF ¢erPos, const int &radius);
|
||||
// AES_COMMON_EXPORT QPolygonF dangerArea(const shm_gefahrenbereich_rt& gb);
|
||||
AES_COMMON_EXPORT QPolygonF borderline(const QVector<QPointF> &segments);
|
||||
AES_COMMON_EXPORT QPolygonF gasPipe(const QVector<Database::AesGasPipeSegment> &segments, double width);
|
||||
AES_COMMON_EXPORT QPolygonF gasPipeSegment(const Database::AesGasPipeSegment &start, const Database::AesGasPipeSegment &end, double width);
|
||||
|
||||
AES_COMMON_EXPORT QPolygonF mineArea(const QVector<Database::AesMineAreaSegment> &segments, double width, double density, bool bIsCenterLine);
|
||||
AES_COMMON_EXPORT QVector<QPolygonF> mineAreaSingleSegments(const QVector<Database::AesMineAreaSegment> &segments, double width, bool bIsCenterLine);
|
||||
AES_COMMON_EXPORT QPolygonF mineAreaSegment(const Database::AesMineAreaSegment &segment, double width, bool bIsCenterLine);
|
||||
AES_COMMON_EXPORT QPolygonF mineCorridor(const QVector<Database::AesMineCorridorSegment> &segments, double width);
|
||||
AES_COMMON_EXPORT QVector<QPolygonF> mineCorridorSingleSegments(const QVector<Database::AesMineCorridorSegment> &segments, double width);
|
||||
AES_COMMON_EXPORT QPolygonF mineCorridorSegment(const Database::AesMineCorridorSegment &start, const Database::AesMineCorridorSegment &end, double width);
|
||||
|
||||
// keep area (instead of width/density) as also random number is needed!
|
||||
AES_COMMON_EXPORT QVector<QPointF> minePositions(const Database::AesMineArea &area, const QVector<Database::AesMineAreaSegment> &segments);
|
||||
|
||||
AES_COMMON_EXPORT int getSequelNumber(const Database::AesExplosionArea &explosionArea);
|
||||
AES_COMMON_EXPORT int getSequelNumber(int startSequelNumber, int impactCount, qint64 originalEffectDurationMs, qint64 effectDurationMs);
|
||||
// calculates the minimum Area Enclosing Rectangle, bottom left is OUR Bottom Left, outangleDregrees is already counter clockwise
|
||||
AES_COMMON_EXPORT QRectF getMinAreaRect(const QVector<QPointF> &points, double &outAngleDegrees);
|
||||
|
||||
// Hilfsfunktion für die konvexe Hülle (Monotone Chain Algorithmus)
|
||||
QVector<QPointF> calculateConvexHull(const QVector<QPointF> &points);
|
||||
AES_COMMON_EXPORT QVector<LtsCoordTrans::UtmCoordinate> geoVecToUtm(const std::vector<Core::GeoPosition2D> &vecPos, int utmZone = -1);
|
||||
AES_COMMON_EXPORT QVector<QPointF> geoVecToUtmPointF(const std::vector<Core::GeoPosition2D> &vecPos, int utmZone = -1);
|
||||
|
||||
} // namespace Polygons
|
||||
} // namespace AreaEffectSimulation
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* main for AreaEffectSimulation
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include <nglapplication/nglapplication.h>
|
||||
#include <NGLLogger/INGLLogger.h>
|
||||
#include <NGLLogger/NGLLoggerBootstrap.h>
|
||||
#include "icons.h"
|
||||
#include "polygons.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLabel>
|
||||
#include <QIcon>
|
||||
|
||||
#include <LtsCoordTrans/lts_coord_trans.h>
|
||||
|
||||
QIcon iconExplosionArea()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesExplosionArea ff;
|
||||
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
ff.impactWidth = 50;
|
||||
ff.impactLength = 10;
|
||||
ff.rotationAngle = 30;
|
||||
|
||||
return AreaEffectSimulation::Icons::explosionArea(ff);
|
||||
}
|
||||
|
||||
QIcon iconExplosionAreaSingleExplosions()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesExplosionArea ff;
|
||||
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
ff.impactWidth = 50;
|
||||
ff.id = 42;
|
||||
ff.impactLength = 500;
|
||||
ff.rotationAngle = 30;
|
||||
ff.effectStart = QDateTime::currentDateTimeUtc().addSecs(-300);
|
||||
ff.effectDuration = 600000;
|
||||
ff.cannonCount = 5;
|
||||
ff.cannonRounds = 5;
|
||||
|
||||
return AreaEffectSimulation::Icons::explosionAreaSingleExplosions(ff);
|
||||
}
|
||||
|
||||
QIcon iconNContamination()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesNContamination ff;
|
||||
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
ff.rotationAngle = 30;
|
||||
ff.borderLengthInner = 50;
|
||||
ff.borderLengthOuter = 100;
|
||||
ff.impactRadius = 50;
|
||||
ff.spanAngle = 60;
|
||||
|
||||
return AreaEffectSimulation::Icons::nContamination(ff);
|
||||
}
|
||||
|
||||
QIcon iconBcContamination()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesBCContamination ff;
|
||||
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
ff.impactRadius = 500;
|
||||
ff.weatherPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7200062000"));
|
||||
ff.weatherTimestamp = QDateTime::currentDateTimeUtc().addSecs(-3600);
|
||||
ff.effectStart = ff.weatherTimestamp.addSecs(360);
|
||||
ff.effectDuration = 360000; // msecs
|
||||
ff.propagationDirection = 20;
|
||||
ff.windSpeed = 2;
|
||||
ff.chargeTypeId = 1;
|
||||
return AreaEffectSimulation::Icons::bcContamination(ff,
|
||||
ProjectDataManagement::VolatilityType::Floating,
|
||||
1.25,
|
||||
QDateTime::currentDateTimeUtc());
|
||||
}
|
||||
|
||||
QIcon iconExplosionSingle()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesExplosionSingle ff;
|
||||
ff.impactPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
ff.chargeTypeId = 5;
|
||||
|
||||
return AreaEffectSimulation::Icons::explosionSingle(ff, 7.43);
|
||||
}
|
||||
|
||||
QIcon IconMineArea()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesMineArea ff;
|
||||
ff.id = 42;
|
||||
ff.density = 7;
|
||||
ff.width = 100;
|
||||
QVector<AreaEffectSimulation::Database::AesMineAreaSegment> vec;
|
||||
AreaEffectSimulation::Database::AesMineAreaSegment seg1;
|
||||
seg1.elementNumber = 1;
|
||||
seg1.mineAreaId = 42;
|
||||
seg1.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
seg1.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7050061000"));
|
||||
|
||||
// AreaEffectSimulation::Database::AesMineAreaSegment seg2;
|
||||
// seg2.elementNumber = 2;
|
||||
// seg2.density = 300;
|
||||
// seg2.mineAreaId = 42;
|
||||
// seg2.mineCount = 10;
|
||||
// seg2.rotationAngle = 10;
|
||||
// seg2.startPosition = QPointF(190,170);
|
||||
// seg2.width = 100;
|
||||
vec << seg1 ; //<< seg2;
|
||||
|
||||
return AreaEffectSimulation::Icons::mineArea(ff, vec);
|
||||
}
|
||||
|
||||
QIcon IconMineAreaPositions()
|
||||
{
|
||||
AreaEffectSimulation::Database::AesMineArea ff;
|
||||
ff.id = 42;
|
||||
ff.density = 0.07;
|
||||
ff.width = 100;
|
||||
|
||||
QVector<AreaEffectSimulation::Database::AesMineAreaSegment> vec;
|
||||
AreaEffectSimulation::Database::AesMineAreaSegment seg1, seg2;
|
||||
seg1.elementNumber = 1;
|
||||
seg1.mineAreaId = 42;
|
||||
seg1.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7000060000"));
|
||||
seg1.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7050061000"));
|
||||
|
||||
seg2.elementNumber = 2;
|
||||
seg2.mineAreaId = 42;
|
||||
seg2.startPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7027260421"));
|
||||
seg2.endPosition = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7060060732"));
|
||||
vec << seg1 << seg2;
|
||||
|
||||
return AreaEffectSimulation::Icons::mineArea(ff, vec);
|
||||
}
|
||||
|
||||
QIcon IconMineCorridor()
|
||||
{
|
||||
QVector<AreaEffectSimulation::Database::AesMineCorridorSegment> vec;
|
||||
vec.reserve(3);
|
||||
AreaEffectSimulation::Database::AesMineCorridorSegment seg1, seg2, seg3;
|
||||
|
||||
seg1.elementNumber = 1;
|
||||
seg1.mineCorridorId = 43;
|
||||
seg1.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7010060100"));
|
||||
|
||||
seg2 = seg1;
|
||||
seg2.elementNumber++;
|
||||
seg2.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7020060100"));
|
||||
|
||||
seg3 = seg2;
|
||||
seg3.elementNumber++;
|
||||
seg3.position = LtsCoordTrans::geographicFromMgrs(QLatin1String("32UMD7030060200"));
|
||||
|
||||
|
||||
vec << seg1;
|
||||
vec << seg2;
|
||||
vec << seg3;
|
||||
|
||||
return AreaEffectSimulation::Icons::mineCorridor(vec, 30);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
NGLApplication app(argc, argv);
|
||||
app.createLogger("AES", "log4cxx_aesutilstest.props");
|
||||
|
||||
int iRet = -1;
|
||||
{
|
||||
STARTUP_LOG(app.baseLogger());
|
||||
|
||||
QIcon i;
|
||||
// i = iconExplosionArea();
|
||||
// i = iconExplosionAreaSingleExplosions();
|
||||
// i = iconBcContamination();
|
||||
// i = iconExplosionSingle();
|
||||
// i = iconNContamination();
|
||||
// i = IconMineCorridor();
|
||||
// i = IconMineAreaPositions();
|
||||
// i = IconMineAreaPositions();
|
||||
|
||||
QWidget *p = new QWidget();
|
||||
QLabel *l = new QLabel(p);
|
||||
l->setPixmap(i.pixmap(256, 256));
|
||||
|
||||
p->show();
|
||||
|
||||
iRet = app.exec();
|
||||
SHUTDOWN_LOG(app.baseLogger());
|
||||
}
|
||||
|
||||
return iRet;
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TNE_Algorithemn zur Nachbildung von TNE-Funktionalität, hier Berechnung der Einschlagkoordinaten eines Flächenfeuers
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#include "tne_algorithmen.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
namespace TNE_ALGORITHMEN
|
||||
{
|
||||
|
||||
QVector<QPointF> areaExlosionsGAUSS(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege)
|
||||
{
|
||||
if (anzahl_einschlaege<=0)
|
||||
return {};
|
||||
|
||||
// breite, tiefe sollte ein Vielfaches von 5m sein, vergleiche ICD
|
||||
// const int breite_m = breite_5m * 5;
|
||||
// const int tiefe_m = tiefe_5m * 5;
|
||||
|
||||
QVector<QPointF> impacts;
|
||||
impacts.reserve(anzahl_einschlaege);
|
||||
|
||||
ULONG xor32 = wirkfeldKennung; // ZZ-Generator initialisieren:
|
||||
|
||||
for (int i = 0; i < anzahl_einschlaege; ++i)
|
||||
{
|
||||
QPointF point;
|
||||
|
||||
// p1 bestimmen (Breite/Ostwert)
|
||||
point.rx() = breite_m / 2 + xorshift32_nv_limited(&xor32, breite_m / 2, 3);
|
||||
// ^^^
|
||||
|
||||
// p2 bestimmen (Tiefe/Nordwert)
|
||||
point.ry() = tiefe_m / 2 + xorshift32_nv_limited(&xor32, tiefe_m / 2, 3);
|
||||
// ^^^
|
||||
|
||||
// std::cout << " i=" << i
|
||||
// << ", p1=" << point.x() << ", p2=" << point.y()
|
||||
// << std::endl;
|
||||
|
||||
impacts.append(point);
|
||||
} // for
|
||||
|
||||
return impacts;
|
||||
}
|
||||
|
||||
QVector<QPointF> areaExlosionsGAUSS_aroundCenter(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege)
|
||||
{
|
||||
if (anzahl_einschlaege<=0)
|
||||
return {};
|
||||
|
||||
// breite, tiefe sollte ein Vielfaches von 5m sein, vergleiche ICD
|
||||
// const int breite_m = breite_5m * 5;
|
||||
// const int tiefe_m = tiefe_5m * 5;
|
||||
|
||||
QVector<QPointF> impacts;
|
||||
impacts.reserve(anzahl_einschlaege);
|
||||
|
||||
ULONG xor32 = wirkfeldKennung; // ZZ-Generator initialisieren:
|
||||
|
||||
for (int i = 0; i < anzahl_einschlaege; ++i)
|
||||
{
|
||||
QPointF point;
|
||||
|
||||
// p1 bestimmen (Breite/Ostwert)
|
||||
point.rx() = /*breite_m / 2 + */ xorshift32_nv_limited(&xor32, breite_m / 2, 3);
|
||||
|
||||
// p2 bestimmen (Tiefe/Nordwert)
|
||||
point.ry() = /*tiefe_m / 2 + */ xorshift32_nv_limited(&xor32, tiefe_m / 2, 3);
|
||||
|
||||
// std::cout << " i=" << i
|
||||
// << ", p1=" << point.x() << ", p2=" << point.y()
|
||||
// << std::endl;
|
||||
|
||||
impacts.append(point);
|
||||
} // for
|
||||
|
||||
return impacts;
|
||||
}
|
||||
|
||||
|
||||
USHORT xorshift32(ULONG *_px)
|
||||
{
|
||||
ULONG x32 = *_px;
|
||||
|
||||
x32 ^= x32 << 13;
|
||||
x32 ^= x32 >> 17;
|
||||
x32 ^= x32 << 5;
|
||||
|
||||
*_px = x32;
|
||||
|
||||
return x32 % 32768; // für x % n mit n = 2^b; b natürliche Zahl, gilt: x % n = x & (n - 1)
|
||||
}
|
||||
|
||||
SHORT xorshift32_nv(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze)
|
||||
{
|
||||
//suffix _nv steht für 'normal verteilt'
|
||||
|
||||
//DBG_ASSERT(false, "ZZ-generator eingangs=%lu\r", *_generator);
|
||||
|
||||
LONG sum = 0;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 12; i++)
|
||||
sum += (ULONG)xorshift32(_generator);
|
||||
|
||||
// INTEGER-Implementierung 32bit, vgl. GUEZSB60__TN-0006_Wirksimulationsmodelle.docx, Abschnitt 3.3.2.2
|
||||
short m = 32768 - 1;
|
||||
|
||||
LONG zentrierteSumme = sum - 6 * m;
|
||||
LONG nenner = _sigmaGrenze * m;
|
||||
|
||||
if (zentrierteSumme >= 0)
|
||||
return (SHORT)((zentrierteSumme * _bereich + nenner / 2) / nenner);
|
||||
else
|
||||
return (SHORT)((zentrierteSumme * _bereich - nenner / 2) / nenner);
|
||||
}
|
||||
|
||||
SHORT xorshift32_nv_limited(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze)
|
||||
{
|
||||
SHORT zz = xorshift32_nv(_generator, _bereich, _sigmaGrenze);
|
||||
if (zz > _bereich)
|
||||
return _bereich;
|
||||
if (zz < (-1 * _bereich))
|
||||
return (-1 * _bereich);
|
||||
return zz;
|
||||
}
|
||||
|
||||
|
||||
} // namespace TNE_ALGORITHMEN
|
||||
} // namespace AreaEffectSimulation
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @RDE_HEADER@
|
||||
* TNE_Algorithemn zur Nachbildung von TNE-Funktionalität, hier Berechnung der Einschlagkoordinaten eines Flächenfeuers
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AreaEffectSimulation/aes_export.h>
|
||||
|
||||
#include <QVector>
|
||||
#include <QPointF>
|
||||
|
||||
|
||||
namespace AreaEffectSimulation
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This namespace contains some algorithms, which must behave identical on both sides!!!
|
||||
* Therefore the is an UnitTest with values taken from the TNE: ../UnitTests/UnitTests_TneAlgo
|
||||
* That was created while Michael Moetje was implementing that code at the TNE (PlayerUnit)
|
||||
*/
|
||||
namespace TNE_ALGORITHMEN
|
||||
{
|
||||
|
||||
//
|
||||
// Hinweise für die Übernahme/Updates des wesentlichen Codes (AWES_FF_IteriereEinschlagkoordinaten2)
|
||||
//
|
||||
|
||||
/* moetje:
|
||||
|
||||
"long" -> 32Bit
|
||||
|
||||
MTP -> MittlerTrefferPunkt
|
||||
|
||||
*/
|
||||
|
||||
// some using/typedef to ease updates
|
||||
using SHORT = qint16;
|
||||
using USHORT = quint16;
|
||||
|
||||
using ULONG = quint32; // TNE ist auf 32Bit Plattform
|
||||
using LONG = qint32; // TNE ist auf 32Bit Plattform
|
||||
|
||||
using INT_AS_CHAR = int;
|
||||
|
||||
//
|
||||
// basic TNE wrapper:
|
||||
//
|
||||
|
||||
/**
|
||||
* @brief areaExlosionsGAUSS berechnet die Einschläge eines Flächenfeuers entsprechend der Normal-Verteilung, siehe auch TNGUEZSB60__TN-0006_Wirksimulationsmodelle.docx
|
||||
* @param wirkfeldKennung dient als Startwert für den Zufallzahlen-Generator.
|
||||
* @param breite in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
|
||||
* @param tiefe in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
|
||||
* @param anzahl_einschlaege Die Anzahl der gewuenschten Einschläge
|
||||
* @return Einschläge entsprechend der Normal-Verteilung
|
||||
*/
|
||||
AES_COMMON_EXPORT QVector<QPointF> areaExlosionsGAUSS(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege);
|
||||
|
||||
/**
|
||||
* @brief areaExlosionsGAUSS berechnet die Einschläge eines Flächenfeuers entsprechend der Normal-Verteilung, siehe auch TNGUEZSB60__TN-0006_Wirksimulationsmodelle.docx
|
||||
* @param wirkfeldKennung dient als Startwert für den Zufallzahlen-Generator.
|
||||
* @param breite in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
|
||||
* @param tiefe in Meter, sollte aber ein Vielfaches von 5m sein, vergleiche ICD.
|
||||
* @param anzahl_einschlaege Die Anzahl der gewuenschten Einschläge
|
||||
* @return Einschläge entsprechend der Normal-Verteilung
|
||||
*/
|
||||
AES_COMMON_EXPORT QVector<QPointF> areaExlosionsGAUSS_aroundCenter(int wirkfeldKennung, int breite_m, int tiefe_m, short anzahl_einschlaege);
|
||||
|
||||
|
||||
AES_COMMON_EXPORT USHORT xorshift32(ULONG *_px);
|
||||
AES_COMMON_EXPORT SHORT xorshift32_nv(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze);
|
||||
AES_COMMON_EXPORT SHORT xorshift32_nv_limited(ULONG *_generator, SHORT _bereich, INT_AS_CHAR _sigmaGrenze);
|
||||
|
||||
} // namespace TNE_ALGORITHMEN
|
||||
} // namespace AreaEffectSimulation
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user