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