From ebd916f6d8d03c53e131bdf486155f871446ce00 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sun, 28 Jan 2018 04:42:14 +0100 Subject: [PATCH] Ref T231, Ref T236, Ref T238 added ITimestampWithOffsetBased * base class for offset timefeaturing classes * used with parts/situations --- src/blackmisc/aviation/aircraftparts.cpp | 29 ++-- src/blackmisc/aviation/aircraftparts.h | 14 +- src/blackmisc/aviation/aircraftsituation.cpp | 14 +- src/blackmisc/aviation/aircraftsituation.h | 12 +- src/blackmisc/timestampbased.cpp | 140 +++++++++++++++---- src/blackmisc/timestampbased.h | 84 +++++++++-- 6 files changed, 212 insertions(+), 81 deletions(-) diff --git a/src/blackmisc/aviation/aircraftparts.cpp b/src/blackmisc/aviation/aircraftparts.cpp index ec61d16ec..f5a5bf925 100644 --- a/src/blackmisc/aviation/aircraftparts.cpp +++ b/src/blackmisc/aviation/aircraftparts.cpp @@ -22,25 +22,19 @@ namespace BlackMisc { QString CAircraftParts::convertToQString(bool i18n) const { - const QString s = - m_lights.toQString(i18n) % - " gear down: " % - BlackMisc::boolToYesNo(m_gearDown) % - " flaps pct: " % - QString::number(m_flapsPercentage) % - " spoilers out: " % - BlackMisc::boolToYesNo(m_spoilersOut) % - " engines on: " % - m_engines.toQString(i18n) % - " on ground: " % - BlackMisc::boolToYesNo(m_isOnGround); - return s; + return QStringLiteral("ts: ") % this->getFormattedTimestampAndOffset(true) % + QStringLiteral(" lights: ") % m_lights.toQString(i18n) % + QStringLiteral(" gear down: ") % BlackMisc::boolToYesNo(m_gearDown) % + QStringLiteral(" flaps pct: ") % QString::number(m_flapsPercentage) % + QStringLiteral(" spoilers out: ") % BlackMisc::boolToYesNo(m_spoilersOut) % + QStringLiteral(" engines on: ") % m_engines.toQString(i18n) % + QStringLiteral(" on ground: ") % BlackMisc::boolToYesNo(m_isOnGround); } CVariant CAircraftParts::propertyByIndex(const BlackMisc::CPropertyIndex &index) const { if (index.isMyself()) { return CVariant::from(*this); } - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { return ITimestampWithOffsetBased::propertyByIndex(index); } const ColumnIndex i = index.frontCasted(); switch (i) @@ -57,7 +51,7 @@ namespace BlackMisc void CAircraftParts::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) { if (index.isMyself()) { (*this) = variant.to(); return; } - if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { ITimestampWithOffsetBased::setPropertyByIndex(index, variant); return; } const ColumnIndex i = index.frontCasted(); switch (i) @@ -73,8 +67,8 @@ namespace BlackMisc int CAircraftParts::comparePropertyByIndex(const CPropertyIndex &index, const CAircraftParts &compareValue) const { - if (index.isMyself()) { return ITimestampBased::comparePropertyByIndex(CPropertyIndex(), compareValue); } - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + if (index.isMyself()) { return ITimestampWithOffsetBased::comparePropertyByIndex(CPropertyIndex(), compareValue); } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { return ITimestampWithOffsetBased::comparePropertyByIndex(index, compareValue); } const ColumnIndex i = index.frontCasted(); switch (i) @@ -123,6 +117,5 @@ namespace BlackMisc } return m_isOnGroundInterpolated; } - } // namespace } // namespace diff --git a/src/blackmisc/aviation/aircraftparts.h b/src/blackmisc/aviation/aircraftparts.h index fd44521f7..5b2280d54 100644 --- a/src/blackmisc/aviation/aircraftparts.h +++ b/src/blackmisc/aviation/aircraftparts.h @@ -32,13 +32,13 @@ namespace BlackMisc //! Value object encapsulating information of aircraft's parts class BLACKMISC_EXPORT CAircraftParts : public CValueObject, - public ITimestampBased + public ITimestampWithOffsetBased { public: //! Properties by index enum ColumnIndex { - IndexLights = BlackMisc::CPropertyIndex::GlobalIndexCAircraftParts, + IndexLights = CPropertyIndex::GlobalIndexCAircraftParts, IndexGearDown, IndexFlapsPercentage, IndexSpoilersOut, @@ -125,15 +125,6 @@ namespace BlackMisc //! Set aircraft on ground. (Smoothly interpolated between 0 and 1.) void setOnGroundInterpolated(double onGround) { m_isOnGroundInterpolated = onGround; } - //! Milliseconds to add to timestamp for interpolation - void setTimeOffsetMs(qint64 offset) { m_timeOffsetMs = offset; } - - //! Milliseconds to add to timestamp for interpolation - qint64 getTimeOffsetMs() const { return m_timeOffsetMs; } - - //! Timestamp with offset added for interpolation - qint64 getAdjustedMSecsSinceEpoch() const { return this->getMSecsSinceEpoch() + this->getTimeOffsetMs(); } - //! \copydoc BlackMisc::Mixin::String::toQString QString convertToQString(bool i18n = false) const; @@ -145,7 +136,6 @@ namespace BlackMisc bool m_spoilersOut = false; bool m_isOnGround = false; double m_isOnGroundInterpolated = -1; - qint64 m_timeOffsetMs = 0; BLACK_METACLASS( CAircraftParts, diff --git a/src/blackmisc/aviation/aircraftsituation.cpp b/src/blackmisc/aviation/aircraftsituation.cpp index d1a2eb340..30f72a644 100644 --- a/src/blackmisc/aviation/aircraftsituation.cpp +++ b/src/blackmisc/aviation/aircraftsituation.cpp @@ -47,15 +47,15 @@ namespace BlackMisc QString CAircraftSituation::convertToQString(bool i18n) const { - return m_position.toQString(i18n) % + return QStringLiteral("ts: ") % this->getFormattedTimestampAndOffset(true) % + QStringLiteral(" ") % m_position.toQString(i18n) % QStringLiteral(" bank: ") % (m_bank.toQString(i18n)) % QStringLiteral(" pitch: ") % (m_pitch.toQString(i18n)) % QStringLiteral(" heading: ") % (m_heading.toQString(i18n)) % QStringLiteral(" og: ") % this->getOnGroundInfo() % QStringLiteral(" gs: ") % m_groundSpeed.valueRoundedWithUnit(CSpeedUnit::kts(), 1, true) % QStringLiteral(" ") % m_groundSpeed.valueRoundedWithUnit(CSpeedUnit::m_s(), 1, true) % - QStringLiteral(" elevation: ") % (m_groundElevation.toQString(i18n)) % - QStringLiteral(" timestamp: ") % (this->hasValidTimestamp() ? this->getFormattedUtcTimestampDhms() : QStringLiteral("-")); + QStringLiteral(" elevation: ") % (m_groundElevation.toQString(i18n)); } const QString &CAircraftSituation::isOnGroundToString(CAircraftSituation::IsOnGround onGround) @@ -96,7 +96,7 @@ namespace BlackMisc CVariant CAircraftSituation::propertyByIndex(const BlackMisc::CPropertyIndex &index) const { if (index.isMyself()) { return CVariant::from(*this); } - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { return ITimestampWithOffsetBased::propertyByIndex(index); } if (ICoordinateGeodetic::canHandleIndex(index)) { return ICoordinateGeodetic::propertyByIndex(index); } const ColumnIndex i = index.frontCasted(); @@ -123,7 +123,7 @@ namespace BlackMisc void CAircraftSituation::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) { if (index.isMyself()) { (*this) = variant.to(); return; } - if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { ITimestampWithOffsetBased::setPropertyByIndex(index, variant); return; } const ColumnIndex i = index.frontCasted(); switch (i) @@ -142,7 +142,7 @@ namespace BlackMisc int CAircraftSituation::comparePropertyByIndex(const CPropertyIndex &index, const CAircraftSituation &compareValue) const { - if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + if (ITimestampWithOffsetBased::canHandleIndex(index)) { return ITimestampWithOffsetBased::comparePropertyByIndex(index, compareValue); } if (ICoordinateGeodetic::canHandleIndex(index)) { return ICoordinateGeodetic::comparePropertyByIndex(index, compareValue); } const ColumnIndex i = index.frontCasted(); switch (i) @@ -246,7 +246,7 @@ namespace BlackMisc } else { - CAltitude groundPlusCG = this->getGroundElevation().withOffset(centerOfGravity); + const CAltitude groundPlusCG = this->getGroundElevation().withOffset(centerOfGravity); const bool c = (this->getAltitude() < groundPlusCG); if (corrected) { *corrected = c; } return c ? diff --git a/src/blackmisc/aviation/aircraftsituation.h b/src/blackmisc/aviation/aircraftsituation.h index caba56de9..343916ac9 100644 --- a/src/blackmisc/aviation/aircraftsituation.h +++ b/src/blackmisc/aviation/aircraftsituation.h @@ -42,7 +42,7 @@ namespace BlackMisc //! Value object encapsulating information of an aircraft's situation class BLACKMISC_EXPORT CAircraftSituation : public CValueObject, - public Geo::ICoordinateGeodetic, public ITimestampBased + public Geo::ICoordinateGeodetic, public ITimestampWithOffsetBased { public: //! Properties by index @@ -231,15 +231,6 @@ namespace BlackMisc //! Corresponding callsign void setCallsign(const CCallsign &callsign); - //! Milliseconds to add to timestamp for interpolation - void setTimeOffsetMs(qint64 offset) { m_timeOffsetMs = offset; } - - //! Milliseconds to add to timestamp for interpolation - qint64 getTimeOffsetMs() const { return m_timeOffsetMs; } - - //! Timestamp with offset added for interpolation - qint64 getAdjustedMSecsSinceEpoch() const { return this->getMSecsSinceEpoch() + this->getTimeOffsetMs(); } - //! Set flag indicating this is an interim position update void setInterimFlag(bool flag) { m_isInterim = flag; } @@ -266,7 +257,6 @@ namespace BlackMisc CAltitude m_groundElevation{0, CAltitude::MeanSeaLevel, PhysicalQuantities::CLengthUnit::nullUnit()}; int m_isOnGround = static_cast(CAircraftSituation::OnGroundSituationUnknown); int m_onGroundReliability = static_cast(CAircraftSituation::OnGroundReliabilityNoSet); - qint64 m_timeOffsetMs = 0; bool m_isInterim = false; BLACK_METACLASS( diff --git a/src/blackmisc/timestampbased.cpp b/src/blackmisc/timestampbased.cpp index 3191f58da..3371ef872 100644 --- a/src/blackmisc/timestampbased.cpp +++ b/src/blackmisc/timestampbased.cpp @@ -142,6 +142,20 @@ namespace BlackMisc this->m_timestampMSecsSinceEpoch += ms; } + QString ITimestampBased::getFormattedUtcTimestampMdhms() const + { + return this->hasValidTimestamp() ? + this->getUtcTimestamp().toString("MM-dd hh:mm:ss") : + ""; + } + + QString ITimestampBased::getFormattedUtcTimestampMdhmsz() const + { + return this->hasValidTimestamp() ? + this->getUtcTimestamp().toString("MM-dd hh:mm:ss.zzz") : + ""; + } + QString ITimestampBased::getFormattedUtcTimestampDhms() const { return this->hasValidTimestamp() ? @@ -156,6 +170,13 @@ namespace BlackMisc ""; } + QString ITimestampBased::getFormattedUtcTimestampHmsz() const + { + return this->hasValidTimestamp() ? + this->getUtcTimestamp().toString("hh:mm:ss.zzz") : + ""; + } + QString ITimestampBased::getFormattedUtcTimestampHm() const { return this->hasValidTimestamp() ? @@ -196,22 +217,16 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexUtcTimestamp: - return CVariant::fromValue(this->getUtcTimestamp()); - case IndexMSecsSinceEpoch: - return CVariant::fromValue(this->getMSecsSinceEpoch()); - case IndexUtcTimestampFormattedDhms: - return CVariant::fromValue(this->getFormattedUtcTimestampDhms()); - case IndexUtcTimestampFormattedHm: - return CVariant::fromValue(this->getFormattedUtcTimestampHm()); - case IndexUtcTimestampFormattedHms: - return CVariant::fromValue(this->getFormattedUtcTimestampHms()); - case IndexUtcTimestampFormattedYmdhms: - return CVariant::fromValue(this->getFormattedUtcTimestampYmdhms()); - case IndexUtcTimestampFormattedYmdhmsz: - return CVariant::fromValue(this->getFormattedUtcTimestampYmdhmsz()); - default: - break; + case IndexUtcTimestamp: return CVariant::fromValue(this->getUtcTimestamp()); + case IndexMSecsSinceEpoch: return CVariant::fromValue(this->getMSecsSinceEpoch()); + case IndexUtcTimestampFormattedDhms: return CVariant::fromValue(this->getFormattedUtcTimestampDhms()); + case IndexUtcTimestampFormattedHm: return CVariant::fromValue(this->getFormattedUtcTimestampHm()); + case IndexUtcTimestampFormattedHms: return CVariant::fromValue(this->getFormattedUtcTimestampHms()); + case IndexUtcTimestampFormattedYmdhms: return CVariant::fromValue(this->getFormattedUtcTimestampYmdhms()); + case IndexUtcTimestampFormattedYmdhmsz: return CVariant::fromValue(this->getFormattedUtcTimestampYmdhmsz()); + case IndexUtcTimestampFormattedMdhms: return CVariant::fromValue(this->getFormattedUtcTimestampMdhms()); + case IndexUtcTimestampFormattedMdhmsz: return CVariant::fromValue(this->getFormattedUtcTimestampMdhmsz()); + default: break; } } const QString m = QString("Cannot handle index %1").arg(index.toQString()); @@ -226,12 +241,8 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexUtcTimestamp: - this->setUtcTimestamp(variant.toDateTime()); - return; - case IndexMSecsSinceEpoch: - this->setMSecsSinceEpoch(variant.toInt()); - return; + case IndexUtcTimestamp: this->setUtcTimestamp(variant.toDateTime()); return; + case IndexMSecsSinceEpoch: this->setMSecsSinceEpoch(variant.toInt()); return; case IndexUtcTimestampFormattedYmdhms: case IndexUtcTimestampFormattedYmdhmsz: case IndexUtcTimestampFormattedHm: @@ -241,8 +252,8 @@ namespace BlackMisc const QDateTime dt = QDateTime::fromString(variant.toQString()); this->m_timestampMSecsSinceEpoch = dt.toMSecsSinceEpoch(); } - default: - break; + return; + default: break; } } const QString m = QString("Cannot handle index %1").arg(index.toQString()); @@ -255,4 +266,85 @@ namespace BlackMisc return Compare::compare(this->m_timestampMSecsSinceEpoch, compareValue.m_timestampMSecsSinceEpoch); } + QString ITimestampWithOffsetBased::getTimestampAndOffset(bool formatted) const + { + static const QString ts("%1 (%2)"); + if (!this->hasValidTimestamp()) { return ts.arg("-", this->getTimeOffsetWithUnit()); } + if (formatted) { return ts.arg(this->getFormattedUtcTimestampHms(), this->getTimeOffsetWithUnit()); } + return ts.arg(m_timeOffsetMs).arg(this->getTimeOffsetWithUnit()); + } + + QString ITimestampWithOffsetBased::getFormattedTimestampAndOffset(bool includeRawTimestamp) const + { + if (!includeRawTimestamp) { return this->getTimestampAndOffset(true); } + static const QString ts("%1/%2 (%3)"); + if (!this->hasValidTimestamp()) { return ts.arg("-", "-", this->getTimeOffsetWithUnit()); } + return ts.arg(this->getFormattedUtcTimestampHmsz()).arg(m_timestampMSecsSinceEpoch).arg(this->getTimeOffsetWithUnit()); + } + + bool ITimestampWithOffsetBased::canHandleIndex(const CPropertyIndex &index) + { + if (ITimestampBased::canHandleIndex(index)) { return true; } + if (index.isEmpty()) { return false; } + const int i = index.frontCasted(); + return (i >= static_cast(IndexOffsetMs)) && (i <= static_cast(IndexOffsetWithUnit)); + } + + QString ITimestampWithOffsetBased::getTimeOffsetWithUnit() const + { + static const QString os("%1ms"); + return os.arg(this->getTimeOffsetMs()); + } + + CVariant ITimestampWithOffsetBased::propertyByIndex(const CPropertyIndex &index) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::propertyByIndex(index); } + if (!index.isEmpty()) + { + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexOffsetMs: { return QVariant::fromValue(m_timeOffsetMs); } + case IndexAdjustedMisWithOffset: { return QVariant::fromValue(this->getAdjustedMSecsSinceEpoch()); } + case IndexOffsetWithUnit: { return QVariant::fromValue(this->getTimeOffsetWithUnit()); } + default: break; + } + } + const QString m = QString("Cannot handle index %1").arg(index.toQString()); + BLACK_VERIFY_X(false, Q_FUNC_INFO, qUtf8Printable(m)); + return CVariant(); + } + + void ITimestampWithOffsetBased::setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant) + { + if (ITimestampBased::canHandleIndex(index)) { ITimestampBased::setPropertyByIndex(index, variant); return; } + if (!index.isEmpty()) + { + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexOffsetMs: { m_timeOffsetMs = variant.value(); return; } + default: break; + } + } + const QString m = QString("Cannot handle index %1").arg(index.toQString()); + BLACK_VERIFY_X(false, Q_FUNC_INFO, qUtf8Printable(m)); + } + + int ITimestampWithOffsetBased::comparePropertyByIndex(const CPropertyIndex &index, const ITimestampWithOffsetBased &compareValue) const + { + if (ITimestampBased::canHandleIndex(index)) { return ITimestampBased::comparePropertyByIndex(index, compareValue); } + if (!index.isEmpty()) + { + const ColumnIndex i = index.frontCasted(); + switch (i) + { + case IndexOffsetMs: { return Compare::compare(m_timeOffsetMs, compareValue.m_timeOffsetMs); } + default: break; + } + } + const QString m = QString("Cannot handle index %1").arg(index.toQString()); + BLACK_VERIFY_X(false, Q_FUNC_INFO, qUtf8Printable(m)); + return 0; + } } // namespace diff --git a/src/blackmisc/timestampbased.h b/src/blackmisc/timestampbased.h index 872c645ac..2c9e5977a 100644 --- a/src/blackmisc/timestampbased.h +++ b/src/blackmisc/timestampbased.h @@ -29,9 +29,11 @@ namespace BlackMisc //! Properties by index enum ColumnIndex { - IndexUtcTimestamp = BlackMisc::CPropertyIndex::GlobalIndexITimestampBased, + IndexUtcTimestamp = CPropertyIndex::GlobalIndexITimestampBased, IndexUtcTimestampFormattedYmdhms, IndexUtcTimestampFormattedYmdhmsz, + IndexUtcTimestampFormattedMdhms, + IndexUtcTimestampFormattedMdhmsz, IndexUtcTimestampFormattedDhms, IndexUtcTimestampFormattedHms, IndexUtcTimestampFormattedHm, @@ -89,15 +91,24 @@ namespace BlackMisc //! Add the given number of milliseconds to the timestamp. void addMsecs(qint64 ms); - //! Formatted timestamp - QString getFormattedUtcTimestampDhms() const; - //! As hh:mm:ss QString getFormattedUtcTimestampHms() const; + //! As hh:mm:ss.zzz + QString getFormattedUtcTimestampHmsz() const; + //! As hh:mm QString getFormattedUtcTimestampHm() const; + //! As dd HH mm ss + QString getFormattedUtcTimestampDhms() const; + + //! As MM dd HH mm ss + QString getFormattedUtcTimestampMdhms() const; + + //! As MM dd HH mm ss.zzz + QString getFormattedUtcTimestampMdhmsz() const; + //! As yyyy MM dd HH mm ss QString getFormattedUtcTimestampYmdhms() const; @@ -107,10 +118,10 @@ namespace BlackMisc //! Valid timestamp? bool hasValidTimestamp() const; - //! Can given index be handled - static bool canHandleIndex(const BlackMisc::CPropertyIndex &index); - protected: + //! Can given index be handled + static bool canHandleIndex(const CPropertyIndex &index); + //! Constructor ITimestampBased(); @@ -121,16 +132,71 @@ namespace BlackMisc ITimestampBased(const QDateTime ×tamp); //! \copydoc BlackMisc::Mixin::Index::propertyByIndex - CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const; + CVariant propertyByIndex(const CPropertyIndex &index) const; //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex - void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const CVariant &variant); + void setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant); //! Compare for index int comparePropertyByIndex(const CPropertyIndex &index, const ITimestampBased &compareValue) const; qint64 m_timestampMSecsSinceEpoch = -1; //!< timestamp value }; + + //! Entity with timestamp + class BLACKMISC_EXPORT ITimestampWithOffsetBased : public ITimestampBased + { + public: + //! Properties by index + enum ColumnIndex + { + IndexOffsetMs = CPropertyIndex::GlobalIndexITimestampBased + ITimestampBased::IndexMSecsSinceEpoch + 1, + IndexAdjustedMisWithOffset, + IndexOffsetWithUnit // keep this as last item + }; + + //! Milliseconds to add to timestamp for interpolation + void setTimeOffsetMs(qint64 offset) { m_timeOffsetMs = offset; } + + //! Milliseconds to add to timestamp for interpolation + qint64 getTimeOffsetMs() const { return m_timeOffsetMs; } + + //! Offset with unit + QString getTimeOffsetWithUnit() const; + + //! Timestamp with offset added for interpolation + qint64 getAdjustedMSecsSinceEpoch() const { return this->getMSecsSinceEpoch() + this->getTimeOffsetMs(); } + + //! Timestamp and offset + QString getTimestampAndOffset(bool formatted) const; + + //! Timestamp and offset + QString getFormattedTimestampAndOffset(bool includeRawTimestamp) const; + + protected: + //! Can given index be handled + static bool canHandleIndex(const CPropertyIndex &index); + + //! Constructor + ITimestampWithOffsetBased() : ITimestampBased() {} + + //! Constructor + ITimestampWithOffsetBased(qint64 msSincePoch) : ITimestampBased(msSincePoch) {} + + //! Constructor + ITimestampWithOffsetBased(const QDateTime ×tamp) : ITimestampBased(timestamp) {} + + //! \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 for index + int comparePropertyByIndex(const CPropertyIndex &index, const ITimestampWithOffsetBased &compareValue) const; + + qint64 m_timeOffsetMs = 0; //!< offset time in ms + }; } // namespace #endif // guard