swift
vatsimmetarreader.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QByteArray>
7 #include <QMetaObject>
8 #include <QNetworkReply>
9 #include <QReadLocker>
10 #include <QScopedPointer>
11 #include <QScopedPointerDeleteLater>
12 #include <QString>
13 #include <QTextStream>
14 #include <QTimer>
15 #include <QUrl>
16 #include <QWriteLocker>
17 #include <QtGlobal>
18 
19 #include "core/application.h"
20 #include "misc/logmessage.h"
23 #include "misc/network/url.h"
24 
25 using namespace swift::misc;
26 using namespace swift::misc::aviation;
27 using namespace swift::misc::network;
28 using namespace swift::misc::weather;
29 using namespace swift::core::data;
30 
31 namespace swift::core::vatsim
32 {
33  CVatsimMetarReader::CVatsimMetarReader(QObject *owner) : CThreadedReaderPeriodic(owner, "CVatsimMetarReader")
34  {
35  this->reloadSettings();
36  }
37 
39  {
40  QReadLocker l(&m_lock);
41  return m_metars;
42  }
43 
45  {
46  QReadLocker l(&m_lock);
47  return m_metars.getMetarForAirport(icao);
48  }
49 
50  void CVatsimMetarReader::doWorkImpl() { this->read(); }
51 
52  void CVatsimMetarReader::read()
53  {
54  this->threadAssertCheck();
55  if (!this->doWorkCheck()) { return; }
56 
57  const CUrl url(sApp->getVatsimMetarUrl());
58  if (url.isEmpty()) { return; }
59  Q_ASSERT_X(sApp, Q_FUNC_INFO, "No Application");
60  this->getFromNetworkAndLog(url.withAppendedQuery("id=all"), { this, &CVatsimMetarReader::decodeMetars });
61  }
62 
63  void CVatsimMetarReader::decodeMetars(QNetworkReply *nwReplyPtr)
64  {
65  // wrap pointer, make sure any exit cleans up reply
66  // required to use delete later as object is created in a different thread
67  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
68 
69  // Worker thread, make sure to write thread safe!
70  this->threadAssertCheck();
71 
72  if (!this->doWorkCheck())
73  {
74  CLogMessage(this).info(u"Terminated METAR decoding process"); // for users
75  return; // stop, terminate straight away, ending thread
76  }
77 
78  this->logNetworkReplyReceived(nwReplyPtr);
79  const QUrl url = nwReply->url();
80  const QString metarUrl = url.toString();
81 
82  if (nwReply->error() == QNetworkReply::NoError)
83  {
84  QString metarData = nwReply->readAll();
85  nwReply->close(); // close asap
86 
87  if (metarData.isEmpty()) // Quick check by hash
88  {
89  CLogMessage(this).warning(u"No METAR data from '%1', skipped") << metarUrl;
90  return;
91  }
92  if (!this->didContentChange(metarData)) // Quick check by hash
93  {
94  CLogMessage(this).info(u"METAR file from '%1' has same content, skipped") << metarUrl;
95  return;
96  }
97 
98  CMetarList metars;
99  int invalidLines = 0;
100  QTextStream lineReader(&metarData);
101  while (!lineReader.atEnd())
102  {
103  if (!this->doWorkCheck()) { return; }
104  const QString line = lineReader.readLine();
105  // some check for obvious errors
106  if (line.contains("<html")) { continue; }
107  const CMetar metar = m_metarDecoder.decode(line);
108  if (metar != CMetar()) { metars.push_back(metar); }
109  else { invalidLines++; }
110  }
111 
112  CLogMessage(this).info(u"METARs: %1 Metars (invalid %2) from '%3'")
113  << metars.size() << invalidLines << metarUrl;
114  {
115  QWriteLocker l(&m_lock);
116  m_metars = metars;
117  }
118 
119  emit metarsRead(metars);
120  emit dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFinished, metars.size(), url);
121  }
122  else
123  {
124  // network error
125  CLogMessage(this).warning(u"Reading METARs failed '%1' for '%2'") << nwReply->errorString() << metarUrl;
126  nwReply->abort();
127  emit this->dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFailed, 0, url);
128  }
129  } // method
130 
131  void CVatsimMetarReader::reloadSettings()
132  {
133  const CReaderSettings s = m_settings.get();
134  this->setInitialAndPeriodicTime(std::chrono::milliseconds(s.getInitialTime().toMs()),
135  std::chrono::milliseconds(s.getPeriodicTime().toMs()));
136  }
137 } // namespace swift::core::vatsim
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
swift::misc::network::CUrl getVatsimMetarUrl() const
Consolidated version of METAR URLs, either from CGlobalSetup or CVatsimSetup.
void threadAssertCheck() const
Make sure everything runs correctly in own thread.
void logNetworkReplyReceived(QNetworkReply *reply)
Network reply received, mark in m_urlReadLog.
QReadWriteLock m_lock
lock which can be used from the derived classes
bool didContentChange(const QString &content, int startPosition=-1)
Stores new content hash and returns if content changed (based on hash value.
QNetworkReply * getFromNetworkAndLog(const swift::misc::network::CUrl &url, const swift::misc::CSlot< void(QNetworkReply *)> &callback)
Get request from network, and log with m_urlReadLog.
bool doWorkCheck() const
Still enabled etc.?
Periodically executes doWorkImpl() in a separate thread.
void setInitialAndPeriodicTime(std::chrono::milliseconds initialTime, std::chrono::milliseconds periodicTime)
Set initial and periodic times Changes only apply after the next time the timer restarts.
void metarsRead(const swift::misc::weather::CMetarList &metars)
METARs have been read and converted to swift::misc::weather::CMetarList.
void dataRead(swift::misc::network::CEntityFlags::Entity entity, swift::misc::network::CEntityFlags::ReadState state, int number, const QUrl &url)
Data have been read.
virtual swift::misc::weather::CMetarList getMetars() const
Get METARs.
virtual swift::misc::weather::CMetar getMetarForAirport(const swift::misc::aviation::CAirportIcaoCode &icao) const
Get METAR for airport.
virtual void doWorkImpl()
This method does the actual work in the derived class.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
Value object encapsulating information of airport ICAO data.
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
Definition: url.h:27
CMetar decode(const QString &metarString) const
Decode metar.
Value object encapsulating information about METAR FIXME: runway visibilities FIXME: runway wind shea...
Definition: metar.h:38
Sequence of Metars.
Definition: metarlist.h:24
CMetar getMetarForAirport(const aviation::CAirportIcaoCode &icao) const
METAR for ICAO code.
Definition: metarlist.cpp:14
Core data traits (aka cached values) and classes.
Free functions in swift::misc.