QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ComponentInformationCache.cc
Go to the documentation of this file.
2#include "QGCFileHelper.h"
4
5#include <QtCore/QFile>
6#include <QtCore/QDirIterator>
7#include <QtCore/QStandardPaths>
8
9QGC_LOGGING_CATEGORY(ComponentInformationCacheLog, "ComponentInformation.ComponentInformationCache")
10
11ComponentInformationCache::ComponentInformationCache(const QDir& path, int maxNumFiles)
12 : _path(path), _maxNumFiles(maxNumFiles)
13{
14 initializeDirectory();
15}
16
18{
19 QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/QGCCompInfoCache");
20 static ComponentInformationCache instance(cacheDir, 50);
21 return instance;
22}
23
24QString ComponentInformationCache::metaFileName(const QString& fileTag)
25{
26 return _path.filePath(fileTag+_metaExtension);
27}
28
29QString ComponentInformationCache::dataFileName(const QString& fileTag)
30{
31 return _path.filePath(fileTag+_cacheExtension);
32}
33
34QString ComponentInformationCache::access(const QString &fileTag)
35{
36 QFile meta(metaFileName(fileTag));
37 QFile data(dataFileName(fileTag));
38 if (!meta.exists() || !data.exists()) {
39 qCDebug(ComponentInformationCacheLog) << "Cache miss for" << fileTag;
40 return "";
41 }
42
43 qCDebug(ComponentInformationCacheLog) << "Cache hit for" << fileTag;
44
45 // mark access
46 Meta m{};
47 AccessCounterType previousCounter = -1;
48 if (meta.open(QIODevice::ReadWrite)) {
49 if (meta.read((char*)&m, sizeof(m)) == sizeof(m)) {
50 previousCounter = m.accessCounter;
51 m.accessCounter = _nextAccessCounter;
52 meta.seek(0);
53 if (meta.write((const char*)&m, sizeof(m)) != sizeof(m)) {
54 qCWarning(ComponentInformationCacheLog) << "Meta write failed" << meta.fileName() << meta.errorString();
55 }
56 } else {
57 qCWarning(ComponentInformationCacheLog) << "Meta read failed" << meta.fileName() << meta.errorString();
58 }
59 meta.close();
60 } else {
61 qCWarning(ComponentInformationCacheLog) << "Failed to open" << meta.fileName() << meta.errorString();
62 }
63
64 _cachedFiles.remove(previousCounter);
65 _cachedFiles[_nextAccessCounter] = fileTag;
66 ++_nextAccessCounter;
67
68 return data.fileName();
69}
70
71QString ComponentInformationCache::insert(const QString &fileTag, const QString &fileName)
72{
73 QFile meta(metaFileName(fileTag));
74 QFile data(dataFileName(fileTag));
75 QFile fileToCache(fileName);
76 if (meta.exists() || data.exists()) {
77 qCDebug(ComponentInformationCacheLog) << "Not inserting, entry already exists" << fileTag;
78 fileToCache.remove();
79 return data.fileName();
80 }
81
82 // move the file to the cache location (fall back to copy+remove for cross-device)
83 if (!fileToCache.rename(data.fileName())) {
84 qCDebug(ComponentInformationCacheLog) << "File rename failed from:to" << fileName << data.fileName() << fileToCache.errorString();
85 if (!fileToCache.copy(data.fileName())) {
86 qCWarning(ComponentInformationCacheLog) << "File copy failed from:to" << fileName << data.fileName() << fileToCache.errorString();
87 return "";
88 }
89 if (!fileToCache.remove()) {
90 qCWarning(ComponentInformationCacheLog) << "File remove failed after copy for" << fileToCache.fileName() << fileToCache.errorString();
91 }
92 }
93
94 // write meta data
95 Meta m{};
96 m.accessCounter = _nextAccessCounter;
97 if (meta.open(QIODevice::WriteOnly)) {
98 if (meta.write((const char*)&m, sizeof(m)) != sizeof(m)) {
99 qCWarning(ComponentInformationCacheLog) << "Meta write failed" << meta.fileName() << meta.errorString();
100 }
101 meta.close();
102 } else {
103 qCWarning(ComponentInformationCacheLog) << "Failed to open" << meta.fileName() << meta.errorString();
104 }
105
106 // update internal data
107 _cachedFiles[_nextAccessCounter++] = fileTag;
108 ++_numFiles;
109
110 removeOldEntries();
111 return data.fileName();
112}
113
114void ComponentInformationCache::initializeDirectory()
115{
116 if (!QGCFileHelper::ensureDirectoryExists(_path.path())) {
117 qCWarning(ComponentInformationCacheLog) << "Failed to create dir" << _path.path();
118 }
119
120 QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot;
121 QDirIterator it(_path.path(), filters, QDirIterator::NoIteratorFlags);
122 while (it.hasNext()) {
123 QString path = it.next();
124
125 if (path.endsWith(_metaExtension)) {
126 QFile meta(path);
127 QFile data(path.mid(0, path.length()-strlen(_metaExtension))+_cacheExtension);
128 bool validationFailed = false;
129 if (!data.exists()) {
130 validationFailed = true;
131 }
132
133 // read meta + validate
134 Meta m{};
135 const uint32_t expectedMagic = m.magic;
136 const uint32_t expectedVersion = m.version;
137 if (meta.open(QIODevice::ReadOnly)) {
138 if (meta.read((char*)&m, sizeof(m)) == sizeof(m)) {
139 if (m.magic != expectedMagic || m.version != expectedVersion) {
140 validationFailed = true;
141 }
142 } else {
143 validationFailed = true;
144 }
145 meta.close();
146 } else {
147 validationFailed = true;
148 }
149
150 if (validationFailed) {
151 qCWarning(ComponentInformationCacheLog) << "Validation failed, removing cache files" << path;
152 meta.remove();
153 data.remove();
154 } else {
155 // extract the tag
156 QString tag = it.fileName();
157 tag = tag.mid(0, tag.length()-strlen(_metaExtension));
158 _cachedFiles[m.accessCounter] = tag;
159
160 qCDebug(ComponentInformationCacheLog) << "Found cached file:counter" << meta.fileName() << m.accessCounter;
161
162 if (m.accessCounter >= _nextAccessCounter) {
163 _nextAccessCounter = m.accessCounter + 1;
164 }
165 }
166
167 } else if (!path.endsWith(_cacheExtension)) {
168 QFile::remove(path);
169 }
170 }
171 _numFiles = _cachedFiles.size();
172 removeOldEntries();
173}
174
175void ComponentInformationCache::removeOldEntries()
176{
177 while (_numFiles > _maxNumFiles) {
178 auto iter = _cachedFiles.begin();
179 QFile meta(metaFileName(iter.value()));
180 QFile data(dataFileName(iter.value()));
181 qCDebug(ComponentInformationCacheLog) << "Removing cache entry num:counter:file" << _numFiles << iter.key() << iter.value();
182 meta.remove();
183 data.remove();
184
185 _cachedFiles.erase(iter);
186 --_numFiles;
187 }
188}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static ComponentInformationCache & defaultInstance()
QString insert(const QString &fileTag, const QString &fileName)
QString access(const QString &fileTag)
bool ensureDirectoryExists(const QString &path)