File indexing completed on 2024-04-21 14:52:04

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "job.h"
0007 #include "logger.h"
0008 #include "upload.h"
0009 
0010 #include <QDebug>
0011 #include <QDateTime>
0012 #include <QProcess>
0013 
0014 Job::Job(const Region &region, const JobParameters &parameters, QObject *parent) :
0015     QObject(parent), m_status(Waiting), m_region(region), m_parameters(parameters)
0016 {
0017     // nothing to do
0018 }
0019 
0020 Job::Status Job::status() const
0021 {
0022     return m_status;
0023 }
0024 
0025 QString Job::statusMessage() const
0026 {
0027     return m_statusMessage;
0028 }
0029 
0030 Region Job::region() const
0031 {
0032     return m_region;
0033 }
0034 
0035 void Job::setTransport(const QString &transport)
0036 {
0037     m_transport = transport;
0038 }
0039 
0040 QString Job::transport() const
0041 {
0042     return m_transport;
0043 }
0044 
0045 void Job::setProfile(const QString &profile)
0046 {
0047     m_profile = profile;
0048 }
0049 
0050 void Job::setMonavSettings(const QString &filename)
0051 {
0052     m_monavSettings = filename;
0053 }
0054 
0055 bool Job::operator ==(const Job &other) const
0056 {
0057     return m_transport == other.m_transport && m_region == other.m_region;
0058 }
0059 
0060 void Job::run()
0061 {
0062     if (download() && monav() && search() && package() && upload()) {
0063         // Nothing to do.
0064     }
0065 
0066     cleanup();
0067     emit finished(this);
0068 }
0069 
0070 void Job::changeStatus(Job::Status status, const QString &message)
0071 {
0072     QString statusType;
0073     switch (status) {
0074     case Waiting: statusType = "waiting"; break;
0075     case Downloading: statusType = "downloading"; break;
0076     case Routing: statusType = "routing"; break;
0077     case Search: statusType = "search"; break;
0078     case Packaging: statusType = "packaging"; break;
0079     case Uploading: statusType = "uploading"; break;
0080     case Finished: statusType = "finished"; break;
0081     case Error: statusType = "error"; break;
0082     }
0083 
0084     Logger::instance().setStatus(m_region.id() + QLatin1Char('_') + m_transport,
0085                                  m_region.name() + QLatin1String(" (") + m_transport + QLatin1Char(')'), statusType, message);
0086     m_statusMessage = message;
0087     m_status = status;
0088 }
0089 
0090 bool Job::download()
0091 {
0092     changeStatus(Downloading, "Downloading data.");
0093     qDebug() << "Saving file to " << osmFile().absoluteFilePath();
0094     if (osmFile().exists()) {
0095         QDateTime now = QDateTime::currentDateTime();
0096         if (osmFile().lastModified().daysTo(now) > 7) {
0097             qDebug() << "Old file is outdated, re-downloading " << osmFile().absoluteFilePath();
0098             QFile::remove(osmFile().absoluteFilePath());
0099         } else {
0100             qDebug() << "Old file is still ok, reusing" << osmFile().absoluteFilePath();
0101             return true;
0102         }
0103     }
0104 
0105     QProcess wget;
0106     QStringList arguments;
0107     QString url = m_region.pbfFile();
0108     arguments << "-O" << osmFile().absoluteFilePath() << url;
0109     qDebug() << "Downloading " << url;
0110     wget.start("wget", arguments);
0111     wget.waitForFinished(1000 * 60 * 60 * 12); // wait up to 12 hours for download to complete
0112     if (wget.exitStatus() == QProcess::NormalExit && wget.exitCode() == 0) {
0113         return true;
0114     } else {
0115         qDebug() << "Failed to download " << url;
0116         QFile::remove(osmFile().absoluteFilePath());
0117         changeStatus(Error, QLatin1String("Error downloading .osm.pbf file: ") + wget.readAllStandardError());
0118         return false;
0119     }
0120 }
0121 
0122 //bool Job::marble()
0123 //{
0124 //    changeStatus(Routing, "Extracting bounding box.");
0125 //    QStringList arguments;
0126 //    arguments << "--name" << m_region.name();
0127 //    arguments << "--version" << "0.2";
0128 //    arguments << "--date" << QDateTime::currentDateTime().toString("yyyy/dd/MM");
0129 //    arguments << "--transport" << m_transport;
0130 //    arguments << "--payload" << targetFile().fileName();
0131 //    arguments << m_parameters.base().absoluteFilePath("poly/" + m_region.polyFile());
0132 //    arguments << monavDir().absoluteFilePath() + QLatin1String("/marble.kml");
0133 //    QProcess poly2kml;
0134 //    poly2kml.start("poly2kml", arguments);
0135 //    poly2kml.waitForFinished(1000 * 60 * 30); // wait up to half an hour for poly2kml to convert the data
0136 //    if (poly2kml.exitStatus() == QProcess::NormalExit && poly2kml.exitCode() == 0) {
0137 //        qDebug() << "Processed kml file for marble";
0138 //        return true;
0139 //    } else {
0140 //        qDebug() << "poly2kml exiting with status " << poly2kml.exitCode();
0141 //        changeStatus(Error, "Error creating marble.kml: " + poly2kml.readAllStandardError());
0142 //        return false;
0143 //    }
0144 //}
0145 
0146 bool Job::monav()
0147 {
0148     QString const status = QString("Generating offline routing map from %1 (%2).").arg(osmFile().fileName()).arg(Region::fileSize(osmFile()));
0149     changeStatus(Routing, status);
0150     QStringList arguments;
0151     arguments << QLatin1String("-s=") + m_monavSettings;
0152     arguments << QLatin1String("-i=") + osmFile().absoluteFilePath();
0153     arguments << QLatin1String("-o=") + monavDir().absoluteFilePath();
0154     arguments << "-pi=OpenStreetMap Importer" << "-pro=Contraction Hierarchies";
0155     arguments << "-pg=GPS Grid" << "-di";
0156     arguments << QLatin1String("-dro=") + m_transport;
0157     arguments << QLatin1String("--profile=") + m_profile;
0158     arguments << "-dd" /*<< "-dc"*/;
0159     QProcess monav;
0160     monav.start("monav-preprocessor", arguments);
0161     monav.waitForFinished(1000 * 60 * 60 * 6); // wait up to 6 hours for monav to convert the data
0162     if (monav.exitStatus() == QProcess::NormalExit && monav.exitCode() == 0) {
0163         qDebug() << "Processed osm file for monav";
0164     } else {
0165         qDebug() << "monav exiting with status " << monav.exitCode();
0166         changeStatus(Error, QLatin1String("Routing map conversion failed: ") + monav.readAllStandardError());
0167         return false;
0168     }
0169 
0170     QFile pluginsFile(monavDir().absoluteFilePath() + QLatin1String("/plugins.ini"));
0171     pluginsFile.open(QFile::WriteOnly | QFile::Truncate);
0172     QTextStream pluginsStream(&pluginsFile);
0173     pluginsStream << "[General]\nrouter=Contraction Hierarchies\nrenderer=Mapnik Renderer\ngpsLookup=GPS Grid\naddressLookup=Unicode Tournament Trie\n";
0174     pluginsFile.close();
0175 
0176     QFileInfo subdir = QFileInfo(monavDir().absoluteFilePath() + QLatin1String("/routing_") + m_transport.toLower());
0177     if (subdir.exists() && subdir.isDir()) {
0178         QFileInfoList files = QDir(subdir.absoluteFilePath()).entryInfoList(QDir::Files);
0179         for(const QFileInfo &file: files) {
0180             if (!QFile::rename(file.absoluteFilePath(), monavDir().absoluteFilePath() + QLatin1Char('/') + file.fileName())) {
0181                 changeStatus(Error, "Unable to move monav files to target directory.");
0182                 return false;
0183             }
0184         }
0185         QDir("/").rmdir(subdir.absoluteFilePath());
0186     } else {
0187         changeStatus(Error, "Unable to find files created by monav");
0188         return false;
0189     }
0190 
0191     return true;
0192 }
0193 
0194 bool Job::search()
0195 {
0196     QString const status = QString("Generating offline search database from %1 (%2).").arg(osmFile().fileName()).arg(Region::fileSize(osmFile()));
0197     changeStatus(Search, status);
0198     QStringList arguments;
0199     arguments << "--name" << m_region.name();
0200     arguments << "--version" << "0.3";
0201     arguments << "--date" << QDateTime::currentDateTime().toString("MM/dd/yy");
0202     arguments << "--transport" << m_transport;
0203     arguments << "--payload" << targetFile().fileName();
0204     arguments << osmFile().absoluteFilePath();
0205     arguments << searchFile().absoluteFilePath();
0206     QFileInfo kmlFile(monavDir().absoluteFilePath() + QLatin1String("/marble.kml"));
0207     arguments << kmlFile.absoluteFilePath();
0208     QProcess osmAddresses;
0209     osmAddresses.start("osm-addresses", arguments);
0210     osmAddresses.waitForFinished(1000 * 60 * 60 * 18); // wait up to 18 hours for osm-addresses to convert the data
0211     if (osmAddresses.exitStatus() == QProcess::NormalExit && osmAddresses.exitCode() == 0) {
0212         searchFile().refresh();
0213         if (!searchFile().exists()) {
0214             qDebug() << "osm-addresses did not create the .sqlite file";
0215             changeStatus(Error, "Unknown error when creating the search database");
0216             return false;
0217         } else if (searchFile().size() < 8000) {
0218             qDebug() << "The .sqlite database has a suspiciously small size.";
0219             changeStatus(Error, "Search database is too small. Too little memory?");
0220             return false;
0221         }
0222 
0223         kmlFile.refresh();
0224         if (!kmlFile.exists()) {
0225             qDebug() << "File marble.kml has not been generated.";
0226             changeStatus(Error, "Failed to generate marble.kml. Too little memory?");
0227             return false;
0228         }
0229 
0230         return true;
0231     } else {
0232         qDebug() << "osm-addresses exiting with status " << osmAddresses.exitCode();
0233         changeStatus(Error, QLatin1String("Error creating search database: ") + osmAddresses.readAllStandardError());
0234         return false;
0235     }
0236 }
0237 
0238 bool Job::package()
0239 {
0240     changeStatus(Packaging, "Creating archive.");
0241     QStringList arguments;
0242     arguments << "czf" << targetFile().absoluteFilePath() << "earth/monav/" << "earth/placemarks";
0243     QProcess tar;
0244     tar.setWorkingDirectory(m_parameters.base().absolutePath() + QLatin1String("/data/") + m_region.id());
0245     tar.start("tar", arguments);
0246     tar.waitForFinished(1000 * 60 * 60); // wait up to 1 hour for tar to package things
0247     if (tar.exitStatus() == QProcess::NormalExit && tar.exitCode() == 0) {
0248         qDebug() << "Packaged tar file";
0249         return true;
0250     } else {
0251         changeStatus(Error, QLatin1String("Packaging failed: ") + tar.readAllStandardError());
0252         return false;
0253     }
0254 }
0255 
0256 bool Job::upload()
0257 {
0258     changeStatus(Uploading, "Uploading file");
0259     if (targetFile().exists()) {
0260         Upload::instance().uploadAndDelete(m_region, targetFile(), m_transport);
0261         return true;
0262     }
0263 
0264     changeStatus(Error, "Target file does not exist.");
0265     return false;
0266 }
0267 
0268 bool Job::cleanup()
0269 {
0270     if (!m_parameters.cacheData()) {
0271         QFile::remove(osmFile().absoluteFilePath());
0272     }
0273 
0274     QFileInfo subdir = QFileInfo(monavDir().absoluteFilePath());
0275     if (subdir.exists() && subdir.isDir()) {
0276         QFileInfoList files = QDir(subdir.absoluteFilePath()).entryInfoList(QDir::Files);
0277         for(const QFileInfo &file: files) {
0278             QFile::remove(file.absoluteFilePath());
0279         }
0280     }
0281 
0282     QFile::remove(searchFile().absoluteFilePath());
0283     return true;
0284 }
0285 
0286 QFileInfo Job::osmFile()
0287 {
0288     m_parameters.base().mkdir("download");
0289     QFileInfo result(m_parameters.base(), QLatin1String("download/") + m_region.id() + QLatin1String(".osm.pbf"));
0290     return result;
0291 }
0292 
0293 QFileInfo Job::monavDir()
0294 {
0295     QString const subdir = QLatin1String("data/") + m_region.id() + QLatin1String("/earth/monav/") + m_transport.toLower() + QLatin1Char('/') + m_region.path();
0296     m_parameters.base().mkpath(subdir);
0297     QFileInfo result(m_parameters.base(), subdir);
0298     return result;
0299 }
0300 
0301 QFileInfo Job::targetFile()
0302 {
0303     m_parameters.base().mkdir("finished");
0304     QFileInfo result(m_parameters.base(), QLatin1String("finished/") + m_region.id() + QLatin1Char('_') + m_transport.toLower() + QLatin1String(".tar.gz"));
0305     return result;
0306 }
0307 
0308 QFileInfo Job::searchFile()
0309 {
0310     QString const subdir = QLatin1String("data/") + m_region.id() + QLatin1String("/earth/placemarks/") + QFileInfo(m_region.path()).path();
0311     m_parameters.base().mkpath(subdir);
0312     QFileInfo result(m_parameters.base(), subdir + QLatin1Char('/') + m_region.id() + QLatin1String(".sqlite"));
0313     return result;
0314 }
0315 
0316 #include "moc_job.cpp"