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) : CThreadedReader(owner, "CVatsimMetarReader")
34  {
35  this->reloadSettings();
36  }
37 
39  {
40  QPointer<CVatsimMetarReader> myself(this);
41  QTimer::singleShot(0, this, [=] {
42  if (!myself) { return; }
43  myself->read();
44  });
45  }
46 
48  {
49  QReadLocker l(&m_lock);
50  return m_metars;
51  }
52 
54  {
55  QReadLocker l(&m_lock);
56  return m_metars.getMetarForAirport(icao);
57  }
58 
59  void CVatsimMetarReader::doWorkImpl() { this->read(); }
60 
61  void CVatsimMetarReader::read()
62  {
63  this->threadAssertCheck();
64  if (!this->doWorkCheck()) { return; }
65 
66  const CUrl url(sApp->getVatsimMetarUrl());
67  if (url.isEmpty()) { return; }
68  Q_ASSERT_X(sApp, Q_FUNC_INFO, "No Application");
69  this->getFromNetworkAndLog(url.withAppendedQuery("id=all"), { this, &CVatsimMetarReader::decodeMetars });
70  }
71 
72  void CVatsimMetarReader::decodeMetars(QNetworkReply *nwReplyPtr)
73  {
74  // wrap pointer, make sure any exit cleans up reply
75  // required to use delete later as object is created in a different thread
76  QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
77 
78  // Worker thread, make sure to write thread safe!
79  this->threadAssertCheck();
80 
81  if (!this->doWorkCheck())
82  {
83  CLogMessage(this).info(u"Terminated METAR decoding process"); // for users
84  return; // stop, terminate straight away, ending thread
85  }
86 
87  this->logNetworkReplyReceived(nwReplyPtr);
88  const QUrl url = nwReply->url();
89  const QString metarUrl = url.toString();
90 
91  if (nwReply->error() == QNetworkReply::NoError)
92  {
93  QString metarData = nwReply->readAll();
94  nwReply->close(); // close asap
95 
96  if (metarData.isEmpty()) // Quick check by hash
97  {
98  CLogMessage(this).warning(u"No METAR data from '%1', skipped") << metarUrl;
99  return;
100  }
101  if (!this->didContentChange(metarData)) // Quick check by hash
102  {
103  CLogMessage(this).info(u"METAR file from '%1' has same content, skipped") << metarUrl;
104  return;
105  }
106 
107  CMetarList metars;
108  int invalidLines = 0;
109  QTextStream lineReader(&metarData);
110  while (!lineReader.atEnd())
111  {
112  if (!this->doWorkCheck()) { return; }
113  const QString line = lineReader.readLine();
114  // some check for obvious errors
115  if (line.contains("<html")) { continue; }
116  const CMetar metar = m_metarDecoder.decode(line);
117  if (metar != CMetar()) { metars.push_back(metar); }
118  else { invalidLines++; }
119  }
120 
121  CLogMessage(this).info(u"METARs: %1 Metars (invalid %2) from '%3'")
122  << metars.size() << invalidLines << metarUrl;
123  {
124  QWriteLocker l(&m_lock);
125  m_metars = metars;
126  }
127 
128  emit metarsRead(metars);
129  emit dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFinished, metars.size(), url);
130  }
131  else
132  {
133  // network error
134  CLogMessage(this).warning(u"Reading METARs failed '%1' for '%2'") << nwReply->errorString() << metarUrl;
135  nwReply->abort();
136  emit this->dataRead(CEntityFlags::MetarEntity, CEntityFlags::ReadFailed, 0, url);
137  }
138  } // method
139 
140  void CVatsimMetarReader::reloadSettings()
141  {
142  const CReaderSettings s = m_settings.get();
143  this->setInitialAndPeriodicTime(s.getInitialTime().toMs(), s.getPeriodicTime().toMs());
144  }
145 } // 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.
Support for threaded based reading and parsing tasks such as data files via http, or file system and ...
void threadAssertCheck() const
Make sure everthing 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.
void setInitialAndPeriodicTime(int initialTime, int periodicTime)
Set initial and periodic times.
bool doWorkCheck() const
Still enabled etc.?
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.
void readInBackgroundThread()
Read / re-read 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.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Definition: threadutils.h:30