diff --git a/src/blackmisc/network/network.h b/src/blackmisc/network/network.h index 94ec9562a..f5ec3d07c 100644 --- a/src/blackmisc/network/network.h +++ b/src/blackmisc/network/network.h @@ -32,6 +32,8 @@ #include "blackmisc/network/textmessagelist.h" #include "blackmisc/network/url.h" #include "blackmisc/network/urllist.h" +#include "blackmisc/network/urllog.h" +#include "blackmisc/network/urlloglist.h" #include "blackmisc/network/user.h" #include "blackmisc/network/userlist.h" #include "blackmisc/network/voicecapabilities.h" diff --git a/src/blackmisc/network/registermetadatanetwork.cpp b/src/blackmisc/network/registermetadatanetwork.cpp index 453f91959..4922011b5 100644 --- a/src/blackmisc/network/registermetadatanetwork.cpp +++ b/src/blackmisc/network/registermetadatanetwork.cpp @@ -32,6 +32,8 @@ namespace BlackMisc CUrl::registerMetadata(); CUrlList::registerMetadata(); CFailoverUrlList::registerMetadata(); + CUrlLog::registerMetadata(); + CUrlLogList::registerMetadata(); CUser::registerMetadata(); CUserList::registerMetadata(); CVoiceCapabilities::registerMetadata(); diff --git a/src/blackmisc/network/urllog.cpp b/src/blackmisc/network/urllog.cpp new file mode 100644 index 000000000..d55ed70b6 --- /dev/null +++ b/src/blackmisc/network/urllog.cpp @@ -0,0 +1,96 @@ +/* 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 "blackmisc/network/urllog.h" +#include "blackmisc/network/networkutils.h" +#include "blackmisc/propertyindex.h" + +#include +#include +#include + +#include + +using namespace BlackMisc::Db; + +namespace BlackMisc +{ + namespace Network + { + CUrlLog::CUrlLog(const CUrl &url) : + ITimestampBased(), m_id(uniqueId()), m_url(url) + { + ITimestampBased::setCurrentUtcTime(); + } + + void CUrlLog::setResponseTimestampToNow() + { + m_responseTimeMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); + m_responseTimeMs = m_responseTimeMSecsSinceEpoch - ITimestampBased::getMSecsSinceEpoch(); + } + + bool CUrlLog::isPending() const + { + return m_responseTimeMs < 0; + } + + CVariant CUrlLog::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 IndexId: return CVariant::from(m_id); + case IndexSuccess: return CVariant::from(m_success); + case IndexUrl: return this->m_url.propertyByIndex(index.copyFrontRemoved()); + case IndexResponseTimestamp: return CVariant::fromValue(this->getResponseTimestamp()); + case IndexResponseTime: return CVariant::fromValue(m_responseTimeMs); + default: return CValueObject::propertyByIndex(index); + } + } + + void CUrlLog::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 IndexId: m_id = variant.toInt(); break; + case IndexSuccess: m_success = variant.toBool(); break; + case IndexUrl: m_url.setPropertyByIndex(index.copyFrontRemoved(), variant); break; + case IndexResponseTime: this->setResponseTimestampToNow(); break; // a bit unusual + default: CValueObject::setPropertyByIndex(index, variant); break; + } + } + + QString CUrlLog::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + static const QString s("Id: %1, success: %2 response: %3ms, started: %4 ended: %5"); + return s.arg(m_id).arg(boolToYesNo(m_success)).arg(m_responseTimeMs).arg(this->getMSecsSinceEpoch()).arg(m_responseTimeMSecsSinceEpoch); + } + + const char *CUrlLog::propertyNameId() + { + static const QByteArray p(QString("urlLogId").toLatin1()); + return p.constData(); + } + + int CUrlLog::uniqueId() + { + static std::atomic_int s_id {1}; // 0 means default in property system, so I start with 1 + const int id = s_id++; + return id; + } + } // namespace +} // namespace diff --git a/src/blackmisc/network/urllog.h b/src/blackmisc/network/urllog.h new file mode 100644 index 000000000..6269c7803 --- /dev/null +++ b/src/blackmisc/network/urllog.h @@ -0,0 +1,111 @@ +/* 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_NETWORK_URLLOG_H +#define BLACKMISC_NETWORK_URLLOG_H + +#include "url.h" +#include "blackmisc/timestampbased.h" +#include "blackmisc/valueobject.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/propertyindex.h" + +#include +#include + +namespace BlackMisc +{ + namespace Network + { + //! Information about accessing one URL over the network + class BLACKMISC_EXPORT CUrlLog : + public BlackMisc::CValueObject, + public BlackMisc::ITimestampBased + { + public: + //! Properties by index + enum ColumnIndex + { + IndexId = BlackMisc::CPropertyIndex::GlobalIndexCUrlLog, + IndexSuccess, + IndexUrl, + IndexResponseTimestamp, + IndexResponseTime + }; + + //! Constructor, setting created to now and getting a valid id + CUrlLog(const CUrl &url = {}); + + //! Unique id + int getId() const { return this->m_id; } + + //! Get URL. + const CUrl &getUrl() const { return m_url; } + + //! Response timestamp + QDateTime getResponseTimestamp() const { return QDateTime::fromMSecsSinceEpoch(m_responseTimeMSecsSinceEpoch); } + + //! Response timestamp milliseconds since epoch + qint64 getResponseTimeMSecsSinceEpoch() const { return m_responseTimeMSecsSinceEpoch; } + + //! Set response time and response timestamp + void setResponseTimestampToNow(); + + //! Response time + qint64 getResponseTimeMs() const { return m_responseTimeMs; } + + //! Pending + bool isPending() const; + + //! Success? + bool isSuccess() const { return m_success; } + + //! Set success + void setSuccess(bool s) { m_success = s; } + + //! \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); + + //! \copydoc BlackMisc::Mixin::String::toQString() + QString convertToQString(bool i18n = false) const; + + //! Property name used for request + static const char *propertyNameId(); + + private: + int m_id = -1; + CUrl m_url; + bool m_success = false; + qint64 m_responseTimeMSecsSinceEpoch = -1; + qint64 m_responseTimeMs = -1; + + //! Get a unique id + //! \threadsafe + static int uniqueId(); + + BLACK_METACLASS( + CUrlLog, + BLACK_METAMEMBER(id), + BLACK_METAMEMBER(url), + BLACK_METAMEMBER(success), + BLACK_METAMEMBER(responseTimeMSecsSinceEpoch), + BLACK_METAMEMBER(responseTimeMs) + ); + }; + } // namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Network::CUrlLog) + +#endif // guard diff --git a/src/blackmisc/network/urlloglist.cpp b/src/blackmisc/network/urlloglist.cpp new file mode 100644 index 000000000..f152a88a9 --- /dev/null +++ b/src/blackmisc/network/urlloglist.cpp @@ -0,0 +1,150 @@ +/* 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 "blackmisc/network/urlloglist.h" + +namespace BlackMisc +{ + namespace Network + { + CUrlLogList::CUrlLogList() { } + + CUrlLogList::CUrlLogList(const CSequence &other) : CSequence(other) + { } + + int CUrlLogList::addPendingUrl(const CUrl &url, int maxNumber) + { + if (maxNumber > 0) this->truncate(maxNumber - 1); + const CUrlLog rl(url); + this->push_front(rl); + return rl.getId(); + } + + int CUrlLogList::addPendingUrl(const CUrl &url, QNetworkReply *nwReply, int maxNumber) + { + const int id = this->addPendingUrl(url, maxNumber); + if (nwReply) + { + nwReply->setProperty(CUrlLog::propertyNameId(), QVariant::fromValue(id)); + } + return id; + } + + CUrlLogList CUrlLogList::findPending() const + { + return this->findBy(&CUrlLog::isPending, true); + } + + CUrlLogList CUrlLogList::findErrors() const + { + return this->findBy(&CUrlLog::isPending, false, &CUrlLog::isSuccess, false); + } + + int CUrlLogList::sizePending() const + { + if (this->isEmpty()) return 0; + return this->findPending().size(); + } + + bool CUrlLogList::hasPending() const + { + // faster as using sizePending() + return this->contains(&CUrlLog::isPending, true); + } + + bool CUrlLogList::hasCompleted() const + { + // faster as using sizePending() + return this->contains(&CUrlLog::isPending, false); + } + + int CUrlLogList::sizeErrors() const + { + if (this->isEmpty()) return 0; + return this->findErrors().size(); + } + + CUrlLog CUrlLogList::findByIdOrDefault(int id) const + { + return this->findFirstByOrDefault(&CUrlLog::getId, id); + } + + bool CUrlLogList::markAsReceived(int id, bool success) + { + for (CUrlLog &rl : *this) + { + if (rl.getId() == id) + { + rl.setResponseTimestampToNow(); + rl.setSuccess(success); + return true; + } + } + return false; + } + + bool CUrlLogList::markAsReceived(const QNetworkReply *nwReply, bool success) + { + Q_ASSERT_X(nwReply, Q_FUNC_INFO, "missing reply"); + bool ok; + const int id = nwReply->property(CUrlLog::propertyNameId()).toInt(&ok); + return (ok && id >= 0) ? this->markAsReceived(id, success) : false; + } + + bool CUrlLogList::containsId(int id) const + { + return this->contains(&CUrlLog::getId, id); + } + + qint64 CUrlLogList::getMaxResponseTime() const + { + qint64 max = 0; + for (const CUrlLog &rl : *this) + { + if (rl.isPending()) { continue; } + if (rl.getResponseTimeMs() > max) { max = rl.getResponseTimeMs(); } + } + return max; + } + + qint64 CUrlLogList::getMinResponseTime() const + { + if (!this->hasCompleted()) { return 0; } + qint64 min = std::numeric_limits < qint64 >::max(); + for (const CUrlLog &rl : *this) + { + if (rl.isPending()) { continue; } + if (rl.getResponseTimeMs() < min) { min = rl.getResponseTimeMs(); } + } + return min; + } + + qint64 CUrlLogList::getAverageResponseTime() const + { + qint64 sum = 0; + int c = 0; + for (const CUrlLog &rl : *this) + { + if (rl.isPending()) { continue; } + sum += rl.getResponseTimeMs(); + c++; + } + if (c == 0) return 0; + return sum / c; + } + + QString CUrlLogList::getSummary() const + { + static const QString s("Entries: %1, pending: %2, errors: %3, min: %4ms avg: %5ms max: %6ms"); + static const QString e("No data"); + if (this->isEmpty()) return e; + return s.arg(this->size()).arg(this->sizePending()).arg(this->sizeErrors()).arg(this->getMinResponseTime()).arg(this->getAverageResponseTime()).arg(this->getMaxResponseTime()); + } + } // namespace +} // namespace diff --git a/src/blackmisc/network/urlloglist.h b/src/blackmisc/network/urlloglist.h new file mode 100644 index 000000000..b064bacd4 --- /dev/null +++ b/src/blackmisc/network/urlloglist.h @@ -0,0 +1,97 @@ +/* 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_NETWORK_URLLOGLIST_H +#define BLACKMISC_NETWORK_URLLOGLIST_H + +#include "blackmisc/network/urllog.h" +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/timestampobjectlist.h" +#include "blackmisc/collection.h" +#include "blackmisc/sequence.h" +#include "blackmisc/variant.h" +#include + +namespace BlackMisc +{ + namespace Network + { + //! Value object encapsulating a list of voice rooms. + class BLACKMISC_EXPORT CUrlLogList : + public CSequence, + public BlackMisc::ITimestampObjectList, + public BlackMisc::Mixin::MetaType + { + public: + BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CUrlLogList) + + //! Default constructor. + CUrlLogList(); + + //! Construct from a base class object. + CUrlLogList(const CSequence &other); + + //! Add a pending URL + int addPendingUrl(const CUrl &url, int maxNumber = 10); + + //! Add a pending URL + int addPendingUrl(const CUrl &url, QNetworkReply *nwReply, int maxNumber = 10); + + //! Find pending log entries + CUrlLogList findPending() const; + + //! Find log entries with errors (not pending) + CUrlLogList findErrors() const; + + //! Pending calls + int sizePending() const; + + //! Any pending calls + bool hasPending() const; + + //! Any completed calls + bool hasCompleted() const; + + //! Erroneous calls + int sizeErrors() const; + + //! Find by id + CUrlLog findByIdOrDefault(int id) const; + + //! Mark as received + bool markAsReceived(int id, bool success); + + //! Mark as received + bool markAsReceived(const QNetworkReply *nwReply, bool success); + + //! Contains the id? + bool containsId(int id) const; + + //! Maximum response time + qint64 getMaxResponseTime() const; + + //! Minimum response time + qint64 getMinResponseTime() const; + + //! Average response time + qint64 getAverageResponseTime() const; + + //! Summary + QString getSummary() const; + }; + } //namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Network::CUrlLogList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif //guard diff --git a/src/blackmisc/propertyindex.h b/src/blackmisc/propertyindex.h index 9f5e60dc7..3a2e391c2 100644 --- a/src/blackmisc/propertyindex.h +++ b/src/blackmisc/propertyindex.h @@ -120,7 +120,8 @@ namespace BlackMisc GlobalIndexCServer = 6500, GlobalIndexCFsdSetup = 6600, GlobalIndexCUrl = 6700, - GlobalIndexCRemoteFile = 6800, + GlobalIndexCUrlLog = 6800, + GlobalIndexCRemoteFile = 6900, GlobalIndexCAircraftModel = 7000, GlobalIndexCSimulatedAircraft = 7100, GlobalIndexCTextMessage = 7200, diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index 1d97ab711..a5e5ff89d 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -18,6 +18,8 @@ #include "blackmisc/db/distributionlist.h" #include "blackmisc/network/textmessage.h" #include "blackmisc/network/textmessagelist.h" +#include "blackmisc/network/urllog.h" +#include "blackmisc/network/urlloglist.h" #include "blackmisc/simulation/distributorlist.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/distributorlist.h" @@ -248,6 +250,7 @@ namespace BlackMisc template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; + template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; template class BLACKMISC_EXPORT_DEFINE_TEMPLATE ITimestampObjectList; diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 9162037be..3737c2d91 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -127,6 +127,8 @@ namespace BlackMisc { class CTextMessage; class CTextMessageList; + class CUrlLog; + class CUrlLogList; } namespace Db @@ -163,6 +165,7 @@ namespace BlackMisc extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; + extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList; extern template class BLACKMISC_EXPORT_DECLARE_TEMPLATE ITimestampObjectList;