Warning, file /education/marble/tools/vectorosm-tilecreator/TirexBackend.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "TirexBackend.h" 0008 0009 #include <QDir> 0010 #include <QScopedValueRollback> 0011 #include <QSettings> 0012 #include <QSocketNotifier> 0013 0014 #include <cstdlib> 0015 #include <cstring> 0016 #include <unistd.h> 0017 0018 TirexBackend::TirexBackend(QObject *parent) 0019 : QObject(parent) 0020 { 0021 // setup command socket 0022 const auto socketFd = getenv("TIREX_BACKEND_SOCKET_FILENO"); 0023 if (socketFd) { 0024 m_commandSocketFd = std::atoi(socketFd); 0025 m_socketNotifier = new QSocketNotifier(m_commandSocketFd, QSocketNotifier::Read, this); 0026 connect(m_socketNotifier, &QSocketNotifier::activated, this, &TirexBackend::commandReadyRead); 0027 } else { 0028 qFatal("TIREX_BACKEND_SOCKET_FILENO not set!"); 0029 } 0030 0031 // setup heartbeat pipe and timer 0032 const auto pipeFd = getenv("TIREX_BACKEND_PIPE_FILENO"); 0033 const auto aliveTimeout = getenv("TIREX_BACKEND_ALIVE_TIMEOUT"); 0034 if (pipeFd && aliveTimeout) { 0035 m_heartbeatFd = std::atoi(pipeFd); 0036 m_heartbeatTimer.setInterval(std::chrono::seconds(std::atoi(aliveTimeout))); 0037 m_heartbeatTimer.setSingleShot(false); 0038 connect(&m_heartbeatTimer, &QTimer::timeout, this, [this]() { 0039 write(m_heartbeatFd, "alive", 5); 0040 }); 0041 m_heartbeatTimer.start(); 0042 } else { 0043 qWarning() << "not using heartbeat timer"; 0044 } 0045 0046 // read map configuration 0047 m_tileDir = configValue(QStringLiteral("tiledir")).toString(); 0048 if (m_tileDir.isEmpty()) { 0049 m_tileDir = QStringLiteral("output/"); 0050 } 0051 } 0052 0053 TirexBackend::~TirexBackend() = default; 0054 0055 void TirexBackend::commandReadyRead() 0056 { 0057 if (m_recursionLock) { 0058 return; 0059 } 0060 QScopedValueRollback<bool> lock(m_recursionLock, true); 0061 0062 while (true) { 0063 m_renderTime.restart(); 0064 TirexMetatileRequest req; 0065 req.addrSize = sizeof(req.addr); 0066 bzero(&req.addr, req.addrSize); 0067 QByteArray data(0xffff, 0); 0068 auto n = recvfrom(m_commandSocketFd, data.data(), 0xffff, MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&req.addr), &req.addrSize); 0069 if (n <= 0) { 0070 break; 0071 } 0072 data.resize(n); 0073 0074 int nextIdx = 0; 0075 const char *type = nullptr; 0076 int typeLen = 0; 0077 0078 while (nextIdx < data.size() && nextIdx >= 0) { 0079 const auto endIdx = data.indexOf('\n', nextIdx); 0080 if (endIdx < 0) { 0081 break; 0082 } 0083 const auto midIdx = data.indexOf('=', nextIdx); 0084 if (midIdx < 0 || midIdx >= endIdx) { 0085 break; 0086 } 0087 0088 const auto key = data.constData() + nextIdx; 0089 const auto keyLen = midIdx - nextIdx; 0090 const auto value = data.constData() + midIdx + 1; 0091 const auto valueLen = endIdx - midIdx - 1; 0092 0093 if (keyLen == 4 && std::strncmp(key, "type", 4) == 0) { 0094 type = value; 0095 typeLen = valueLen; 0096 } else if (keyLen == 2 && std::strncmp(key, "id", 2) == 0) { 0097 req.id = QByteArray(value, valueLen); 0098 } else if (keyLen == 1 && std::strncmp(key, "x", 1) == 0) { 0099 req.tile.x = std::atoi(value); 0100 } else if (keyLen == 1 && std::strncmp(key, "y", 1) == 0) { 0101 req.tile.y = std::atoi(value); 0102 } else if (keyLen == 1 && std::strncmp(key, "z", 1) == 0) { 0103 req.tile.z = std::atoi(value); 0104 } else if (keyLen == 3 && std::strncmp(key, "map", 3) == 0) { 0105 req.map = QByteArray(value, valueLen); 0106 } 0107 0108 nextIdx = endIdx + 1; 0109 } 0110 0111 if (std::strncmp(type, "metatile_render_request\n", 24) != 0) { 0112 QByteArray errorMsg; 0113 errorMsg += "id=" + req.id + "\n"; 0114 errorMsg += "type=" + QByteArray(type, typeLen) + "\n"; 0115 errorMsg += "result=error\nerrormsg=unsupported requested\n"; 0116 sendto(m_commandSocketFd, errorMsg.constData(), errorMsg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize); 0117 continue; 0118 } 0119 0120 emit tileRequested(req); 0121 } 0122 } 0123 0124 void TirexBackend::tileDone(const TirexMetatileRequest &req) 0125 { 0126 QByteArray msg = "id=" + req.id 0127 + "\ntype=metatile_render_request\nresult=ok\nx=" + QByteArray::number(req.tile.x) 0128 + "\ny=" + QByteArray::number(req.tile.y) 0129 + "\nz=" + QByteArray::number(req.tile.z) 0130 + "\nmetatile=" + metatileFileName(req).toUtf8() 0131 + "\nrender_time=" + QByteArray::number(m_renderTime.elapsed()) + "\n"; 0132 sendto(m_commandSocketFd, msg.constData(), msg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize); 0133 } 0134 0135 void TirexBackend::tileError(const TirexMetatileRequest &req, const QString &errMsg) 0136 { 0137 QByteArray msg = "id=" + req.id + "\ntype=metatile_render_request\nresult=error\nerrmsg=" + errMsg.toUtf8() + "\n"; 0138 sendto(m_commandSocketFd, msg.constData(), msg.size(), 0, reinterpret_cast<const sockaddr*>(&req.addr), req.addrSize); 0139 } 0140 0141 QVariant TirexBackend::configValue(const QString &key) const 0142 { 0143 const auto configFiles = getenv("TIREX_BACKEND_MAP_CONFIGS"); 0144 if (configFiles) { 0145 QSettings settings(QString::fromUtf8(configFiles), QSettings::IniFormat); 0146 return settings.value(key); 0147 } 0148 return {}; 0149 } 0150 0151 QString TirexBackend::metatileFileName(const TirexMetatileRequest &req) 0152 { 0153 auto x = req.tile.x; 0154 auto y = req.tile.y; 0155 uint8_t hash[5]; 0156 for (auto i = 0; i < 5; i++) { 0157 hash[i] = ((x & 0x0f) << 4) | (y & 0x0f); 0158 x >>= 4; 0159 y >>= 4; 0160 } 0161 0162 QString path = m_tileDir + QLatin1Char('/') + 0163 QString::number(req.tile.z) + QLatin1Char('/') + 0164 QString::number(hash[4]) + QLatin1Char('/') + 0165 QString::number(hash[3]) + QLatin1Char('/') + 0166 QString::number(hash[2]) + QLatin1Char('/') + 0167 QString::number(hash[1]) + QLatin1Char('/'); 0168 QDir().mkpath(path); 0169 path += QString::number(hash[0]) + QLatin1String(".meta"); 0170 return path; 0171 } 0172 0173 int TirexBackend::metatileColumns() const 0174 { 0175 return m_metatileCols; 0176 } 0177 0178 int TirexBackend::metatileRows() const 0179 { 0180 return m_metatileRows; 0181 } 0182 0183 struct TirexMetatileHeader { 0184 char magic[4]; 0185 int count; 0186 int x; 0187 int y; 0188 int z; 0189 }; 0190 0191 struct TirexMetatileEntry { 0192 int offset; 0193 int size; 0194 }; 0195 0196 void TirexBackend::writeMetatileHeader(QIODevice *io, const TirexMetatile &tile) const 0197 { 0198 TirexMetatileHeader header; 0199 header.magic[0] = 'M'; 0200 header.magic[1] = 'E'; 0201 header.magic[2] = 'T'; 0202 header.magic[3] = 'A'; 0203 header.count = m_metatileRows * m_metatileCols; 0204 header.x = tile.x; 0205 header.y = tile.y; 0206 header.z = tile.z; 0207 io->write(reinterpret_cast<const char*>(&header), sizeof(header)); 0208 0209 TirexMetatileEntry entry; 0210 entry.offset = 0; 0211 entry.size = 0; 0212 for (int i = 0; i < header.count; ++i) { 0213 io->write(reinterpret_cast<const char*>(&entry), sizeof(entry)); 0214 } 0215 } 0216 0217 void TirexBackend::writeMetatileEntry(QIODevice *io, int entryIdx, int offset, int size) const 0218 { 0219 const auto seekPos = io->pos(); 0220 0221 io->seek(sizeof(TirexMetatileHeader) + entryIdx * sizeof(TirexMetatileEntry)); 0222 TirexMetatileEntry entry; 0223 entry.offset = offset; 0224 entry.size = size; 0225 io->write(reinterpret_cast<const char*>(&entry), sizeof(TirexMetatileEntry)); 0226 0227 io->seek(seekPos); 0228 } 0229 0230 #include "moc_TirexBackend.cpp"