diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 2825fb756..ba54c61b4 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -127,6 +127,7 @@ namespace BlackMisc GlobalIndexCModelSettings = 7400, GlobalIndexCAircraftCfgEntries = 7500, GlobalIndexCDistributor = 7600, + GlobalIndexCMatchingStatisticsEntry = 7700, GlobalIndexCVPilotModelRule = 8000, GlobalIndexCVoiceRoom = 9000, GlobalIndexCSettingKeyboardHotkey = 10000, diff --git a/src/blackmisc/simulation/matchingstatistics.cpp b/src/blackmisc/simulation/matchingstatistics.cpp new file mode 100644 index 000000000..c1d0a7258 --- /dev/null +++ b/src/blackmisc/simulation/matchingstatistics.cpp @@ -0,0 +1,86 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "matchingstatistics.h" + +namespace BlackMisc +{ + namespace Simulation + { + CMatchingStatistics::CMatchingStatistics() + { } + + CMatchingStatistics::CMatchingStatistics(const CSequence &other) : + CSequence(other) + { } + + CMatchingStatistics CMatchingStatistics::findBySessionId(const QString &sessionId) const + { + return this->findBy(&CMatchingStatisticsEntry::getSessionId, sessionId); + } + + CMatchingStatistics CMatchingStatistics::findMissingOnly() const + { + return this->findBy(&CMatchingStatisticsEntry::isMissing, true); + } + + bool CMatchingStatistics::containsSessionId(const QString &sessionId) const + { + return this->contains(&CMatchingStatisticsEntry::getSessionId, sessionId); + } + + bool CMatchingStatistics::containsAircraftAirlineCombination(const QString &aircraftDesignator, const QString &airlineDesignator) const + { + return aircraftDesignator.isEmpty() ? + this->contains(&CMatchingStatisticsEntry::getAircraftDesignator, aircraftDesignator) : + this->contains(&CMatchingStatisticsEntry::getAircraftDesignator, aircraftDesignator, &CMatchingStatisticsEntry::getAirlineDesignator, airlineDesignator); + } + + bool CMatchingStatistics::containsAircraftAirlineCombination(const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator) const + { + return aircraftDesignator.isEmpty() ? + this->contains(&CMatchingStatisticsEntry::getSessionId, sessionId, &CMatchingStatisticsEntry::getAircraftDesignator, aircraftDesignator) : + this->contains(&CMatchingStatisticsEntry::getSessionId, sessionId, &CMatchingStatisticsEntry::getAircraftDesignator, aircraftDesignator, &CMatchingStatisticsEntry::getAirlineDesignator, airlineDesignator); + } + + bool CMatchingStatistics::increaseCountIfFound(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator) + { + bool found = false; + for (CMatchingStatisticsEntry &entry : *this) + { + if (entry.matches(type, sessionId, aircraftDesignator, airlineDesignator)) + { + entry.increaseCount(); + found = true; + } + } + return found; + } + + void CMatchingStatistics::addAircraft(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, bool avoidDuplicates) + { + if (avoidDuplicates) + { + const bool didIncrease = this->increaseCountIfFound(type, sessionId, aircraftDesignator); + if (didIncrease) { return; } + } + this->push_back(CMatchingStatisticsEntry(type, sessionId, modelSetId, description, aircraftDesignator)); + } + + void CMatchingStatistics::addAircraftAirlineCombination(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, const QString &airlineDesignator, bool avoidDuplicates) + { + if (avoidDuplicates) + { + const bool didIncrease = this->increaseCountIfFound(type, sessionId, aircraftDesignator, airlineDesignator); + if (didIncrease) { return; } + } + this->push_back(CMatchingStatisticsEntry(type, sessionId, modelSetId, description, aircraftDesignator, airlineDesignator)); + } + } // namespace +} // namespace diff --git a/src/blackmisc/simulation/matchingstatistics.h b/src/blackmisc/simulation/matchingstatistics.h new file mode 100644 index 000000000..0921c5c39 --- /dev/null +++ b/src/blackmisc/simulation/matchingstatistics.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_SIMULATION_MATCHINGSTATISTICS_H +#define BLACKMISC_SIMULATION_MATCHINGSTATISTICS_H + +#include "matchingstatisticsentry.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/timestampobjectlist.h" +#include "blackmisc/collection.h" + +namespace BlackMisc +{ + namespace Simulation + { + //! Value object for matching statistics. + class BLACKMISC_EXPORT CMatchingStatistics : + public BlackMisc::CSequence, + public BlackMisc::ITimestampObjectList, + public BlackMisc::Mixin::MetaType + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CMatchingStatistics) + + //! Default constructor. + CMatchingStatistics(); + + //! Construct from a base class object. + CMatchingStatistics(const CSequence &other); + + //! Find by session id + CMatchingStatistics findBySessionId(const QString &sessionId) const; + + //! Find entires denoting missing entries only + CMatchingStatistics findMissingOnly() const; + + //! Contains session id + bool containsSessionId(const QString &sessionId) const; + + //! Contains given aircraft / airline combination + bool containsAircraftAirlineCombination(const QString &aircraftDesignator, const QString &airlineDesignator) const; + + //! Contains given aircraft / airline combination + bool containsAircraftAirlineCombination(const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator) const; + + //! Increase count if found + bool increaseCountIfFound(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator = {}); + + //! Add a combination, normally with no duplicates (in that case count is increased + void addAircraft(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, bool avoidDuplicates = true); + + //! Add a combination, normally with no duplicates (in that case count is increased + void addAircraftAirlineCombination(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, const QString &airlineDesignator, bool avoidDuplicates = true); + }; + } // namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Simulation::CMatchingStatistics) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif // guard diff --git a/src/blackmisc/simulation/matchingstatisticsentry.cpp b/src/blackmisc/simulation/matchingstatisticsentry.cpp new file mode 100644 index 000000000..babaee77a --- /dev/null +++ b/src/blackmisc/simulation/matchingstatisticsentry.cpp @@ -0,0 +1,181 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "matchingstatisticsentry.h" +#include "blackmisc/comparefunctions.h" +#include "blackmisc/icon.h" + +namespace BlackMisc +{ + namespace Simulation + { + CMatchingStatisticsEntry::CMatchingStatisticsEntry() { } + + CMatchingStatisticsEntry::CMatchingStatisticsEntry(EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, const QString &airlineDesignator) : + m_sessionId(sessionId.trimmed()), m_modelSetId(modelSetId.trimmed()), + m_description(description), + m_aircraftDesignator(aircraftDesignator.trimmed().toUpper()), + m_airlineDesignator(airlineDesignator.trimmed().toUpper()), + m_entryType(type) + { + this->setCurrentUtcTime(); + } + + CMatchingStatisticsEntry::EntryType CMatchingStatisticsEntry::getEntryType() const + { + return static_cast(m_entryType); + } + + bool CMatchingStatisticsEntry::isMissing() const + { + return this->getEntryType() == Missing; + } + + void CMatchingStatisticsEntry::setEntryType(CMatchingStatisticsEntry::EntryType type) + { + m_entryType = static_cast(type); + } + + int CMatchingStatisticsEntry::getCount() const + { + return m_count; + } + + void CMatchingStatisticsEntry::increaseCount() + { + m_count++; + } + + bool CMatchingStatisticsEntry::matches(CMatchingStatisticsEntry::EntryType type, const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator) const + { + return this->getEntryType() == type && sessionId == this->getSessionId() && aircraftDesignator == this->getAircraftDesignator() && airlineDesignator == this->getAirlineDesignator(); + } + + bool CMatchingStatisticsEntry::hasAircraftAirlineCombination() const + { + return !m_aircraftDesignator.isEmpty() && !m_airlineDesignator.isEmpty(); + } + + const CIcon &CMatchingStatisticsEntry::entryTypeToIcon(CMatchingStatisticsEntry::EntryType type) + { + switch (type) + { + case Found: return CIcon::iconByIndex(CIcons::StandardIconTick16); + case Missing: return CIcon::iconByIndex(CIcons::StandardIconCross16); + default: + qFatal("Wrong Type"); + return CIcon::iconByIndex(CIcons::StandardIconUnknown16); + } + } + + const QString &CMatchingStatisticsEntry::entryTypeToString(CMatchingStatisticsEntry::EntryType type) + { + static const QString f("found"); + static const QString m("missing"); + static const QString x("ups"); + + switch (type) + { + case Found: return f; + case Missing: return m; + default: + qFatal("Wrong Type"); + return x; + } + } + + CVariant CMatchingStatisticsEntry::propertyByIndex(const CPropertyIndex &index) const + { + if (index.isMyself()) { return CVariant::from(*this); } + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexSessionId: return CVariant::from(this->m_sessionId); + case IndexModelSetId: return CVariant::from(this->m_modelSetId); + case IndexCount: return CVariant::from(this->m_count); + case IndexEntryType: return CVariant::from(this->m_entryType); + case IndexEntryTypeAsString: return CVariant::from(entryTypeToString(this->getEntryType())); + case IndexEntryTypeAsIcon: return CVariant::from(entryTypeToIcon(this->getEntryType())); + case IndexAircraftDesignator: return CVariant::from(this->m_aircraftDesignator); + case IndexAirlineDesignator: return CVariant::from(this->m_airlineDesignator); + case IndexDescription: return CVariant::from(this->m_description); + case IndexHasAircraftAirlineCombination: return CVariant::from(this->hasAircraftAirlineCombination()); + default: + return CValueObject::propertyByIndex(index); + } + } + + void CMatchingStatisticsEntry::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (index.isMyself()) { (*this) = variant.to(); return; } + if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexSessionId: + this->setSessionId(variant.value()); + break; + case IndexModelSetId: + this->setModelSetId(variant.value()); + break; + case IndexAircraftDesignator: + this->m_aircraftDesignator = variant.value(); + break; + case IndexEntryType: + this->setEntryType(static_cast(variant.toInt())); + break; + case IndexCount: + this->m_count = variant.toInt(); + break; + case IndexAirlineDesignator: + this->m_airlineDesignator = variant.value(); + break; + case IndexDescription: + this->m_description = variant.value(); + break; + default: + CValueObject::setPropertyByIndex(index, variant); + break; + } + } + + int CMatchingStatisticsEntry::comparePropertyByIndex(const CPropertyIndex &index, const CMatchingStatisticsEntry &compareValue) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexSessionId: return this->m_sessionId.compare(compareValue.m_sessionId, Qt::CaseInsensitive); + case IndexModelSetId: return this->m_modelSetId.compare(compareValue.getModelSetId(), Qt::CaseInsensitive); + case IndexEntryTypeAsIcon: + case IndexEntryTypeAsString: + case IndexEntryType: return Compare::compare(this->m_entryType, compareValue.m_entryType); + case IndexCount: return Compare::compare(this->m_count, compareValue.m_count); + case IndexAircraftDesignator: return this->m_aircraftDesignator.compare(compareValue.m_aircraftDesignator, Qt::CaseInsensitive); + case IndexAirlineDesignator: return this->m_airlineDesignator.compare(compareValue.m_airlineDesignator, Qt::CaseInsensitive); + case IndexHasAircraftAirlineCombination: return Compare::compare(this->hasAircraftAirlineCombination(), compareValue.hasAircraftAirlineCombination()); + default: + break; + } + Q_ASSERT_X(false, Q_FUNC_INFO, "Compare failed"); + return 0; + } + + QString CMatchingStatisticsEntry::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + const QString s = QString("%1 Session: '%2' model set: '%3' aircraft: '%4' airline: '%5' description: '%6'") + .arg(entryTypeToString(getEntryType()), m_sessionId, m_modelSetId, m_aircraftDesignator, m_airlineDesignator, m_description); + return s; + } + } // namespace +} // namespace diff --git a/src/blackmisc/simulation/matchingstatisticsentry.h b/src/blackmisc/simulation/matchingstatisticsentry.h new file mode 100644 index 000000000..cd8e971db --- /dev/null +++ b/src/blackmisc/simulation/matchingstatisticsentry.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2017 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_SIMULATION_MATCHINGSTATISTICSENTRY_H +#define BLACKMISC_SIMULATION_MATCHINGSTATISTICSENTRY_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/valueobject.h" +#include "blackmisc/timestampbased.h" +#include "blackmisc/variant.h" + +namespace BlackMisc +{ + namespace Simulation + { + //! Value object for a matching statistics entry. + class BLACKMISC_EXPORT CMatchingStatisticsEntry : + public BlackMisc::CValueObject, + public BlackMisc::ITimestampBased + { + public: + //! Property indexes + enum ColumnIndex + { + IndexSessionId = CPropertyIndex::GlobalIndexCMatchingStatisticsEntry, + IndexModelSetId, + IndexEntryType, + IndexEntryTypeAsString, + IndexEntryTypeAsIcon, + IndexCount, + IndexDescription, + IndexAircraftDesignator, + IndexAirlineDesignator, + IndexHasAircraftAirlineCombination + }; + + //! Represents type of entry + enum EntryType + { + Found, + Missing + }; + + //! Default constructor. + CMatchingStatisticsEntry(); + + //! Constructor + CMatchingStatisticsEntry(EntryType type, const QString &sessionId, const QString &modelSetId, const QString &description, const QString &aircraftDesignator, const QString &airlineDesignator = {}); + + //! Session id + const QString &getSessionId() const { return m_sessionId; } + + //! Set session id + void setSessionId(const QString &sessionId) { m_sessionId = sessionId.trimmed(); } + + //! Get model set id + const QString &getModelSetId() const { return m_modelSetId; } + + //! Set model set id + void setModelSetId(const QString &modelSetId) { m_modelSetId = modelSetId.trimmed(); } + + //! Get missing aircraft designator + const QString &getAircraftDesignator() const { return m_aircraftDesignator;} + + //! Set missing aircraft designator + void setAircraftDesignator(const QString &designator) { m_aircraftDesignator = designator.trimmed().toUpper(); } + + //! Get missing airline designator + const QString &getAirlineDesignator() const { return m_airlineDesignator;} + + //! Set missing airline designator + void setAirlineDesignator(const QString &designator) { m_airlineDesignator = designator.trimmed().toUpper(); } + + //! Type of entry + EntryType getEntryType() const; + + //! Missing entry? + bool isMissing() const; + + //! Set the entry type + void setEntryType(EntryType type); + + //! Get description + const QString &getDescription() const { return m_description;} + + //! Set a description + void setDescription(const QString &description) { m_description = description; } + + //! Current count + int getCount() const; + + //! Count increased by one + void increaseCount(); + + //! Matches given value? + bool matches(EntryType type, const QString &sessionId, const QString &aircraftDesignator, const QString &airlineDesignator) const; + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + + //! Compare by index + int comparePropertyByIndex(const CPropertyIndex &index, const CMatchingStatisticsEntry &compareValue) const; + + //! \copydoc BlackMisc::Mixin::String::toQString + QString convertToQString(bool i18n = false) const; + + //! Missing aircraft/airline combination? + bool hasAircraftAirlineCombination() const; + + //! Convert entry type + static const QString &entryTypeToString(EntryType type); + + //! Convert entry type + static const BlackMisc::CIcon &entryTypeToIcon(EntryType type); + + private: + QString m_sessionId; //!< Created in session + QString m_modelSetId; //!< represents model set + QString m_description; //!< Arbitrary description + QString m_aircraftDesignator; //!< missing aircraft designator + QString m_airlineDesignator; //!< missing airline designator + int m_entryType = Missing; //!< type + int m_count = 1; //!< quantity + + BLACK_METACLASS( + CMatchingStatisticsEntry, + BLACK_METAMEMBER(sessionId), + BLACK_METAMEMBER(modelSetId), + BLACK_METAMEMBER(aircraftDesignator), + BLACK_METAMEMBER(airlineDesignator), + BLACK_METAMEMBER(description), + BLACK_METAMEMBER(entryType), + BLACK_METAMEMBER(count) + ); + }; + } // namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Simulation::CMatchingStatisticsEntry) +Q_DECLARE_METATYPE(BlackMisc::Simulation::CMatchingStatisticsEntry::EntryType) + +#endif // guard diff --git a/src/blackmisc/simulation/registermetadatasimulation.cpp b/src/blackmisc/simulation/registermetadatasimulation.cpp index 12dde759f..33bc7b3ae 100644 --- a/src/blackmisc/simulation/registermetadatasimulation.cpp +++ b/src/blackmisc/simulation/registermetadatasimulation.cpp @@ -30,6 +30,8 @@ namespace BlackMisc CDistributorListPreferences::registerMetadata(); CInterpolationAndRenderingSetup::registerMetadata(); CInterpolationHints::registerMetadata(); + CMatchingStatisticsEntry::registerMetadata(); + CMatchingStatistics::registerMetadata(); CModelSettings::registerMetadata(); CSimConnectUtilities::registerMetadata(); CSimulatedAircraft::registerMetadata(); diff --git a/src/blackmisc/simulation/simulation.h b/src/blackmisc/simulation/simulation.h index 198bf4470..af1c5350e 100644 --- a/src/blackmisc/simulation/simulation.h +++ b/src/blackmisc/simulation/simulation.h @@ -21,6 +21,7 @@ #include "blackmisc/simulation/distributorlistpreferences.h" #include "blackmisc/simulation/interpolationhints.h" #include "blackmisc/simulation/interpolationrenderingsetup.h" +#include "blackmisc/simulation/matchingstatistics.h" #include "blackmisc/simulation/modelsettings.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/simulation/simulatedaircraftlist.h"