MythTV master
sourceManager.cpp
Go to the documentation of this file.
1
2// QT headers
3#include <QApplication>
4#include <QDir>
5#include <QFileInfo>
6#include <QString>
7#include <QStringList>
8#include <algorithm>
9
10// MythTV headers
12#include <libmythbase/mythdb.h>
17
18// MythWeather headers
19#include "sourceManager.h"
20#include "weatherScreen.h"
21#include "weatherSource.h"
22
23#define LOC QString("SourceManager: ")
24#define LOC_ERR QString("SourceManager Error: ")
25
27{
30}
31
33{
35}
36
38{
40 QString query =
41 "SELECT DISTINCT wss.sourceid, source_name, update_timeout, "
42 "retrieve_timeout, path, author, version, email, types "
43 "FROM weathersourcesettings wss "
44 "LEFT JOIN weatherdatalayout wdl "
45 "ON wss.sourceid = wdl.weathersourcesettings_sourceid "
46 "WHERE hostname = :HOST;";
47
48 db.prepare(query);
49 db.bindValue(":HOST", gCoreContext->GetHostName());
50 if (!db.exec())
51 {
52 MythDB::DBError("Finding weather source scripts for host", db);
53 return false;
54 }
55
56 while (db.next())
57 {
58 QFileInfo fi(db.value(4).toString());
59
60 if (!fi.isExecutable())
61 {
62 // scripts will be deleted from db in the more robust (i.e. slower)
63 // findScripts() -- run when entering setup
64 continue;
65 }
66 auto *si = new ScriptInfo;
67 si->id = db.value(0).toInt();
68 si->name = db.value(1).toString();
69 si->updateTimeout = std::chrono::seconds(db.value(2).toUInt());
70 si->scriptTimeout = std::chrono::seconds(db.value(3).toUInt());
71 si->path = fi.absolutePath();
72 si->program = fi.absoluteFilePath();
73 si->author = db.value(5).toString();
74 si->version = db.value(6).toString();
75 si->email = db.value(7).toString();
76 si->types = db.value(8).toString().split(",");
77 m_scripts.append(si);
78 }
79
80 return true;
81}
82
84{
85 QString path = GetShareDir() + "mythweather/scripts/";
86 QDir dir(path);
87 dir.setFilter(QDir::Executable | QDir::Files | QDir::Dirs);
88
89 if (!dir.exists())
90 {
91 LOG(VB_GENERAL, LOG_ERR, "MythWeather: Scripts directory not found");
92 return false;
93 }
94 QString busymessage = tr("Searching for scripts");
95
96 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("weather stack");
97 if (popupStack == nullptr)
98 popupStack = GetMythMainWindow()->GetStack("popup stack");
99
100 auto *busyPopup = new MythUIBusyDialog(busymessage, popupStack,
101 "mythweatherbusydialog");
102
103 if (busyPopup->Create())
104 {
105 popupStack->AddScreen(busyPopup, false);
106 }
107 else
108 {
109 delete busyPopup;
110 busyPopup = nullptr;
111 }
112
113 QCoreApplication::processEvents();
114
115 recurseDirs(dir);
116
117 // run through and see if any scripts have been deleted
119
120 db.prepare("SELECT sourceid, path FROM weathersourcesettings "
121 "WHERE hostname = :HOST;");
122 db.bindValue(":HOST", gCoreContext->GetHostName());
123 if (!db.exec())
124 MythDB::DBError("SourceManager::findScripts - select", db);
125 QStringList toRemove;
126 while (db.next())
127 {
128 QFileInfo fi(db.value(1).toString());
129 if (!fi.isExecutable())
130 {
131 toRemove << db.value(0).toString();
132 LOG(VB_GENERAL, LOG_ERR, QString("'%1' no longer exists")
133 .arg(fi.absoluteFilePath()));
134 }
135 }
136
137 db.prepare("DELETE FROM weathersourcesettings WHERE sourceid = :ID;");
138 for (int i = 0; i < toRemove.count(); ++i)
139 {
140 db.bindValue(":ID", toRemove[i]);
141 if (!db.exec())
142 {
143 // MythDB::DBError("Deleting weather source settings", db);
144 }
145 }
146
147 if (busyPopup)
148 {
149 busyPopup->Close();
150 busyPopup = nullptr;
151 }
152
153 return m_scripts.count() > 0;
154}
155
157{
158 while (!m_scripts.isEmpty())
159 delete m_scripts.takeFirst();
160 m_scripts.clear();
161
162 while (!m_sources.isEmpty())
163 delete m_sources.takeFirst();
164 m_sources.clear();
165}
166
168{
170
171 db.prepare(
172 "SELECT DISTINCT location, weathersourcesettings_sourceid, "
173 " weatherscreens.units, weatherscreens.screen_id "
174 "FROM weatherdatalayout,weatherscreens "
175 "WHERE weatherscreens.screen_id = weatherscreens_screen_id AND "
176 " weatherscreens.hostname = :HOST");
177 db.bindValue(":HOST", gCoreContext->GetHostName());
178 if (!db.exec())
179 {
180 MythDB::DBError("Finding weather sources for this host", db);
181 return;
182 }
183
184 m_sourcemap.clear();
185
186 while (db.next())
187 {
188 QString loc = db.value(0).toString();
189 uint sourceid = db.value(1).toUInt();
190 units_t units = db.value(2).toUInt();
191 uint screen = db.value(3).toUInt();
192 const WeatherSource *src = needSourceFor(sourceid, loc, units);
193 if (src)
194 m_sourcemap.insert((long)screen, src);
195 }
196}
197
199{
200 ScriptInfo *src = nullptr;
201 for (auto *script : std::as_const(m_scripts))
202 {
203 src = script;
204 if (src->name == name)
205 {
206 return src;
207 }
208 }
209
210 if (!src)
211 {
212 LOG(VB_GENERAL, LOG_ERR, "No Source found for " + name);
213 }
214
215 return nullptr;
216}
217
218QStringList SourceManager::getLocationList(ScriptInfo *si, const QString &str)
219{
220 if (!m_scripts.contains(si))
221 return {};
222 auto *ws = new WeatherSource(si);
223
224 QStringList locationList(ws->getLocationList(str));
225
226 delete ws;
227
228 return locationList;
229}
230
231WeatherSource *SourceManager::needSourceFor(int id, const QString &loc,
232 units_t units)
233{
234 // matching source exists?
235 for (auto *src : std::as_const(m_sources))
236 {
237 if (src->getId() == id && src->getLocale() == loc &&
238 src->getUnits() == units)
239 {
240 return src;
241 }
242 }
243
244 // no matching source, make one
245 auto it = std::ranges::find(std::as_const(m_scripts), id, &ScriptInfo::id);
246 if (it != m_scripts.cend())
247 {
248 auto *ws = new WeatherSource(*it);
249 ws->setLocale(loc);
250 ws->setUnits(units);
251 m_sources.append(ws);
252 return ws;
253 }
254
255 LOG(VB_GENERAL, LOG_ERR, LOC +
256 QString("NeedSourceFor: Unable to find source for %1, %2, %3")
257 .arg(id).arg(loc).arg(units));
258 return nullptr;
259}
260
262{
263 for (auto *src : std::as_const(m_sources))
264 src->startUpdateTimer();
265}
266
268{
269 for (auto *src : std::as_const(m_sources))
270 src->stopUpdateTimer();
271}
272
273void SourceManager::doUpdate(bool forceUpdate)
274{
275 for (auto *src : std::as_const(m_sources))
276 {
277 if (src->inUse())
278 src->startUpdate(forceUpdate);
279 }
280}
281
283 QList<ScriptInfo *> &sources)
284{
285 for (auto *si : std::as_const(m_scripts))
286 {
287 QStringList stypes = si->types;
288 bool handled = true;
289 for (int i = 0; i < types.count() && handled; ++i)
290 {
291 handled = stypes.contains(types[i]);
292 }
293 if (handled)
294 sources.append(si);
295 }
296
297 return sources.count() != 0;
298}
299
301{
302 if (!screen)
303 {
304 LOG(VB_GENERAL, LOG_ERR, LOC +
305 QString("Cannot connect nonexistent screen 0x%1")
306 .arg((uint64_t)screen,0,16));
307
308 return false;
309 }
310
311 SourceMap::iterator it = m_sourcemap.find(id);
312 if (it == m_sourcemap.end())
313 {
314 LOG(VB_GENERAL, LOG_ERR, LOC +
315 QString("Cannot connect nonexistent source '%1'").arg(id));
316
317 return false;
318 }
319
320 (const_cast<WeatherSource*>(*it))->connectScreen(screen);
321
322 return true;
323}
324
326{
327 if (!screen)
328 {
329 LOG(VB_GENERAL, LOG_ERR, LOC +
330 QString("Cannot disconnect nonexistent screen 0x%1")
331 .arg((uint64_t)screen,0,16));
332
333 return false;
334 }
335
336 SourceMap::iterator it = m_sourcemap.find(screen->getId());
337 if (it == m_sourcemap.end())
338 {
339 LOG(VB_GENERAL, LOG_ERR, LOC +
340 QString("Cannot disconnect nonexistent source %1")
341 .arg(screen->getId()));
342
343 return false;
344 }
345
346 (const_cast<WeatherSource*>(*it))->disconnectScreen(screen);
347
348 return true;
349}
350
351// Recurses dir for script files
353{
354 if (!dir.exists())
355 return;
356
357 dir.setFilter(QDir::Executable | QDir::Files | QDir::Dirs |
358 QDir::NoDotAndDotDot);
359 QFileInfoList files = dir.entryInfoList();
360
361 for (const auto & file : std::as_const(files))
362 {
363 QCoreApplication::processEvents();
364 if (file.isDir())
365 {
366 QDir recurseTo(file.filePath());
367 recurseDirs(recurseTo);
368 }
369
370 if (file.isExecutable() && !(file.isDir()))
371 {
373 if (info)
374 {
375 m_scripts.append(info);
376 LOG(VB_FILE, LOG_INFO, QString("Found Script '%1'")
377 .arg(file.absoluteFilePath()));
378 }
379 }
380 }
381}
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
QString GetHostName(void)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
QString name
Definition: weatherSource.h:24
void doUpdate(bool forceUpdate=false)
bool connectScreen(uint id, WeatherScreen *screen)
bool findScriptsDB()
~SourceManager() override
bool findPossibleSources(QStringList types, QList< ScriptInfo * > &sources)
WeatherSource * needSourceFor(int id, const QString &loc, units_t units)
QStringList getLocationList(ScriptInfo *si, const QString &str)
ScriptInfo * getSourceByName(const QString &name)
bool disconnectScreen(WeatherScreen *screen)
SourceMap m_sourcemap
Definition: sourceManager.h:45
QList< WeatherSource * > m_sources
Definition: sourceManager.h:44
void recurseDirs(QDir dir)
QList< ScriptInfo * > m_scripts
Definition: sourceManager.h:43
Weather screen.
Definition: weatherScreen.h:27
int getId() const
Definition: weatherScreen.h:49
static ScriptInfo * ProbeScript(const QFileInfo &fi)
unsigned int uint
Definition: compat.h:60
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static const struct wl_interface * types[]
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetShareDir(void)
Definition: mythdirs.cpp:283
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
dictionary info
Definition: azlyrics.py:7
#define LOC
unsigned char units_t
Definition: weatherUtils.h:25