DiskIOController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2012-2025, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package RobotStateComponent::
19 * @author Samet Soenmez (uewtt at student dot kit dot edu)
20 * @date 2025t
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#include "DiskIOController.h"
26
27#include <QThread>
28#include <QTimer>
29#include <QBoxLayout>
30#include <QLabel>
31
32#include <filesystem>
33
34#include <SimoxUtility/algorithm/string/string_tools.h>
35
37
42
44{
46 MemoryViewerUIContext& uiContext,
47 std::shared_ptr<armem::gui::model::MemoryViewerModel> model)
48 {
49 _model = std::move(model);
50 _statusLabel = uiContext.statusLabel;
51
52 if (uiContext.diskIOViewLayout)
53 {
54 _diskIOView = new armem::gui::view::DiskIOView();
55 uiContext.diskIOViewLayout->addWidget(_diskIOView);
56 }
57
58 connect(_diskIOView,
60 this,
62
63 connect(_diskIOView,
65 this,
67 }
68
69 void
71 {
72 QThread* thread = new QThread;
73 QObject* worker = new QObject;
74
75 worker->moveToThread(thread);
76
77 connect(thread,
78 &QThread::started,
79 worker,
80 [this, thread, worker, directory]()
81 {
82
83 ARMARX_IMPORTANT << "Loading Snapshots from Disk";
84
85 loadFromDisk(directory.toStdString());
86
87 QTimer::singleShot(0, this, [this, thread, worker]()
88 {
89 ARMARX_INFO << "After other class";
90 _diskIOView->loadFinished();
91
92 thread->quit();
93 thread->wait();
94
95 worker->deleteLater();
96 thread->deleteLater();
97 });
98 });
99
100 thread->start();
101 }
102
103 void
105 {
106 TIMING_START(MemoryExport)
107
108 std::string status;
109 // only store selected Data
110 auto memoryData = _model->memoryDataCopy();
111 auto enabledMemories = _model->getEnabledMemories();
112 for (auto it = memoryData.begin(); it != memoryData.end();)
113 {
114 if (std::find(enabledMemories.begin(), enabledMemories.end(), it->first) ==
115 enabledMemories.end())
116 {
117 it = memoryData.erase(it);
118 }
119 else {
120 it++;
121 }
122 }
123
124 std::vector<wm::Memory> memoryDataVec = simox::alg::get_values(memoryData);
125 storeOnDisk(directory, memoryDataVec, &status);
126
127 _statusLabel->setText(QString::fromStdString(status));
128 TIMING_END_STREAM(MemoryExport, ARMARX_VERBOSE)
129 }
130
131 static const std::string&
132 handleSingular(int num, const std::string& singular, const std::string& plural)
133 {
134 return num == 1 ? singular : plural;
135 }
136
137 void
138 DiskIOController::storeOnDisk(QString directory,
139 const std::vector<wm::Memory> memoryData,
140 std::string* outStatus)
141 {
142 std::filesystem::path path(directory.toUtf8().constData());
143 ARMARX_CHECK_POSITIVE(path.string().size()); // An empty path indicates an error.
144
145 std::stringstream status;
146 if (std::filesystem::is_regular_file(path))
147 {
148 status << "Could not export memories contents to " << path
149 << ": Cannot overwrite existing file.";
150 }
151 else
152 {
153 int numStored = 0;
154 for (const auto& data : memoryData)
155 {
156 std::string name = data.id().memoryName;
157 if (std::filesystem::is_regular_file(path / name))
158 {
159 status << "Could not export memory '" << name << "' to " << path
160 << ": Cannot overwrite existing file.\n";
161 }
162 else
163 {
164 std::string defaultIdent = "DefaultDisk";
165 std::string exportName = "MemoryExport";
166 std::shared_ptr<armem::server::ltm::persistence::DiskPersistence>
167 diskPersistence =
168 std::make_shared<armem::server::ltm::persistence::DiskPersistence>(
169 defaultIdent, exportName, path);
170
171 armem::server::ltm::Memory memory(exportName, name);
172
173 if (memory.getPersistenceStrategy())
174 {
175 memory.getPersistenceStrategy()->addStrategy(diskPersistence);
176 }
177 else
178 {
179 std::shared_ptr<
181 redundantPersistence = std::make_shared<
183
184 memory.setPersistenceStrategy(redundantPersistence);
185 memory.getPersistenceStrategy()->addStrategy(diskPersistence);
186 }
187
188 memory.directlyStore(data);
189
190 numStored++;
191 }
192 }
193 status << "Exported " << numStored << " "
194 << handleSingular(numStored, "memory", "memories") << " to " << path << ".";
195 }
196
197 if (outStatus)
198 {
199 *outStatus = status.str();
200 }
201 }
202
203 void
204 DiskIOController::loadFromDisk(const std::string& directory)
205 {
206 ARMARX_INFO << "Loading in separate class";
207
208 std::filesystem::path path(directory);
209
210 std::map<std::filesystem::path, wm::Memory> memoryData;
211
212 if (not std::filesystem::is_directory(path))
213 {
214 ARMARX_WARNING << "You are trying to load data from a path which does not exist";
215 return;
216 }
217
218 ARMARX_INFO << "Starting to load from disk";
219
220 // Find out whether this is a single memory or a collection of memories by searching
221 // for a data.aron.* or metadata.aron.* file at depth 5 (if 6 then it is collection of memories)
222 bool isSingleMemory = false;
223 for (auto i = std::filesystem::recursive_directory_iterator(path);
224 i != std::filesystem::recursive_directory_iterator();
225 ++i)
226 {
227 if (i.depth() >
229 {
230 // In case we do not find any memory data in here
232 << "You are trying to import a directory which does not contain memory data";
233 return;
234 }
235
236 auto& dir = *i;
237
238 // if one matches it is enough to check
239 if (std::filesystem::is_regular_file(dir.path()) &&
240 simox::alg::starts_with(dir.path().filename(), "data.aron"))
241 {
242 isSingleMemory =
243 (i.depth() ==
245 break;
246 }
247 }
248
249 int numLoaded = 0;
250 auto loadMemory = [&](const std::filesystem::path& p)
251 {
252 if (std::filesystem::is_directory(p))
253 {
254 std::string defaultIdent = "DefaultDisk";
255 std::string exportFolderName = p.parent_path().filename();
256 ARMARX_INFO << "Name of the folder trying to import: " << exportFolderName;
257 std::string exportName = exportFolderName;
258 std::shared_ptr<armem::server::ltm::persistence::DiskPersistence> diskPersistence =
259 std::make_shared<armem::server::ltm::persistence::DiskPersistence>(
260 defaultIdent, exportName, p.parent_path().parent_path());
261
262 armem::server::ltm::Memory ltm(exportName, p.filename());
263 if (ltm.getPersistenceStrategy())
264 {
265 ltm.getPersistenceStrategy()->addStrategy(diskPersistence);
266 }
267 else
268 {
269 std::shared_ptr<armem::server::ltm::persistence::RedundantPersistenceStrategy>
270 redundantPersistence = std::make_shared<
271 armem::server::ltm::persistence::RedundantPersistenceStrategy>();
272
273 ltm.setPersistenceStrategy(redundantPersistence);
274 ltm.getPersistenceStrategy()->addStrategy(diskPersistence);
275 }
276
277 armem::wm::Memory memory =
278 ltm.loadAllAndResolve(); // load list of references and load data
279 memoryData[p] = std::move(memory);
280
281 numLoaded++;
282 }
283 };
284
285 if (isSingleMemory)
286 {
287 loadMemory(path);
288 }
289 else
290 {
291 // we have to load multiple memories (each subfolder)
292 for (const auto& dir : std::filesystem::directory_iterator(path))
293 {
294 loadMemory(dir.path());
295 }
296 }
297
298 for (auto& [path, memory] : memoryData)
299 {
300 std::string name = memory.id().memoryName;
301
302 // TODO: check if equivalent!!
303 if (auto writers = _model->memoryWritersCopy(); writers.count(name) > 0)
304 {
305 // we need to merge the current memory with the one from the disk.
306 // Please note that this may ignores coresegments, if not already existent
307 try
308 {
309 auto commit = armem::toCommit(memory);
310 writers.at(name).commit(commit);
311 }
312 catch (const Ice::Exception& ex)
313 {
314 ARMARX_ERROR << "Commit failed due to Ice Exception: " << ex.what();
315 }
316 catch (const std::exception& ex)
317 {
318 ARMARX_ERROR << "Commit failed: " << ex.what();
319 }
320 }
321 else
322 {
323 ARMARX_INFO << "No memory with name '" << name
324 << "' available for commit. Create new virtual memory.";
325
326 // Please note: Here we assume that a memory server with the same name does not exist.
327 // I think this assumption is ok, since nobody should use filepaths as memory name.
328 // Nonetheless, we did not restrict the user to do so...
329 std::string virtualMemoryName = name; // + " (at " + path.string() + ")";
330 memory.id().memoryName = virtualMemoryName;
331 memoryData[virtualMemoryName] = std::move(memory);
332 }
333 }
334 }
335}
336
DiskIOController(MemoryViewerUIContext &uiContext, std::shared_ptr< armem::gui::model::MemoryViewerModel > model)
void requestedStoreOnDisk(QString directory)
void requestedLoadFromDisk(QString directory)
A memory storing data on the hard drive and in mongodb (needs 'armarx memory start' to start the mong...
Definition Memory.h:24
Basically the option to use multiple ltm sinks as source or target.
void addStrategy(std::shared_ptr< MemoryPersistenceStrategy > strategy)
Add another strategy to the end.
Brief description of class memory.
Definition memory.h:39
#define ARMARX_CHECK_POSITIVE(number)
This macro evaluates whether number is positive (> 0) and if it turns out to be false it will throw a...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
#define TIMING_START(name)
Helper macro to do timing tests.
Definition TimeUtil.h:289
#define TIMING_END_STREAM(name, os)
Prints duration.
Definition TimeUtil.h:310
Commit toCommit(const ContainerT &container)
Definition operations.h:23