mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-30 20:15:35 +08:00
refs #358, consolidated mapping and model classes
* removed redundant classes in blacksim * more detailed attributes for mapping * more finder functions in mapping list * aircraft model extended as simulator independent description for models
This commit is contained in:
@@ -52,6 +52,15 @@ namespace BlackMisc
|
||||
return s;
|
||||
}
|
||||
|
||||
void CAircraftIcao::updateMissingParts(const CAircraftIcao &icao)
|
||||
{
|
||||
if (this->m_aircraftDesignator.isEmpty()) { this->setAircraftDesignator(icao.getAircraftDesignator()); }
|
||||
if (this->m_airlineDesignator.isEmpty()) { this->setAirlineDesignator(icao.getAirlineDesignator()); }
|
||||
if (this->m_aircraftCombinedType.isEmpty()) { this->setAircraftCombinedType(icao.getAircraftCombinedType()); }
|
||||
if (this->m_aircraftColor.isEmpty()) { this->setAircraftColor(icao.getAircraftColor()); }
|
||||
if (this->m_livery.isEmpty()) { this->setLivery(icao.getLivery()); }
|
||||
}
|
||||
|
||||
bool CAircraftIcao::matchesWildcardIcao(const CAircraftIcao &otherIcao) const
|
||||
{
|
||||
if ((*this) == otherIcao) return true;
|
||||
|
||||
@@ -126,6 +126,9 @@ namespace BlackMisc
|
||||
//! Set type
|
||||
void setAircraftCombinedType(const QString &type) { this->m_aircraftCombinedType = type.trimmed().toUpper(); }
|
||||
|
||||
//! Missing parts from another ICAO object
|
||||
void updateMissingParts(const CAircraftIcao &icao);
|
||||
|
||||
//! Matches wildcard icao object
|
||||
bool matchesWildcardIcao(const CAircraftIcao &otherIcao) const;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ void BlackMisc::Network::registerMetadata()
|
||||
CClient::registerMetadata();
|
||||
CClientList::registerMetadata();
|
||||
CAircraftModel::registerMetadata();
|
||||
CAircraftModelList::registerMetadata();
|
||||
CVoiceCapabilities::registerMetadata();
|
||||
CAircraftMapping::registerMetadata();
|
||||
CAircraftMappingList::registerMetadata();
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
#include "blackmisc/nwtextmessagelist.h"
|
||||
#include "blackmisc/nwclient.h"
|
||||
#include "blackmisc/nwclientlist.h"
|
||||
#include "blackmisc/nwaircraftmodel.h"
|
||||
#include "blackmisc/nwvoicecapabilities.h"
|
||||
#include "blackmisc/nwaircraftmodel.h"
|
||||
#include "blackmisc/nwaircraftmodellist.h"
|
||||
#include "blackmisc/nwaircraftmapping.h"
|
||||
#include "blackmisc/nwaircraftmappinglist.h"
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace BlackMisc
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
CAircraftMapping::CAircraftMapping(const QString &aircraftDesignator, const QString &airlineDesignator, const QString &model) :
|
||||
m_icao(CAircraftIcao(aircraftDesignator, airlineDesignator)), m_model(CAircraftModel(model, false))
|
||||
CAircraftMapping::CAircraftMapping(const QString &source, const QString &packageName, const QString &aircraftDesignator, const QString &airlineDesignator, const QString &model) :
|
||||
m_source(source.trimmed()), m_packageName(packageName.trimmed()), m_icao(CAircraftIcao(aircraftDesignator, airlineDesignator)), m_model(CAircraftModel(model, CAircraftModel::TypeModelMapping))
|
||||
{ }
|
||||
|
||||
/*
|
||||
@@ -54,16 +54,15 @@ namespace BlackMisc
|
||||
{
|
||||
case IndexModel:
|
||||
return this->m_model.propertyByIndex(index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexIcaoCode:
|
||||
case IndexIcao:
|
||||
return this->m_model.propertyByIndex(index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexPackageName:
|
||||
return QVariant::fromValue(this->m_packageName);
|
||||
case IndexSource:
|
||||
return QVariant::fromValue(this->m_source);
|
||||
default:
|
||||
break;
|
||||
return CValueObject::propertyByIndex(index);
|
||||
}
|
||||
Q_ASSERT_X(false, "CAircraftMapping", "index unknown");
|
||||
QString m = QString("no property, index ").append(index.toQString());
|
||||
return CVariant::fromValue(m);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -82,11 +81,17 @@ namespace BlackMisc
|
||||
case IndexModel:
|
||||
this->m_model.setPropertyByIndex(variant, index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexIcaoCode:
|
||||
case IndexIcao:
|
||||
this->m_icao.setPropertyByIndex(variant, index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexPackageName:
|
||||
this->m_packageName = variant.toQString();
|
||||
break;
|
||||
case IndexSource:
|
||||
this->m_source = variant.toQString();
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "CAircraftMapping", "index unknown");
|
||||
CValueObject::setPropertyByIndex(variant, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,9 @@ namespace BlackMisc
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/*!
|
||||
* Mapping
|
||||
*/
|
||||
//! Mapping
|
||||
class CAircraftMapping : public CValueObjectStdTuple<CAircraftMapping>
|
||||
{
|
||||
private:
|
||||
BLACK_ENABLE_TUPLE_CONVERSION(CAircraftMapping)
|
||||
BlackMisc::Aviation::CAircraftIcao m_icao; //!< ICAO code
|
||||
BlackMisc::Network::CAircraftModel m_model; //!< aircraft model
|
||||
|
||||
protected:
|
||||
//! \copydoc CValueObject::convertToQString
|
||||
@@ -39,14 +33,16 @@ namespace BlackMisc
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexModel,
|
||||
IndexIcaoCode
|
||||
IndexIcao,
|
||||
IndexPackageName,
|
||||
IndexSource
|
||||
};
|
||||
|
||||
//! Default constructor
|
||||
CAircraftMapping() = default;
|
||||
|
||||
//! Constructor
|
||||
CAircraftMapping(const QString &aircraftDesignator, const QString &airlineDesignator, const QString &model);
|
||||
CAircraftMapping(const QString &source, const QString &packageName, const QString &aircraftDesignator, const QString &airlineDesignator, const QString &model);
|
||||
|
||||
//! \copydoc CValueObject::propertyByIndex
|
||||
CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const override;
|
||||
@@ -71,6 +67,16 @@ namespace BlackMisc
|
||||
|
||||
//! Matches wildcard icao object
|
||||
bool matchesWildcardIcao(const BlackMisc::Aviation::CAircraftIcao &otherIcao) const { return m_icao.matchesWildcardIcao(otherIcao); }
|
||||
|
||||
private:
|
||||
BLACK_ENABLE_TUPLE_CONVERSION(CAircraftMapping)
|
||||
|
||||
QString m_source; //!< source, e.g. database, vPilot
|
||||
QString m_packageName; //!< something like WoA, ..
|
||||
BlackMisc::Aviation::CAircraftIcao m_icao; //!< ICAO code
|
||||
BlackMisc::Network::CAircraftModel m_model; //!< aircraft model
|
||||
|
||||
// BlackSim::CSimulatorInfo m_simulatorInfo; //!< Mapping is for simulator
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,42 @@ namespace BlackMisc
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByIcaoCodeWildcard(const CAircraftIcao &searchIcao) const
|
||||
{
|
||||
return this->findBy([ = ](const CAircraftMapping &mapping)
|
||||
return this->findBy([ = ](const CAircraftMapping & mapping)
|
||||
{
|
||||
return mapping.matchesWildcardIcao(searchIcao);
|
||||
});
|
||||
}
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByIcaoAircraftDesignator(const CAircraftIcao &searchIcao) const
|
||||
{
|
||||
const QString aircraftIcao = searchIcao.getAircraftDesignator();
|
||||
if (aircraftIcao.isEmpty()) { return CAircraftMappingList(); }
|
||||
return this->findBy([ = ](const CAircraftMapping & mapping)
|
||||
{
|
||||
return mapping.getIcao().getAircraftDesignator() == aircraftIcao;
|
||||
});
|
||||
}
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByIcaoAirlineDesignator(const CAircraftIcao &searchIcao) const
|
||||
{
|
||||
const QString airlineIcao = searchIcao.getAircraftDesignator();
|
||||
if (airlineIcao.isEmpty()) { return CAircraftMappingList(); }
|
||||
return this->findBy([ = ](const CAircraftMapping & mapping)
|
||||
{
|
||||
return mapping.getIcao().getAirlineDesignator() == airlineIcao;
|
||||
});
|
||||
}
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByIcaoAircraftAndAirlineDesignator(const CAircraftIcao &searchIcao, bool allowRelaxedAirline) const
|
||||
{
|
||||
CAircraftMappingList aircraftSearch = findByIcaoAircraftDesignator(searchIcao);
|
||||
if (aircraftSearch.isEmpty()) { return aircraftSearch; }
|
||||
|
||||
CAircraftMappingList aircraftAndAirlineSearch = aircraftSearch.findByIcaoAirlineDesignator(searchIcao);
|
||||
if (!aircraftAndAirlineSearch.isEmpty()) { return aircraftAndAirlineSearch; }
|
||||
return allowRelaxedAirline ? aircraftSearch : aircraftAndAirlineSearch;
|
||||
}
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByIcaoCodeExact(const CAircraftIcao &searchIcao) const
|
||||
{
|
||||
return this->findBy(&CAircraftMapping::getIcao, searchIcao);
|
||||
@@ -44,7 +74,7 @@ namespace BlackMisc
|
||||
|
||||
CAircraftMappingList CAircraftMappingList::findByModelString(const QString modelString, Qt::CaseSensitivity sensitivity) const
|
||||
{
|
||||
return this->findBy([ = ](const CAircraftMapping &mapping)
|
||||
return this->findBy([ = ](const CAircraftMapping & mapping)
|
||||
{
|
||||
return mapping.matchesModelString(modelString, sensitivity);
|
||||
});
|
||||
|
||||
@@ -23,9 +23,7 @@ namespace BlackMisc
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/*!
|
||||
* Value object encapsulating a list of aircraft mappings
|
||||
*/
|
||||
//! Value object encapsulating a list of aircraft mappings
|
||||
class CAircraftMappingList : public CSequence<CAircraftMapping>
|
||||
{
|
||||
public:
|
||||
@@ -38,6 +36,15 @@ namespace BlackMisc
|
||||
//! Find by ICAO code, empty fields treated as wildcards
|
||||
CAircraftMappingList findByIcaoCodeWildcard(const BlackMisc::Aviation::CAircraftIcao &searchIcao) const;
|
||||
|
||||
//! Find by ICAO aircraft designator
|
||||
CAircraftMappingList findByIcaoAircraftDesignator(const BlackMisc::Aviation::CAircraftIcao &searchIcao) const;
|
||||
|
||||
//! Find by ICAO airline designator
|
||||
CAircraftMappingList findByIcaoAirlineDesignator(const BlackMisc::Aviation::CAircraftIcao &searchIcao) const;
|
||||
|
||||
//! Find by ICAO aircraft and airline designator
|
||||
CAircraftMappingList findByIcaoAircraftAndAirlineDesignator(const BlackMisc::Aviation::CAircraftIcao &searchIcao, bool allowRelaxedAirline) const;
|
||||
|
||||
//! Find by ICAO code, empty fields treated literally
|
||||
CAircraftMappingList findByIcaoCodeExact(const BlackMisc::Aviation::CAircraftIcao &searchIcao) const;
|
||||
|
||||
|
||||
@@ -8,21 +8,34 @@
|
||||
*/
|
||||
|
||||
#include "nwaircraftmodel.h"
|
||||
#include "variant.h"
|
||||
#include <QString>
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
CAircraftModel::CAircraftModel(const Aviation::CAircraft &aircraft) :
|
||||
m_callsign(aircraft.getCallsign()), m_icao(aircraft.getIcaoInfo())
|
||||
{ }
|
||||
|
||||
/*
|
||||
* Convert to string
|
||||
*/
|
||||
QString CAircraftModel::convertToQString(bool /** i18n **/) const
|
||||
QString CAircraftModel::convertToQString(bool i18n) const
|
||||
{
|
||||
QString s = this->m_modelString;
|
||||
if (!s.isEmpty()) s.append(' ');
|
||||
s.append(this->m_queriedModelStringFlag ? "queried" : "mapped");
|
||||
if (!s.isEmpty()) { s += ' '; }
|
||||
s += this->getModelTypeAsString();
|
||||
s += ' ';
|
||||
s += this->m_icao.toQString(i18n);
|
||||
if (!this->m_fileName.isEmpty())
|
||||
{
|
||||
s += ' ';
|
||||
s += m_fileName;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -31,19 +44,28 @@ namespace BlackMisc
|
||||
*/
|
||||
CVariant CAircraftModel::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
|
||||
{
|
||||
if (index.isMyself()) { return this->toCVariant(); }
|
||||
if (index.isMyself()) { return this->toQVariant(); }
|
||||
ColumnIndex i = index.frontCasted<ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case IndexModelString:
|
||||
return CVariant::from(this->m_modelString);
|
||||
break;
|
||||
case IndexIsQueriedModelString:
|
||||
return CVariant::from(this->m_queriedModelStringFlag);
|
||||
break;
|
||||
return CVariant(this->m_modelString);
|
||||
case IndexHasQueriedModelString:
|
||||
return CVariant::fromValue(this->hasQueriedModelString());
|
||||
case IndexModelType:
|
||||
return CVariant::fromValue(this->m_modelType);
|
||||
case IndexModelTypeAsString:
|
||||
return CVariant(this->getModelTypeAsString());
|
||||
case IndexDescription:
|
||||
return CVariant(this->m_description);
|
||||
case IndexFileName:
|
||||
return CVariant(this->m_fileName);
|
||||
case IndexIcao:
|
||||
return m_icao.propertyByIndex(index.copyFrontRemoved());
|
||||
case IndexCallsign:
|
||||
return m_callsign.propertyByIndex(index.copyFrontRemoved());
|
||||
default:
|
||||
return CValueObject::propertyByIndex(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +85,20 @@ namespace BlackMisc
|
||||
case IndexModelString:
|
||||
this->m_modelString = variant.toQString();
|
||||
break;
|
||||
case IndexIsQueriedModelString:
|
||||
this->m_queriedModelStringFlag = variant.toBool();
|
||||
case IndexIcao:
|
||||
this->m_icao.setPropertyByIndex(variant, index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexDescription:
|
||||
this->m_description = variant.toQString();
|
||||
break;
|
||||
case IndexCallsign:
|
||||
this->m_callsign.setPropertyByIndex(variant, index.copyFrontRemoved());
|
||||
break;
|
||||
case IndexFileName:
|
||||
this->m_fileName = variant.toQString();
|
||||
break;
|
||||
case IndexModelType:
|
||||
this->m_modelType = variant.toInt();
|
||||
break;
|
||||
default:
|
||||
CValueObject::setPropertyByIndex(variant, index);
|
||||
@@ -72,15 +106,44 @@ namespace BlackMisc
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update missing parts
|
||||
*/
|
||||
void CAircraftModel::updateMissingParts(const CAircraftModel &model)
|
||||
{
|
||||
if (this->m_modelString.isEmpty()) { this->m_modelString = model.getModelString(); }
|
||||
if (this->m_description.isEmpty()) { this->m_description = model.getDescription(); }
|
||||
if (this->m_fileName.isEmpty()) { this->m_fileName = model.getFileName(); }
|
||||
if (this->m_callsign.isEmpty()) { this->m_callsign = model.getCallsign(); }
|
||||
this->m_icao.updateMissingParts(model.getIcao());
|
||||
}
|
||||
|
||||
/*
|
||||
* Matches string?
|
||||
*/
|
||||
bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const
|
||||
{
|
||||
if (sensitivity == Qt::CaseSensitive)
|
||||
{
|
||||
return modelString == this->m_modelString;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this->m_modelString.indexOf(modelString) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
QString CAircraftModel::modelTypeToString(CAircraftModel::ModelType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case TypeQueriedFromNetwork: return "queried";
|
||||
case TypeModelMatching: return "matching";
|
||||
case TypeModelMapping: return "mapping";
|
||||
case TypeOwnSimulatorModel: return "own simulator";
|
||||
case TypeUnknown:
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -12,31 +12,52 @@
|
||||
#ifndef BLACKMISC_AIRCRAFTMODEL_H
|
||||
#define BLACKMISC_AIRCRAFTMODEL_H
|
||||
|
||||
#include "avaircraft.h"
|
||||
#include "avaircrafticao.h"
|
||||
#include "nwuser.h"
|
||||
#include "propertyindex.h"
|
||||
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
/*!
|
||||
* Another pilot's aircraft model
|
||||
*/
|
||||
//! Aircraft model (other pilot, my models on disk)
|
||||
//! \remarks Simulator independent class, supposed to be common denominator
|
||||
class CAircraftModel : public CValueObjectStdTuple<CAircraftModel>
|
||||
{
|
||||
public:
|
||||
//! Model type
|
||||
enum ModelType
|
||||
{
|
||||
TypeUnknown,
|
||||
TypeQueriedFromNetwork, //!< model was queried by network protocol
|
||||
TypeModelMatching, //!< model is result of model matching
|
||||
TypeModelMapping, //!< used along with mapping definition
|
||||
TypeOwnSimulatorModel //!< represents own simulator model
|
||||
};
|
||||
|
||||
//! Indexes
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexModelString = BlackMisc::CPropertyIndex::GlobalIndexCAircraftModel,
|
||||
IndexIsQueriedModelString
|
||||
IndexCallsign,
|
||||
IndexDescription,
|
||||
IndexIcao,
|
||||
IndexFileName,
|
||||
IndexModelType,
|
||||
IndexModelTypeAsString,
|
||||
IndexHasQueriedModelString
|
||||
};
|
||||
|
||||
//! Default constructor.
|
||||
CAircraftModel() : m_queriedModelStringFlag(false) {}
|
||||
CAircraftModel() {}
|
||||
|
||||
//! Constructor.
|
||||
CAircraftModel(const QString &model, bool isQueriedString) : m_modelString(model), m_queriedModelStringFlag(isQueriedString) {}
|
||||
CAircraftModel(const QString &model, ModelType type) : m_modelString(model), m_modelType(type) {}
|
||||
|
||||
//! Constructor
|
||||
CAircraftModel(const BlackMisc::Aviation::CAircraft &aircraft);
|
||||
|
||||
//! \copydoc CValueObject::propertyByIndex
|
||||
virtual CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const override;
|
||||
@@ -44,34 +65,97 @@ namespace BlackMisc
|
||||
//! \copydoc CValueObject::setPropertyByIndex
|
||||
virtual void setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index) override;
|
||||
|
||||
//! Corresponding callsign if applicable
|
||||
const BlackMisc::Aviation::CCallsign &getCallsign() const { return this->m_callsign; }
|
||||
|
||||
//! Corresponding callsign if applicable
|
||||
void setCallsign(const BlackMisc::Aviation::CCallsign &callsign) { this->m_callsign = callsign; }
|
||||
|
||||
//! Callsign empty
|
||||
bool isCallsignEmpty() const { return this->m_callsign.isEmpty(); }
|
||||
|
||||
//! Queried model string
|
||||
const QString &getModelString() const { return this->m_modelString; }
|
||||
|
||||
//! Queried model string
|
||||
//! Model string
|
||||
void setModelString(const QString &modelString) { this->m_modelString = modelString; }
|
||||
|
||||
//! Descriptive text
|
||||
const QString &getDescription() const { return this->m_description; }
|
||||
|
||||
//! Descriptive text
|
||||
void setDescription(const QString &description) { this->m_description = description; }
|
||||
|
||||
//! Set queried model string
|
||||
void setQueriedModelString(const QString &model) { this->m_modelString = model; }
|
||||
void setQueriedModelString(const QString &model) { this->m_modelString = model; this->m_modelType = TypeQueriedFromNetwork; }
|
||||
|
||||
//! ICAO code
|
||||
BlackMisc::Aviation::CAircraftIcao getIcao() const { return this->m_icao; }
|
||||
|
||||
//! Set ICAO info
|
||||
void setIcao(const BlackMisc::Aviation::CAircraftIcao &icao) { this->m_icao = icao; }
|
||||
|
||||
//! \copydoc CAircraftIcao::hasAircraftAndAirlineDesignator
|
||||
bool hasAircraftAndAirlineDesignator() const { return this->m_icao.hasAircraftAndAirlineDesignator(); }
|
||||
|
||||
//! \copydoc CAircraftIcao::hasAircraftDesignator
|
||||
bool hasAircraftDesignator() const { return this->m_icao.hasAircraftDesignator(); }
|
||||
|
||||
//! Model type
|
||||
ModelType getModelType() const { return static_cast<ModelType>(m_modelType); }
|
||||
|
||||
//! Model type
|
||||
QString getModelTypeAsString() const { return modelTypeToString(getModelType()); }
|
||||
|
||||
//! Set type
|
||||
void setModelType(ModelType type) { this->m_modelType = static_cast<int>(type); }
|
||||
|
||||
//! File name
|
||||
QString getFileName() const { return m_fileName; }
|
||||
|
||||
//! File name
|
||||
void setFileName(const QString &fileName) { m_fileName = fileName; }
|
||||
|
||||
//! Update missing parts from another model
|
||||
void updateMissingParts(const CAircraftModel &model);
|
||||
|
||||
//! Queried model string?
|
||||
bool hasQueriedModelString() const { return this->m_queriedModelStringFlag && !this->m_modelString.isEmpty(); }
|
||||
bool hasQueriedModelString() const { return this->m_modelType == TypeQueriedFromNetwork && this->hasModelString(); }
|
||||
|
||||
//! Non empty model string
|
||||
bool hasModelString() const { return !m_modelString.isEmpty(); }
|
||||
|
||||
//! Matches model string?
|
||||
bool matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const;
|
||||
|
||||
//! Model type
|
||||
static QString modelTypeToString(ModelType type);
|
||||
|
||||
protected:
|
||||
//! \copydoc CValueObject::convertToQString
|
||||
virtual QString convertToQString(bool i18n = false) const override;
|
||||
|
||||
private:
|
||||
BLACK_ENABLE_TUPLE_CONVERSION(CAircraftModel)
|
||||
QString m_modelString;
|
||||
bool m_queriedModelStringFlag; //!< model string is queried from network?
|
||||
BlackMisc::Aviation::CCallsign m_callsign; //!< aircraft's callsign
|
||||
BlackMisc::Aviation::CAircraftIcao m_icao; //!< ICAO data if available
|
||||
QString m_modelString; //!< Simulator model string
|
||||
QString m_description; //!< descriptive text
|
||||
QString m_fileName; //!< file name
|
||||
int m_modelType = static_cast<int>(TypeUnknown); //!< model string is queried from network?
|
||||
};
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::Network::CAircraftModel, (o.m_modelString, o.m_queriedModelStringFlag))
|
||||
BLACK_DECLARE_TUPLE_CONVERSION(
|
||||
BlackMisc::Network::CAircraftModel, (
|
||||
attr(o.m_callsign),
|
||||
attr(o.m_icao),
|
||||
attr(o.m_modelString, flags<CaseInsensitiveComparison>()),
|
||||
attr(o.m_description, flags<DisabledForComparison>()),
|
||||
attr(o.m_fileName, flags <DisabledForComparison> ()),
|
||||
attr(o.m_modelType)
|
||||
))
|
||||
Q_DECLARE_METATYPE(BlackMisc::Network::CAircraftModel)
|
||||
|
||||
#endif // guard
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include "blacksimfreefunctions.h"
|
||||
/* Copyright (C) 2013
|
||||
* 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 "blacksimfreefunctions.h"
|
||||
#include "blacksim/simulatorinfolist.h"
|
||||
#include "blacksim/setsimulator.h"
|
||||
|
||||
#include "fsx/simconnectutilities.h"
|
||||
#include "fscommon/aircraftcfgentrieslist.h"
|
||||
#include "fscommon/aircraftmappinglist.h"
|
||||
|
||||
|
||||
namespace BlackSim
|
||||
{
|
||||
@@ -17,9 +22,7 @@ namespace BlackSim
|
||||
BlackSim::CSimulatorInfoList::registerMetadata();
|
||||
BlackSim::Settings::CSettingsSimulator::registerMetadata();
|
||||
BlackSim::FsCommon::CAircraftCfgEntries::registerMetadata();
|
||||
BlackSim::FsCommon::CAircraftMapping::registerMetadata();
|
||||
BlackSim::FsCommon::CAircraftCfgEntriesList::registerMetadata();
|
||||
BlackSim::FsCommon::CAircraftMappingList::registerMetadata();
|
||||
BlackSim::Fsx::CSimConnectUtilities::registerMetadata();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 "aircraftmapping.h"
|
||||
#include "blackmisc/blackmiscfreefunctions.h"
|
||||
|
||||
using namespace BlackMisc;
|
||||
|
||||
namespace BlackSim
|
||||
{
|
||||
namespace FsCommon
|
||||
{
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
CAircraftMapping::CAircraftMapping() :
|
||||
m_mappingId(CAircraftMapping::InvalidId), m_proposalId(CAircraftMapping::InvalidId),
|
||||
m_lastChanged(-1), m_simulatorInfo(BlackSim::CSimulatorInfo::UnspecifiedSim()),
|
||||
m_changed(false)
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
CAircraftMapping::CAircraftMapping(
|
||||
qint32 mappingId, qint32 proposalId, const QString &fsAircraftKey, const QString &icaoAircraftDesignator,
|
||||
const QString &icaoAirline, const QString &icaoAircraftType, const QString &icaoWakeTurbulenceCategory, const QString &painting,
|
||||
const QString &lastChanged, BlackSim::CSimulatorInfo simulator) :
|
||||
m_mappingId(mappingId), m_proposalId(proposalId), m_fsAircraftKey(fsAircraftKey), m_aircraftDesignator(icaoAircraftDesignator),
|
||||
m_airlineDesignator(icaoAirline), m_aircraftCombinedType(icaoAircraftType), m_wakeTurbulenceCategory(icaoWakeTurbulenceCategory), m_aircraftColor(painting),
|
||||
m_lastChanged(lastChanged), m_simulatorInfo(simulator), m_changed(false)
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
/*
|
||||
* String for converter
|
||||
*/
|
||||
QString CAircraftMapping::convertToQString(bool i18n) const
|
||||
{
|
||||
QString s("{%1, %2, %3, %4, %5}");
|
||||
s = s.arg(this->m_fsAircraftKey).
|
||||
arg(this->m_mappingId).arg(this->m_proposalId).
|
||||
arg(this->m_aircraftDesignator).arg(this->m_simulatorInfo.toQString(i18n));
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate data
|
||||
*/
|
||||
QString CAircraftMapping::validate() const
|
||||
{
|
||||
QString msg;
|
||||
|
||||
if (this->m_fsAircraftKey.isEmpty())
|
||||
msg.append("Missing sim key. ");
|
||||
if (this->m_aircraftDesignator.isEmpty())
|
||||
msg.append("Missing designator. ");
|
||||
if (this->m_simulatorInfo.isUnspecified())
|
||||
msg.append("Unknown simulator. ");
|
||||
|
||||
if (this->m_aircraftCombinedType.isEmpty())
|
||||
msg.append("Missing type. ");
|
||||
else if (this->m_aircraftCombinedType.length() != 3)
|
||||
msg.append("Wrong type length (req.3). ");
|
||||
|
||||
if (this->m_wakeTurbulenceCategory.isEmpty() || this->m_wakeTurbulenceCategory.length() != 1)
|
||||
msg.append("Invalid WTC. ");
|
||||
else if (this->m_wakeTurbulenceCategory != "L" && this->m_wakeTurbulenceCategory != "M" && this->m_wakeTurbulenceCategory != "H")
|
||||
msg.append("Invalid WTC code. ");
|
||||
|
||||
return msg.trimmed();
|
||||
}
|
||||
|
||||
/*
|
||||
* Last changed formatted
|
||||
*/
|
||||
QString CAircraftMapping::getLastChangedFormatted() const
|
||||
{
|
||||
QString ts = this->m_lastChanged;
|
||||
QString f("%1-%2-%3 %4:%5:%6");
|
||||
return f.arg(ts.left(4)).arg(ts.mid(4, 2)).arg(ts.mid(6, 2))
|
||||
.arg(ts.mid(8, 2)).arg(ts.mid(10, 2)).arg(ts.right(2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash
|
||||
*/
|
||||
uint CAircraftMapping::getValueHash() const
|
||||
{
|
||||
return qHash(TupleConverter<CAircraftMapping>::toTuple(*this));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get column
|
||||
*/
|
||||
CVariant CAircraftMapping::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
|
||||
{
|
||||
if (index.isMyself()) { return this->toCVariant(); }
|
||||
ColumnIndex i = index.frontCasted<ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case IndexMappingId:
|
||||
return CVariant::from(this->m_mappingId);
|
||||
case IndexProposalId:
|
||||
return CVariant::from(m_proposalId);
|
||||
case IndexAircraftKey:
|
||||
return CVariant::from(m_fsAircraftKey);
|
||||
case IndexAircraftDesignator:
|
||||
return CVariant::from(m_aircraftDesignator);
|
||||
case IndexAirlineDesignator:
|
||||
return CVariant::from(m_airlineDesignator);
|
||||
case IndexAircraftCombinedType:
|
||||
return CVariant::from(m_aircraftCombinedType);
|
||||
case IndexWakeTurbulenceCategory:
|
||||
return CVariant::from(m_wakeTurbulenceCategory);
|
||||
case IndexAirlineColor:
|
||||
return CVariant::from(this->m_aircraftColor);
|
||||
case IndexLastChanged:
|
||||
return CVariant::from(this->getLastChangedFormatted());
|
||||
case IndexSimulatorInfo:
|
||||
return this->m_simulatorInfo.propertyByIndex(index.copyFrontRemoved());
|
||||
default:
|
||||
return CValueObject::propertyByIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set column's value
|
||||
*/
|
||||
void CAircraftMapping::setPropertyByIndex(const CVariant &variant, const BlackMisc::CPropertyIndex &index)
|
||||
{
|
||||
if (index.isMyself())
|
||||
{
|
||||
this->convertFromCVariant(variant);
|
||||
return;
|
||||
}
|
||||
ColumnIndex i = index.frontCasted<ColumnIndex>();
|
||||
switch (i)
|
||||
{
|
||||
case IndexMappingId:
|
||||
this->m_mappingId = variant.valueOrDefault<qint32>(CAircraftMapping::InvalidId);
|
||||
break;
|
||||
case IndexProposalId:
|
||||
this->m_proposalId = variant.valueOrDefault<qint32>(CAircraftMapping::InvalidId);
|
||||
break;
|
||||
case IndexAircraftKey:
|
||||
m_fsAircraftKey = variant.toQString();
|
||||
break;
|
||||
case IndexAircraftDesignator:
|
||||
this->setAircraftDesignator(variant.toQString());
|
||||
break;
|
||||
case IndexAirlineDesignator:
|
||||
this->setAirlineDesignator(variant.toQString());
|
||||
break;
|
||||
case IndexAircraftCombinedType:
|
||||
this->setAircraftCombinedType(variant.toQString());
|
||||
break;
|
||||
case IndexWakeTurbulenceCategory:
|
||||
this->setWakeTurbulenceCategory(variant.toQString());
|
||||
break;
|
||||
case IndexAirlineColor:
|
||||
this->m_aircraftColor = variant.toQString();
|
||||
break;
|
||||
default:
|
||||
CValueObject::setPropertyByIndex(variant, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
@@ -1,183 +0,0 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 BLACKSIM_FSCOMMON_AIRCRAFTMAPPING_H
|
||||
#define BLACKSIM_FSCOMMON_AIRCRAFTMAPPING_H
|
||||
|
||||
#include "aircraftcfgentries.h"
|
||||
#include "blacksim/simulatorinfo.h"
|
||||
#include "blackmisc/valueobject.h"
|
||||
#include <QDateTime>
|
||||
|
||||
namespace BlackSim
|
||||
{
|
||||
namespace FsCommon
|
||||
{
|
||||
//! Aircraft mapping class, represents one particular mapping
|
||||
class CAircraftMapping : public BlackMisc::CValueObjectStdTuple<CAircraftMapping>
|
||||
{
|
||||
public:
|
||||
static const qint32 InvalidId = -1; //!< Invalid mapping id
|
||||
|
||||
//! Columns
|
||||
enum ColumnIndex
|
||||
{
|
||||
IndexMappingId = BlackMisc::CPropertyIndex::GlobalIndexCAircraftMapping,
|
||||
IndexProposalId,
|
||||
IndexAircraftKey,
|
||||
IndexAircraftDesignator,
|
||||
IndexAirlineDesignator,
|
||||
IndexAircraftCombinedType,
|
||||
IndexWakeTurbulenceCategory,
|
||||
IndexAirlineColor,
|
||||
IndexLastChanged,
|
||||
IndexSimulatorInfo
|
||||
};
|
||||
|
||||
//! Default mapping
|
||||
CAircraftMapping();
|
||||
|
||||
/*!
|
||||
* Complete constructor
|
||||
* \param mappingId
|
||||
* \param proposalId
|
||||
* \param fsAircraftKey
|
||||
* \param icaoAircraftDesignator
|
||||
* \param icaoAirline
|
||||
* \param icaoAircraftType
|
||||
* \param icaoWakeTurbulenceCategory
|
||||
* \param painting
|
||||
* \param lastChanged
|
||||
* \param simulator
|
||||
*/
|
||||
CAircraftMapping(qint32 mappingId, qint32 proposalId, const QString &fsAircraftKey, const QString &icaoAircraftDesignator, const QString &icaoAirline, const QString &icaoAircraftType, const QString &icaoWakeTurbulenceCategory, const QString &painting, const QString &lastChanged, CSimulatorInfo simulator);
|
||||
|
||||
//! Mapping id
|
||||
qint32 getMappingId() const { return this->m_mappingId; }
|
||||
|
||||
//! Proposal id (if proposal, otherwise <0)
|
||||
qint32 getProposalId() const { return this->m_proposalId; }
|
||||
|
||||
//! Aircraft key
|
||||
QString getFsAircraftKey() const { return this->m_fsAircraftKey; }
|
||||
|
||||
//! ICAO designator (B737)
|
||||
QString getAircraftDesignator() const { return this->m_aircraftDesignator; }
|
||||
|
||||
//! ICAO airline (DLH)
|
||||
QString getAirlineDesignator() const { return this->m_airlineDesignator; }
|
||||
|
||||
//! ICAO aircraft type (L2J)
|
||||
QString getAircraftCombinedType() const { return this->m_aircraftCombinedType; }
|
||||
|
||||
//! ICAO wake turbulence category (L,M,H)
|
||||
QString getWakeTurbulenceCategory() const { return this->m_wakeTurbulenceCategory; }
|
||||
|
||||
//! Painting, basically the airline code for GA planes
|
||||
QString getAircraftColor() const { return this->m_aircraftColor; }
|
||||
|
||||
//! Last changed timestamp YYYYMMDDhhmmss
|
||||
QString getLastChanged() const { return this->m_lastChanged; }
|
||||
|
||||
//! Last changed timestamp YYYYMMDDhhmmss
|
||||
QString getLastChangedFormatted() const;
|
||||
|
||||
//! Simulator
|
||||
BlackSim::CSimulatorInfo getSimulatorInfo() const { return this->m_simulatorInfo; }
|
||||
|
||||
//! Simulator
|
||||
QString getSimulatorText() const;
|
||||
|
||||
//! Valid data?
|
||||
bool isValid() const
|
||||
{
|
||||
QString v = this->validate();
|
||||
return v.isEmpty();
|
||||
}
|
||||
|
||||
//! Validate and return error messages, empty string means all OK.
|
||||
QString validate() const;
|
||||
|
||||
//! Set mapping id
|
||||
void setMappingId(qint32 mappingId) { this->m_mappingId = mappingId; }
|
||||
|
||||
//! Proposal id
|
||||
void setProposalId(qint32 proposalId) { this->m_proposalId = proposalId; }
|
||||
|
||||
//! Aircraft key
|
||||
void setFsAircraftKey(const QString &aircraftKey) { this->m_fsAircraftKey = aircraftKey; }
|
||||
|
||||
//! ICAO designator (B737)
|
||||
void setAircraftDesignator(const QString &icaoDesignator) { this->m_aircraftDesignator = icaoDesignator.toUpper(); }
|
||||
|
||||
//! ICAO airline (DLH)
|
||||
void setAirlineDesignator(const QString &airline) { this->m_airlineDesignator = airline.toUpper(); }
|
||||
|
||||
//! ICAO aircraft type (L2J)
|
||||
void setAircraftCombinedType(const QString &aircraftType) { this->m_aircraftCombinedType = aircraftType.toUpper(); }
|
||||
|
||||
//! ICAO wake turbulence category
|
||||
void setWakeTurbulenceCategory(const QString &wtc) { this->m_wakeTurbulenceCategory = wtc.toUpper(); }
|
||||
|
||||
//! Painting, basically the airline code for GA planes
|
||||
void setAircraftColor(const QString &painting) { this->m_aircraftColor = painting; }
|
||||
|
||||
//! Last changed timestamp YYYYMMDDhhmmss
|
||||
void setLastChanged(qint32 lastChanged) { this->m_lastChanged = lastChanged; }
|
||||
|
||||
//! Simulator
|
||||
void setSimulator(BlackSim::CSimulatorInfo simulator) { this->m_simulatorInfo = simulator; }
|
||||
|
||||
//! Set simulator text
|
||||
void setSimulatorText(const QString &simulator);
|
||||
|
||||
//! \copydoc CValueObject::propertyByIndex
|
||||
BlackMisc::CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const override;
|
||||
|
||||
//! \copydoc CValueObject::setPropertyByIndex()
|
||||
void setPropertyByIndex(const BlackMisc::CVariant &value, const BlackMisc::CPropertyIndex &index) override;
|
||||
|
||||
//! \copydoc CValueObject::getValueHash()
|
||||
virtual uint getValueHash() const override;
|
||||
|
||||
//! Current UTC timestamp
|
||||
static QString currentUtcTimestamp()
|
||||
{
|
||||
QDateTime dateTime = QDateTime::currentDateTimeUtc();
|
||||
QString dateTimeString = dateTime.toString("yyyyMMddhhmmss");
|
||||
return dateTimeString;
|
||||
}
|
||||
|
||||
protected:
|
||||
//! \copydoc CValueObject::convertToQString
|
||||
virtual QString convertToQString(bool i18n = false) const;
|
||||
|
||||
private:
|
||||
BLACK_ENABLE_TUPLE_CONVERSION(CAircraftMapping)
|
||||
qint32 m_mappingId; //!< Kind of primary key for this particular mapping
|
||||
qint32 m_proposalId; //!< If proposal id of the proposal
|
||||
QString m_fsAircraftKey; //!< Id by which the simulator can create the aircraft
|
||||
QString m_aircraftDesignator; //!< Aircraft designator such as B737
|
||||
QString m_airlineDesignator; //!< Airline designator such as DLH
|
||||
QString m_aircraftCombinedType; //!< Engine, type, number of engines L2J, L1P
|
||||
QString m_wakeTurbulenceCategory; //!< Wake turbulence category H, L, M
|
||||
QString m_aircraftColor; //!< Aircrafts painting designator, could be same as airline or specific
|
||||
QString m_lastChanged; //!< Simple timestamp as YYYYMMDDhhmmss
|
||||
BlackSim::CSimulatorInfo m_simulatorInfo; //!< Mapping is for simulator
|
||||
bool m_changed; //! Changed flag
|
||||
};
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
BLACK_DECLARE_TUPLE_CONVERSION(BlackSim::FsCommon::CAircraftMapping, (o.m_mappingId, o.m_proposalId, o.m_fsAircraftKey, o.m_aircraftDesignator, o.m_airlineDesignator, o.m_aircraftCombinedType, o.m_wakeTurbulenceCategory, o.m_aircraftColor, o.m_lastChanged, o.m_simulatorInfo, o.m_changed))
|
||||
Q_DECLARE_METATYPE(BlackSim::FsCommon::CAircraftMapping)
|
||||
|
||||
#endif // guard
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "aircraftmappinglist.h"
|
||||
|
||||
namespace BlackSim
|
||||
{
|
||||
namespace FsCommon
|
||||
{
|
||||
/*
|
||||
* Register metadata
|
||||
*/
|
||||
void CAircraftMappingList::registerMetadata()
|
||||
{
|
||||
qRegisterMetaType<BlackMisc::CSequence<CAircraftMapping>>();
|
||||
qDBusRegisterMetaType<BlackMisc::CSequence<CAircraftMapping>>();
|
||||
qRegisterMetaType<BlackMisc::CCollection<CAircraftMapping>>();
|
||||
qDBusRegisterMetaType<BlackMisc::CCollection<CAircraftMapping>>();
|
||||
qRegisterMetaType<CAircraftMappingList>();
|
||||
qDBusRegisterMetaType<CAircraftMappingList>();
|
||||
BlackMisc::registerMetaValueType<CAircraftMappingList>();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@@ -1,61 +0,0 @@
|
||||
/* Copyright (C) 2013
|
||||
* 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 BLACKSIM_FSCOMMON_AIRCRAFTMAPPINGS_H
|
||||
#define BLACKSIM_FSCOMMON_AIRCRAFTMAPPINGS_H
|
||||
|
||||
#include "aircraftmapping.h"
|
||||
#include "aircraftcfgentrieslist.h"
|
||||
#include "blackmisc/sequence.h"
|
||||
#include <QTemporaryFile>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
namespace BlackSim
|
||||
{
|
||||
namespace FsCommon
|
||||
{
|
||||
//! Aircraft mappings
|
||||
class CAircraftMappingList : public BlackMisc::CSequence<CAircraftMapping>
|
||||
{
|
||||
|
||||
public:
|
||||
//! Default constructor
|
||||
CAircraftMappingList() {}
|
||||
|
||||
//! Virtual destructor
|
||||
virtual ~CAircraftMappingList() {}
|
||||
|
||||
//! \copydoc CValueObject::toQVariant
|
||||
virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); }
|
||||
|
||||
//! \copydoc CValueObject::convertFromQVariant
|
||||
virtual void convertFromQVariant(const QVariant &variant) override { BlackMisc::setFromQVariant(this, variant); }
|
||||
|
||||
//! Unknown mapping
|
||||
static const CAircraftMapping &UnknownMapping()
|
||||
{
|
||||
static CAircraftMapping mapping;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
//! Register metadata
|
||||
static void registerMetadata();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(BlackSim::FsCommon::CAircraftMappingList)
|
||||
Q_DECLARE_METATYPE(BlackMisc::CCollection<BlackSim::FsCommon::CAircraftMapping>)
|
||||
Q_DECLARE_METATYPE(BlackMisc::CSequence<BlackSim::FsCommon::CAircraftMapping>)
|
||||
|
||||
#endif // guard
|
||||
Reference in New Issue
Block a user