PersonInstanceReader.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <iterator>
5#include <string>
6#include <utility>
7#include <vector>
8
9#include <range/v3/range/conversion.hpp>
10#include <range/v3/to_container.hpp>
11#include <range/v3/view/transform.hpp>
12
20
29
31#include <VisionX/libraries/armem_human/aron/FaceRecognition.aron.generated.h>
32#include <VisionX/libraries/armem_human/aron/HumanPose.aron.generated.h>
33#include <VisionX/libraries/armem_human/aron/Person.aron.generated.h>
34#include <VisionX/libraries/armem_human/aron/PersonInstance.aron.generated.h>
36
38{
40
41 armarx::armem::client::query::Builder
43 {
45
47 qb.coreSegments().withName(properties().coreSegmentName);
48
51 {
52 if (not query.providerName.empty())
53 {
54 return coreSegmentQuery.providerSegments().withName(query.providerName);
55 }
56 return coreSegmentQuery.providerSegments().all();
57 }();
58
59 providerQuery.entities().all().snapshots().beforeOrAtTime(query.timestamp);
60
61 return qb;
62 }
63
64 std::string
66 {
67 return "mem.human.person";
68 }
69
72 {
73 return {.memoryName = "Human", .coreSegmentName = "PersonInstance"};
74 }
75
76 std::vector<PersonInstance>
77 asPersonInstances(const wm::ProviderSegment& providerSegment,
78 const DateTime& timestamp,
79 const Duration& maxAge)
80 {
82
83 // ARMARX_CHECK(not providerSegment.empty()) << "No entities";
84 // ARMARX_CHECK(providerSegment.size() == 1) << "There should be only one entity!";
85
86 std::vector<PersonInstance> personInstances;
87 providerSegment.forEachEntity(
88 [&](const wm::Entity& entity)
89 {
91
92 const auto& entitySnapshot = entity.getLatestSnapshot();
93 // Not clear yet why, but it seems that this sometimes happens (not frequently).
94 ARMARX_CHECK(not entitySnapshot.empty()) << "No entity snapshot instances";
95
96 entitySnapshot.forEachInstance(
97 [&personInstances, &timestamp, &maxAge](
98 const armarx::armem::wm::EntityInstance& entityInstance)
99 {
101
102 const core::time::Duration dtToNow =
103 timestamp - entityInstance.metadata().referencedTime;
104
105 if (dtToNow < maxAge and dtToNow.isPositive())
106 {
107 const auto aronDto =
109 ARMARX_CHECK(aronDto) << "Failed casting to PersonInstance";
110
111 PersonInstance personInstance;
112 fromAron(*aronDto, personInstance);
113
114 personInstances.push_back(personInstance);
115 }
116 else
117 {
118 ARMARX_DEBUG << "Skipping person instance "
119 << QUOTED(entityInstance.id()) << " that is too old ("
120 << dtToNow << " > " << maxAge << ")";
121 }
122 });
123 });
124
125 // when no humans are in the scene, this is empty
126 //ARMARX_CHECK_NOT_EMPTY(humanPoses);
127
128 ARMARX_DEBUG << "Found " << personInstances.size() << " person instances.";
129
130 return personInstances;
131 }
132
133 PersonInstanceReader::Result
135 {
137 const auto qb = buildQuery(query);
138
139 ARMARX_DEBUG << "[MappingDataReader] query ... ";
140
141 const armem::client::QueryResult qResult = memoryReader().query(qb.buildQueryInput());
142
143 ARMARX_DEBUG << "[MappingDataReader] result: " << qResult;
144
145 if (not qResult.success)
146 {
147 ARMARX_WARNING << "Failed to query data from memory: " << qResult.errorMessage;
148 return {.personInstances = {},
149 .status = Result::Status::Error,
150 .errorMessage = qResult.errorMessage};
151 }
152
154 const auto coreSegment = qResult.memory.getCoreSegment(properties().coreSegmentName);
155
156 if (query.providerName.empty())
157 {
159
160 std::vector<PersonInstance> allPersonInstances;
161
162 coreSegment.forEachProviderSegment(
163 [&allPersonInstances, &query](const auto& providerSegment)
164 {
165 const std::vector<PersonInstance> personInstances =
166 asPersonInstances(providerSegment, query.timestamp, query.maxAge);
167 if (personInstances.size() > 0)
168 {
169 ARMARX_INFO << deactivateSpam(10) << "Found " << personInstances.size()
170 << " person instances in provider segment `"
171 << providerSegment.name() << "` that are not too old.";
172 }
173 else
174 {
175 ARMARX_DEBUG << deactivateSpam(5) << "Found " << personInstances.size()
176 << " person instances in provider segment `"
177 << providerSegment.name() << "` that are not too old.";
178 }
179
180 std::copy(personInstances.begin(),
181 personInstances.end(),
182 std::back_inserter(allPersonInstances));
183 });
184
185 if (allPersonInstances.empty())
186 {
187 // ARMARX_WARNING << "No entities.";
188 return {.personInstances = {},
189 .status = Result::Status::NoData,
190 .errorMessage = "No entities"};
191 }
192
193 return Result{.personInstances = allPersonInstances, .status = Result::Status::Success};
194 }
195
197
198 // -> provider segment name is set
199 if (not coreSegment.hasProviderSegment(query.providerName))
200 {
201 ARMARX_INFO << deactivateSpam(5) << "Provider segment `" << query.providerName
202 << "` does not exist (yet).";
203 return {.personInstances = {}, .status = Result::Status::NoData};
204 }
205
207
208 const wm::ProviderSegment& providerSegment =
209 coreSegment.getProviderSegment(query.providerName);
210
211 if (providerSegment.empty())
212 {
213 ARMARX_WARNING << "No entities.";
214 return {.personInstances = {},
215 .status = Result::Status::NoData,
216 .errorMessage = "No entities"};
217 }
218
219 try
220 {
222
223 const auto personInstances =
224 asPersonInstances(providerSegment, query.timestamp, query.maxAge);
225 return Result{.personInstances = personInstances, .status = Result::Status::Success};
226 }
227 catch (...)
228 {
229 return Result{.status = Result::Status::Error,
230 .errorMessage = GetHandledExceptionString()};
231 }
232 }
233
236 {
237 ARMARX_DEBUG << "before query";
238 const auto result = query(queryResolved);
239 ARMARX_DEBUG << "after query";
240
241 if (not result)
242 {
244 << "Query for person instances failed: " << result.errorMessage;
245 return ResultResolved{.personInstances = {},
246 .status = result.status,
247 .errorMessage = result.errorMessage};
248 }
249
250 const auto isFullySpecified = [](const armem::MemoryID& memoryId)
251 {
252 return memoryId.hasMemoryName() and memoryId.hasCoreSegmentName() and
253 memoryId.hasProviderSegmentName() and memoryId.hasEntityName() and
254 memoryId.hasTimestamp() and memoryId.hasInstanceIndex();
255 };
256
257 const auto isEntitySpecified = [&](const armem::MemoryID& memoryId)
258 {
259 return memoryId.hasMemoryName() and memoryId.hasCoreSegmentName() and
260 memoryId.hasProviderSegmentName() and memoryId.hasEntityName();
261 };
262
263
264 // to reduce the number of network calls, we query all relevant memory elements at once
265 const auto [allMemoryIds, allLatestMemoryIds] = [&]
266 {
267 std::vector<MemoryID> allLatestMemoryIds;
268 std::vector<MemoryID> allMemoryIds;
269 for (const auto& personInstance : result.personInstances)
270 {
271 if (queryResolved.resolveFaceDetection)
272 {
273 if (isFullySpecified(personInstance.faceRecognitionID))
274 {
275 allMemoryIds.push_back(personInstance.faceRecognitionID);
276 }
277 else if (isEntitySpecified(personInstance.faceRecognitionID))
278 {
279 allLatestMemoryIds.push_back(personInstance.faceRecognitionID);
280 }
281 else
282 {
283 ARMARX_DEBUG << "Face recognition ID is not even entity specified: "
284 << QUOTED(personInstance.faceRecognitionID);
285 }
286 }
287
288 if (queryResolved.resolveHumanPose)
289 {
290 if (isFullySpecified(personInstance.poseID))
291 {
292 allMemoryIds.push_back(personInstance.poseID);
293 }
294 else if (isEntitySpecified(personInstance.poseID))
295 {
296 allLatestMemoryIds.push_back(personInstance.poseID);
297 }
298 else
299 {
300 ARMARX_DEBUG << "Human pose ID is not even entity specified: "
301 << QUOTED(personInstance.poseID);
302 }
303 }
304
305 if (queryResolved.resolveProfile)
306 {
307 if (isFullySpecified(personInstance.profileID))
308 {
309 allMemoryIds.push_back(personInstance.profileID);
310 }
311 else if (isEntitySpecified(personInstance.profileID))
312 {
313 allLatestMemoryIds.push_back(personInstance.profileID);
314 }
315 else
316 {
317 ARMARX_DEBUG << "Profile ID is not even entity specified: "
318 << QUOTED(personInstance.profileID);
319 }
320 }
321 }
322 return std::pair{allMemoryIds, allLatestMemoryIds};
323 }();
324
325 ARMARX_DEBUG << "Need to query " << allMemoryIds.size() << " memory elements";
326
327 ARMARX_DEBUG << "Querying memory ids";
328 auto queryResult = memoryReader().queryMemoryIDs(allMemoryIds);
329
330 // If the instances are not fully specified, we need to query the latest snapshots instead
331 for (const auto& memId : allLatestMemoryIds)
332 {
333 const auto qRes = memoryReader().getLatestSnapshotsIn(memId);
334 queryResult.memory.append(qRes.memory);
335 }
336
337 ARMARX_DEBUG << "Querying memory ids done";
338
339 const auto resolve =
340 [&queryResolved, &queryResult, &isEntitySpecified](
341 const PersonInstance& personInstance) -> MemoryResolvedPersonInstance
342 {
343 MemoryResolvedPersonInstance resolvedInstance;
344
345 // assign the data based on the memory result
346 {
347 if (queryResolved.resolveFaceDetection)
348 {
349 ARMARX_DEBUG << "Face recognition: " << personInstance.faceRecognitionID;
350 const auto& memoryID = personInstance.faceRecognitionID;
351
352 if (isEntitySpecified(memoryID) and queryResult.memory.hasEntity(memoryID))
353 {
354 ARMARX_DEBUG << "Face recognition.";
355 const auto& entity = queryResult.memory.getEntity(memoryID);
356 const auto instance = entity.getLatestInstance();
357
358
359 const auto data =
360 instance.dataAs<armarx::human::arondto::FaceRecognition>();
361
362 auto& faceRecognition = resolvedInstance.faceRecognition.emplace();
363 fromAron(data, faceRecognition);
364 }
365 else
366 {
368 << "Requested face detection but failed to resolve "
369 << QUOTED(memoryID);
370 }
371 }
372
373 if (queryResolved.resolveHumanPose)
374 {
375 ARMARX_DEBUG << "Human pose id: " << personInstance.poseID;
376 const auto& memoryID = personInstance.poseID;
377
378 if (isEntitySpecified(memoryID) and queryResult.memory.hasEntity(memoryID))
379 {
380 const auto& entity = queryResult.memory.getEntity(memoryID);
381 const auto instance = entity.getLatestInstance();
382
383
384 const auto data = instance.dataAs<armarx::human::arondto::HumanPose>();
385
386 auto& humanPose = resolvedInstance.humanPose.emplace();
387 fromAron(data, humanPose);
388 }
389 else
390 {
392 << "Requested human pose but failed to resolve "
393 << QUOTED(memoryID);
394 }
395 }
396
397 if (queryResolved.resolveProfile)
398 {
399 ARMARX_INFO << deactivateSpam(10) << "Profile: " << personInstance.profileID;
400 const auto& memoryID = personInstance.profileID;
401
402 if (isEntitySpecified(memoryID) and queryResult.memory.hasEntity(memoryID))
403 {
404 ARMARX_DEBUG << "Profile";
405 const auto& entity = queryResult.memory.getEntity(memoryID);
406 const auto instance = entity.getLatestInstance();
407
408 // const auto& instance = queryResult.memory.getInstance(memoryID);
409
410 const auto data = instance.dataAs<armarx::human::arondto::Person>();
411
412 auto& profile = resolvedInstance.profile.emplace();
413 fromAron(data, profile);
414 }
415 else
416 {
418 << "Requested profile but failed to resolve "
419 << QUOTED(memoryID);
420 }
421 }
422 }
423
424 resolvedInstance.globalPose = personInstance.globalPose;
425 resolvedInstance.personInstance = personInstance;
426
427 return resolvedInstance;
428 };
429
430
432 resultResolved.personInstances =
433 result.personInstances | ranges::views::transform(resolve) | ranges::to_vector;
434 resultResolved.errorMessage = "";
435 resultResolved.status = ResultResolved::Status::Success; // FIXME refine
436
437 return resultResolved;
438 }
439
440
441} // namespace armarx::armem::human::client
std::string timestamp()
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define QUOTED(x)
void append(const OtherDerivedT &other)
Merge another memory into this one.
Definition MemoryBase.h:363
CoreSegmentT & getCoreSegment(const std::string &name)
Definition MemoryBase.h:134
QueryResult query(const QueryInput &input) const
Perform a query on the WM.
Definition Reader.cpp:119
QueryResult getLatestSnapshotsIn(const MemoryID &id, armem::query::DataMode dataMode=armem::query::DataMode::WithData) const
Get the latest snapshots under the given memory ID.
Definition Reader.cpp:427
QueryResult queryMemoryIDs(const std::vector< MemoryID > &ids, armem::query::DataMode dataMode=armem::query::DataMode::WithData) const
Query a specific set of memory IDs.
Definition Reader.cpp:374
The query::Builder class provides a fluent-style specification of hierarchical queries.
Definition Builder.h:22
CoreSegmentSelector & coreSegments()
Start specifying core segments.
Definition Builder.cpp:42
CoreSegmentSelector & withName(const std::string &name) override
ProviderSegmentSelector & providerSegments()
Start specifying provider segments.
SnapshotSelector & snapshots()
Start specifying entity snapshots.
Definition selectors.cpp:92
ProviderSegmentSelector & withName(const std::string &name) override
EntitySelector & entities()
Start specifying entities.
ProviderSegmentSelector & all() override
SnapshotSelector & beforeOrAtTime(Time timestamp)
Definition selectors.cpp:73
const armem::client::Reader & memoryReader() const
ResultResolved queryResolved(const QueryResolved &query) const
::armarx::armem::client::query::Builder buildQuery(const Query &query) const
Client-side working entity instance.
Represents a point in time.
Definition DateTime.h:25
Represents a duration.
Definition Duration.h:17
bool isPositive() const
Tests whether the duration is positive (value in µs > 0).
Definition Duration.cpp:168
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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
std::vector< PersonInstance > asPersonInstances(const wm::ProviderSegment &providerSegment, const DateTime &timestamp, const Duration &maxAge)
void fromAron(const armarx::human::arondto::HumanPose &dto, HumanPose &bo)
armarx::core::time::Duration Duration
std::optional< AronClass > tryCast(const wm::EntityInstance &item)
Tries to cast a armem::EntityInstance to AronClass.
Definition util.h:45
std::string GetHandledExceptionString()
Time referencedTime
Time this instance refers to.
auto & getLatestSnapshot(int snapshotIndex=0)
Retrieve the latest entity snapshot.
Result of a QueryInput.
Definition Query.h:51
wm::Memory memory
The slice of the memory that matched the query.
Definition Query.h:58
std::optional< FaceRecognition > faceRecognition
Definition types.h:339
std::optional< armarx::armem::human::Person > profile
Definition types.h:338
#define ARMARX_TRACE
Definition trace.h:77