KnownGraspProviderSegment.cpp
Go to the documentation of this file.
2
3#include <cstring>
4#include <filesystem>
5#include <map>
6#include <optional>
7#include <string>
8#include <vector>
9
10#include <sys/inotify.h>
11#include <sys/types.h>
12#include <unistd.h>
13
14#include <SimoxUtility/algorithm/string/string_tools.h>
15#include <VirtualRobot/Grasping/Grasp.h> // IWYU pragma: keep
16#include <VirtualRobot/Grasping/GraspSet.h> // IWYU pragma: keep
17#include <VirtualRobot/ManipulationObject.h> // IWYU pragma: keep
18#include <VirtualRobot/VirtualRobot.h>
19#include <VirtualRobot/XML/ObjectIO.h>
20
27
34#include <RobotAPI/libraries/armem_grasping/aron/KnownGraspCandidate.aron.generated.h>
35
36#include <linux/limits.h>
37
39{
40
46
47 void
49 {
50 Base::init();
51
52 loadMemory();
53
54 installFileWatcher();
55 }
56
57 std::optional<arondto::KnownGraspInfo>
58 KnownGraspProviderSegment::knownGraspInfoFromObjectInfo(const ObjectInfo& info)
59 {
60 return knownGraspInfoFromFile(GraspFileInfo::FromObjectInfo(info));
61 }
62
63 std::optional<arondto::KnownGraspInfo>
64 KnownGraspProviderSegment::knownGraspInfoFromFile(const GraspFileInfo& graspFileInfo)
65 {
66
67 const std::filesystem::path graspFilePath = graspFileInfo.fileLocInfo.absolutePath;
68 if (not std::filesystem::is_regular_file(graspFilePath))
69 {
70 return std::nullopt; // file does not exist
71 }
72
73 ARMARX_VERBOSE << "Loading `" << graspFilePath << "`.";
74 try
75 {
76 const std::string objectClassName = graspFileInfo.objectId.className();
77
78 // we only load the grasp information, no visual and collision models are needed
79 auto manipulationObject = VirtualRobot::ObjectIO::loadManipulationObject(
80 graspFilePath,
81 VirtualRobot::ObjectIO::ObjectDescription::eStructure);
82
83 if (manipulationObject == nullptr)
84 {
85 ARMARX_WARNING << "Invalid file content: " << graspFilePath;
86 return std::nullopt;
87 }
88
89 arondto::KnownGraspInfo ret;
90 ret.correspondingObject.memoryName = "Object";
91 ret.correspondingObject.coreSegmentName = "Class";
92 ret.correspondingObject.providerSegmentName = "PriorKnowledgeData";
93 ret.correspondingObject.entityName = graspFileInfo.objectId.str();
94 ret.xml.package = graspFileInfo.fileLocInfo.package;
95 ret.xml.path = graspFileInfo.fileLocInfo.relativePath;
96
97 for (const VirtualRobot::GraspSetPtr& graspSet : manipulationObject->getAllGraspSets())
98 {
99 ARMARX_CHECK_NOT_NULL(graspSet);
100
101 arondto::KnownGraspSet retGraspSet;
102
103 retGraspSet.name = graspSet->getName();
104
105 retGraspSet.robot = simox::alg::split(graspSet->getRobotType(), " ").front();
106
107 retGraspSet.endeffector = graspSet->getEndEffector();
108
109 VirtualRobot::GraspSetPtr preGraspSet = [&graspSet]()
110 {
111 std::vector<VirtualRobot::GraspPtr> preGrasps;
112 for (const auto& grasp : graspSet->getGrasps())
113 {
114 // check if grasp is a prepose by checking if grasp name ends with "_Prepose"
115 if (simox::alg::ends_with(grasp->getName(), PREPOSE_SUFFIX))
116 {
117 preGrasps.emplace_back(grasp);
118 }
119
120 // everything else is assumed to be a grasp
121 }
122
123 // create a new grasp set with the preposes
124 VirtualRobot::GraspSetPtr preGraspSet = graspSet->clone();
125 preGraspSet->removeAllGrasps(); // just keep the EEF info etc
126 for (const auto& preGrasp : preGrasps)
127 {
128 preGraspSet->addGrasp(preGrasp);
129 }
130
131 return preGraspSet;
132 }();
133
134
135 ARMARX_DEBUG << VAROUT(preGraspSet->getSize());
136
137 // remove all preposes from the grasp set
138 for (const auto& preGrasp : preGraspSet->getGrasps())
139 {
140 graspSet->removeGrasp(preGrasp);
141 }
142
143
144 for (const VirtualRobot::GraspPtr& grasp : graspSet->getGrasps())
145 {
147
148 arondto::KnownGrasp retGrasp;
149
150 retGrasp.name = grasp->getName();
151 retGrasp.quality = grasp->getQuality();
152 retGrasp.creator = grasp->getCreationMethod();
153 retGrasp.pose = grasp->getTransformation();
154 retGrasp.prepose.reset();
155
156 // check if grasp has a prepose by checking if grasp name ends with "_Prepose"
157 {
158 const std::string prePoseName = retGrasp.name + PREPOSE_SUFFIX;
159
160 ARMARX_DEBUG << "Checking for prepose '" << prePoseName << "' ...";
161
162 if (preGraspSet->hasGrasp(prePoseName))
163 {
164 retGrasp.prepose =
165 preGraspSet->getGrasp(prePoseName)->getTransformation();
166
167 // remove the prepose from the set as it found its match
168 preGraspSet->removeGrasp(preGraspSet->getGrasp(prePoseName));
169
170
171 ARMARX_DEBUG << "Found prepose `" + prePoseName + "` for grasp '"
172 << retGrasp.name << "' in set '" << retGraspSet.name
173 << "' for obj '" << objectClassName << "' with pose \n"
174 << retGrasp.prepose.value();
175 }
176 }
177
178 // check if grasp has a prepose for a grasp with a specific name, e.g., "XY_Grasp" (GRASP_OPTIONAL_SUFFIX)
179 if ( // not retGrasp.prepose.has_value() and
180 simox::alg::ends_with(retGrasp.name, GRASP_OPTIONAL_SUFFIX))
181 {
182 const std::string prePoseName =
183 retGrasp.name.substr(0,
184 retGrasp.name.size()
185 - std::strlen(GRASP_OPTIONAL_SUFFIX))
186 + PREPOSE_SUFFIX;
187
188 ARMARX_DEBUG << "Checking for prepose '" << prePoseName << "' ...";
189
190 if (preGraspSet->hasGrasp(prePoseName))
191 {
192 retGrasp.prepose =
193 preGraspSet->getGrasp(prePoseName)->getTransformation();
194
195 // remove the prepose from the set as it found its match
196 preGraspSet->removeGrasp(preGraspSet->getGrasp(prePoseName));
197
198 ARMARX_DEBUG << "Found prepose `" + prePoseName + "` for grasp '"
199 << retGrasp.name << "' in set '" << retGraspSet.name
200 << "' for obj '" << objectClassName << "' with pose \n"
201 << retGrasp.prepose.value();
202 }
203 }
204
205 ARMARX_DEBUG << "Found grasp '" << retGrasp.name << "' in set '"
206 << retGraspSet.name << "' for obj '" << objectClassName
207 << "' with pose \n"
208 << retGrasp.pose;
209
210 retGraspSet.grasps.push_back(retGrasp);
211 }
212
213 // Now, check if there are any preposes left in the set. This should not have happened.
214 if (preGraspSet->getSize() > 0)
215 {
216 ARMARX_WARNING << "Found " << preGraspSet->getSize()
217 << " preposes in the grasp set '" << retGraspSet.name
218 << "' for obj '" << objectClassName
219 << "' that do not have a corresponding grasp!";
220 for (const auto& preGrasp : preGraspSet->getGrasps())
221 {
222 ARMARX_WARNING << "Prepose '" << preGrasp->getName();
223 }
224 }
225
226 ARMARX_CHECK(ret.graspSets.count(retGraspSet.robot + "/" + retGraspSet.name) == 0)
227 << "The grasp set `" << retGraspSet.robot + "/" + retGraspSet.name
228 << "` was defined twice!";
229
230 ret.graspSets[retGraspSet.robot + "/" + retGraspSet.name] = retGraspSet;
231 }
232 return ret;
233 }
234 catch (...)
235 {
236 ARMARX_WARNING << graspFilePath << " is not a manipulation object!"
238 return std::nullopt;
239 }
240 }
241
242 void
243 KnownGraspProviderSegment::loadMemory()
244 {
245 // load data from prior knowledge
246 ObjectFinder objectFinder;
247 const auto now = armem::Time::Now();
248
249 const bool checkPaths = false;
250 std::vector<ObjectInfo> infos = objectFinder.findAllObjects(checkPaths);
251
252 const MemoryID providerID =
253 segmentPtr->id().withProviderSegmentName(objectFinder.getPackageName());
254 ARMARX_INFO << "Checking up to " << infos.size() << " object classes from '"
255 << objectFinder.getPackageName() << "' ...";
256
257 Commit commit;
258 for (ObjectInfo& info : infos)
259 {
260 info.setLogError(false);
261 if (auto knownGraspCandidate = knownGraspInfoFromObjectInfo(info); knownGraspCandidate)
262 {
263 EntityUpdate& update = commit.add();
264 update.entityID = providerID.withEntityName(info.id().str());
265 update.entityID.timestamp = update.arrivedTime = update.referencedTime =
266 update.sentTime = now;
267
268 update.instancesData = {knownGraspCandidate->toAron()};
269
270 ARMARX_DEBUG << VAROUT(knownGraspCandidate->graspSets.size());
271 for (const auto& gs : knownGraspCandidate->graspSets)
272 {
273 ARMARX_DEBUG << VAROUT(gs.second.grasps.size());
274 for (const auto& grasp : gs.second.grasps)
275 {
276 ARMARX_DEBUG << VAROUT(grasp.name);
277 }
278 }
279 }
280 }
281 ARMARX_INFO << "Loaded " << commit.updates.size()
282 << " grasp candidates from object classes from '"
283 << objectFinder.getPackageName() << "'.";
284 auto result = iceMemory.commitLocking(commit);
285 if (!result.allSuccess())
286 {
287 ARMARX_WARNING << "Got errors for commit: " << result.allErrorMessages();
288 }
289 }
290
291 void
292 KnownGraspProviderSegment::installFileWatcher()
293 {
294 // load data from prior knowledge
295 const ObjectFinder objectFinder;
296 const std::vector<ObjectInfo> infos = objectFinder.findAllObjects(false);
297
298 const bool autoReloadSceneSnapshotsOnFileChange = true; // FIXME param
299
300 if (autoReloadSceneSnapshotsOnFileChange)
301 {
302 int inotifyFd;
303
304 // init inotify
305 {
306
307 inotifyFd = inotify_init();
308 if (inotifyFd == -1)
309 {
310 ARMARX_WARNING << "inotify_init failed";
311 }
312 }
313
314 std::map<int, GraspFileInfo> wds;
315
316 // set up inotify watchers
317 for (const auto& info : infos)
318 {
319 const auto graspFileInfo = GraspFileInfo::FromObjectInfo(info);
320
321 if (std::filesystem::exists(graspFileInfo.fileLocInfo.absolutePath))
322 {
323 auto wd = inotify_add_watch(inotifyFd,
324 graspFileInfo.fileLocInfo.absolutePath.c_str(),
325 IN_MODIFY);
326 if (wd == -1)
327 {
328 ARMARX_WARNING << "inotify_add_watch for grasp file: "
329 << graspFileInfo.fileLocInfo.absolutePath << "` failed.";
330 }
331
332 ARMARX_VERBOSE << "inotify_add_watch for grasp file: "
333 << graspFileInfo.fileLocInfo.absolutePath << "` added.";
334 wds.emplace(wd, graspFileInfo);
335 }
336 }
337
338 ARMARX_INFO << "Set up " << wds.size() << " inotify events to watch for file changes.";
339
340 fileWatcherTask = new SimpleRunningTask<>(
341 [this, inotifyFd, wds]()
342 {
343 ObjectFinder objectFinder;
344
345 constexpr std::size_t BUF_LEN = (10 * (sizeof(inotify_event) + NAME_MAX + 1));
346 char buf[BUF_LEN];
347
348 for (;;)
349 {
350 ssize_t numRead;
351
352 numRead = read(inotifyFd, buf, BUF_LEN);
353 if (numRead == 0)
354 {
355 ARMARX_WARNING << "read() from inotify fd returned 0!";
356 }
357
358 if (numRead == -1)
359 {
360 ARMARX_VERBOSE << "read";
361 }
362
363 ARMARX_DEBUG << VAROUT(numRead);
364
365 for (char* p = buf; p < buf + numRead;)
366 {
367 auto* event = reinterpret_cast<inotify_event*>(p);
368
369 const auto& graspFileInfo = wds.at(event->wd);
370 ARMARX_VERBOSE << "File changed: "
371 << VAROUT(graspFileInfo.fileLocInfo.absolutePath);
372
373 ARMARX_INFO << "Reloading file for object `" << graspFileInfo.objectId
374 << "`.";
375
376 p += sizeof(struct inotify_event) + event->len;
377
378 const bool lockMemory = true;
379
380 // read updated grasp file and store it in the memory
381 {
382 const MemoryID providerID =
383 segmentPtr->id().withProviderSegmentName(
384 objectFinder.getPackageName());
385
386 // create commit from new file content
387 Commit commit;
388 {
389 if (auto knownGraspCandidate =
390 knownGraspInfoFromFile(graspFileInfo);
391 knownGraspCandidate)
392 {
393 EntityUpdate& update = commit.add();
394 update.entityID =
395 providerID.withEntityName(graspFileInfo.objectId.str());
396 update.entityID.timestamp = update.arrivedTime =
397 update.referencedTime = update.sentTime =
399
400 update.instancesData = {knownGraspCandidate->toAron()};
401
403 << VAROUT(knownGraspCandidate->graspSets.size());
404 for (const auto& gs : knownGraspCandidate->graspSets)
405 {
406 ARMARX_DEBUG << VAROUT(gs.second.grasps.size());
407 for (const auto& grasp : gs.second.grasps)
408 {
409 ARMARX_DEBUG << VAROUT(grasp.name);
410 }
411 }
412 }
413 }
414
415 // now store commit in memory
416 if (not commit.updates.empty())
417 {
418 ARMARX_VERBOSE << "Storing update in memory";
419
420 const auto result = iceMemory.commitLocking(commit);
421 if (not result.allSuccess())
422 {
423 ARMARX_WARNING << "Got errors for commit: "
424 << result.allErrorMessages();
425 }
426 }
427 }
428 }
429 }
430 });
431
432 fileWatcherTask->start();
433 }
434 }
435} // namespace armarx::armem::grasping::segment
#define VAROUT(x)
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition ObjectID.cpp:60
Accessor for the object files.
Definition ObjectInfo.h:37
void setLogError(bool enabled)
ObjectID id() const
Return "dataset/name".
KnownGraspProviderSegment(armem::server::MemoryToIceAdapter &iceMemory)
Helps connecting a Memory server to the Ice interface.
static DateTime Now()
Definition DateTime.cpp:51
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#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
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition mongodb.cpp:68
const armem::MemoryID MemoryID
Definition memory_ids.cpp:6
void read(auto &eigen, auto *table)
std::string GetHandledExceptionString()
SimpleRunningTask(Ts...) -> SimpleRunningTask< std::function< void(void)> >